summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMaxwell Beck <max@rastertail.net>2025-06-08 11:34:44 -0500
committerMaxwell Beck <max@rastertail.net>2025-06-08 11:34:44 -0500
commite15b681db958182ae112dc11f48330c0ece793a9 (patch)
tree3386f812fc74aade5412c436b6462c048936bee2 /src
Initial commit
Diffstat (limited to 'src')
-rw-r--r--src/main.rs138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..81fa7a8
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,138 @@
+use std::{
+    env::{self},
+    pin::pin,
+    time::Duration,
+};
+
+use bluer::{Session, Uuid};
+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");
+
+const REPORT_REF: Result<Uuid, uuid::Error> =
+    Uuid::try_parse("00002908-0000-1000-8000-00805f9b34fb");
+
+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();
+
+    let session = Session::new().await?;
+    let adapter = session.default_adapter().await?;
+
+    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()),
+                            _ => {}
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    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;
+    let mut timer = interval(Duration::from_millis(1));
+    loop {
+        select! {
+            _ = 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();
+
+                sdx *= 0.97;
+                sdy *= 0.97;
+            },
+            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;
+                }
+            }
+        }
+    }
+}