From 7844d4f6ebe26f53971fd515ceffa0ae2ef29ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20V=C3=B6gele?= Date: Wed, 16 Feb 2022 23:17:05 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + Cargo.lock | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 ++++ rustfmt.toml | 1 + src/main.rs | 91 +++++++++++++++++++++++++ 5 files changed, 292 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rustfmt.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a3f6de5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,187 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "headsetd" +version = "0.1.0" +dependencies = [ + "hidapi", + "libpulse-binding", + "pulsectl-rs", + "thiserror", +] + +[[package]] +name = "hidapi" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd21b5ac4a597bf657ed92a5b078f46a011837bda14afb7a07a5c1157c33ac2" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "libc" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" + +[[package]] +name = "libpulse-binding" +version = "2.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17be42160017e0ae993c03bfdab4ecb6f82ce3f8d515bd8da8fdf18d10703663" +dependencies = [ + "bitflags", + "libc", + "libpulse-sys", + "num-derive", + "num-traits", + "winapi", +] + +[[package]] +name = "libpulse-sys" +version = "1.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991e6bd0efe2a36e6534e136e7996925e4c1a8e35b7807fe533f2beffff27c30" +dependencies = [ + "libc", + "num-derive", + "num-traits", + "pkg-config", + "winapi", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pulsectl-rs" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a988bceed1981b2c5fc4a3da0e4e073fdaff8e6bd022b089f54bc573dc3cfc" +dependencies = [ + "libpulse-binding", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..ee2abf5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "headsetd" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hidapi = {version="1.3.3", default-features=false, features=["linux-shared-hidraw"]} +libpulse-binding = "2.26.0" +pulsectl-rs = "0.3.2" +thiserror = "1.0.30" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..218e203 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a871fa1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,91 @@ +use hidapi::{HidApi, HidDevice, HidError}; +use libpulse_binding::proplist::Proplist; +use pulsectl::controllers::{types::DeviceInfo, DeviceControl, SinkController}; + +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_sink_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 handle_report(device: &HidDevice, report: &[u8]) -> Result<(), HidError> { + if report[0] == 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_sink_headset(*sink)).unwrap(); + sink_controller + .set_default_device(new_sink.name.as_ref().unwrap()) + .unwrap(); + } 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_sink_headset(*sink)).unwrap(); + sink_controller + .set_default_device(headset.name.as_ref().unwrap()) + .unwrap(); + } + } + } + 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(); + } +}