use glam::Vec2; use idmap::{idmap, IdMap}; use idmap_derive::IntegerId; use input_linux::{ AbsoluteAxis, AbsoluteInfo, AbsoluteInfoSetup, EventKind, InputId, Key, RelativeAxis, UInputHandle, }; use libc::{input_event, timeval}; use once_cell::sync::Lazy; use std::mem::transmute; use std::{fs::File, sync::atomic::AtomicBool}; use strum::{EnumIter, EnumString, IntoEnumIterator}; use xkbcommon::xkb; #[cfg(feature = "wayland")] mod wayland; #[cfg(feature = "x11")] mod x11; pub static USE_UINPUT: AtomicBool = AtomicBool::new(true); pub fn initialize() -> Box { if !USE_UINPUT.load(std::sync::atomic::Ordering::Relaxed) { log::info!("Uinput disabled by user."); return Box::new(DummyProvider {}); } if let Some(uinput) = UInputProvider::try_new() { log::info!("Initialized uinput."); return Box::new(uinput); } log::error!("Could not create uinput provider. Keyboard/Mouse input will not work!"); log::error!("To check if you're in input group, run: id -nG"); if let Ok(user) = std::env::var("USER") { log::error!( "To add yourself to the input group, run: sudo usermod -aG input {}", user ); log::error!("After adding yourself to the input group, you will need to reboot."); } Box::new(DummyProvider {}) } pub trait HidProvider { fn mouse_move(&mut self, pos: Vec2); fn send_button(&mut self, button: u16, down: bool); fn wheel(&mut self, delta: i32); fn set_modifiers(&mut self, mods: u8); fn send_key(&self, key: VirtualKey, down: bool); fn set_desktop_extent(&mut self, extent: Vec2); fn set_desktop_origin(&mut self, origin: Vec2); fn commit(&mut self); } struct MouseButtonAction { button: u16, down: bool, } #[derive(Default)] struct MouseAction { last_requested_pos: Option, pos: Option, button: Option, scroll: Option, } pub struct UInputProvider { handle: UInputHandle, desktop_extent: Vec2, desktop_origin: Vec2, cur_modifiers: u8, current_action: MouseAction, } pub struct DummyProvider; pub const MOUSE_LEFT: u16 = 0x110; pub const MOUSE_RIGHT: u16 = 0x111; pub const MOUSE_MIDDLE: u16 = 0x112; const MOUSE_EXTENT: f32 = 32768.; const EV_SYN: u16 = 0x0; const EV_KEY: u16 = 0x1; const EV_REL: u16 = 0x2; const EV_ABS: u16 = 0x3; impl UInputProvider { fn try_new() -> Option { if let Ok(file) = File::create("/dev/uinput") { let handle = UInputHandle::new(file); let id = InputId { bustype: 0x03, vendor: 0x4711, product: 0x0829, version: 5, }; let name = b"WlxOverlay-S Keyboard-Mouse Hybrid Thing\0"; let abs_info = vec![ AbsoluteInfoSetup { axis: input_linux::AbsoluteAxis::X, info: AbsoluteInfo { value: 0, minimum: 0, maximum: MOUSE_EXTENT as _, fuzz: 0, flat: 0, resolution: 10, }, }, AbsoluteInfoSetup { axis: input_linux::AbsoluteAxis::Y, info: AbsoluteInfo { value: 0, minimum: 0, maximum: MOUSE_EXTENT as _, fuzz: 0, flat: 0, resolution: 10, }, }, ]; if handle.set_evbit(EventKind::Key).is_err() { return None; } if handle.set_evbit(EventKind::Absolute).is_err() { return None; } if handle.set_evbit(EventKind::Relative).is_err() { return None; } for btn in MOUSE_LEFT..=MOUSE_MIDDLE { let key: Key = unsafe { transmute(btn) }; if handle.set_keybit(key).is_err() { return None; } } for key in VirtualKey::iter() { let key: Key = unsafe { transmute((key as u16) - 8) }; if handle.set_keybit(key).is_err() { return None; } } if handle.set_absbit(AbsoluteAxis::X).is_err() { return None; } if handle.set_absbit(AbsoluteAxis::Y).is_err() { return None; } if handle.set_relbit(RelativeAxis::Wheel).is_err() { return None; } if handle.create(&id, name, 0, &abs_info).is_ok() { return Some(UInputProvider { handle, desktop_extent: Vec2::ZERO, desktop_origin: Vec2::ZERO, current_action: Default::default(), cur_modifiers: 0, }); } } None } fn send_button_internal(&self, button: u16, down: bool) { let time = get_time(); let events = [ new_event(time, EV_KEY, button, down as _), new_event(time, EV_SYN, 0, 0), ]; if let Err(res) = self.handle.write(&events) { log::error!("send_button: {}", res.to_string()); } } fn mouse_move_internal(&mut self, pos: Vec2) { #[cfg(debug_assertions)] log::trace!("Mouse move: {:?}", pos); let pos = (pos - self.desktop_origin) * (MOUSE_EXTENT / self.desktop_extent); let time = get_time(); let events = [ new_event(time, EV_ABS, AbsoluteAxis::X as _, pos.x as i32), new_event(time, EV_ABS, AbsoluteAxis::Y as _, pos.y as i32), new_event(time, EV_SYN, 0, 0), ]; if let Err(res) = self.handle.write(&events) { log::error!("{}", res.to_string()); } } fn wheel_internal(&self, delta: i32) { let time = get_time(); let events = [ new_event(time, EV_REL, RelativeAxis::Wheel as _, delta), new_event(time, EV_SYN, 0, 0), ]; if let Err(res) = self.handle.write(&events) { log::error!("wheel: {}", res.to_string()); } } } impl HidProvider for UInputProvider { fn set_modifiers(&mut self, modifiers: u8) { let changed = self.cur_modifiers ^ modifiers; for i in 0..8 { let m = 1 << i; if changed & m != 0 { if let Some(vk) = MODS_TO_KEYS.get(m).into_iter().flatten().next() { self.send_key(*vk, modifiers & m != 0); } } } self.cur_modifiers = modifiers; } fn send_key(&self, key: VirtualKey, down: bool) { log::debug!("send_key: {:?} {}", key, down); let time = get_time(); let events = [ new_event(time, EV_KEY, (key as u16) - 8, down as _), new_event(time, EV_SYN, 0, 0), ]; if let Err(res) = self.handle.write(&events) { log::error!("send_key: {}", res.to_string()); } } fn set_desktop_extent(&mut self, extent: Vec2) { self.desktop_extent = extent; } fn set_desktop_origin(&mut self, origin: Vec2) { self.desktop_origin = origin; } fn mouse_move(&mut self, pos: Vec2) { if self.current_action.pos.is_none() { self.current_action.pos = Some(pos); } self.current_action.last_requested_pos = Some(pos); } fn send_button(&mut self, button: u16, down: bool) { if self.current_action.button.is_none() { self.current_action.button = Some(MouseButtonAction { button, down }); self.current_action.pos = self.current_action.last_requested_pos; } } fn wheel(&mut self, delta: i32) { if self.current_action.scroll.is_none() { self.current_action.scroll = Some(delta); } } fn commit(&mut self) { if let Some(pos) = self.current_action.pos.take() { self.mouse_move_internal(pos); } if let Some(button) = self.current_action.button.take() { self.send_button_internal(button.button, button.down); } if let Some(scroll) = self.current_action.scroll.take() { self.wheel_internal(scroll); } } } impl HidProvider for DummyProvider { fn mouse_move(&mut self, _pos: Vec2) {} fn send_button(&mut self, _button: u16, _down: bool) {} fn wheel(&mut self, _delta: i32) {} fn set_modifiers(&mut self, _modifiers: u8) {} fn send_key(&self, _key: VirtualKey, _down: bool) {} fn set_desktop_extent(&mut self, _extent: Vec2) {} fn set_desktop_origin(&mut self, _origin: Vec2) {} fn commit(&mut self) {} } #[inline] fn get_time() -> timeval { let mut time = timeval { tv_sec: 0, tv_usec: 0, }; unsafe { libc::gettimeofday(&mut time, std::ptr::null_mut()) }; time } #[inline] fn new_event(time: timeval, type_: u16, code: u16, value: i32) -> input_event { input_event { time, type_, code, value, } } pub type KeyModifier = u8; pub const SHIFT: KeyModifier = 0x01; pub const CAPS_LOCK: KeyModifier = 0x02; pub const CTRL: KeyModifier = 0x04; pub const ALT: KeyModifier = 0x08; pub const NUM_LOCK: KeyModifier = 0x10; pub const SUPER: KeyModifier = 0x40; pub const META: KeyModifier = 0x80; #[allow(non_camel_case_types)] #[repr(u16)] #[derive(Debug, PartialEq, Clone, Copy, IntegerId, EnumString, EnumIter)] pub enum VirtualKey { Escape = 9, N1, // number row N2, N3, N4, N5, N6, N7, N8, N9, N0, Minus, Plus, BackSpace, Tab, Q, W, E, R, T, Y, U, I, O, P, Oem4, // [ { Oem6, // ] } Return, LCtrl, A, S, D, F, G, H, J, K, L, Oem1, // ; : Oem7, // ' " Oem3, // ` ~ LShift, Oem5, // \ | Z, X, C, V, B, N, M, Comma, // , < Period, // . > Oem2, // / ? RShift, KP_Multiply, LAlt, Space, Caps, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, NumLock, Scroll, KP_7, // KeyPad KP_8, KP_9, KP_Subtract, KP_4, KP_5, KP_6, KP_Add, KP_1, KP_2, KP_3, KP_0, KP_Decimal, Oem102 = 94, // Optional key usually between LShift and Z F11, F12, AbntC1, Katakana, Hiragana, Henkan, Kana, Muhenkan, KP_Enter = 104, RCtrl, KP_Divide, Print, Meta, // Right Alt aka AltGr Home = 110, Up, Prior, Left, Right, End, Down, Next, Insert, Delete, XF86AudioMute = 121, XF86AudioLowerVolume, XF86AudioRaiseVolume, Pause = 127, AbntC2 = 129, Hangul, Hanja, LSuper = 133, RSuper, Menu, Help = 146, XF86MenuKB, XF86Sleep = 150, XF86Xfer = 155, XF86Launch1, XF86Launch2, XF86WWW, XF86Mail = 163, XF86Favorites, XF86MyComputer, XF86Back, XF86Forward, XF86AudioNext = 171, XF86AudioPlay, XF86AudioPrev, XF86AudioStop, XF86HomePage = 180, XF86Reload, F13 = 191, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, Hyper = 207, XF86Launch3, XF86Launch4, XF86LaunchB, XF86Search = 225, } pub static KEYS_TO_MODS: Lazy> = Lazy::new(|| { idmap! { VirtualKey::LShift => SHIFT, VirtualKey::RShift => SHIFT, VirtualKey::Caps => CAPS_LOCK, VirtualKey::LCtrl => CTRL, VirtualKey::RCtrl => CTRL, VirtualKey::LAlt => ALT, VirtualKey::NumLock => NUM_LOCK, VirtualKey::LSuper => SUPER, VirtualKey::RSuper => SUPER, VirtualKey::Meta => META, } }); pub static MODS_TO_KEYS: Lazy>> = Lazy::new(|| { idmap! { SHIFT => vec![VirtualKey::LShift, VirtualKey::RShift], CAPS_LOCK => vec![VirtualKey::Caps], CTRL => vec![VirtualKey::LCtrl, VirtualKey::RCtrl], ALT => vec![VirtualKey::LAlt], NUM_LOCK => vec![VirtualKey::NumLock], SUPER => vec![VirtualKey::LSuper, VirtualKey::RSuper], META => vec![VirtualKey::Meta], } }); pub enum KeyType { Symbol, NumPad, Other, } macro_rules! key_between { ($key:expr, $start:expr, $end:expr) => { $key as u32 >= $start as u32 && $key as u32 <= $end as u32 }; } macro_rules! key_is { ($key:expr, $val:expr) => { $key as u32 == $val as u32 }; } pub fn get_key_type(key: VirtualKey) -> KeyType { if key_between!(key, VirtualKey::N1, VirtualKey::Plus) || key_between!(key, VirtualKey::Q, VirtualKey::Oem6) || key_between!(key, VirtualKey::A, VirtualKey::Oem3) || key_between!(key, VirtualKey::Oem5, VirtualKey::Oem2) || key_is!(key, VirtualKey::Oem102) { KeyType::Symbol } else if key_between!(key, VirtualKey::KP_7, VirtualKey::KP_0) && !key_is!(key, VirtualKey::KP_Subtract) && !key_is!(key, VirtualKey::KP_Add) { KeyType::NumPad } else { KeyType::Other } } pub struct XkbKeymap { pub context: xkb::Context, pub keymap: xkb::Keymap, } impl XkbKeymap { pub fn label_for_key(&self, key: VirtualKey, modifier: KeyModifier) -> String { let mut state = xkb::State::new(&self.keymap); if modifier > 0 { if let Some(mod_key) = MODS_TO_KEYS.get(modifier) { state.update_key( xkb::Keycode::from(mod_key[0] as u32), xkb::KeyDirection::Down, ); } } state.key_get_utf8(xkb::Keycode::from(key as u32)) } pub fn has_altgr(&self) -> bool { let state0 = xkb::State::new(&self.keymap); let mut state1 = xkb::State::new(&self.keymap); state1.update_key( xkb::Keycode::from(VirtualKey::Meta as u32), xkb::KeyDirection::Down, ); for key in [ VirtualKey::N0, VirtualKey::N1, VirtualKey::N2, VirtualKey::N3, VirtualKey::N4, VirtualKey::N5, VirtualKey::N6, VirtualKey::N7, VirtualKey::N8, VirtualKey::N9, ] { let sym0 = state0.key_get_one_sym(xkb::Keycode::from(key as u32)); let sym1 = state1.key_get_one_sym(xkb::Keycode::from(key as u32)); if sym0 != sym1 { return true; } } false } } #[cfg(feature = "wayland")] pub use wayland::get_keymap_wl; #[cfg(not(feature = "wayland"))] pub fn get_keymap_wl() -> anyhow::Result { anyhow::bail!("Wayland support not enabled.") } #[cfg(feature = "x11")] pub use x11::get_keymap_x11; #[cfg(not(feature = "x11"))] pub fn get_keymap_x11() -> anyhow::Result { anyhow::bail!("X11 support not enabled.") }