113 lines
3.1 KiB
Rust
113 lines
3.1 KiB
Rust
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<Option<HidDevice>, 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();
|
|
}
|
|
}
|