use std::thread::sleep;
use std::time::Duration;
use anyhow::{Context, Result};
use evdev::{
uinput::VirtualDevice, AbsInfo, AbsoluteAxisCode, AttributeSet, EventType, InputEvent, KeyCode,
PropType, UinputAbsSetup,
};
pub struct AbsPointer {
device: VirtualDevice,
width: i32,
height: i32,
}
impl AbsPointer {
pub fn create(width: i32, height: i32) -> Result<Self> {
let width = width.max(1);
let height = height.max(1);
let abs_x =
UinputAbsSetup::new(AbsoluteAxisCode::ABS_X, AbsInfo::new(0, 0, width, 0, 0, 1));
let abs_y =
UinputAbsSetup::new(AbsoluteAxisCode::ABS_Y, AbsInfo::new(0, 0, height, 0, 0, 1));
let keys =
AttributeSet::from_iter([KeyCode::BTN_LEFT, KeyCode::BTN_RIGHT, KeyCode::BTN_MIDDLE]);
let props = AttributeSet::from_iter([PropType::DIRECT]);
let device = VirtualDevice::builder()
.context("uinput builder (is /dev/uinput writable?)")?
.name("computer-use-linux absolute pointer")
.with_properties(&props)?
.with_absolute_axis(&abs_x)?
.with_absolute_axis(&abs_y)?
.with_keys(&keys)?
.build()
.context("failed to create uinput absolute pointer device")?;
sleep(Duration::from_millis(500));
Ok(Self {
device,
width,
height,
})
}
pub fn move_to(&mut self, x: i32, y: i32) -> Result<()> {
let x = x.clamp(0, self.width);
let y = y.clamp(0, self.height);
self.device
.emit(&[
InputEvent::new_now(EventType::ABSOLUTE.0, AbsoluteAxisCode::ABS_X.0, x),
InputEvent::new_now(EventType::ABSOLUTE.0, AbsoluteAxisCode::ABS_Y.0, y),
])
.context("failed to emit absolute motion")?;
Ok(())
}
pub fn click(&mut self, x: i32, y: i32, button: PointerButton, count: u32) -> Result<()> {
self.move_to(x, y)?;
sleep(Duration::from_millis(30));
let code = button.key_code();
for _ in 0..count.max(1) {
self.device
.emit(&[InputEvent::new_now(EventType::KEY.0, code, 1)])?;
sleep(Duration::from_millis(30));
self.device
.emit(&[InputEvent::new_now(EventType::KEY.0, code, 0)])?;
sleep(Duration::from_millis(40));
}
Ok(())
}
pub fn drag(
&mut self,
start: (i32, i32),
end: (i32, i32),
button: PointerButton,
) -> Result<()> {
let code = button.key_code();
self.move_to(start.0, start.1)?;
sleep(Duration::from_millis(30));
self.device
.emit(&[InputEvent::new_now(EventType::KEY.0, code, 1)])?;
sleep(Duration::from_millis(40));
self.move_to(end.0, end.1)?;
sleep(Duration::from_millis(40));
self.device
.emit(&[InputEvent::new_now(EventType::KEY.0, code, 0)])?;
Ok(())
}
}
#[derive(Clone, Copy, Debug)]
pub enum PointerButton {
Left,
Right,
Middle,
}
impl PointerButton {
pub fn from_name(name: Option<&str>) -> Self {
match name.unwrap_or("left").to_ascii_lowercase().as_str() {
"right" => Self::Right,
"middle" => Self::Middle,
_ => Self::Left,
}
}
fn key_code(self) -> u16 {
match self {
Self::Left => KeyCode::BTN_LEFT.0,
Self::Right => KeyCode::BTN_RIGHT.0,
Self::Middle => KeyCode::BTN_MIDDLE.0,
}
}
}