diff options
Diffstat (limited to 'third_party/rust/midir/src/backend/winrt/mod.rs')
-rw-r--r-- | third_party/rust/midir/src/backend/winrt/mod.rs | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/third_party/rust/midir/src/backend/winrt/mod.rs b/third_party/rust/midir/src/backend/winrt/mod.rs new file mode 100644 index 0000000000..a8204ec742 --- /dev/null +++ b/third_party/rust/midir/src/backend/winrt/mod.rs @@ -0,0 +1,299 @@ +extern crate winrt; + +use std::sync::{Arc, Mutex}; + +use ::errors::*; +use ::Ignore; + +use self::winrt::{AbiTransferable, HString, TryInto}; + +winrt::import!( + dependencies + os + types + windows::foundation::* + windows::devices::midi::* + windows::devices::enumeration::DeviceInformation + windows::storage::streams::{Buffer, DataWriter} +); + +use self::windows::foundation::*; +use self::windows::devices::midi::*; +use self::windows::devices::enumeration::DeviceInformation; +use self::windows::storage::streams::{Buffer, DataWriter}; + +#[derive(Clone, PartialEq)] +pub struct MidiInputPort { + id: HString +} + +unsafe impl Send for MidiInputPort {} // because HString doesn't ... + +pub struct MidiInput { + selector: HString, + ignore_flags: Ignore +} + +#[repr(C)] +pub struct abi_IMemoryBufferByteAccess { + __base: [usize; 3], + get_buffer: extern "system" fn( + winrt::NonNullRawComPtr<IMemoryBufferByteAccess>, + value: *mut *mut u8, + capacity: *mut u32, + ) -> winrt::ErrorCode, +} + +unsafe impl winrt::ComInterface for IMemoryBufferByteAccess { + type VTable = abi_IMemoryBufferByteAccess; + fn iid() -> winrt::Guid { + winrt::Guid::from_values(0x5b0d3235, 0x4dba, 0x4d44, [0x86, 0x5e, 0x8f, 0x1d, 0x0e, 0x4f, 0xd0, 0x4d]) + } +} + +unsafe impl AbiTransferable for IMemoryBufferByteAccess { + type Abi = winrt::RawComPtr<Self>; + + fn get_abi(&self) -> Self::Abi { + self.ptr.get_abi() + } + + fn set_abi(&mut self) -> *mut Self::Abi { + self.ptr.set_abi() + } +} + +#[repr(transparent)] +#[derive(Default, Clone)] +pub struct IMemoryBufferByteAccess { + ptr: winrt::ComPtr<IMemoryBufferByteAccess>, +} + +impl IMemoryBufferByteAccess { + pub unsafe fn get_buffer(&self) -> winrt::Result<&[u8]> { + match self.get_abi() { + None => panic!("The `this` pointer was null when calling method"), + Some(ptr) => { + let mut bufptr = std::ptr::null_mut(); + let mut capacity: u32 = 0; + (ptr.vtable().get_buffer)(ptr, &mut bufptr, &mut capacity).ok()?; + if capacity == 0 { + bufptr = 1 as *mut u8; // null pointer is not allowed + } + Ok(std::slice::from_raw_parts(bufptr, capacity as usize)) + } + } + } +} + + +unsafe impl Send for MidiInput {} // because HString doesn't ... + +impl MidiInput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + let device_selector = MidiInPort::get_device_selector().map_err(|_| InitError)?; + Ok(MidiInput { selector: device_selector, ignore_flags: Ignore::None }) + } + + pub fn ignore(&mut self, flags: Ignore) { + self.ignore_flags = flags; + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + let count = device_collection.size().expect("get_size failed") as usize; + let mut result = Vec::with_capacity(count as usize); + for device_info in device_collection.into_iter() { + let device_id = device_info.id().expect("get_id failed"); + result.push(::common::MidiInputPort { + imp: MidiInputPort { id: device_id } + }); + } + result + } + + pub fn port_count(&self) -> usize { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + device_collection.size().expect("get_size failed") as usize + } + + pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> { + let device_info_async = DeviceInformation::create_from_id_async(&port.id).map_err(|_| PortInfoError::InvalidPort)?; + let device_info = device_info_async.get().map_err(|_| PortInfoError::InvalidPort)?; + let device_name = device_info.name().map_err(|_| PortInfoError::CannotRetrievePortName)?; + Ok(device_name.to_string()) + } + + fn handle_input<T>(args: &MidiMessageReceivedEventArgs, handler_data: &mut HandlerData<T>) { + let ignore = handler_data.ignore_flags; + let data = &mut handler_data.user_data.as_mut().unwrap(); + let timestamp; + let byte_access: IMemoryBufferByteAccess; + let message_bytes; + let message = args.message().expect("get_message failed"); + timestamp = message.timestamp().expect("get_timestamp failed").duration as u64 / 10; + let buffer = message.raw_data().expect("get_raw_data failed"); + let membuffer = Buffer::create_memory_buffer_over_ibuffer(&buffer).expect("create_memory_buffer_over_ibuffer failed"); + byte_access = membuffer.create_reference().expect("create_reference failed").try_into().unwrap(); + message_bytes = unsafe { byte_access.get_buffer().expect("get_buffer failed") }; // TODO: somehow make sure that the buffer is not invalidated while we're reading from it ... + + // The first byte in the message is the status + let status = message_bytes[0]; + + if !(status == 0xF0 && ignore.contains(Ignore::Sysex) || + status == 0xF1 && ignore.contains(Ignore::Time) || + status == 0xF8 && ignore.contains(Ignore::Time) || + status == 0xFE && ignore.contains(Ignore::ActiveSense)) + { + (handler_data.callback)(timestamp, message_bytes, data); + } + } + + pub fn connect<F, T: Send + 'static>( + self, port: &MidiInputPort, _port_name: &str, callback: F, data: T + ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>> + where F: FnMut(u64, &[u8], &mut T) + Send + 'static { + + let in_port = match MidiInPort::from_id_async(&port.id) { + Ok(port_async) => match port_async.get() { + Ok(port) => port, + _ => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + } + Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + + let handler_data = Arc::new(Mutex::new(HandlerData { + ignore_flags: self.ignore_flags, + callback: Box::new(callback), + user_data: Some(data) + })); + let handler_data2 = handler_data.clone(); + + let handler = TypedEventHandler::new(move |_sender, args| { + MidiInput::handle_input(args, &mut *handler_data2.lock().unwrap()); + Ok(()) + }); + + let event_token = in_port.message_received(&handler).expect("add_message_received failed"); + + Ok(MidiInputConnection { port: RtMidiInPort(in_port), event_token: event_token, handler_data: handler_data }) + } +} + +struct RtMidiInPort(MidiInPort); +unsafe impl Send for RtMidiInPort {} + +pub struct MidiInputConnection<T> { + port: RtMidiInPort, + event_token: EventRegistrationToken, + // TODO: get rid of Arc & Mutex? + // synchronization is required because the borrow checker does not + // know that the callback we're in here is never called concurrently + // (always in sequence) + handler_data: Arc<Mutex<HandlerData<T>>> +} + + +impl<T> MidiInputConnection<T> { + pub fn close(self) -> (MidiInput, T) { + let _ = self.port.0.remove_message_received(self.event_token); + let closable: IClosable = self.port.0.try_into().unwrap(); + let _ = closable.close(); + let device_selector = MidiInPort::get_device_selector().expect("get_device_selector failed"); // probably won't ever fail here, because it worked previously + let mut handler_data_locked = self.handler_data.lock().unwrap(); + (MidiInput { + selector: device_selector, + ignore_flags: handler_data_locked.ignore_flags + }, handler_data_locked.user_data.take().unwrap()) + } +} + +/// This is all the data that is stored on the heap as long as a connection +/// is opened and passed to the callback handler. +/// +/// It is important that `user_data` is the last field to not influence +/// offsets after monomorphization. +struct HandlerData<T> { + ignore_flags: Ignore, + callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send>, + user_data: Option<T> +} + +#[derive(Clone, PartialEq)] +pub struct MidiOutputPort { + id: HString +} + +unsafe impl Send for MidiOutputPort {} // because HString doesn't ... + +pub struct MidiOutput { + selector: HString // TODO: change to FastHString? +} + +unsafe impl Send for MidiOutput {} // because HString doesn't ... + +impl MidiOutput { + pub fn new(_client_name: &str) -> Result<Self, InitError> { + let device_selector = MidiOutPort::get_device_selector().map_err(|_| InitError)?; + Ok(MidiOutput { selector: device_selector }) + } + + pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + let count = device_collection.size().expect("get_size failed") as usize; + let mut result = Vec::with_capacity(count as usize); + for device_info in device_collection.into_iter() { + let device_id = device_info.id().expect("get_id failed"); + result.push(::common::MidiOutputPort { + imp: MidiOutputPort { id: device_id } + }); + } + result + } + + pub fn port_count(&self) -> usize { + let device_collection = DeviceInformation::find_all_async_aqs_filter(&self.selector).unwrap().get().expect("find_all_async failed"); + device_collection.size().expect("get_size failed") as usize + } + + pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> { + let device_info_async = DeviceInformation::create_from_id_async(&port.id).map_err(|_| PortInfoError::InvalidPort)?; + let device_info = device_info_async.get().map_err(|_| PortInfoError::InvalidPort)?; + let device_name = device_info.name().map_err(|_| PortInfoError::CannotRetrievePortName)?; + Ok(device_name.to_string()) + } + + pub fn connect(self, port: &MidiOutputPort, _port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> { + let out_port = match MidiOutPort::from_id_async(&port.id) { + Ok(port_async) => match port_async.get() { + Ok(port) => port, + _ => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + } + Err(_) => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)) + }; + Ok(MidiOutputConnection { port: out_port }) + } +} + +pub struct MidiOutputConnection { + port: IMidiOutPort +} + +unsafe impl Send for MidiOutputConnection {} + +impl MidiOutputConnection { + pub fn close(self) -> MidiOutput { + let closable: IClosable = self.port.try_into().unwrap(); + let _ = closable.close(); + let device_selector = MidiOutPort::get_device_selector().expect("get_device_selector failed"); // probably won't ever fail here, because it worked previously + MidiOutput { selector: device_selector } + } + + pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { + let data_writer = DataWriter::new().unwrap(); + data_writer.write_bytes(message).map_err(|_| SendError::Other("write_bytes failed"))?; + let buffer = data_writer.detach_buffer().map_err(|_| SendError::Other("detach_buffer failed"))?; + self.port.send_buffer(&buffer).map_err(|_| SendError::Other("send_buffer failed"))?; + Ok(()) + } +} |