use libc::{c_uint, c_int, c_short, c_uchar, c_void, c_long, size_t, pollfd}; usesuper::error::*; usecrate::alsa; usesuper::{Direction, poll}; use std::{ptr, fmt, mem, slice, time, cell}; use std::str::{FromStr, Split}; use std::ffi::{CStr}; use std::borrow::Cow;
// Workaround for improper alignment of snd_seq_ev_ext_t in alsa-sys #[repr(packed)] struct EvExtPacked {
len: c_uint,
ptr: *mut c_void,
}
/// [snd_seq_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___sequencer.html) wrapper /// /// To access the functions `event_input`, `event_input_pending` and `set_input_buffer_size`, /// you first have to obtain an instance of `Input` by calling `input()`. Only one instance of /// `Input` may exist at any time for a given `Seq`. pubstruct Seq(*mut alsa::snd_seq_t, cell::Cell<bool>);
unsafeimpl Send for Seq {}
impl Drop for Seq { fn drop(&mutself) { unsafe { alsa::snd_seq_close(self.0) }; }
}
/// Opens the sequencer. /// /// If name is None, "default" will be used. That's almost always what you usually want to use anyway. pubfn open(name: Option<&CStr>, dir: Option<Direction>, nonblock: bool) -> Result<Seq> { let n2 = name.unwrap_or(unsafe { CStr::from_bytes_with_nul_unchecked(b"default\0") }); letmut h = ptr::null_mut(); let mode = if nonblock { alsa::SND_SEQ_NONBLOCK } else { 0 }; let streams = match dir {
None => alsa::SND_SEQ_OPEN_DUPLEX,
Some(Direction::Playback) => alsa::SND_SEQ_OPEN_OUTPUT,
Some(Direction::Capture) => alsa::SND_SEQ_OPEN_INPUT,
};
acheck!(snd_seq_open(&mut h, n2.as_ptr(), streams, mode))
.map(|_| Seq(h, cell::Cell::new(false)))
}
pubfn client_id(&self) -> Result<i32> {
acheck!(snd_seq_client_id(self.0)).map(|q| q as i32)
}
pubfn drain_output(&self) -> Result<i32> {
acheck!(snd_seq_drain_output(self.0)).map(|q| q as i32)
}
pubfn get_any_client_info(&self, client: i32) -> Result<ClientInfo> { let c = ClientInfo::new()?;
acheck!(snd_seq_get_any_client_info(self.0, client, c.0)).map(|_| c)
}
pubfn get_any_port_info(&self, a: Addr) -> Result<PortInfo> { let c = PortInfo::new()?;
acheck!(snd_seq_get_any_port_info(self.0, a.client as c_int, a.port as c_int, c.0)).map(|_| c)
}
/// Call this function to obtain an instance of `Input` to access the functions `event_input`, /// `event_input_pending` and `set_input_buffer_size`. See the documentation of `Input` for details. pubfn input(&self) -> Input {
Input::new(self)
}
/// Struct for receiving input events from a sequencer. The methods offered by this /// object may modify the internal input buffer of the sequencer, which must not happen /// while an `Event` is alive that has been obtained from a call to `event_input` (which /// takes `Input` by mutable reference for this reason). This is because the event might /// directly reference the sequencer's input buffer for variable-length messages (e.g. Sysex). /// /// Note: Only one `Input` object is allowed in scope at a time. pubstruct Input<'a>(&'a Seq);
impl<'a> Drop for Input<'a> { fn drop(&mutself) { (self.0).1.set(false) }
}
pubfn event_input(&mutself) -> Result<Event> { // The returned event might reference the input buffer of the `Seq`. // Therefore we mutably borrow the `Input` structure, preventing any // other function call that might change the input buffer while the // event is alive. letmut z = ptr::null_mut();
acheck!(snd_seq_event_input((self.0).0, &mut z))?; unsafe { Event::extract (&mut *z, "snd_seq_event_input") }
}
pubfn event_input_pending(&self, fetch_sequencer: bool) -> Result<u32> {
acheck!(snd_seq_event_input_pending((self.0).0, if fetch_sequencer {1} else {0})).map(|q| q as u32)
}
// Not sure if it's useful for this one to be public. fn set_client(&self, client: i32) { unsafe { alsa::snd_seq_client_info_set_client(self.0, client as c_int) };
}
/// Creates a new PortInfo with all fields set to zero. pubfn empty() -> Result<Self> { let z = Self::new()?; unsafe { ptr::write_bytes(z.0as *mut u8, 0, alsa::snd_seq_port_info_sizeof()) };
Ok(z)
}
// Not sure if it's useful for this one to be public. fn set_client(&self, client: i32) { unsafe { alsa::snd_seq_port_info_set_client(self.0, client as c_int) };
}
// Not sure if it's useful for this one to be public. fn set_port(&self, port: i32) { unsafe { alsa::snd_seq_port_info_set_port(self.0, port as c_int) };
}
pubfn get_name(&self) -> Result<&str> { let c = unsafe { alsa::snd_seq_port_info_get_name(self.0) };
from_const("snd_seq_port_info_get_name", c)
}
pubfn set_name(&mutself, name: &CStr) { // Note: get_name returns an interior reference, so this one must take &mut self unsafe { alsa::snd_seq_port_info_set_name(self.0, name.as_ptr()) };
}
#[derive(Copy, Clone)] /// Iterates over clients connected to the seq API (both kernel and userspace clients). pubstruct PortIter<'a>(&'a Seq, i32, i32);
/// Creates a new PortSubscribe with all fields set to zero. pubfn empty() -> Result<Self> { let z = Self::new()?; unsafe { ptr::write_bytes(z.0as *mut u8, 0, alsa::snd_seq_port_subscribe_sizeof()) };
Ok(z)
}
pubfn get_sender(&self) -> Addr { unsafe { let z = alsa::snd_seq_port_subscribe_get_sender(self.0);
Addr { client: (*z).client as i32, port: (*z).port as i32 }
} }
pubfn get_dest(&self) -> Addr { unsafe { let z = alsa::snd_seq_port_subscribe_get_dest(self.0);
Addr { client: (*z).client as i32, port: (*z).port as i32 }
} }
pubfn set_root(&self, value: Addr) { unsafe { let a = alsa::snd_seq_addr_t { client: value.client as c_uchar, port: value.port as c_uchar};
alsa::snd_seq_query_subscribe_set_root(self.0, &a);
} } pubfn set_type(&self, value: QuerySubsType) { unsafe {
alsa::snd_seq_query_subscribe_set_type(self.0, value as alsa::snd_seq_query_subs_type_t)
} } pubfn set_index(&self, value: i32) { unsafe { alsa::snd_seq_query_subscribe_set_index(self.0, value as c_int) } }
}
#[derive(Copy, Clone)] /// Iterates over port subscriptions for a given client:port/type. pubstruct PortSubscribeIter<'a> {
seq: &'a Seq,
addr: Addr,
query_subs_type: QuerySubsType,
index: i32
}
/// [snd_seq_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html) wrapper /// /// Fields of the event is not directly exposed. Instead call `Event::new` to set data (which can be, e g, an EvNote). /// Use `get_type` and `get_data` to retrieve data. /// /// The lifetime parameter refers to the lifetime of an associated external buffer that might be used for /// variable-length messages (e.g. SysEx). pubstruct Event<'a>(alsa::snd_seq_event_t, EventType, Option<Cow<'a, [u8]>>);
unsafeimpl<'a> Send for Event<'a> {}
impl<'a> Event<'a> { /// Creates a new event. For events that carry variable-length data (e.g. Sysex), `new_ext` has to be used instead. pubfn new<D: EventData>(t: EventType, data: &D) -> Event<'static> {
assert!(!Event::has_ext_data(t), "event type must not carry variable-length data"); letmut z = Event(unsafe { mem::zeroed() }, t, None);
(z.0).type_ = t as c_uchar;
(z.0).flags |= Event::get_length_flag(t);
debug_assert!(D::has_data(t));
data.set_data(&mut z);
z
}
/// Creates a new event carrying variable-length data. This is required for event types `Sysex`, `Bounce`, and the `UsrVar` types. pubfn new_ext<D: Into<Cow<'a, [u8]>>>(t: EventType, data: D) -> Event<'a> {
assert!(Event::has_ext_data(t), "event type must carry variable-length data"); letmut z = Event(unsafe { mem::zeroed() }, t, Some(data.into()));
(z.0).type_ = t as c_uchar;
(z.0).flags |= Event::get_length_flag(t);
z
}
/// Consumes this event and returns an (otherwise unchanged) event where the externally referenced /// buffer for variable length messages (e.g. SysEx) has been copied into the event. /// The returned event has a static lifetime, i e, it's decoupled from the original buffer. pubfn into_owned(self) -> Event<'static> {
Event(self.0, self.1, self.2.map(|cow| Cow::Owned(cow.into_owned())))
}
fn get_length_flag(t: EventType) -> u8 { match t {
EventType::Sysex => alsa::SND_SEQ_EVENT_LENGTH_VARIABLE,
EventType::Bounce => alsa::SND_SEQ_EVENT_LENGTH_VARIABLE, // not clear whether this should be VARIABLE or VARUSR
EventType::UsrVar0 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
EventType::UsrVar1 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
EventType::UsrVar2 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
EventType::UsrVar3 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
EventType::UsrVar4 => alsa::SND_SEQ_EVENT_LENGTH_VARUSR,
_ => alsa::SND_SEQ_EVENT_LENGTH_FIXED
}
}
/// Extracts event type and data. Produces a result with an arbitrary lifetime, hence the unsafety. unsafefn extract<'any>(z: &mut alsa::snd_seq_event_t, func: &'static str) -> Result<Event<'any>> { let t = EventType::from_c_int((*z).type_ as c_int, func)?; let ext_data = if Event::has_ext_data(t) {
assert_ne!((*z).flags & alsa::SND_SEQ_EVENT_LENGTH_MASK, alsa::SND_SEQ_EVENT_LENGTH_FIXED);
Some(Cow::Borrowed({ let zz: &EvExtPacked = &*(&(*z).data as *const alsa::snd_seq_event__bindgen_ty_1 as *const _);
slice::from_raw_parts((*zz).ptr as *mut u8, (*zz).len as usize)
}))
} else {
None
};
Ok(Event(ptr::read(z), t, ext_data))
}
/// Ensures that the ev.ext union element points to the correct resize_buffer for events /// with variable length content fn ensure_buf(&mutself) { if !Event::has_ext_data(self.1) { return; } let slice: &[u8] = matchself.2 {
Some(Cow::Owned(refmut vec)) => &vec[..],
Some(Cow::Borrowed(buf)) => buf, // The following case is always a logic error in the program, thus panicking is okay.
None => panic!("event type requires variable-length data, but none was provided")
}; let z: &mut EvExtPacked = unsafe { &mut *(&an style='color:red'>mut self.0.data as *mut alsa::snd_seq_event__bindgen_ty_1 as *mut _) };
z.len = slice.len() as c_uint;
z.ptr = slice.as_ptr() as *mut c_void;
}
/// Extract the event data from an event. /// Use `get_ext` instead for events carrying variable-length data. pubfn get_data<D: EventData>(&self) -> Option<D> { if D::has_data(self.1) { Some(D::get_data(self)) } else { None } }
/// Extract the variable-length data carried by events of type `Sysex`, `Bounce`, or the `UsrVar` types. pubfn get_ext(&self) -> Option<&[u8]> { if Event::has_ext_data(self.1) { matchself.2 {
Some(Cow::Owned(ref vec)) => Some(&vec[..]),
Some(Cow::Borrowed(buf)) => Some(buf), // The following case is always a logic error in the program, thus panicking is okay.
None => panic!("event type requires variable-length data, but none was found")
}
} else {
None
}
}
pubfn get_time(&self) -> Option<time::Duration> { if (self.0.flags & alsa::SND_SEQ_TIME_STAMP_REAL) != 0 { let d = self.0.time; let t = unsafe { &d.time };
Some(time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32))
} else { None }
}
pubfn get_tick(&self) -> Option<u32> { if (self.0.flags & alsa::SND_SEQ_TIME_STAMP_REAL) == 0 { let d = self.0.time; let t = unsafe { &d.tick };
Some(*t)
} else { None }
}
/// Returns true if the message is high priority. pubfn get_priority(&self) -> bool { (self.0.flags & alsa::SND_SEQ_PRIORITY_HIGH) != 0 }
impl EventData for Connect { fn get_data(ev: &Event) -> Self { let d = unsafe { ptr::read(&ev.0.data) }; let z = unsafe { &d.connect };
Connect {
sender: Addr { client: z.sender.client as i32, port: z.sender.port as i32 },
dest: Addr { client: z.dest.client as i32, port: z.dest.port as i32 }
}
} fn has_data(e: EventType) -> bool {
matches!(e,
EventType::PortSubscribed |
EventType::PortUnsubscribed)
} fn set_data(&self, ev: &mut Event) { let z = unsafe { &mut ev.0.data.connect };
z.sender.client = self.sender.client as c_uchar;
z.sender.port = self.sender.port as c_uchar;
z.dest.client = self.dest.client as c_uchar;
z.dest.port = self.dest.port as c_uchar;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] /// [snd_seq_ev_queue_control_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__ev__queue__control__t.html) wrapper /// /// Note: This struct is generic, but what types of T are required for the different EvQueueControl messages is /// not very well documented in alsa-lib. Right now, Tempo is i32, Tick, SetposTick and SyncPos are u32, SetposTime is time::Duration, /// and the rest is (). If I guessed wrong, let me know. pubstruct EvQueueControl<T> { pub queue: i32, pub value: T,
}
impl EventData for EvQueueControl<()> { fn get_data(ev: &Event) -> Self { let d = unsafe { ptr::read(&ev.0.data) }; let z = unsafe { &d.queue };
EvQueueControl { queue: z.queue as i32, value: () }
} fn has_data(e: EventType) -> bool {
matches!(e,
EventType::Start |
EventType::Continue |
EventType::Stop |
EventType::Clock |
EventType::QueueSkew)
} fn set_data(&self, ev: &mut Event) { let z = unsafe { &mut ev.0.data.queue };
z.queue = self.queue as c_uchar;
}
}
impl EventData for EvQueueControl<i32> { fn get_data(ev: &Event) -> Self { unsafe { letmut d = ptr::read(&ev.0.data); let z = &mut d.queue;
EvQueueControl { queue: z.queue as i32, value: z.param.value as i32 }
} } fn has_data(e: EventType) -> bool {
matches!(e,
EventType::Tempo)
} fn set_data(&self, ev: &mut Event) { unsafe { let z = &mut ev.0.data.queue;
z.queue = self.queue as c_uchar;
z.param.value = self.value as c_int;
} }
}
impl EventData for EvQueueControl<u32> { fn get_data(ev: &Event) -> Self { unsafe { letmut d = ptr::read(&ev.0.data); let z = &mut d.queue;
EvQueueControl { queue: z.queue as i32, value: z.param.position as u32 }
} } fn has_data(e: EventType) -> bool {
matches!(e,
EventType::SyncPos |
EventType::Tick |
EventType::SetposTick)
} fn set_data(&self, ev: &mut Event) { unsafe { let z = &mut ev.0.data.queue;
z.queue = self.queue as c_uchar;
z.param.position = self.value as c_uint;
} }
}
impl EventData for EvQueueControl<time::Duration> { fn get_data(ev: &Event) -> Self { unsafe { letmut d = ptr::read(&ev.0.data); let z = &mut d.queue; let t = &mut z.param.time.time;
EvQueueControl { queue: z.queue as i32, value: time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32) }
} } fn has_data(e: EventType) -> bool {
matches!(e,
EventType::SetposTime)
} fn set_data(&self, ev: &mut Event) { unsafe { let z = &mut ev.0.data.queue;
z.queue = self.queue as c_uchar; let t = &mut z.param.time.time;
t.tv_sec = self.value.as_secs() as c_uint;
t.tv_nsec = self.value.subsec_nanos() as c_uint;
} }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Default)] /// [snd_seq_result_t](http://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__result__t.html) wrapper /// /// It's called EvResult instead of Result, in order to not be confused with Rust's Result type. pubstruct EvResult { pub event: i32, pub result: i32,
}
impl EventData for EvResult { fn get_data(ev: &Event) -> Self { let d = unsafe { ptr::read(&ev.0.data) }; let z = unsafe { &d.result };
EvResult { event: z.event as i32, result: z.result as i32 }
} fn has_data(e: EventType) -> bool {
matches!(e,
EventType::System |
EventType::Result)
} fn set_data(&self, ev: &mut Event) { let z = unsafe { &mut ev.0.data.result };
z.event = self.event as c_int;
z.result = self.result as c_int;
}
}
/// Creates a new QueueTempo with all fields set to zero. pubfn empty() -> Result<Self> { let q = QueueTempo::new()?; unsafe { ptr::write_bytes(q.0as *mut u8, 0, alsa::snd_seq_queue_tempo_sizeof()) };
Ok(q)
}
/// Creates a new QueueStatus with all fields set to zero. pubfn empty() -> Result<Self> { let q = QueueStatus::new()?; unsafe { ptr::write_bytes(q.0as *mut u8, 0, alsa::snd_seq_queue_status_sizeof()) };
Ok(q)
}
pubfn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_queue_status_get_queue(self.0) as i32 } } pubfn get_events(&self) -> i32 { unsafe { alsa::snd_seq_queue_status_get_events(self.0) as i32 } } pubfn get_tick_time(&self) -> u32 { unsafe {alsa::snd_seq_queue_status_get_tick_time(self.0) as u32 } } pubfn get_real_time(&self) -> time::Duration { unsafe { let t = &(*alsa::snd_seq_queue_status_get_real_time(self.0));
time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32)
} } pubfn get_status(&self) -> u32 { unsafe { alsa::snd_seq_queue_status_get_status(self.0) as u32 } }
}
pubfn get_condition(&self) -> Remove { unsafe {
Remove::from_bits_truncate(alsa::snd_seq_remove_events_get_condition(self.0) as u32)
} } pubfn get_queue(&self) -> i32 { unsafe { alsa::snd_seq_remove_events_get_queue(self.0) as i32 } } pubfn get_time(&self) -> time::Duration { unsafe { let d = ptr::read(alsa::snd_seq_remove_events_get_time(self.0)); let t = &d.time;
time::Duration::new(t.tv_sec as u64, t.tv_nsec as u32)
} } pubfn get_dest(&self) -> Addr { unsafe { let a = &(*alsa::snd_seq_remove_events_get_dest(self.0));
/// Note: this corresponds to snd_midi_event_no_status, but on and off are switched. /// /// Alsa-lib is a bit confusing here. Anyhow, set "enable" to true to enable running status. pubfn enable_running_status(&self, enable: bool) { unsafe { alsa::snd_midi_event_no_status(self.0, if enable {0} else {1}) } }
/// Resets both encoder and decoder pubfn init(&self) { unsafe { alsa::snd_midi_event_init(self.0) } }
pubfn decode(&self, buf: &mut [u8], ev: &an style='color:red'>mut Event) -> Result<usize> {
ev.ensure_buf();
acheck!(snd_midi_event_decode(self.0, buf.as_mut_ptr() as *mut c_uchar, buf.len() as c_long, &ev.0)).map(|r| r as usize)
}
/// In case of success, returns a tuple of (bytes consumed from buf, found Event). pubfn encode<'a>(&'a mutself, buf: &[u8]) -> Result<(usize, Option<Event<'a>>)> { // The ALSA documentation clearly states that the event will be valid as long as the Encoder // is not messed with (because the data pointer for sysex events may point into the Encoder's // buffer). We make this safe by taking self by unique reference and coupling it to // the event's lifetime. letmut ev = unsafe { mem::zeroed() }; let r = acheck!(snd_midi_event_encode(self.0, buf.as_ptr() as *const c_uchar, buf.len() as c_long, &mut ev))?; let e = if ev.type_ == alsa::SND_SEQ_EVENT_NONE as u8 {
None
} else {
Some(unsafe { Event::extract(&mut ev, "snd_midi_event_encode") }?)
};
Ok((r as usize, e))
}
}
#[test] fn print_seqs() { use std::ffi::CString; let s = super::Seq::open(None, None, false).unwrap();
s.set_client_name(&CString::new("rust_test_print_seqs").unwrap()).unwrap(); let clients: Vec<_> = ClientIter::new(&s).collect(); for a in &clients { let ports: Vec<_> = PortIter::new(&s, a.get_client()).collect();
println!("{:?}: {:?}", a, ports);
}
}
#[test] fn seq_subscribe() { use std::ffi::CString; let s = super::Seq::open(None, None, false).unwrap();
s.set_client_name(&CString::new("rust_test_seq_subscribe").unwrap()).unwrap(); let timer_info = s.get_any_port_info(Addr { client: 0, port: 0 }).unwrap();
assert_eq!(timer_info.get_name().unwrap(), "Timer"); let info = PortInfo::empty().unwrap(); let _port = s.create_port(&info); let subs = PortSubscribe::empty().unwrap();
subs.set_sender(Addr { client: 0, port: 0 });
subs.set_dest(Addr { client: s.client_id().unwrap(), port: info.get_port() });
s.subscribe_port(&subs).unwrap();
}
#[test] fn seq_loopback() { use std::ffi::CString; let s = super::Seq::open(Some(&CString::new("default").unwrap()), None, false).unwrap();
s.set_client_name(&CString::new("rust_test_seq_loopback").unwrap()).unwrap();
// Create ports let sinfo = PortInfo::empty().unwrap();
sinfo.set_capability(PortCap::READ | PortCap::SUBS_READ);
sinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION);
s.create_port(&sinfo).unwrap(); let sport = sinfo.get_port(); let dinfo = PortInfo::empty().unwrap();
dinfo.set_capability(PortCap::WRITE | PortCap::SUBS_WRITE);
dinfo.set_type(PortType::MIDI_GENERIC | PortType::APPLICATION);
s.create_port(&dinfo).unwrap(); let dport = dinfo.get_port();
// Connect them let subs = PortSubscribe::empty().unwrap();
subs.set_sender(Addr { client: s.client_id().unwrap(), port: sport });
subs.set_dest(Addr { client: s.client_id().unwrap(), port: dport });
s.subscribe_port(&subs).unwrap();
println!("Connected {:?} to {:?}", subs.get_sender(), subs.get_dest());
// Receive the note! letmut input = s.input(); let e2 = input.event_input().unwrap();
println!("Receiving {:?}", e2);
assert_eq!(e2.get_type(), EventType::Noteon);
assert_eq!(e2.get_data(), Some(note));
}
#[test] fn seq_encode_sysex() { letmut me = MidiEvent::new(16).unwrap(); let sysex = &[0xf0, 1, 2, 3, 4, 5, 6, 7, 0xf7]; let (s, ev) = me.encode(sysex).unwrap();
assert_eq!(s, 9); let ev = ev.unwrap(); let v = ev.get_ext().unwrap();
assert_eq!(&*v, sysex);
}
#[test] fn seq_decode_sysex() { let sysex = [0xf0, 1, 2, 3, 4, 5, 6, 7, 0xf7]; letmut ev = Event::new_ext(EventType::Sysex, &sysex[..]); let me = MidiEvent::new(0).unwrap(); letmut buffer = vec![0; sysex.len()];
assert_eq!(me.decode(&mut buffer[..], &>mut ev).unwrap(), sysex.len());
assert_eq!(buffer, sysex);
}
#[test] #[should_panic] fn seq_get_input_twice() { use std::ffi::CString; let s = super::Seq::open(None, None, false).unwrap();
s.set_client_name(&CString::new("rust_test_seq_get_input_twice").unwrap()).unwrap(); let input1 = s.input(); let input2 = s.input(); // this should panic let _ = (input1, input2);
}
#[test] fn seq_has_data() { for v in EventType::all() { let v = *v; letmut i = 0; if <() as EventData>::has_data(v) { i += 1; } if <[u8; 12] as EventData>::has_data(v) { i += 1; } if Event::has_ext_data(v) { i += 1; } if EvNote::has_data(v) { i += 1; } if EvCtrl::has_data(v) { i += 1; } if Addr::has_data(v) { i += 1; } if Connect::has_data(v) { i += 1; } if EvResult::has_data(v) { i += 1; } if EvQueueControl::<()>::has_data(v) { i += 1; } if EvQueueControl::<u32>::has_data(v) { i += 1; } if EvQueueControl::<i32>::has_data(v) { i += 1; } if EvQueueControl::<time::Duration>::has_data(v) { i += 1; } if i != 1 { panic!("{:?}: {} has_data", v, i) }
}
}
#[test] fn seq_remove_events() -> std::result::Result<(), Box<dyn std::error::Error>> { let info = RemoveEvents::new()?;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.