feat: add signal handling
This commit is contained in:
parent
45f5f447c0
commit
11509109be
5 changed files with 123 additions and 47 deletions
|
|
@ -17,6 +17,7 @@ thiserror = "1.0"
|
||||||
enum-display-derive = "0.1"
|
enum-display-derive = "0.1"
|
||||||
queues = "1.1.0"
|
queues = "1.1.0"
|
||||||
duration-str = { version = "0.5.1", features = ["serde"] }
|
duration-str = { version = "0.5.1", features = ["serde"] }
|
||||||
|
signal-hook = "0.3.17"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
alsa = "0.7.0"
|
alsa = "0.7.0"
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,11 @@ use cli::Cli;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let c = Cli::parse();
|
let c = Cli::parse();
|
||||||
match run_file(&c.map_file) {
|
loop {
|
||||||
Ok(_) => (),
|
match run_file(&c.map_file) {
|
||||||
Err(err) => println!("Error: {}", err)
|
Ok(_) => (),
|
||||||
|
Err(err) => println!("Error: {}", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,8 +198,10 @@ impl MidiInputAlsa {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input_internal<F, D>(&mut self, callback: F, userdata: D) -> Result<(), Error>
|
fn binary_input_handler<F, D>(&mut self, callback: F, userdata: D) -> Result<(), Error>
|
||||||
where F: Fn(Option<SystemTime>, &[u8], &mut D) + Send {
|
where
|
||||||
|
F: Fn(&Self, Option<SystemTime>, &[u8], &mut D) -> Result<(),Error> + Send
|
||||||
|
{
|
||||||
let decoder = MidiEvent::new(0)?;
|
let decoder = MidiEvent::new(0)?;
|
||||||
decoder.enable_running_status(false);
|
decoder.enable_running_status(false);
|
||||||
|
|
||||||
|
|
@ -212,7 +214,7 @@ impl MidiInputAlsa {
|
||||||
_ => SystemTime::now(),
|
_ => SystemTime::now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.alsa_input_handler(|_, mut ev, (message, buffer, continue_sysex, userdata)| {
|
self.alsa_input_handler(|s, mut ev, (message, buffer, continue_sysex, userdata)| {
|
||||||
if !*continue_sysex { message.clear() }
|
if !*continue_sysex { message.clear() }
|
||||||
|
|
||||||
let do_decode = match ev.get_type() {
|
let do_decode = match ev.get_type() {
|
||||||
|
|
@ -241,12 +243,57 @@ impl MidiInputAlsa {
|
||||||
if message.is_empty() || *continue_sysex { return Ok(false); }
|
if message.is_empty() || *continue_sysex { return Ok(false); }
|
||||||
|
|
||||||
let ts: Option<SystemTime> = ev.get_time().map(|v| ts+v);
|
let ts: Option<SystemTime> = ev.get_time().map(|v| ts+v);
|
||||||
(callback)(ts, message, userdata);
|
(callback)(s, ts, message, userdata)?;
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
, (message, buffer, continue_sysex, userdata))?;
|
, (message, buffer, continue_sysex, userdata))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn threaded_alsa_input<F, D>(&mut self, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(),Error>
|
||||||
|
where
|
||||||
|
F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error> + Send,
|
||||||
|
D: Send,
|
||||||
|
{
|
||||||
|
thread::scope( |sc| -> Result<(), Error> {
|
||||||
|
let stop_trigger = self.stop_trigger[1];
|
||||||
|
let t = sc.spawn(move || -> Result<(), Error> {
|
||||||
|
let userdata = userdata;
|
||||||
|
self.alsa_input_handler(callback, userdata)?;
|
||||||
|
ts.send(false).expect("unexpected send() error");
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
match rs.recv()? {
|
||||||
|
true => Self::signal_stop_input_internal(stop_trigger)?,
|
||||||
|
false => ()
|
||||||
|
};
|
||||||
|
t.join().expect("unexpected thread error")?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn threaded_handler<H, F, T, RF: ?Sized, D, R>(&mut self, handler: H, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(),Error>
|
||||||
|
where
|
||||||
|
H: Fn(&mut Self, F,D) -> Result<(), Error> + Send,
|
||||||
|
F: Fn(&Self, T, &RF, &mut D) -> Result<R,Error> + Send,
|
||||||
|
D: Send,
|
||||||
|
{
|
||||||
|
thread::scope( |sc| -> Result<(), Error> {
|
||||||
|
let stop_trigger = self.stop_trigger[1];
|
||||||
|
let t = sc.spawn(move || -> Result<(), Error> {
|
||||||
|
let userdata = userdata;
|
||||||
|
handler(self, callback, userdata)?;
|
||||||
|
ts.send(false).expect("unexpected send() error");
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
match rs.recv()? {
|
||||||
|
true => Self::signal_stop_input_internal(stop_trigger)?,
|
||||||
|
false => ()
|
||||||
|
};
|
||||||
|
t.join().expect("unexpected thread error")?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidiInput<Addr> for MidiInputAlsa {
|
impl MidiInput<Addr> for MidiInputAlsa {
|
||||||
|
|
@ -301,6 +348,7 @@ impl MidiInput<Addr> for MidiInputAlsa {
|
||||||
PortFilter::Name(s) => p.name.contains(s),
|
PortFilter::Name(s) => p.name.contains(s),
|
||||||
PortFilter::Regex(s) => s.is_match(&p.name),
|
PortFilter::Regex(s) => s.is_match(&p.name),
|
||||||
PortFilter::Addr(MidiAddrHandler::ALSA(s)) => p.addr == *s,
|
PortFilter::Addr(MidiAddrHandler::ALSA(s)) => p.addr == *s,
|
||||||
|
_ => panic!("unexpected error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -325,27 +373,26 @@ impl MidiInput<Addr> for MidiInputAlsa {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_events(&mut self, ts: mpsc::Sender<MidiPortHandler>) -> Result<(), Error> {
|
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPortHandler>>, (tss, rss): (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error> {
|
||||||
let ports = self.ports()?;
|
let ports = self.ports()?;
|
||||||
let port = self.filter_ports(ports, PortFilter::Name("System:Announce".to_string()));
|
let port = self.filter_ports(ports, PortFilter::Name("System:Announce".to_string()));
|
||||||
self.connect(&port[0].addr, "rmidimap-alsa-announce")?;
|
self.connect(&port[0].addr, "rmidimap-alsa-announce")?;
|
||||||
self.alsa_input_handler(|s, ev, _|{
|
self.threaded_alsa_input(move |s: &Self, ev: alsa::seq::Event, _| -> Result<bool, Error> {
|
||||||
// handle disconnect event on watched port
|
// handle disconnect event on watched port
|
||||||
match ev.get_type() {
|
match ev.get_type() {
|
||||||
// EventType::PortStart | EventType::ClientStart | EventType::PortExit | EventType::ClientExit => {
|
|
||||||
EventType::PortStart => {
|
EventType::PortStart => {
|
||||||
if let Some(a) = ev.get_data::<alsa::seq::Addr>() {
|
if let Some(a) = ev.get_data::<alsa::seq::Addr>() {
|
||||||
let p = s.ports()?;
|
let p = s.ports()?;
|
||||||
let pp = s.filter_ports(p, PortFilter::Addr( MidiAddrHandler::ALSA(a) ));
|
let pp = s.filter_ports(p, PortFilter::Addr( MidiAddrHandler::ALSA(a) ));
|
||||||
if !pp.is_empty() {
|
if !pp.is_empty() {
|
||||||
ts.send(MidiPortHandler::ALSA(pp[0].clone())).expect("unexpected send() error");
|
ts.send(Some(MidiPortHandler::ALSA(pp[0].clone()))).expect("unexpected send() error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
_ => Ok(false),
|
_ => Ok(false),
|
||||||
}
|
}
|
||||||
}, ())?;
|
}, (tss, rss), ())?;
|
||||||
self.close_internal();
|
self.close_internal();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -357,26 +404,16 @@ impl MidiInputHandler for MidiInputAlsa
|
||||||
Self::signal_stop_input_internal(self.stop_trigger[1])
|
Self::signal_stop_input_internal(self.stop_trigger[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input<F, D>(&mut self, callback: F, (rs, ts): (mpsc::Receiver<bool>, mpsc::Sender<bool>), userdata: D) -> Result<(), Error>
|
fn handle_input<F, D>(&mut self, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Fn(Option<SystemTime>, &[u8], &mut D) + Send,
|
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
|
||||||
D: Send,
|
D: Send,
|
||||||
{
|
{
|
||||||
thread::scope( |sc| -> Result<(), Error> {
|
self.threaded_handler(Self::binary_input_handler,
|
||||||
let stop_trigger = self.stop_trigger[1];
|
|s, t, m, d| -> Result<(),Error> {
|
||||||
let t = sc.spawn(move || -> Result<(), Error> {
|
callback(s,m,t,d);
|
||||||
let userdata = userdata;
|
|
||||||
self.handle_input_internal(callback, userdata)?;
|
|
||||||
ts.send(false).expect("unexpected send() error");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
}, (ts, rs), userdata)
|
||||||
match rs.recv()? {
|
|
||||||
true => Self::signal_stop_input_internal(stop_trigger)?,
|
|
||||||
false => ()
|
|
||||||
};
|
|
||||||
t.join().expect("unexpected thread error")?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ pub mod alsa;
|
||||||
use queues::{CircularBuffer, IsQueue};
|
use queues::{CircularBuffer, IsQueue};
|
||||||
|
|
||||||
use crate::config::device::Identifier;
|
use crate::config::device::Identifier;
|
||||||
use crate::midi::alsa::MidiInputAlsa;
|
use super::midi::alsa::MidiInputAlsa;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
@ -53,6 +53,7 @@ impl From<MidiPortHandler> for MidiAddrHandler {
|
||||||
fn from(a: MidiPortHandler) -> Self {
|
fn from(a: MidiPortHandler) -> Self {
|
||||||
match a {
|
match a {
|
||||||
MidiPortHandler::ALSA(p) => MidiAddrHandler::ALSA(p.addr),
|
MidiPortHandler::ALSA(p) => MidiAddrHandler::ALSA(p.addr),
|
||||||
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,15 +82,15 @@ pub trait MidiInput<T> {
|
||||||
|
|
||||||
fn connect(&mut self, port_addr: &T, port_name: &str) -> Result<(), Error>;
|
fn connect(&mut self, port_addr: &T, port_name: &str) -> Result<(), Error>;
|
||||||
|
|
||||||
fn device_events(&mut self, ts: mpsc::Sender<MidiPortHandler>) -> Result<(), Error>;
|
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPortHandler>>, ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MidiInputHandler {
|
pub trait MidiInputHandler {
|
||||||
fn signal_stop_input(&self) -> Result<(), Error>;
|
fn signal_stop_input(&self) -> Result<(), Error>;
|
||||||
|
|
||||||
fn handle_input<F, D>(&mut self, callback: F, rts: (mpsc::Receiver<bool>, mpsc::Sender<bool>), userdata: D) -> Result<(), Error>
|
fn handle_input<F, D>(&mut self, callback: F, rts: (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: Fn(Option<SystemTime>, &[u8], &mut D) + Send,
|
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
|
||||||
D: Send,
|
D: Send,
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
@ -146,6 +147,7 @@ impl MidiHandler {
|
||||||
pub fn new_with_driver(name: &str, driver: MidiHandlerDriver) -> Result<Self, Error> {
|
pub fn new_with_driver(name: &str, driver: MidiHandlerDriver) -> Result<Self, Error> {
|
||||||
match driver {
|
match driver {
|
||||||
MidiHandlerDriver::ALSA => Ok(MidiHandler::ALSA(MidiInputAlsa::new(name)?)),
|
MidiHandlerDriver::ALSA => Ok(MidiHandler::ALSA(MidiInputAlsa::new(name)?)),
|
||||||
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,9 +166,9 @@ impl MidiHandler {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, (rs,ts): (mpsc::Receiver<bool>, mpsc::Sender<bool>)) -> Result<(), Error> {
|
pub fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, trs: (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error> {
|
||||||
handler_fcall!{
|
handler_fcall!{
|
||||||
self, handle_inputport, (conf, eventmap,(rs,ts)),
|
self, handle_inputport, (conf, eventmap,trs),
|
||||||
ALSA
|
ALSA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,9 +180,9 @@ impl MidiHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn device_events(&mut self, ts: mpsc::Sender<MidiPortHandler>) -> Result<(), Error> {
|
pub fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPortHandler>>, (tss, rss): (mpsc::Sender<bool>,mpsc::Receiver<bool>)) -> Result<(), Error> {
|
||||||
handler_fcall!{
|
handler_fcall!{
|
||||||
self, device_events, ts,
|
self, device_events, (ts,tss,rss),
|
||||||
ALSA
|
ALSA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +194,7 @@ where T: MidiInput<A>
|
||||||
input.ports_handle()
|
input.ports_handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_inputport<T>(input: &mut T, (conf, eventmap, (rs, ts)): (&DeviceConfig, &EventMap, (mpsc::Receiver<bool>, mpsc::Sender<bool>))) -> Result<(), Error>
|
fn handle_inputport<T>(input: &mut T, (conf, eventmap, (ts, rs)): (&DeviceConfig, &EventMap, (mpsc::Sender<bool>, mpsc::Receiver<bool>))) -> Result<(), Error>
|
||||||
where T: MidiInputHandler + Send
|
where T: MidiInputHandler + Send
|
||||||
{
|
{
|
||||||
thread::scope(|s| -> Result<(), Error> {
|
thread::scope(|s| -> Result<(), Error> {
|
||||||
|
|
@ -229,13 +231,13 @@ where T: MidiInputHandler + Send
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
input.handle_input(|t,m,(evq,pts)| {
|
input.handle_input(|_,m,t,(evq,pts)| {
|
||||||
let mut event: EventBuf = Event::from(m).into();
|
let mut event: EventBuf = Event::from(m).into();
|
||||||
event.timestamp = t;
|
event.timestamp = t;
|
||||||
let mut evq = evq.lock().unwrap();
|
let mut evq = evq.lock().unwrap();
|
||||||
evq.add(event).unwrap();
|
evq.add(event).unwrap();
|
||||||
pts.send(false).expect("unexpected write error");
|
pts.send(false).expect("unexpected write error");
|
||||||
}, (rs,ts), (evq,pts.clone()))?;
|
}, (ts,rs), (evq,pts.clone()))?;
|
||||||
|
|
||||||
pts.send(true).expect("unexpected write error");
|
pts.send(true).expect("unexpected write error");
|
||||||
let _ = exec_thread.join();
|
let _ = exec_thread.join();
|
||||||
|
|
@ -252,9 +254,9 @@ where T: MidiInputHandler
|
||||||
input.signal_stop_input()
|
input.signal_stop_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_events<T, A>(input: &mut T, ts: mpsc::Sender<MidiPortHandler>) -> Result<(), Error>
|
fn device_events<T, A>(input: &mut T, (ts,tss,rss): (mpsc::Sender<Option<MidiPortHandler>>, mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error>
|
||||||
where T: MidiInput<A>
|
where T: MidiInput<A>
|
||||||
{
|
{
|
||||||
input.device_events(ts)
|
input.device_events(ts, (tss, rss))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
46
src/run.rs
46
src/run.rs
|
|
@ -2,8 +2,10 @@ use std::sync::mpsc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::{Mutex,Arc};
|
use std::sync::{Mutex,Arc};
|
||||||
|
|
||||||
use crate::Error;
|
use libc::SIGUSR1;
|
||||||
|
use signal_hook::iterator::Signals;
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
use crate::midi::{PortFilter,MidiHandler,MidiPortHandler};
|
use crate::midi::{PortFilter,MidiHandler,MidiPortHandler};
|
||||||
use crate::config::{Config,DeviceConfig};
|
use crate::config::{Config,DeviceConfig};
|
||||||
use crate::eventmap::EventMap;
|
use crate::eventmap::EventMap;
|
||||||
|
|
@ -31,21 +33,53 @@ pub fn run_config(conf: &Config) -> Result<(), Error> {
|
||||||
|
|
||||||
let input = MidiHandler::new("rmidimap")?;
|
let input = MidiHandler::new("rmidimap")?;
|
||||||
|
|
||||||
|
let (tdev,rdev) = mpsc::channel::<Option<MidiPortHandler>>();
|
||||||
|
let (tsd,rsd) = mpsc::channel::<bool>();
|
||||||
|
|
||||||
|
let ntsd = tsd.clone();
|
||||||
|
let ntdev = tdev.clone();
|
||||||
|
let mut signals = Signals::new(&[SIGUSR1])?;
|
||||||
|
let _signal_thread = thread::spawn(move || {
|
||||||
|
for sig in signals.forever() {
|
||||||
|
match sig {
|
||||||
|
10 => {
|
||||||
|
println!("Recieved SIGUSR1, reloading config file");
|
||||||
|
ntsd.send(true).unwrap();
|
||||||
|
ntdev.send(None).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
thread::scope(|s| -> Result<(), Error> {
|
thread::scope(|s| -> Result<(), Error> {
|
||||||
let (tdev,rdev) = mpsc::channel::<MidiPortHandler>();
|
|
||||||
let mut threads: Vec<(thread::ScopedJoinHandle<'_, ()>, mpsc::Sender<bool>)> = Vec::new();
|
let mut threads: Vec<(thread::ScopedJoinHandle<'_, ()>, mpsc::Sender<bool>)> = Vec::new();
|
||||||
let ports = input.ports()?;
|
let ports = input.ports()?;
|
||||||
for p in ports {
|
for p in ports {
|
||||||
if let Some(v) = try_connect_process(&input, s, &p, &cfevmap)? { threads.push(v) }
|
if let Some(v) = try_connect_process(&input, s, &p, &cfevmap)? { threads.push(v) }
|
||||||
}
|
}
|
||||||
let _event_thread = s.spawn(|| {
|
|
||||||
|
let event_thread = s.spawn(move || {
|
||||||
let mut input = MidiHandler::new("rmidimap-event-watcher").unwrap();
|
let mut input = MidiHandler::new("rmidimap-event-watcher").unwrap();
|
||||||
input.device_events(tdev).unwrap();
|
let r = input.device_events(tdev.clone(), (tsd,rsd));
|
||||||
|
tdev.send(None).unwrap();
|
||||||
|
r
|
||||||
});
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let p = rdev.recv()?;
|
let p = rdev.recv()?;
|
||||||
if let Some(v) = try_connect_process(&input, s, &p, &cfevmap)? { threads.push(v) }
|
if p.is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Some(v) = try_connect_process(&input, s, &p.unwrap(), &cfevmap)? { threads.push(v) }
|
||||||
|
};
|
||||||
|
event_thread.join().unwrap()?;
|
||||||
|
for (thread,ss) in threads {
|
||||||
|
let _ = ss.send(true);
|
||||||
|
thread.join().unwrap();
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +108,7 @@ fn try_connect_process<'a>(
|
||||||
let mm = m.as_ref().map(Arc::clone);
|
let mm = m.as_ref().map(Arc::clone);
|
||||||
let t = s.spawn( move || {
|
let t = s.spawn( move || {
|
||||||
dev.run_connect().unwrap();
|
dev.run_connect().unwrap();
|
||||||
c.run(dev, eventmap, (srs,nsts)).unwrap();
|
c.run(dev, eventmap, (nsts,srs)).unwrap();
|
||||||
if let Some(m) = mm {
|
if let Some(m) = mm {
|
||||||
let mut m = m.lock().unwrap();
|
let mut m = m.lock().unwrap();
|
||||||
m.0 -= 1;
|
m.0 -= 1;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue