tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lib.rs (12217B)


      1 extern crate thin_vec;
      2 
      3 use midir::{
      4    InitError, MidiInput, MidiInputConnection, MidiInputPort, MidiOutput, MidiOutputConnection,
      5    MidiOutputPort,
      6 };
      7 use nsstring::{nsAString, nsString};
      8 use std::ptr;
      9 use thin_vec::ThinVec;
     10 use uuid::Uuid;
     11 
     12 /* This Source Code Form is subject to the terms of the Mozilla Public
     13 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     14 * You can obtain one at http://mozilla.org/MPL/2.0/. */
     15 extern crate midir;
     16 
     17 #[repr(C)]
     18 #[derive(Clone, Copy)]
     19 pub struct GeckoTimeStamp {
     20    value: u64,
     21 }
     22 
     23 enum MidiConnection {
     24    Input(MidiInputConnection<CallbackData>),
     25    Output(MidiOutputConnection),
     26 }
     27 
     28 struct MidiConnectionWrapper {
     29    id: String,
     30    connection: MidiConnection,
     31 }
     32 
     33 enum MidiPort {
     34    Input(MidiInputPort),
     35    Output(MidiOutputPort),
     36 }
     37 
     38 struct MidiPortWrapper {
     39    id: String,
     40    name: String,
     41    port: MidiPort,
     42    open_count: u32,
     43 }
     44 
     45 impl MidiPortWrapper {
     46    fn input(self: &MidiPortWrapper) -> bool {
     47        match self.port {
     48            MidiPort::Input(_) => true,
     49            MidiPort::Output(_) => false,
     50        }
     51    }
     52 }
     53 
     54 pub struct MidirWrapper {
     55    ports: Vec<MidiPortWrapper>,
     56    connections: Vec<MidiConnectionWrapper>,
     57 }
     58 
     59 struct CallbackData {
     60    nsid: nsString,
     61    open_timestamp: GeckoTimeStamp,
     62 }
     63 
     64 type AddCallback = unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool);
     65 type RemoveCallback = AddCallback;
     66 
     67 impl MidirWrapper {
     68    fn refresh(
     69        self: &mut MidirWrapper,
     70        add_callback: AddCallback,
     71        remove_callback: Option<RemoveCallback>,
     72    ) {
     73        if let Ok(ports) = collect_ports() {
     74            if let Some(remove_callback) = remove_callback {
     75                self.remove_missing_ports(&ports, remove_callback);
     76            }
     77 
     78            self.add_new_ports(ports, add_callback);
     79        }
     80    }
     81 
     82    fn remove_missing_ports(
     83        self: &mut MidirWrapper,
     84        ports: &Vec<MidiPortWrapper>,
     85        remove_callback: RemoveCallback,
     86    ) {
     87        let old_ports = &mut self.ports;
     88        let mut i = 0;
     89        while i < old_ports.len() {
     90            if !ports
     91                .iter()
     92                .any(|p| p.name == old_ports[i].name && p.input() == old_ports[i].input())
     93            {
     94                let port = old_ports.remove(i);
     95                let id = nsString::from(&port.id);
     96                let name = nsString::from(&port.name);
     97                unsafe { remove_callback(&id, &name, port.input()) };
     98            } else {
     99                i += 1;
    100            }
    101        }
    102    }
    103 
    104    fn add_new_ports(
    105        self: &mut MidirWrapper,
    106        ports: Vec<MidiPortWrapper>,
    107        add_callback: AddCallback,
    108    ) {
    109        for port in ports {
    110            if !self.is_port_present(&port) && !Self::is_microsoft_synth_output(&port) {
    111                let id = nsString::from(&port.id);
    112                let name = nsString::from(&port.name);
    113                unsafe { add_callback(&id, &name, port.input()) };
    114                self.ports.push(port);
    115            }
    116        }
    117    }
    118 
    119    fn is_port_present(self: &MidirWrapper, port: &MidiPortWrapper) -> bool {
    120        self.ports
    121            .iter()
    122            .any(|p| p.name == port.name && p.input() == port.input())
    123    }
    124 
    125    // We explicitly disable Microsoft's soft synthesizer, see bug 1798097
    126    fn is_microsoft_synth_output(port: &MidiPortWrapper) -> bool {
    127        !port.input() && (port.name == "Microsoft GS Wavetable Synth")
    128    }
    129 
    130    fn open_port(
    131        self: &mut MidirWrapper,
    132        nsid: &nsString,
    133        timestamp: GeckoTimeStamp,
    134        callback: unsafe extern "C" fn(
    135            id: &nsString,
    136            data: *const u8,
    137            length: usize,
    138            timestamp: &GeckoTimeStamp,
    139            micros: u64,
    140        ),
    141    ) -> Result<(), ()> {
    142        let id = nsid.to_string();
    143        let connections = &mut self.connections;
    144        let port = self.ports.iter_mut().find(|e| e.id.eq(&id));
    145        if let Some(port) = port {
    146            if port.open_count == 0 {
    147                let connection = match &port.port {
    148                    MidiPort::Input(port) => {
    149                        let input = MidiInput::new("WebMIDI input").map_err(|_err| ())?;
    150                        let data = CallbackData {
    151                            nsid: nsid.clone(),
    152                            open_timestamp: timestamp,
    153                        };
    154                        let connection = input
    155                            .connect(
    156                                port,
    157                                "Input connection",
    158                                move |stamp, message, data| unsafe {
    159                                    callback(
    160                                        &data.nsid,
    161                                        message.as_ptr(),
    162                                        message.len(),
    163                                        &data.open_timestamp,
    164                                        stamp,
    165                                    );
    166                                },
    167                                data,
    168                            )
    169                            .map_err(|_err| ())?;
    170                        MidiConnectionWrapper {
    171                            id: id.clone(),
    172                            connection: MidiConnection::Input(connection),
    173                        }
    174                    }
    175                    MidiPort::Output(port) => {
    176                        let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?;
    177                        let connection = output
    178                            .connect(port, "Output connection")
    179                            .map_err(|_err| ())?;
    180                        MidiConnectionWrapper {
    181                            connection: MidiConnection::Output(connection),
    182                            id: id.clone(),
    183                        }
    184                    }
    185                };
    186 
    187                connections.push(connection);
    188            }
    189 
    190            port.open_count += 1;
    191            return Ok(());
    192        }
    193 
    194        Err(())
    195    }
    196 
    197    fn close_port(self: &mut MidirWrapper, id: &str) {
    198        let port = self.ports.iter_mut().find(|e| e.id.eq(&id)).unwrap();
    199        port.open_count -= 1;
    200 
    201        if port.open_count > 0 {
    202            return;
    203        }
    204 
    205        let connections = &mut self.connections;
    206        let index = connections.iter().position(|e| e.id.eq(id)).unwrap();
    207        let connection_wrapper = connections.remove(index);
    208 
    209        match connection_wrapper.connection {
    210            MidiConnection::Input(connection) => {
    211                connection.close();
    212            }
    213            MidiConnection::Output(connection) => {
    214                connection.close();
    215            }
    216        }
    217    }
    218 
    219    fn send(self: &mut MidirWrapper, id: &str, data: &[u8]) -> Result<(), ()> {
    220        let connections = &mut self.connections;
    221        let index = connections.iter().position(|e| e.id.eq(id)).ok_or(())?;
    222        let connection_wrapper = connections.get_mut(index).unwrap();
    223 
    224        match &mut connection_wrapper.connection {
    225            MidiConnection::Output(connection) => {
    226                connection.send(data).map_err(|_err| ())?;
    227            }
    228            _ => {
    229                panic!("Sending on an input port!");
    230            }
    231        }
    232 
    233        Ok(())
    234    }
    235 }
    236 
    237 fn collect_ports() -> Result<Vec<MidiPortWrapper>, InitError> {
    238    let input = MidiInput::new("WebMIDI input")?;
    239    let output = MidiOutput::new("WebMIDI output")?;
    240    let mut ports = Vec::<MidiPortWrapper>::new();
    241    collect_input_ports(&input, &mut ports);
    242    collect_output_ports(&output, &mut ports);
    243    Ok(ports)
    244 }
    245 
    246 impl MidirWrapper {
    247    fn new() -> Result<MidirWrapper, InitError> {
    248        let ports = Vec::new();
    249        let connections: Vec<MidiConnectionWrapper> = Vec::new();
    250        Ok(MidirWrapper { ports, connections })
    251    }
    252 }
    253 
    254 /// Create the C++ wrapper that will be used to talk with midir.
    255 ///
    256 /// This function will be exposed to C++
    257 ///
    258 /// # Safety
    259 ///
    260 /// This function deliberately leaks the wrapper because ownership is
    261 /// transfered to the C++ code. Use [midir_impl_shutdown()] to free it.
    262 #[no_mangle]
    263 pub unsafe extern "C" fn midir_impl_init(callback: AddCallback) -> *mut MidirWrapper {
    264    if let Ok(mut midir_impl) = MidirWrapper::new() {
    265        midir_impl.refresh(callback, None);
    266 
    267        // Gecko invokes this initialization on a separate thread from all the
    268        // other operations, so make it clear to Rust this needs to be Send.
    269        fn assert_send<T: Send>(_: &T) {}
    270        assert_send(&midir_impl);
    271 
    272        let midir_box = Box::new(midir_impl);
    273        // Leak the object as it will be owned by the C++ code from now on
    274        Box::leak(midir_box) as *mut _
    275    } else {
    276        ptr::null_mut()
    277    }
    278 }
    279 
    280 /// Refresh the list of ports.
    281 ///
    282 /// This function will be exposed to C++
    283 ///
    284 /// # Safety
    285 ///
    286 /// `wrapper` must be the pointer returned by [midir_impl_init()].
    287 #[no_mangle]
    288 pub unsafe extern "C" fn midir_impl_refresh(
    289    wrapper: *mut MidirWrapper,
    290    add_callback: AddCallback,
    291    remove_callback: RemoveCallback,
    292 ) {
    293    (*wrapper).refresh(add_callback, Some(remove_callback))
    294 }
    295 
    296 /// Shutdown midir and free the C++ wrapper.
    297 ///
    298 /// This function will be exposed to C++
    299 ///
    300 /// # Safety
    301 ///
    302 /// `wrapper` must be the pointer returned by [midir_impl_init()]. After this
    303 /// has been called the wrapper object will be destoyed and cannot be accessed
    304 /// anymore.
    305 #[no_mangle]
    306 pub unsafe extern "C" fn midir_impl_shutdown(wrapper: *mut MidirWrapper) {
    307    // The MidirImpl object will be automatically destroyed when the contents
    308    // of this box are automatically dropped at the end of the function
    309    let _midir_box = Box::from_raw(wrapper);
    310 }
    311 
    312 /// Open a MIDI port.
    313 ///
    314 /// This function will be exposed to C++
    315 ///
    316 /// # Safety
    317 ///
    318 /// `wrapper` must be the pointer returned by [midir_impl_init()].
    319 #[no_mangle]
    320 pub unsafe extern "C" fn midir_impl_open_port(
    321    wrapper: *mut MidirWrapper,
    322    nsid: *mut nsString,
    323    timestamp: *mut GeckoTimeStamp,
    324    callback: unsafe extern "C" fn(
    325        id: &nsString,
    326        data: *const u8,
    327        length: usize,
    328        timestamp: &GeckoTimeStamp,
    329        micros: u64,
    330    ),
    331 ) -> bool {
    332    (*wrapper)
    333        .open_port(nsid.as_ref().unwrap(), *timestamp, callback)
    334        .is_ok()
    335 }
    336 
    337 /// Close a MIDI port.
    338 ///
    339 /// This function will be exposed to C++
    340 ///
    341 /// # Safety
    342 ///
    343 /// `wrapper` must be the pointer returned by [midir_impl_init()].
    344 #[no_mangle]
    345 pub unsafe extern "C" fn midir_impl_close_port(wrapper: *mut MidirWrapper, id: *mut nsString) {
    346    (*wrapper).close_port(&(*id).to_string());
    347 }
    348 
    349 /// Send a message over a MIDI output port.
    350 ///
    351 /// This function will be exposed to C++
    352 ///
    353 /// # Safety
    354 ///
    355 /// `wrapper` must be the pointer returned by [midir_impl_init()].
    356 #[no_mangle]
    357 pub unsafe extern "C" fn midir_impl_send(
    358    wrapper: *mut MidirWrapper,
    359    id: *const nsAString,
    360    data: *const ThinVec<u8>,
    361 ) -> bool {
    362    (*wrapper)
    363        .send(&(*id).to_string(), (*data).as_slice())
    364        .is_ok()
    365 }
    366 
    367 fn collect_input_ports(input: &MidiInput, wrappers: &mut Vec<MidiPortWrapper>) {
    368    let ports = input.ports();
    369    for port in ports {
    370        let id = Uuid::new_v4()
    371            .as_hyphenated()
    372            .encode_lower(&mut Uuid::encode_buffer())
    373            .to_owned();
    374        let name = input
    375            .port_name(&port)
    376            .unwrap_or_else(|_| "unknown input port".to_string());
    377        let port = MidiPortWrapper {
    378            id,
    379            name,
    380            port: MidiPort::Input(port),
    381            open_count: 0,
    382        };
    383        wrappers.push(port);
    384    }
    385 }
    386 
    387 fn collect_output_ports(output: &MidiOutput, wrappers: &mut Vec<MidiPortWrapper>) {
    388    let ports = output.ports();
    389    for port in ports {
    390        let id = Uuid::new_v4()
    391            .as_hyphenated()
    392            .encode_lower(&mut Uuid::encode_buffer())
    393            .to_owned();
    394        let name = output
    395            .port_name(&port)
    396            .unwrap_or_else(|_| "unknown input port".to_string());
    397        let port = MidiPortWrapper {
    398            id,
    399            name,
    400            port: MidiPort::Output(port),
    401            open_count: 0,
    402        };
    403        wrappers.push(port);
    404    }
    405 }