diff options
-rw-r--r-- | Cargo.lock | 191 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/main.rs | 264 |
3 files changed, 206 insertions, 251 deletions
diff --git a/Cargo.lock b/Cargo.lock index ac1596b..a873ec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] name = "bluer" version = "0.17.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -61,7 +73,7 @@ dependencies = [ "libc", "log", "macaddr", - "nix 0.29.0", + "nix", "num-derive", "num-traits", "pin-project", @@ -93,12 +105,6 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" @@ -126,12 +132,6 @@ dependencies = [ ] [[package]] -name = "custom_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" - -[[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -211,10 +211,16 @@ dependencies = [ ] [[package]] -name = "enum_derive" -version = "0.1.7" +name = "evdev" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406ac2a8c9eedf8af9ee1489bee9e50029278a6456c740f7454cf8a158abc816" +checksum = "a3c10865aeab1a7399b3c2d6046e8dcc7f5227b656f235ed63ef5ee45a47b8f8" +dependencies = [ + "bitvec", + "cfg-if", + "libc", + "nix", +] [[package]] name = "fnv" @@ -223,6 +229,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -348,12 +360,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "ioctl-sys" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" - -[[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -391,36 +397,6 @@ dependencies = [ ] [[package]] -name = "libudev" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" -dependencies = [ - "libc", - "libudev-sys", -] - -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "lock_api" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -460,25 +436,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", -] - -[[package]] -name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", ] @@ -518,29 +482,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "parking_lot" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] name = "pin-project" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -603,13 +544,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] -name = "redox_syscall" -version = "0.5.12" +name = "radium" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" -dependencies = [ - "bitflags", -] +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rustc-demangle" @@ -630,12 +568,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -668,22 +600,13 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" -dependencies = [ - "libc", -] - -[[package]] name = "siriremote" version = "0.1.0" dependencies = [ "bluer", + "evdev", "tokio", "tokio-stream", - "uinput-tokio", "uuid", ] @@ -697,12 +620,6 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] name = "socket2" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -763,6 +680,12 @@ dependencies = [ ] [[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] name = "tokio" version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -772,9 +695,7 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -803,31 +724,6 @@ dependencies = [ ] [[package]] -name = "uinput-sys" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aabddd8174ccadd600afeab346bb276cb1db5fafcf6a7c5c5708b8cc4b2cac7" -dependencies = [ - "ioctl-sys", - "libc", -] - -[[package]] -name = "uinput-tokio" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c66e97fd950d42439ab3695229e65631af82d2e6ffd418293583d5c06a5f56" -dependencies = [ - "custom_derive", - "enum_derive", - "libc", - "libudev", - "nix 0.28.0", - "tokio", - "uinput-sys", -] - -[[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1029,3 +925,12 @@ checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/Cargo.toml b/Cargo.toml index 66582e5..5dc7f97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] bluer = { version = "0.17.4", features = ["bluetoothd"] } +evdev = "0.13.1" tokio = "1.45.1" tokio-stream = "0.1.17" -uinput-tokio = "0.1.36" uuid = "1.17.0" diff --git a/src/main.rs b/src/main.rs index 81fa7a8..56586ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ -use std::{ - env::{self}, - pin::pin, - time::Duration, -}; +use std::{collections::HashSet, pin::pin, time::Duration}; -use bluer::{Session, Uuid}; +use bluer::{ + Adapter, AdapterEvent, Device, DiscoveryFilter, DiscoveryTransport, Session, Uuid, + gatt::{local::Profile, remote::Characteristic}, +}; +use evdev::{ + AbsInfo, AbsoluteAxisCode, AttributeSet, EventType, InputEvent, KeyCode, MiscCode, PropType, + SynchronizationCode, UinputAbsSetup, + uinput::{VirtualDevice, VirtualDeviceBuilder}, +}; use tokio::{select, time::interval}; use tokio_stream::StreamExt; -use uinput_tokio::event::{controller::Mouse, relative}; const HID_SERVICE: Result<Uuid, uuid::Error> = Uuid::try_parse("00001812-0000-1000-8000-00805f9b34fb"); @@ -19,120 +22,167 @@ const MAGIC_REPORT: u8 = 240; const BUTTON_REPORT: u8 = 251; const TOUCH_REPORT: u8 = 252; -#[tokio::main(flavor = "current_thread")] -async fn main() -> bluer::Result<()> { - let mut udevice = uinput_tokio::default() - .unwrap() - .name("siriremote") - .unwrap() - .event(Mouse::Left) - .unwrap() - .event(relative::Position::X) - .unwrap() - .event(relative::Position::Y) - .unwrap() - .create() - .await - .unwrap(); +struct RemoteCharateristics { + touch: Characteristic, + button: Characteristic, +} - let session = Session::new().await?; - let adapter = session.default_adapter().await?; +impl RemoteCharateristics { + async fn get(device: &Device) -> bluer::Result<Self> { + let mut touch = None; + let mut button = None; - let addr = env::args() - .nth(1) - .expect("must pass address") - .parse() - .expect("invalid address"); - - eprintln!("connecting..."); - let device = adapter.device(addr)?; - device.connect().await?; - - let mut touch_char = None; - let mut button_char = None; - for service in device.services().await? { - if service.uuid().await? == HID_SERVICE.unwrap() { - for characteristic in service.characteristics().await? { - for descriptor in characteristic.descriptors().await? { - if descriptor.uuid().await? == REPORT_REF.unwrap() { - let resp = descriptor.read().await?; - match resp[0] { - MAGIC_REPORT => characteristic.write(&[0xf0, 0x00]).await?, - TOUCH_REPORT => touch_char = Some(characteristic.clone()), - BUTTON_REPORT => button_char = Some(characteristic.clone()), - _ => {} + for service in device.services().await? { + if service.uuid().await? == HID_SERVICE.unwrap() { + for characteristic in service.characteristics().await? { + for descriptor in characteristic.descriptors().await? { + if descriptor.uuid().await? == REPORT_REF.unwrap() { + let resp = descriptor.read().await?; + match resp[0] { + // MAGIC_REPORT => characteristic.write(&[0xf0, 0x00]).await?, + TOUCH_REPORT => touch = Some(characteristic.clone()), + BUTTON_REPORT => button = Some(characteristic.clone()), + _ => {} + } } } } } } + + Ok(Self { + touch: touch.expect("did not find touch characteristic"), + button: button.expect("did not find button characteristic"), + }) + } +} + +async fn find_remote(adapter: &Adapter, rssi_thresh: i16) -> bluer::Result<Device> { + let mut uuids = HashSet::new(); + uuids.insert(HID_SERVICE.unwrap()); + + adapter + .set_discovery_filter(DiscoveryFilter { + transport: DiscoveryTransport::Le, + rssi: Some(rssi_thresh), + uuids, + ..Default::default() + }) + .await?; + let mut discover = pin!(adapter.discover_devices().await?); + while let Some(evt) = discover.next().await { + if let AdapterEvent::DeviceAdded(mac) = evt { + let device = adapter.device(mac)?; + if let Some(data) = device.manufacturer_data().await? { + if data.contains_key(&0x4c) { + return Ok(device); + } + } + } + } + panic!("discovery ended"); +} + +async fn run(adapter: &Adapter) -> bluer::Result<()> { + eprintln!("DBG searching"); + let remote = find_remote(&adapter, -25).await?; + + if !remote.is_paired().await? { + eprintln!("DBG pairing"); + remote.pair().await?; } - eprintln!("connected"); - let mut touch = pin!(touch_char.unwrap().notify().await?); - let mut button = pin!(button_char.unwrap().notify().await?); - let mut last = None; - let mut sdx = 0f32; - let mut sdy = 0f32; + eprintln!("DBG connecting"); + remote.connect().await?; + + let chars = RemoteCharateristics::get(&remote).await?; + let mut touch_stream = pin!(chars.touch.notify().await?); + + eprintln!("DBG starting"); + let mut keys = AttributeSet::new(); + keys.insert(KeyCode::BTN_TOUCH); + keys.insert(KeyCode::BTN_TOOL_FINGER); + + let mut msc = AttributeSet::new(); + msc.insert(MiscCode::MSC_TIMESTAMP); + + let mut props = AttributeSet::new(); + props.insert(PropType::POINTER); + + let mut uinput = VirtualDevice::builder() + .unwrap() + .name("Siri Remote") + .with_properties(&props) + .unwrap() + .with_keys(&keys) + .unwrap() + .with_absolute_axis(&UinputAbsSetup::new( + AbsoluteAxisCode::ABS_X, + AbsInfo::new(0, -2048, 2048, 32, 0, 16), + )) + .unwrap() + .with_absolute_axis(&UinputAbsSetup::new( + AbsoluteAxisCode::ABS_Y, + AbsInfo::new(0, -2048, 2048, 32, 0, 16), + )) + .unwrap() + .with_msc(&msc) + .unwrap() + .build() + .unwrap(); + let mut timer = interval(Duration::from_millis(1)); + let mut x = 0f32; + let mut y = 0f32; + let mut tx = 0f32; + let mut ty = 0f32; + let mut touch = 0; loop { select! { + ev = touch_stream.next() => { + let ev = ev.unwrap(); + tx = ((((ev[5] & 15) as u16) << 8) + ev[4] as u16) as f32; + ty = ((((ev[5] & !15) as u16) >> 4) + ((ev[6] as u16) << 4)) as f32; + if tx >= 2048.0 { + tx -= 4096.0; + } + if ty >= 2048.0 { + ty -= 4096.0; + } + + touch = if ev[3] > 0 { 1 } else { 0 }; + // ts = (((ev[2] as u16) << 8) + (ev[1] as u16)) as i32; + }, _ = timer.tick() => { - udevice.send(relative::Position::X, sdx as i32).await.unwrap(); - udevice.send(relative::Position::Y, sdy as i32).await.unwrap(); - udevice.synchronize().await.unwrap(); + x = 0.5 * x + 0.5 * tx; + y = 0.5 * y + 0.5 * ty; - sdx *= 0.97; - sdy *= 0.97; + uinput + .emit(&[ + InputEvent::new(EventType::KEY.0, KeyCode::BTN_TOUCH.0, touch), + InputEvent::new(EventType::KEY.0, KeyCode::BTN_TOOL_FINGER.0, touch), + InputEvent::new(EventType::ABSOLUTE.0, AbsoluteAxisCode::ABS_X.0, x as i32), + InputEvent::new(EventType::ABSOLUTE.0, AbsoluteAxisCode::ABS_Y.0, -y as i32), + InputEvent::new( + EventType::SYNCHRONIZATION.0, + SynchronizationCode::SYN_REPORT.0, + 0, + ), + ]) + .unwrap(); }, - v = button.next() => { - let v = v.unwrap(); - match (v[0], v[1]) { - (8, 0) => { - udevice.send(Mouse::Left, 1).await.unwrap(); - }, - (0, 0) => { - udevice.send(Mouse::Left, 0).await.unwrap(); - }, - (_, _) => (), - } - } - v = touch.next() => { - let v = v.unwrap(); - let ts = (((v[2] as u16) << 8) + (v[1] as u16)) as i32; - let touched = v[3] > 0; - - let x = ((((v[5] & 15) as u16) << 8) + v[4] as u16) as i32; - let y = ((((v[5] & !15) as u16) >> 4) + ((v[6] as u16) << 4)) as i32; - - if touched { - if let Some(&(xp, yp, tsp)) = last.as_ref() { - let mut dx: i32 = x - xp; - let mut dy: i32 = y - yp; - let mut dts: i32 = ts - tsp; - if dx >= 2048 { - dx -= 4096; - } - if dx <= -2048 { - dx += 4096; - } - if dy >= -2048 { - dy -= 4096; - } - if dy <= -2048 { - dy += 4096; - } - if dts <= -32768 { - dts += 65536; - } - sdx += (dx as f32) / (dts as f32); - sdy -= (dy as f32) / (dts as f32); - } - last = Some((x, y, ts)); - } else { - last = None; - } - } - } + }; } } + +#[tokio::main(flavor = "current_thread")] +async fn main() -> bluer::Result<()> { + let session = Session::new().await?; + let adapter = session.default_adapter().await?; + + if let Err(e) = run(&adapter).await { + eprintln!("ERR {e}"); + } + + Ok(()) +} |