use hidapi::{HidApi, HidDevice, HidError}; use libpulse_binding::proplist::Proplist; use pulsectl::controllers::{types::DeviceInfo, DeviceControl, SinkController, SourceController, AppControl}; trait Headset { const VENDOR_ID: u16; const PRODUCT_ID: u16; } struct G733; impl Headset for G733 { const VENDOR_ID: u16 = 0x046d; const PRODUCT_ID: u16 = 0x0ab5; } fn find_device() -> Result, HidError> { let api = HidApi::new()?; for device in api.device_list() { if device.vendor_id() != G733::VENDOR_ID || device.product_id() != G733::PRODUCT_ID { continue; } return Ok(Some(device.open_device(&api)?)); } Ok(None) } fn hex_prop_is(proplist: &Proplist, prop_name: &str, expected: u16) -> bool { proplist .get_str(prop_name) .map(|prop| u16::from_str_radix(&prop, 16).unwrap()) .map(|prop| prop == expected) .unwrap_or(false) } fn is_device_headset(sink: &DeviceInfo) -> bool { let proplist = &sink.proplist; if !hex_prop_is(proplist, "device.vendor.id", G733::VENDOR_ID) { return false; } if !hex_prop_is(proplist, "device.product.id", G733::PRODUCT_ID) { return false; } return true; } fn switch_sink(new_sink: &DeviceInfo, sink_controller: &mut SinkController) { let old_sink = sink_controller.get_default_device().unwrap(); for app in sink_controller.list_applications().unwrap() { if app.connection_id == old_sink.index { sink_controller.move_app_by_index(app.index, new_sink.index).unwrap(); } } sink_controller.set_default_device(new_sink.name.as_ref().unwrap()).unwrap(); } fn handle_report(device: &HidDevice, report: &[u8]) -> Result<(), HidError> { match report[0] { 8 => { // Mute button let mut source_controller = SourceController::create().unwrap(); let sources = source_controller.list_devices().unwrap(); let mic = sources .iter() .find(|source| source.monitor.is_none() && is_device_headset(*source)) .unwrap(); // Invert mute state of pulse device source_controller.set_device_mute_by_index(mic.index, !mic.mute); } 17 => { if report[2] == 8 { // Power event let mut sink_controller = SinkController::create().unwrap(); if report[4] == 0 { // Power off // Set default sink to non headset let sinks = sink_controller.list_devices().unwrap(); let new_sink = sinks.iter().find(|sink| !is_device_headset(*sink)).unwrap(); switch_sink(new_sink, &mut sink_controller); } else { // Power on event // Turn off the lights device.write(&[ 0x11, 0xff, 0x04, 0x3c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ])?; // Set default sink to the headset let sinks = sink_controller.list_devices().unwrap(); let headset = sinks.iter().find(|sink| is_device_headset(*sink)).unwrap(); switch_sink(headset, &mut sink_controller); } } } _ => {} } Ok(()) } fn main() { let mut buf = [0; 100]; let device = find_device().unwrap().expect("No supported headset found"); loop { let size = device.read(&mut buf).unwrap(); let report = &buf[0..size]; handle_report(&device, report).unwrap(); } }