titan2-touchpadd/src/evloop.rs
Peter Cai e15517aaf9 Emit simulated keyboard events from the keyboard uinput
Android doesn't like a mouse that also acts as a keyboard
2025-11-16 19:15:03 -05:00

124 lines
5 KiB
Rust

use std::sync::{Arc, Mutex};
use evdev::{
Device, EventType, InputEvent, KeyCode, RelativeAxisCode, SynchronizationCode,
uinput::VirtualDevice,
};
use tracing::{debug, info, warn};
use crate::{
constants::NUMERIC_KEYCODES,
gesture::{Gesture, GestureDetector, SwipeGesture},
keyboard::KeyboardHandler,
};
pub(crate) fn run_evloop(
keyboard_features_enabled: bool,
touchpad_dev: Device,
keyboard_dev: Device,
mut uinput_dev: VirtualDevice,
keyboard_uinput_dev: Option<Arc<Mutex<VirtualDevice>>>,
) -> eyre::Result<()> {
let inhibitor = KeyboardHandler::start(keyboard_dev, keyboard_uinput_dev.clone());
let detector = GestureDetector::start(keyboard_features_enabled, touchpad_dev, inhibitor)?;
info!("Main event loop started");
for gesture in detector {
let Ok(gesture) = gesture.inspect_err(|e| warn!("Could not construct touch state from events, ignoring the current SYN_REPORT: {:?}", e)) else {
continue;
};
debug!("Gesture acquired: {:?}", gesture);
match gesture {
Gesture::PointerMove(delta_x, delta_y) => {
debug!("Pointer move, deltaX={delta_x}, deltaY={delta_y}");
uinput_dev.emit(&[
InputEvent::new(EventType::RELATIVE.0, RelativeAxisCode::REL_X.0, delta_x),
InputEvent::new(EventType::RELATIVE.0, RelativeAxisCode::REL_Y.0, delta_y),
])?;
}
Gesture::Click => {
debug!("Left click!");
uinput_dev.emit(&[
InputEvent::new(EventType::KEY.0, KeyCode::BTN_LEFT.0, 1),
// Need a SYN_REPORT in between to make sure it registers as a click (two separate states)
InputEvent::new(
EventType::SYNCHRONIZATION.0,
SynchronizationCode::SYN_REPORT.0,
0,
),
InputEvent::new(EventType::KEY.0, KeyCode::BTN_LEFT.0, 0),
])?;
}
Gesture::LongClick => {
debug!("Right click!");
uinput_dev.emit(&[
InputEvent::new(EventType::KEY.0, KeyCode::BTN_RIGHT.0, 1),
// Need a SYN_REPORT in between to make sure it registers as a click (two separate states)
InputEvent::new(
EventType::SYNCHRONIZATION.0,
SynchronizationCode::SYN_REPORT.0,
0,
),
InputEvent::new(EventType::KEY.0, KeyCode::BTN_RIGHT.0, 0),
])?;
}
Gesture::DragStart => {
debug!("Drag started!");
uinput_dev.emit(&[InputEvent::new(EventType::KEY.0, KeyCode::BTN_LEFT.0, 1)])?;
}
Gesture::DragEnd => {
debug!("Drag ended!");
uinput_dev.emit(&[InputEvent::new(EventType::KEY.0, KeyCode::BTN_LEFT.0, 0)])?;
}
Gesture::VerticalScroll(val) => {
debug!("Vertical scroll!");
uinput_dev.emit(&[InputEvent::new(
EventType::RELATIVE.0,
RelativeAxisCode::REL_WHEEL_HI_RES.0,
val,
)])?;
}
Gesture::Swipe(swipe) => {
debug!("Swipe!");
let key = match swipe {
SwipeGesture::Left => KeyCode::KEY_LEFT,
SwipeGesture::Right => KeyCode::KEY_RIGHT,
SwipeGesture::Up => KeyCode::KEY_UP,
SwipeGesture::Down => KeyCode::KEY_DOWN,
};
if let Some(keyboard_uinput_dev) = keyboard_uinput_dev.as_ref() {
keyboard_uinput_dev.lock().unwrap().emit(&[
InputEvent::new(EventType::KEY.0, key.code(), 1),
InputEvent::new(
EventType::SYNCHRONIZATION.0,
SynchronizationCode::SYN_REPORT.0,
0,
),
InputEvent::new(EventType::KEY.0, key.code(), 0),
])?;
}
}
Gesture::TopRowDoubleTap(i) => {
debug!("Double-tapping top row!");
let key = NUMERIC_KEYCODES[i as usize];
if let Some(keyboard_uinput_dev) = keyboard_uinput_dev.as_ref() {
keyboard_uinput_dev.lock().unwrap().emit(&[
InputEvent::new(EventType::KEY.0, key.code(), 1),
InputEvent::new(
EventType::SYNCHRONIZATION.0,
SynchronizationCode::SYN_REPORT.0,
0,
),
InputEvent::new(EventType::KEY.0, key.code(), 0),
])?;
}
}
}
}
Ok(())
}