use std::{collections::HashSet, pin::pin, time::Duration}; use bluer::{ Adapter, AdapterEvent, Device, DeviceEvent, DeviceProperty, DiscoveryFilter, DiscoveryTransport, Session, Uuid, gatt::remote::Characteristic, }; use evdev::{ AbsInfo, AbsoluteAxisCode, AttributeSet, EventType, InputEvent, KeyCode, PropType, SynchronizationCode, UinputAbsSetup, uinput::VirtualDevice, }; 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 mut remote_events = pin!(remote.events().await?); let chars = RemoteCharateristics::get(&remote).await?; let mut touch_stream = pin!(chars.touch.notify().await?); let mut button_stream = pin!(chars.button.notify().await?); eprintln!("DBG starting"); let mut keys = AttributeSet::new(); keys.insert(KeyCode::BTN_TOUCH); keys.insert(KeyCode::BTN_TOOL_FINGER); keys.insert(KeyCode::BTN_LEFT); 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() .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; 'main: loop { select! { ev = remote_events.next() => { if let Some(DeviceEvent::PropertyChanged(DeviceProperty::Connected(false))) = ev { break 'main; } } ev = touch_stream.next() => { let ev = ev.unwrap(); let touch_next = if ev[3] > 0 { 1 } else { 0 }; if touch_next > 0 { 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; } if touch == 0 { x = tx; y = ty; } } touch = touch_next; // ts = (((ev[2] as u16) << 8) + (ev[1] as u16)) as i32; }, ev = button_stream.next() => { let ev = ev.unwrap(); match (ev[0], ev[1]) { (8, 0) => { uinput .emit(&[ InputEvent::new(EventType::KEY.0, KeyCode::BTN_LEFT.0, 1), InputEvent::new( EventType::SYNCHRONIZATION.0, SynchronizationCode::SYN_REPORT.0, 0, ), ]) .unwrap(); }, (0, 0) => { uinput .emit(&[ InputEvent::new(EventType::KEY.0, KeyCode::BTN_LEFT.0, 0), InputEvent::new( EventType::SYNCHRONIZATION.0, SynchronizationCode::SYN_REPORT.0, 0, ), ]) .unwrap(); }, (_, _) => (), } } _ = timer.tick() => { x = 0.95 * x + 0.05 * tx; y = 0.95 * y + 0.05 * 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(); }, }; } Ok(()) } #[tokio::main(flavor = "current_thread")] async fn main() -> bluer::Result<()> { let session = Session::new().await?; let adapter = session.default_adapter().await?; loop { if let Err(e) = run(&adapter).await { eprintln!("ERR {e}"); } } }