use super::keysym;
use super::{InputHandler, ScrollDirection};
use enigo::{Axis, Button, Coordinate, Direction, Enigo, Key, Keyboard, Mouse, Settings};
pub struct EnigoInput {
enigo: Enigo,
ctrl_alt_held: u8,
shift_held: u8,
}
impl EnigoInput {
pub fn new() -> Self {
Self {
enigo: Enigo::new(&Settings::default()).unwrap(),
ctrl_alt_held: 0,
shift_held: 0,
}
}
fn map_control_key(ks: u32) -> Option<Key> {
match ks {
0xFF08 => Some(Key::Backspace),
0xFF09 => Some(Key::Tab),
0xFF0D | 0xFF8D => Some(Key::Return),
0xFF1B => Some(Key::Escape),
0xFFFF => Some(Key::Delete),
0xFF50 => Some(Key::Home),
0xFF51 => Some(Key::LeftArrow),
0xFF52 => Some(Key::UpArrow),
0xFF53 => Some(Key::RightArrow),
0xFF54 => Some(Key::DownArrow),
0xFF55 => Some(Key::PageUp),
0xFF56 => Some(Key::PageDown),
0xFF57 => Some(Key::End),
0xFFBE => Some(Key::F1),
0xFFBF => Some(Key::F2),
0xFFC0 => Some(Key::F3),
0xFFC1 => Some(Key::F4),
0xFFC2 => Some(Key::F5),
0xFFC3 => Some(Key::F6),
0xFFC4 => Some(Key::F7),
0xFFC5 => Some(Key::F8),
0xFFC6 => Some(Key::F9),
0xFFC7 => Some(Key::F10),
0xFFC8 => Some(Key::F11),
0xFFC9 => Some(Key::F12),
0xFFE5 => Some(Key::CapsLock),
0xFF63 => Some(Key::Other(0x2D)), _ => None,
}
}
fn map_modifier(ks: u32) -> Option<Key> {
match ks {
0xFFE1 => Some(Key::LShift),
0xFFE2 => Some(Key::RShift),
0xFFE3 => Some(Key::LControl),
0xFFE4 => Some(Key::RControl),
0xFFE9 => Some(Key::Alt),
0xFFEA => Some(Key::Alt),
0xFFEB | 0xFFEC => Some(Key::Meta),
_ => None,
}
}
fn is_modifier(ks: u32) -> bool {
matches!(ks, 0xFFE1..=0xFFEE)
}
fn is_ctrl_or_alt(ks: u32) -> bool {
matches!(ks, 0xFFE3 | 0xFFE4 | 0xFFE9 | 0xFFEA)
}
fn is_shift(ks: u32) -> bool {
matches!(ks, 0xFFE1 | 0xFFE2)
}
fn char_to_vk(ch: char) -> Option<u16> {
match ch {
'0' | ')' => Some(0x30),
'1' | '!' => Some(0x31),
'2' | '@' => Some(0x32),
'3' | '#' => Some(0x33),
'4' | '$' => Some(0x34),
'5' | '%' => Some(0x35),
'6' | '^' => Some(0x36),
'7' | '&' => Some(0x37),
'8' | '*' => Some(0x38),
'9' | '(' => Some(0x39),
'a'..='z' => Some(ch as u16 - b'a' as u16 + 0x41),
'A'..='Z' => Some(ch as u16 - b'A' as u16 + 0x41),
';' | ':' => Some(0xBA), '=' | '+' => Some(0xBB), ',' | '<' => Some(0xBC), '-' | '_' => Some(0xBD), '.' | '>' => Some(0xBE), '/' | '?' => Some(0xBF), '`' | '~' => Some(0xC0), '[' | '{' => Some(0xDB), '\\' | '|' => Some(0xDC), ']' | '}' => Some(0xDD), '\'' | '"' => Some(0xDE), _ => None,
}
}
}
impl InputHandler for EnigoInput {
fn move_mouse(&mut self, x: u16, y: u16) {
let _ = self.enigo.move_mouse(x as i32, y as i32, Coordinate::Abs);
}
fn mouse_button(&mut self, button: u8, pressed: bool) {
let dir = if pressed {
Direction::Press
} else {
Direction::Release
};
let btn = match button {
1 => Button::Left,
2 => Button::Middle,
3 => Button::Right,
_ => return,
};
let _ = self.enigo.button(btn, dir);
}
fn scroll(&mut self, direction: ScrollDirection) {
let (length, axis) = match direction {
ScrollDirection::Up => (3, Axis::Vertical),
ScrollDirection::Down => (-3, Axis::Vertical),
ScrollDirection::Left => (-3, Axis::Horizontal),
ScrollDirection::Right => (3, Axis::Horizontal),
};
let _ = self.enigo.scroll(length, axis);
}
fn key_event(&mut self, ks: u32, down: bool) {
let dir = if down {
Direction::Press
} else {
Direction::Release
};
if Self::is_modifier(ks) {
if Self::is_ctrl_or_alt(ks) {
if down {
self.ctrl_alt_held += 1;
} else if self.ctrl_alt_held > 0 {
self.ctrl_alt_held -= 1;
}
}
if Self::is_shift(ks) {
if down {
self.shift_held += 1;
} else if self.shift_held > 0 {
self.shift_held -= 1;
}
}
if let Some(key) = Self::map_modifier(ks) {
let _ = self.enigo.key(key, dir);
}
return;
}
if let Some(key) = Self::map_control_key(ks) {
let _ = self.enigo.key(key, dir);
return;
}
if let Some(ch) = keysym::keysym_to_unicode(ks) {
let has_modifiers = self.ctrl_alt_held > 0 || self.shift_held > 0;
if let Some(vk) = Self::char_to_vk(ch) {
if has_modifiers {
let _ = self.enigo.key(Key::Other(vk.into()), dir);
} else if down {
let _ = self.enigo.text(&ch.to_string());
}
} else if self.ctrl_alt_held > 0 {
let _ = self.enigo.key(Key::Unicode(ch), dir);
} else if down {
let _ = self.enigo.text(&ch.to_string());
}
}
}
}