use crate::error::Result;
use crate::platform::Simulation;
use crate::platform::linux::mapping::{button_to_platform, key_to_platform};
use crate::types::*;
use evdev::uinput::VirtualDevice;
use evdev::{AbsInfo, AbsoluteAxisCode, AttributeSet, KeyCode, RelativeAxisCode, UinputAbsSetup};
use smallvec::SmallVec;
use std::time::Duration;
use strum::IntoEnumIterator;
pub struct Backend {
device: VirtualDevice,
events: SmallVec<[evdev::InputEvent; 32]>,
pos_x: i32,
pos_y: i32,
screen: Screen,
screen_max_x: i32,
screen_max_y: i32,
}
impl Backend {
pub(crate) fn new(screen: Screen) -> Result<Self> {
let mut keys = AttributeSet::new();
for key in Key::iter() {
if let Some(k) = key_to_platform(key) {
keys.insert(k);
}
}
for button in Button::iter() {
if let Some(b) = button_to_platform(button) {
keys.insert(b);
}
}
let max_x = screen.left + screen.width;
let max_y = screen.top + screen.height;
let abs_setup_x = UinputAbsSetup::new(
AbsoluteAxisCode::ABS_X,
AbsInfo::new(0, screen.left, max_x, 0, 0, 0),
);
let abs_setup_y = UinputAbsSetup::new(
AbsoluteAxisCode::ABS_Y,
AbsInfo::new(0, screen.top, max_y, 0, 0, 0),
);
let relative_axes = AttributeSet::<RelativeAxisCode>::from_iter([
RelativeAxisCode::REL_X,
RelativeAxisCode::REL_Y,
RelativeAxisCode::REL_WHEEL,
RelativeAxisCode::REL_HWHEEL,
]);
let device = VirtualDevice::builder()?
.name("keyflow_device")
.with_keys(&keys)?
.with_absolute_axis(&abs_setup_x)?
.with_absolute_axis(&abs_setup_y)?
.with_relative_axes(&relative_axes)?
.build()?;
Ok(Self {
device,
events: SmallVec::new(),
pos_x: 0,
pos_y: 0,
screen,
screen_max_x: max_x,
screen_max_y: max_y,
})
}
fn emit(&mut self) -> Result<()> {
self.device.emit(&self.events)?;
self.events.clear();
Ok(())
}
fn add_key_or_button(&mut self, kc: KeyCode, action: Action) -> Result<()> {
match action {
Action::Press => {
self.events.push(*evdev::KeyEvent::new(kc, 1));
}
Action::Release => {
self.events.push(*evdev::KeyEvent::new(kc, 0));
}
Action::Click => {
self.events.push(*evdev::KeyEvent::new(kc, 1));
self.emit()?;
std::thread::sleep(Duration::from_millis(1));
self.events.push(*evdev::KeyEvent::new(kc, 0));
}
}
Ok(())
}
fn add_movement(&mut self, movement: Movement) {
match movement {
Movement::Relative { dx, dy } => {
self.events
.push(*evdev::RelativeAxisEvent::new(RelativeAxisCode::REL_X, dx));
self.events
.push(*evdev::RelativeAxisEvent::new(RelativeAxisCode::REL_Y, dy));
self.pos_x += dx;
self.pos_y += dy;
self.pos_x = self.pos_x.clamp(self.screen.left, self.screen_max_x);
self.pos_y = self.pos_y.clamp(self.screen.top, self.screen_max_y);
}
Movement::Absolute { x, y } => {
let x = x.clamp(self.screen.left, self.screen_max_x);
let y = y.clamp(self.screen.top, self.screen_max_y);
if self.pos_x == x && self.pos_y == y {
let nx = check_boundaries(x, self.screen_max_x);
let ny = check_boundaries(y, self.screen_max_y);
self.events
.push(*evdev::AbsoluteAxisEvent::new(AbsoluteAxisCode::ABS_X, nx));
self.events
.push(*evdev::AbsoluteAxisEvent::new(AbsoluteAxisCode::ABS_Y, ny));
}
self.events
.push(*evdev::AbsoluteAxisEvent::new(AbsoluteAxisCode::ABS_X, x));
self.events
.push(*evdev::AbsoluteAxisEvent::new(AbsoluteAxisCode::ABS_Y, y));
self.pos_x = x;
self.pos_y = x;
}
}
}
fn add_scroll(&mut self, scroll: Scroll) {
match scroll {
Scroll::Vertical(delta) => {
self.events.push(*evdev::RelativeAxisEvent::new(
RelativeAxisCode::REL_WHEEL,
delta,
));
}
Scroll::Horizontal(delta) => {
self.events.push(*evdev::RelativeAxisEvent::new(
RelativeAxisCode::REL_HWHEEL,
delta,
));
}
}
}
}
impl Simulation for Backend {
fn send_key(&mut self, key: Key, action: Action) -> Result<()> {
if let Some(evdev_key) = key_to_platform(key) {
self.add_key_or_button(evdev_key, action)?;
}
self.emit()
}
fn send_button(&mut self, button: Button, action: Action) -> Result<()> {
if let Some(evdev_button) = button_to_platform(button) {
self.add_key_or_button(evdev_button, action)?;
}
self.emit()
}
fn send_movement(&mut self, movement: Movement) -> Result<()> {
self.add_movement(movement);
self.emit()
}
fn send_scroll(&mut self, scroll: Scroll) -> Result<()> {
self.add_scroll(scroll);
self.emit()
}
fn send_batch(&mut self, batch: Vec<InputEvent>) -> Result<()> {
for event in batch {
match event {
InputEvent::KeyEvent { key, action } => {
if let Some(evdev_key) = key_to_platform(key) {
self.add_key_or_button(evdev_key, action)?;
}
}
InputEvent::ButtonEvent { button, action } => {
if let Some(evdev_button) = button_to_platform(button) {
self.add_key_or_button(evdev_button, action)?;
}
}
InputEvent::MouseMove(movement) => {
self.add_movement(movement);
}
InputEvent::MouseScroll(scroll) => {
self.add_scroll(scroll);
}
InputEvent::Delay(duration) => {
if !self.events.is_empty() {
self.emit()?;
}
std::thread::sleep(duration);
}
}
}
if !self.events.is_empty() {
self.emit()?;
}
Ok(())
}
}
fn check_boundaries(pos: i32, max: i32) -> i32 {
if pos <= max { pos + 1 } else { pos - 1 }
}