use std::{collections::HashSet, pin::pin, time::Duration}; 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; const HID_SERVICE: Result = Uuid::try_parse("00001812-0000-1000-8000-00805f9b34fb"); const REPORT_REF: Result = Uuid::try_parse("00002908-0000-1000-8000-00805f9b34fb"); const MAGIC_REPORT: u8 = 240; const BUTTON_REPORT: u8 = 251; const TOUCH_REPORT: u8 = 252; struct RemoteCharateristics { touch: Characteristic, button: Characteristic, } impl RemoteCharateristics { async fn get(device: &Device) -> bluer::Result { let mut touch = None; let mut button = 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 = 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 { 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!("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() => { x = 0.5 * x + 0.5 * tx; y = 0.5 * y + 0.5 * ty; 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(); }, }; } } #[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(()) }