From ed114ff635d7fe884deba085e1a17e134ad74176 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Wed, 7 Feb 2024 21:42:54 +0000 Subject: [PATCH 01/16] Lets try and make this headless --- Cargo.lock | 25 ++++ lefthk-core/Cargo.toml | 15 ++- lefthk-core/src/] | 120 ++++++++++++++++++ lefthk-core/src/config/command/chord.rs | 2 +- lefthk-core/src/evdev.rs | 158 ++++++++++++++++++++++++ lefthk-core/src/lib.rs | 1 + lefthk-core/src/worker/context/chord.rs | 2 +- lefthk-core/src/worker/mod.rs | 124 ++++++++++--------- lefthk/src/config/mod.rs | 2 +- 9 files changed, 382 insertions(+), 67 deletions(-) create mode 100644 lefthk-core/src/] create mode 100644 lefthk-core/src/evdev.rs diff --git a/Cargo.lock b/Cargo.lock index 008a9aa..339a4be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,6 +135,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "evdev-rs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9812d5790fb6fcce449333eb6713dad335e8c979225ed98755c84a3987e06dba" +dependencies = [ + "bitflags 1.3.2", + "evdev-sys", + "libc", + "log", + "serde", +] + +[[package]] +name = "evdev-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ead42b547b15d47089c1243d907bcf0eb94e457046d3b315a26ac9c9e9ea6d" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -210,6 +234,7 @@ dependencies = [ name = "lefthk-core" version = "0.2.1" dependencies = [ + "evdev-rs", "inventory", "mio", "nix", diff --git a/lefthk-core/Cargo.toml b/lefthk-core/Cargo.toml index f4c5d72..cb94bcc 100644 --- a/lefthk-core/Cargo.toml +++ b/lefthk-core/Cargo.toml @@ -8,15 +8,24 @@ repository = "https://github.com/leftwm/lefthk" description = "A hotkey daemon for Adventurers" [dependencies] +evdev-rs = { version = "0.6.1", features = ["serde"] } mio = "0.8.0" -nix = {version = "0.27.1", features = ["fs", "signal"]} +nix = { version = "0.27.1", features = ["fs", "signal"] } signal-hook = "0.3.4" thiserror = "1.0.30" -tokio = { version = "1.14.0", features = ["fs", "io-util", "macros", "net", "rt-multi-thread", "sync", "time"] } +tokio = { version = "1.14.0", features = [ + "fs", + "io-util", + "macros", + "net", + "rt-multi-thread", + "sync", + "time", +] } x11-dl = "2.19.1" xdg = "2.4.0" ron = "0.8.0" -serde = { version = "1.0.145", features= ["derive"]} +serde = { version = "1.0.145", features = ["derive"] } inventory = "0.3.2" # logging diff --git a/lefthk-core/src/] b/lefthk-core/src/] new file mode 100644 index 0000000..11e0c9a --- /dev/null +++ b/lefthk-core/src/] @@ -0,0 +1,120 @@ +use evdev::Device; +use std::cmp::Ordering; +use std::path::PathBuf; + +use crate::errors::{self, Error, LeftError, Result}; + +#[derive(Debug, Clone)] +pub struct EvDev { + pub name: String, + pub path: PathBuf, + pub phys: String, +} + +impl EvDev { + pub fn with_path(path: PathBuf) -> Result { + let mut input = Device::open(path)?; + + Ok(Self { + name: input.name().unwrap_or("").to_string(), + phys: input.phys().unwrap_or("").to_string(), + path, + }) + } + + pub fn with_name(name: &str, phys: Option<&str>) -> Result { + let mut devices = Self::obtain_device_list()?; + + if let Some(phys) = phys { + match devices.iter().position(|item| item.phys == phys) { + Some(idx) => return Ok(devices.remove(idx)), + None => { + tracing::error!("Exiting due to error: 1"); + std::process::exit(1); + } + } + } + + let mut devices_with_name: Vec<_> = devices + .into_iter() + .filter(|item| item.name == name) + .collect(); + + if devices_with_name.is_empty() { + tracing::error!("Exiting due to error: No devices"); + std::process::exit(1); + } + + if devices_with_name.len() > 1 { + log::warn!("The following devices match name `{}`:", name); + for dev in &devices_with_name { + log::warn!("{:?}", dev); + } + log::warn!( + "evremap will use the first entry. If you want to \ + use one of the others, add the corresponding phys \ + value to your configuration, for example, \ + `phys = \"{}\"` for the second entry in the list.", + devices_with_name[1].phys + ); + } + + Ok(devices_with_name.remove(0)) + } + + fn obtain_device_list() -> Result> { + let mut devices = vec![]; + for entry in std::fs::read_dir("/dev/input")? { + let entry = entry?; + + if !entry + .file_name() + .to_str() + .unwrap_or("") + .starts_with("event") + { + continue; + } + let path = entry.path(); + if path.is_dir() { + continue; + } + + match EvDev::with_path(path) { + Ok(item) => devices.push(item), + Err(err) => log::error!("{:#}", err), + } + } + + // Order by name, but when multiple devices have the same name, + // order by the event device unit number + devices.sort_by(|a, b| match a.name.cmp(&b.name) { + Ordering::Equal => { + event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) + } + different => different, + }); + Ok(devices) + } +} + +fn event_number_from_path(path: &PathBuf) -> u32 { + match path.to_str() { + Some(s) => match s.rfind("event") { + Some(idx) => s[idx + 5..].parse().unwrap_or(0), + None => 0, + }, + None => 0, + } +} + +pub fn list_devices() -> Error { + let devices = EvDev::obtain_device_list()?; + for item in &devices { + println!("Name: {}", item.name); + println!("Path: {}", item.path.display()); + println!("Phys: {}", item.phys); + println!(); + } + Ok(()) +} diff --git a/lefthk-core/src/config/command/chord.rs b/lefthk-core/src/config/command/chord.rs index 25321de..b640b5f 100644 --- a/lefthk-core/src/config/command/chord.rs +++ b/lefthk-core/src/config/command/chord.rs @@ -32,7 +32,7 @@ impl Command for Chord { } fn execute(&self, worker: &mut Worker) -> Error { - worker.xwrap.grab_keys(&self.0); + // worker.xwrap.grab_keys(&self.0); worker.chord_ctx.keybinds = Some(self.0.clone()); Ok(()) } diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs new file mode 100644 index 0000000..cabdc4f --- /dev/null +++ b/lefthk-core/src/evdev.rs @@ -0,0 +1,158 @@ +use evdev_rs::{Device, DeviceWrapper}; +use std::cmp::Ordering; +use std::future::Future; +use std::os::fd::AsRawFd; +use std::path::PathBuf; +use std::pin::Pin; +use std::ptr; +use std::sync::Arc; +use std::task::{Context, Waker}; +use tokio::sync::{oneshot, Notify}; +use tokio::time::Duration; + +use crate::errors::{self, Error, LeftError, Result}; + +pub struct EvDev { + pub devices: Vec, + pub task_notify: Arc, + _task_guards: Vec>, +} + +// impl From<(PathBuf, Device)> for EvDev { +// fn from(value: (PathBuf, Device)) -> Self { +// Self { +// name: value.1.name().unwrap_or("").to_string(), +// phys: value.1.physical_path().unwrap_or("").to_string(), +// path: value.0, +// } +// } +// } + +impl EvDev { + pub fn new() -> Self { + let task_notify = Arc::new(Notify::new()); + + let mut task_guards: Vec> = vec![]; + let mut devices = vec![]; + for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { + let entry = errors::exit_on_error!(entry); + + if !entry + .file_name() + .to_str() + .unwrap_or("") + .starts_with("event") + { + continue; + } + let path = entry.path(); + if path.is_dir() { + continue; + } + + match device_with_path(path) { + Ok(item) => devices.push(item), + Err(err) => tracing::error!("{:#}", err), + } + } + devices + .iter() + .filter(|device| { + device.has(evdev_rs::enums::EventType::EV_KEY) + && device.phys().unwrap().contains("input0") + }) + .for_each(|device| { + let (guard, task_guard) = oneshot::channel(); + let notify = task_notify.clone(); + const SERVER: mio::Token = mio::Token(0); + let fd = device.file().as_raw_fd(); + let mut poll = errors::exit_on_error!(mio::Poll::new()); + let mut events = mio::Events::with_capacity(1); + errors::exit_on_error!(poll.registry().register( + &mut mio::unix::SourceFd(&fd), + SERVER, + mio::Interest::READABLE, + )); + let timeout = Duration::from_millis(100); + tokio::task::spawn_blocking(move || loop { + if guard.is_closed() { + println!("Bye"); + return; + } + + if let Err(err) = poll.poll(&mut events, Some(timeout)) { + tracing::warn!("Xlib socket poll failed with {:?}", err); + continue; + } + + events + .iter() + .filter(|event| SERVER == event.token()) + .for_each(|_| notify.notify_one()); + }); + task_guards.push(task_guard); + }); + + Self { + devices, + task_notify, + _task_guards: task_guards, + } + } + + pub fn wait_readable(&mut self) -> Pin>> { + let task_notify = self.task_notify.clone(); + Box::pin(async move { + task_notify.notified().await; + }) + } + + // fn obtain_device_list() -> Result> { + // let mut devices: Vec = evdev::enumerate() + // .filter(|(_, device)| { + // device + // .supported_keys() + // .map_or(false, |keys| keys.contains(evdev::Key::KEY_ENTER)) + // }) + // .map(|device| Self::from(device)) + // .collect(); + // + // // Order by name, but when multiple devices have the same name, + // // order by the event device unit number + // devices.sort_by(|a, b| { + // match event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) { + // Ordering::Equal => { + // event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) + // } + // different => different, + // } + // }); + // Ok(devices) + // } +} + +pub fn device_with_path(path: PathBuf) -> Result { + let f = std::fs::File::open(&path)?; + Ok(Device::new_from_path(path)?) +} + +// fn event_number_from_path(path: &PathBuf) -> u32 { +// match path.to_str() { +// Some(s) => match s.rfind("event") { +// Some(idx) => s[idx + 5..].parse().unwrap_or(0), +// None => 0, +// }, +// None => 0, +// } +// } +// +// pub fn list_devices() -> Error { +// let devices = EvDev::obtain_device_list()?; +// for item in &devices { +// println!("Name: {}", item.name); +// println!("Path: {}", item.path.display()); +// println!("Phys: {}", item.phys); +// println!(); +// } +// Ok(()) +// } diff --git a/lefthk-core/src/lib.rs b/lefthk-core/src/lib.rs index 5cc427a..a49abd6 100644 --- a/lefthk-core/src/lib.rs +++ b/lefthk-core/src/lib.rs @@ -3,6 +3,7 @@ mod tests; pub mod child; pub mod config; pub mod errors; +pub mod evdev; pub mod ipc; pub mod worker; pub mod xkeysym_lookup; diff --git a/lefthk-core/src/worker/context/chord.rs b/lefthk-core/src/worker/context/chord.rs index e1ae8d2..1f745c9 100644 --- a/lefthk-core/src/worker/context/chord.rs +++ b/lefthk-core/src/worker/context/chord.rs @@ -18,7 +18,7 @@ impl Chord { impl Worker { pub fn evaluate_chord(&mut self) { if self.chord_ctx.elapsed { - self.xwrap.grab_keys(&self.keybinds); + // self.xwrap.grab_keys(&self.keybinds); self.chord_ctx.keybinds = None; self.chord_ctx.elapsed = false; } diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 0f1a00a..23769f5 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -1,12 +1,13 @@ pub mod context; +use std::path::Path; + use crate::child::Children; use crate::config::{command, Keybind}; use crate::errors::{self, Error, LeftError}; +use crate::evdev; +use crate::evdev::EvDev; use crate::ipc::Pipe; -use crate::xkeysym_lookup; -use crate::xwrap::XWrap; -use x11_dl::xlib; use xdg::BaseDirectories; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -20,7 +21,7 @@ pub struct Worker { keybinds: Vec, base_directory: BaseDirectories, - pub xwrap: XWrap, + pub evdev: EvDev, pub children: Children, pub status: Status, @@ -34,30 +35,31 @@ impl Worker { status: Status::Continue, keybinds, base_directory, - xwrap: XWrap::new(), + evdev: EvDev::new(), children: Children::default(), chord_ctx: context::Chord::new(), } } pub async fn event_loop(mut self) -> Status { - self.xwrap.grab_keys(&self.keybinds); let mut pipe = self.get_pipe().await; while self.status == Status::Continue { - self.xwrap.flush(); - self.evaluate_chord(); tokio::select! { () = self.children.wait_readable() => { self.children.reap(); } - () = self.xwrap.wait_readable() => { - let event_in_queue = self.xwrap.queue_len(); - for _ in 0..event_in_queue { - let xlib_event = self.xwrap.get_next_event(); - self.handle_event(&xlib_event); + () = self.evdev.wait_readable() => { + for device in &self.evdev.devices { + while device.has_event_pending() { + match device.next_event(evdev_rs::ReadFlag::NORMAL) { + Ok((_, event)) if event.value == 1 => println!("{:?}", event), + Err(_) => println!("Boo"), + _ => {}, + } + } } } Some(command) = pipe.get_next_command() => { @@ -75,52 +77,52 @@ impl Worker { errors::exit_on_error!(Pipe::new(pipe_file).await) } - fn handle_event(&mut self, xlib_event: &xlib::XEvent) { - let error = match xlib_event.get_type() { - xlib::KeyPress => self.handle_key_press(&xlib::XKeyEvent::from(xlib_event)), - xlib::MappingNotify => { - self.handle_mapping_notify(&mut xlib::XMappingEvent::from(xlib_event)) - } - _ => return, - }; - errors::log_on_error!(error); - } - - fn handle_key_press(&mut self, event: &xlib::XKeyEvent) -> Error { - let key = self.xwrap.keycode_to_keysym(event.keycode); - let mask = xkeysym_lookup::clean_mask(event.state); - if let Some(keybind) = self.get_keybind((mask, key)) { - if let Ok(command) = command::denormalize(&keybind.command) { - return command.execute(self); - } - } else { - return Err(LeftError::CommandNotFound); - } - Ok(()) - } - - fn get_keybind(&self, mask_key_pair: (u32, u32)) -> Option { - let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds { - keybinds - } else { - &self.keybinds - }; - keybinds - .iter() - .find(|keybind| { - if let Some(key) = xkeysym_lookup::into_keysym(&keybind.key) { - let mask = xkeysym_lookup::into_modmask(&keybind.modifier); - return mask_key_pair == (mask, key); - } - false - }) - .cloned() - } - - fn handle_mapping_notify(&self, event: &mut xlib::XMappingEvent) -> Error { - if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard { - return self.xwrap.refresh_keyboard(event); - } - Ok(()) - } + // fn handle_event(&mut self, xlib_event: &xlib::XEvent) { + // let error = match xlib_event.get_type() { + // xlib::KeyPress => self.handle_key_press(&xlib::XKeyEvent::from(xlib_event)), + // xlib::MappingNotify => { + // self.handle_mapping_notify(&mut xlib::XMappingEvent::from(xlib_event)) + // } + // _ => return, + // }; + // errors::log_on_error!(error); + // } + // + // fn handle_key_press(&mut self, event: &xlib::XKeyEvent) -> Error { + // let key = self.xwrap.keycode_to_keysym(event.keycode); + // let mask = xkeysym_lookup::clean_mask(event.state); + // if let Some(keybind) = self.get_keybind((mask, key)) { + // if let Ok(command) = command::denormalize(&keybind.command) { + // return command.execute(self); + // } + // } else { + // return Err(LeftError::CommandNotFound); + // } + // Ok(()) + // } + // + // fn get_keybind(&self, mask_key_pair: (u32, u32)) -> Option { + // let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds { + // keybinds + // } else { + // &self.keybinds + // }; + // keybinds + // .iter() + // .find(|keybind| { + // if let Some(key) = xkeysym_lookup::into_keysym(&keybind.key) { + // let mask = xkeysym_lookup::into_modmask(&keybind.modifier); + // return mask_key_pair == (mask, key); + // } + // false + // }) + // .cloned() + // } + // + // fn handle_mapping_notify(&self, event: &mut xlib::XMappingEvent) -> Error { + // if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard { + // return self.xwrap.refresh_keyboard(event); + // } + // Ok(()) + // } } diff --git a/lefthk/src/config/mod.rs b/lefthk/src/config/mod.rs index 3ae27a2..dcfb93c 100644 --- a/lefthk/src/config/mod.rs +++ b/lefthk/src/config/mod.rs @@ -70,7 +70,7 @@ pub fn load() -> Result { let file_name = path.place_config_file("config.ron")?; if Path::new(&file_name).exists() { let contents = fs::read_to_string(file_name)?; - Config::try_from(contents)?; + return Config::try_from(contents); } Err(LeftError::NoConfigFound) } From e9daf21e6606d697ffbfaff93ba0871ab98f2b04 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Mon, 4 Mar 2024 18:27:01 +0000 Subject: [PATCH 02/16] keysym_lookup --- Cargo.lock | 207 +++++++++++++++------------------- lefthk-core/Cargo.toml | 1 - lefthk-core/src/] | 120 -------------------- lefthk-core/src/lib.rs | 3 +- lefthk-core/src/worker/mod.rs | 7 +- 5 files changed, 98 insertions(+), 240 deletions(-) delete mode 100644 lefthk-core/src/] diff --git a/Cargo.lock b/Cargo.lock index 339a4be..cd25a26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -72,9 +72,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" dependencies = [ "serde", ] @@ -87,12 +87,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" [[package]] name = "cfg-if" @@ -188,9 +185,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "indexmap" @@ -204,9 +201,9 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0508c56cfe9bfd5dfeb0c22ab9a6abfda2f27bdca422132e494266351ed8d83c" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "lazy_static" @@ -245,27 +242,26 @@ dependencies = [ "thiserror", "tokio", "tracing", - "x11-dl", "xdg", ] [[package]] name = "libc" -version = "0.2.151" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -278,24 +274,24 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -309,7 +305,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "libc", ] @@ -330,15 +326,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -369,46 +365,37 @@ checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -423,9 +410,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -451,7 +438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64", - "bitflags 2.4.1", + "bitflags 2.4.2", "serde", "serde_derive", ] @@ -464,11 +451,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -477,18 +464,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -525,18 +512,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -547,9 +534,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.41" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -558,46 +545,45 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -606,9 +592,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -616,9 +602,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -767,7 +753,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -787,17 +773,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -808,9 +794,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -820,9 +806,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -832,9 +818,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -844,9 +830,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -856,9 +842,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -868,9 +854,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -880,20 +866,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "xdg" diff --git a/lefthk-core/Cargo.toml b/lefthk-core/Cargo.toml index cb94bcc..23a912f 100644 --- a/lefthk-core/Cargo.toml +++ b/lefthk-core/Cargo.toml @@ -22,7 +22,6 @@ tokio = { version = "1.14.0", features = [ "sync", "time", ] } -x11-dl = "2.19.1" xdg = "2.4.0" ron = "0.8.0" serde = { version = "1.0.145", features = ["derive"] } diff --git a/lefthk-core/src/] b/lefthk-core/src/] deleted file mode 100644 index 11e0c9a..0000000 --- a/lefthk-core/src/] +++ /dev/null @@ -1,120 +0,0 @@ -use evdev::Device; -use std::cmp::Ordering; -use std::path::PathBuf; - -use crate::errors::{self, Error, LeftError, Result}; - -#[derive(Debug, Clone)] -pub struct EvDev { - pub name: String, - pub path: PathBuf, - pub phys: String, -} - -impl EvDev { - pub fn with_path(path: PathBuf) -> Result { - let mut input = Device::open(path)?; - - Ok(Self { - name: input.name().unwrap_or("").to_string(), - phys: input.phys().unwrap_or("").to_string(), - path, - }) - } - - pub fn with_name(name: &str, phys: Option<&str>) -> Result { - let mut devices = Self::obtain_device_list()?; - - if let Some(phys) = phys { - match devices.iter().position(|item| item.phys == phys) { - Some(idx) => return Ok(devices.remove(idx)), - None => { - tracing::error!("Exiting due to error: 1"); - std::process::exit(1); - } - } - } - - let mut devices_with_name: Vec<_> = devices - .into_iter() - .filter(|item| item.name == name) - .collect(); - - if devices_with_name.is_empty() { - tracing::error!("Exiting due to error: No devices"); - std::process::exit(1); - } - - if devices_with_name.len() > 1 { - log::warn!("The following devices match name `{}`:", name); - for dev in &devices_with_name { - log::warn!("{:?}", dev); - } - log::warn!( - "evremap will use the first entry. If you want to \ - use one of the others, add the corresponding phys \ - value to your configuration, for example, \ - `phys = \"{}\"` for the second entry in the list.", - devices_with_name[1].phys - ); - } - - Ok(devices_with_name.remove(0)) - } - - fn obtain_device_list() -> Result> { - let mut devices = vec![]; - for entry in std::fs::read_dir("/dev/input")? { - let entry = entry?; - - if !entry - .file_name() - .to_str() - .unwrap_or("") - .starts_with("event") - { - continue; - } - let path = entry.path(); - if path.is_dir() { - continue; - } - - match EvDev::with_path(path) { - Ok(item) => devices.push(item), - Err(err) => log::error!("{:#}", err), - } - } - - // Order by name, but when multiple devices have the same name, - // order by the event device unit number - devices.sort_by(|a, b| match a.name.cmp(&b.name) { - Ordering::Equal => { - event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) - } - different => different, - }); - Ok(devices) - } -} - -fn event_number_from_path(path: &PathBuf) -> u32 { - match path.to_str() { - Some(s) => match s.rfind("event") { - Some(idx) => s[idx + 5..].parse().unwrap_or(0), - None => 0, - }, - None => 0, - } -} - -pub fn list_devices() -> Error { - let devices = EvDev::obtain_device_list()?; - for item in &devices { - println!("Name: {}", item.name); - println!("Path: {}", item.path.display()); - println!("Phys: {}", item.phys); - println!(); - } - Ok(()) -} diff --git a/lefthk-core/src/lib.rs b/lefthk-core/src/lib.rs index a49abd6..f6a13db 100644 --- a/lefthk-core/src/lib.rs +++ b/lefthk-core/src/lib.rs @@ -5,9 +5,8 @@ pub mod config; pub mod errors; pub mod evdev; pub mod ipc; +pub mod keysym_lookup; pub mod worker; -pub mod xkeysym_lookup; -pub mod xwrap; /// The directory name for xdg pub const LEFTHK_DIR_NAME: &str = "lefthk"; diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 23769f5..49991c9 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -8,6 +8,8 @@ use crate::errors::{self, Error, LeftError}; use crate::evdev; use crate::evdev::EvDev; use crate::ipc::Pipe; +use evdev_rs::enums::EV_KEY; +use evdev_rs::ReadFlag; use xdg::BaseDirectories; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -21,6 +23,8 @@ pub struct Worker { keybinds: Vec, base_directory: BaseDirectories, + keys_pressed: Vec, + pub evdev: EvDev, pub children: Children, pub status: Status, @@ -35,6 +39,7 @@ impl Worker { status: Status::Continue, keybinds, base_directory, + keys_pressed: Vec::new(), evdev: EvDev::new(), children: Children::default(), chord_ctx: context::Chord::new(), @@ -54,7 +59,7 @@ impl Worker { () = self.evdev.wait_readable() => { for device in &self.evdev.devices { while device.has_event_pending() { - match device.next_event(evdev_rs::ReadFlag::NORMAL) { + match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { Ok((_, event)) if event.value == 1 => println!("{:?}", event), Err(_) => println!("Boo"), _ => {}, From 6fdb435f91d2416fc7beb6eded113840b9be4b8e Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Mon, 4 Mar 2024 18:27:21 +0000 Subject: [PATCH 03/16] keysym_lookup --- lefthk-core/src/keysym_lookup.rs | 624 +++++++++++++++++++++++++++++++ 1 file changed, 624 insertions(+) create mode 100644 lefthk-core/src/keysym_lookup.rs diff --git a/lefthk-core/src/keysym_lookup.rs b/lefthk-core/src/keysym_lookup.rs new file mode 100644 index 0000000..ea56202 --- /dev/null +++ b/lefthk-core/src/keysym_lookup.rs @@ -0,0 +1,624 @@ +use evdev_rs::enums::EV_KEY; + +// We allow this because this function is simply a mapping wrapper. +#[allow(clippy::too_many_lines)] +#[must_use] +pub fn into_key(key: &str) -> Option { + match key { + "Reserved" => Some(EV_KEY::KEY_RESERVED), + "Escape" => Some(EV_KEY::KEY_ESC), + "1" => Some(EV_KEY::KEY_1), + "2" => Some(EV_KEY::KEY_2), + "3" => Some(EV_KEY::KEY_3), + "4" => Some(EV_KEY::KEY_4), + "5" => Some(EV_KEY::KEY_5), + "6" => Some(EV_KEY::KEY_6), + "7" => Some(EV_KEY::KEY_7), + "8" => Some(EV_KEY::KEY_8), + "9" => Some(EV_KEY::KEY_9), + "0" => Some(EV_KEY::KEY_0), + "Minus" => Some(EV_KEY::KEY_MINUS), + "Equal" => Some(EV_KEY::KEY_EQUAL), + "Backspace" => Some(EV_KEY::KEY_BACKSPACE), + "Tab" => Some(EV_KEY::KEY_TAB), + "Q" => Some(EV_KEY::KEY_Q), + "W" => Some(EV_KEY::KEY_W), + "E" => Some(EV_KEY::KEY_E), + "R" => Some(EV_KEY::KEY_R), + "T" => Some(EV_KEY::KEY_T), + "Y" => Some(EV_KEY::KEY_Y), + "U" => Some(EV_KEY::KEY_U), + "I" => Some(EV_KEY::KEY_I), + "O" => Some(EV_KEY::KEY_O), + "P" => Some(EV_KEY::KEY_P), + "Leftbrace" => Some(EV_KEY::KEY_LEFTBRACE), + "Rightbrace" => Some(EV_KEY::KEY_RIGHTBRACE), + "Enter" => Some(EV_KEY::KEY_ENTER), + "Leftctrl" => Some(EV_KEY::KEY_LEFTCTRL), + "A" => Some(EV_KEY::KEY_A), + "S" => Some(EV_KEY::KEY_S), + "D" => Some(EV_KEY::KEY_D), + "F" => Some(EV_KEY::KEY_F), + "G" => Some(EV_KEY::KEY_G), + "H" => Some(EV_KEY::KEY_H), + "J" => Some(EV_KEY::KEY_J), + "K" => Some(EV_KEY::KEY_K), + "L" => Some(EV_KEY::KEY_L), + "Semicolon" => Some(EV_KEY::KEY_SEMICOLON), + "Apostrophe" => Some(EV_KEY::KEY_APOSTROPHE), + "Grave" => Some(EV_KEY::KEY_GRAVE), + "Leftshift" => Some(EV_KEY::KEY_LEFTSHIFT), + "Backslash" => Some(EV_KEY::KEY_BACKSLASH), + "Z" => Some(EV_KEY::KEY_Z), + "X" => Some(EV_KEY::KEY_X), + "C" => Some(EV_KEY::KEY_C), + "V" => Some(EV_KEY::KEY_V), + "B" => Some(EV_KEY::KEY_B), + "N" => Some(EV_KEY::KEY_N), + "M" => Some(EV_KEY::KEY_M), + "Comma" => Some(EV_KEY::KEY_COMMA), + "Dot" => Some(EV_KEY::KEY_DOT), + "Slash" => Some(EV_KEY::KEY_SLASH), + "Rightshift" => Some(EV_KEY::KEY_RIGHTSHIFT), + "Kpasterisk" => Some(EV_KEY::KEY_KPASTERISK), + "Leftalt" => Some(EV_KEY::KEY_LEFTALT), + "Space" => Some(EV_KEY::KEY_SPACE), + "Capslock" => Some(EV_KEY::KEY_CAPSLOCK), + "F1" => Some(EV_KEY::KEY_F1), + "F2" => Some(EV_KEY::KEY_F2), + "F3" => Some(EV_KEY::KEY_F3), + "F4" => Some(EV_KEY::KEY_F4), + "F5" => Some(EV_KEY::KEY_F5), + "F6" => Some(EV_KEY::KEY_F6), + "F7" => Some(EV_KEY::KEY_F7), + "F8" => Some(EV_KEY::KEY_F8), + "F9" => Some(EV_KEY::KEY_F9), + "F10" => Some(EV_KEY::KEY_F10), + "Numlock" => Some(EV_KEY::KEY_NUMLOCK), + "Scrolllock" => Some(EV_KEY::KEY_SCROLLLOCK), + "Kp7" => Some(EV_KEY::KEY_KP7), + "Kp8" => Some(EV_KEY::KEY_KP8), + "Kp9" => Some(EV_KEY::KEY_KP9), + "Kpminus" => Some(EV_KEY::KEY_KPMINUS), + "Kp4" => Some(EV_KEY::KEY_KP4), + "Kp5" => Some(EV_KEY::KEY_KP5), + "Kp6" => Some(EV_KEY::KEY_KP6), + "Kpplus" => Some(EV_KEY::KEY_KPPLUS), + "Kp1" => Some(EV_KEY::KEY_KP1), + "Kp2" => Some(EV_KEY::KEY_KP2), + "Kp3" => Some(EV_KEY::KEY_KP3), + "Kp0" => Some(EV_KEY::KEY_KP0), + "Kpdot" => Some(EV_KEY::KEY_KPDOT), + "Zenkakuhankaku" => Some(EV_KEY::KEY_ZENKAKUHANKAKU), + "102nd" => Some(EV_KEY::KEY_102ND), + "F11" => Some(EV_KEY::KEY_F11), + "F12" => Some(EV_KEY::KEY_F12), + "Ro" => Some(EV_KEY::KEY_RO), + "Katakana" => Some(EV_KEY::KEY_KATAKANA), + "Hiragana" => Some(EV_KEY::KEY_HIRAGANA), + "Henkan" => Some(EV_KEY::KEY_HENKAN), + "Katakanahiragana" => Some(EV_KEY::KEY_KATAKANAHIRAGANA), + "Muhenkan" => Some(EV_KEY::KEY_MUHENKAN), + "Kpjpcomma" => Some(EV_KEY::KEY_KPJPCOMMA), + "Kpenter" => Some(EV_KEY::KEY_KPENTER), + "Rightctrl" => Some(EV_KEY::KEY_RIGHTCTRL), + "Kpslash" => Some(EV_KEY::KEY_KPSLASH), + "Sysrq" => Some(EV_KEY::KEY_SYSRQ), + "Rightalt" => Some(EV_KEY::KEY_RIGHTALT), + "Linefeed" => Some(EV_KEY::KEY_LINEFEED), + "Home" => Some(EV_KEY::KEY_HOME), + "Up" => Some(EV_KEY::KEY_UP), + "Pageup" => Some(EV_KEY::KEY_PAGEUP), + "Left" => Some(EV_KEY::KEY_LEFT), + "Right" => Some(EV_KEY::KEY_RIGHT), + "End" => Some(EV_KEY::KEY_END), + "Down" => Some(EV_KEY::KEY_DOWN), + "Pagedown" => Some(EV_KEY::KEY_PAGEDOWN), + "Insert" => Some(EV_KEY::KEY_INSERT), + "Delete" => Some(EV_KEY::KEY_DELETE), + "Macro" => Some(EV_KEY::KEY_MACRO), + "Mute" => Some(EV_KEY::KEY_MUTE), + "Volumedown" => Some(EV_KEY::KEY_VOLUMEDOWN), + "Volumeup" => Some(EV_KEY::KEY_VOLUMEUP), + "Power" => Some(EV_KEY::KEY_POWER), + "Kpequal" => Some(EV_KEY::KEY_KPEQUAL), + "Kpplusminus" => Some(EV_KEY::KEY_KPPLUSMINUS), + "Pause" => Some(EV_KEY::KEY_PAUSE), + "Scale" => Some(EV_KEY::KEY_SCALE), + "Kpcomma" => Some(EV_KEY::KEY_KPCOMMA), + "Hangeul" => Some(EV_KEY::KEY_HANGEUL), + "Hanja" => Some(EV_KEY::KEY_HANJA), + "Yen" => Some(EV_KEY::KEY_YEN), + "Leftmeta" => Some(EV_KEY::KEY_LEFTMETA), + "Rightmeta" => Some(EV_KEY::KEY_RIGHTMETA), + "Compose" => Some(EV_KEY::KEY_COMPOSE), + "Stop" => Some(EV_KEY::KEY_STOP), + "Again" => Some(EV_KEY::KEY_AGAIN), + "Props" => Some(EV_KEY::KEY_PROPS), + "Undo" => Some(EV_KEY::KEY_UNDO), + "Front" => Some(EV_KEY::KEY_FRONT), + "Copy" => Some(EV_KEY::KEY_COPY), + "Open" => Some(EV_KEY::KEY_OPEN), + "Paste" => Some(EV_KEY::KEY_PASTE), + "Find" => Some(EV_KEY::KEY_FIND), + "Cut" => Some(EV_KEY::KEY_CUT), + "Help" => Some(EV_KEY::KEY_HELP), + "Menu" => Some(EV_KEY::KEY_MENU), + "Calc" => Some(EV_KEY::KEY_CALC), + "Setup" => Some(EV_KEY::KEY_SETUP), + "Sleep" => Some(EV_KEY::KEY_SLEEP), + "Wakeup" => Some(EV_KEY::KEY_WAKEUP), + "File" => Some(EV_KEY::KEY_FILE), + "Sendfile" => Some(EV_KEY::KEY_SENDFILE), + "Deletefile" => Some(EV_KEY::KEY_DELETEFILE), + "Xfer" => Some(EV_KEY::KEY_XFER), + "Prog1" => Some(EV_KEY::KEY_PROG1), + "Prog2" => Some(EV_KEY::KEY_PROG2), + "Www" => Some(EV_KEY::KEY_WWW), + "Msdos" => Some(EV_KEY::KEY_MSDOS), + "Coffee" => Some(EV_KEY::KEY_COFFEE), + "Rotate_display" => Some(EV_KEY::KEY_ROTATE_DISPLAY), + "Cyclewindows" => Some(EV_KEY::KEY_CYCLEWINDOWS), + "Mail" => Some(EV_KEY::KEY_MAIL), + "Bookmarks" => Some(EV_KEY::KEY_BOOKMARKS), + "Computer" => Some(EV_KEY::KEY_COMPUTER), + "Back" => Some(EV_KEY::KEY_BACK), + "Forward" => Some(EV_KEY::KEY_FORWARD), + "Closecd" => Some(EV_KEY::KEY_CLOSECD), + "Ejectcd" => Some(EV_KEY::KEY_EJECTCD), + "Ejectclosecd" => Some(EV_KEY::KEY_EJECTCLOSECD), + "Nextsong" => Some(EV_KEY::KEY_NEXTSONG), + "Playpause" => Some(EV_KEY::KEY_PLAYPAUSE), + "Previoussong" => Some(EV_KEY::KEY_PREVIOUSSONG), + "Stopcd" => Some(EV_KEY::KEY_STOPCD), + "Record" => Some(EV_KEY::KEY_RECORD), + "Rewind" => Some(EV_KEY::KEY_REWIND), + "Phone" => Some(EV_KEY::KEY_PHONE), + "Iso" => Some(EV_KEY::KEY_ISO), + "Config" => Some(EV_KEY::KEY_CONFIG), + "Homepage" => Some(EV_KEY::KEY_HOMEPAGE), + "Refresh" => Some(EV_KEY::KEY_REFRESH), + "Exit" => Some(EV_KEY::KEY_EXIT), + "Move" => Some(EV_KEY::KEY_MOVE), + "Edit" => Some(EV_KEY::KEY_EDIT), + "Scrollup" => Some(EV_KEY::KEY_SCROLLUP), + "Scrolldown" => Some(EV_KEY::KEY_SCROLLDOWN), + "Kpleftparen" => Some(EV_KEY::KEY_KPLEFTPAREN), + "Kprightparen" => Some(EV_KEY::KEY_KPRIGHTPAREN), + "New" => Some(EV_KEY::KEY_NEW), + "Redo" => Some(EV_KEY::KEY_REDO), + "F13" => Some(EV_KEY::KEY_F13), + "F14" => Some(EV_KEY::KEY_F14), + "F15" => Some(EV_KEY::KEY_F15), + "F16" => Some(EV_KEY::KEY_F16), + "F17" => Some(EV_KEY::KEY_F17), + "F18" => Some(EV_KEY::KEY_F18), + "F19" => Some(EV_KEY::KEY_F19), + "F20" => Some(EV_KEY::KEY_F20), + "F21" => Some(EV_KEY::KEY_F21), + "F22" => Some(EV_KEY::KEY_F22), + "F23" => Some(EV_KEY::KEY_F23), + "F24" => Some(EV_KEY::KEY_F24), + "Playcd" => Some(EV_KEY::KEY_PLAYCD), + "Pausecd" => Some(EV_KEY::KEY_PAUSECD), + "Prog3" => Some(EV_KEY::KEY_PROG3), + "Prog4" => Some(EV_KEY::KEY_PROG4), + "All_applications" => Some(EV_KEY::KEY_ALL_APPLICATIONS), + "Suspend" => Some(EV_KEY::KEY_SUSPEND), + "Close" => Some(EV_KEY::KEY_CLOSE), + "Play" => Some(EV_KEY::KEY_PLAY), + "Fastforward" => Some(EV_KEY::KEY_FASTFORWARD), + "Bassboost" => Some(EV_KEY::KEY_BASSBOOST), + "Print" => Some(EV_KEY::KEY_PRINT), + "Hp" => Some(EV_KEY::KEY_HP), + "Camera" => Some(EV_KEY::KEY_CAMERA), + "Sound" => Some(EV_KEY::KEY_SOUND), + "Question" => Some(EV_KEY::KEY_QUESTION), + "Email" => Some(EV_KEY::KEY_EMAIL), + "Chat" => Some(EV_KEY::KEY_CHAT), + "Search" => Some(EV_KEY::KEY_SEARCH), + "Connect" => Some(EV_KEY::KEY_CONNECT), + "Finance" => Some(EV_KEY::KEY_FINANCE), + "Sport" => Some(EV_KEY::KEY_SPORT), + "Shop" => Some(EV_KEY::KEY_SHOP), + "Alterase" => Some(EV_KEY::KEY_ALTERASE), + "Cancel" => Some(EV_KEY::KEY_CANCEL), + "Brightnessdown" => Some(EV_KEY::KEY_BRIGHTNESSDOWN), + "Brightnessup" => Some(EV_KEY::KEY_BRIGHTNESSUP), + "Media" => Some(EV_KEY::KEY_MEDIA), + "Switchvideomode" => Some(EV_KEY::KEY_SWITCHVIDEOMODE), + "Kbdillumtoggle" => Some(EV_KEY::KEY_KBDILLUMTOGGLE), + "Kbdillumdown" => Some(EV_KEY::KEY_KBDILLUMDOWN), + "Kbdillumup" => Some(EV_KEY::KEY_KBDILLUMUP), + "Send" => Some(EV_KEY::KEY_SEND), + "Reply" => Some(EV_KEY::KEY_REPLY), + "Forwardmail" => Some(EV_KEY::KEY_FORWARDMAIL), + "Save" => Some(EV_KEY::KEY_SAVE), + "Documents" => Some(EV_KEY::KEY_DOCUMENTS), + "Battery" => Some(EV_KEY::KEY_BATTERY), + "Bluetooth" => Some(EV_KEY::KEY_BLUETOOTH), + "Wlan" => Some(EV_KEY::KEY_WLAN), + "Uwb" => Some(EV_KEY::KEY_UWB), + "Unknown" => Some(EV_KEY::KEY_UNKNOWN), + "Video_next" => Some(EV_KEY::KEY_VIDEO_NEXT), + "Video_prev" => Some(EV_KEY::KEY_VIDEO_PREV), + "Brightness_cycle" => Some(EV_KEY::KEY_BRIGHTNESS_CYCLE), + "Brightness_auto" => Some(EV_KEY::KEY_BRIGHTNESS_AUTO), + "Display_off" => Some(EV_KEY::KEY_DISPLAY_OFF), + "Wwan" => Some(EV_KEY::KEY_WWAN), + "Rfkill" => Some(EV_KEY::KEY_RFKILL), + "Micmute" => Some(EV_KEY::KEY_MICMUTE), + "Ok" => Some(EV_KEY::KEY_OK), + "Select" => Some(EV_KEY::KEY_SELECT), + "Goto" => Some(EV_KEY::KEY_GOTO), + "Clear" => Some(EV_KEY::KEY_CLEAR), + "Power2" => Some(EV_KEY::KEY_POWER2), + "Option" => Some(EV_KEY::KEY_OPTION), + "Info" => Some(EV_KEY::KEY_INFO), + "Time" => Some(EV_KEY::KEY_TIME), + "Vendor" => Some(EV_KEY::KEY_VENDOR), + "Archive" => Some(EV_KEY::KEY_ARCHIVE), + "Program" => Some(EV_KEY::KEY_PROGRAM), + "Channel" => Some(EV_KEY::KEY_CHANNEL), + "Favorites" => Some(EV_KEY::KEY_FAVORITES), + "Epg" => Some(EV_KEY::KEY_EPG), + "Pvr" => Some(EV_KEY::KEY_PVR), + "Mhp" => Some(EV_KEY::KEY_MHP), + "Language" => Some(EV_KEY::KEY_LANGUAGE), + "Title" => Some(EV_KEY::KEY_TITLE), + "Subtitle" => Some(EV_KEY::KEY_SUBTITLE), + "Angle" => Some(EV_KEY::KEY_ANGLE), + "Full_screen" => Some(EV_KEY::KEY_FULL_SCREEN), + "Mode" => Some(EV_KEY::KEY_MODE), + "Keyboard" => Some(EV_KEY::KEY_KEYBOARD), + "Aspect_ratio" => Some(EV_KEY::KEY_ASPECT_RATIO), + "Pc" => Some(EV_KEY::KEY_PC), + "Tv" => Some(EV_KEY::KEY_TV), + "Tv2" => Some(EV_KEY::KEY_TV2), + "Vcr" => Some(EV_KEY::KEY_VCR), + "Vcr2" => Some(EV_KEY::KEY_VCR2), + "Sat" => Some(EV_KEY::KEY_SAT), + "Sat2" => Some(EV_KEY::KEY_SAT2), + "Cd" => Some(EV_KEY::KEY_CD), + "Tape" => Some(EV_KEY::KEY_TAPE), + "Radio" => Some(EV_KEY::KEY_RADIO), + "Tuner" => Some(EV_KEY::KEY_TUNER), + "Player" => Some(EV_KEY::KEY_PLAYER), + "Text" => Some(EV_KEY::KEY_TEXT), + "Dvd" => Some(EV_KEY::KEY_DVD), + "Aux" => Some(EV_KEY::KEY_AUX), + "Mp3" => Some(EV_KEY::KEY_MP3), + "Audio" => Some(EV_KEY::KEY_AUDIO), + "Video" => Some(EV_KEY::KEY_VIDEO), + "Directory" => Some(EV_KEY::KEY_DIRECTORY), + "List" => Some(EV_KEY::KEY_LIST), + "Memo" => Some(EV_KEY::KEY_MEMO), + "Calendar" => Some(EV_KEY::KEY_CALENDAR), + "Red" => Some(EV_KEY::KEY_RED), + "Green" => Some(EV_KEY::KEY_GREEN), + "Yellow" => Some(EV_KEY::KEY_YELLOW), + "Blue" => Some(EV_KEY::KEY_BLUE), + "Channelup" => Some(EV_KEY::KEY_CHANNELUP), + "Channeldown" => Some(EV_KEY::KEY_CHANNELDOWN), + "First" => Some(EV_KEY::KEY_FIRST), + "Last" => Some(EV_KEY::KEY_LAST), + "Ab" => Some(EV_KEY::KEY_AB), + "Next" => Some(EV_KEY::KEY_NEXT), + "Restart" => Some(EV_KEY::KEY_RESTART), + "Slow" => Some(EV_KEY::KEY_SLOW), + "Shuffle" => Some(EV_KEY::KEY_SHUFFLE), + "Break" => Some(EV_KEY::KEY_BREAK), + "Previous" => Some(EV_KEY::KEY_PREVIOUS), + "Digits" => Some(EV_KEY::KEY_DIGITS), + "Teen" => Some(EV_KEY::KEY_TEEN), + "Twen" => Some(EV_KEY::KEY_TWEN), + "Videophone" => Some(EV_KEY::KEY_VIDEOPHONE), + "Games" => Some(EV_KEY::KEY_GAMES), + "Zoomin" => Some(EV_KEY::KEY_ZOOMIN), + "Zoomout" => Some(EV_KEY::KEY_ZOOMOUT), + "Zoomreset" => Some(EV_KEY::KEY_ZOOMRESET), + "Wordprocessor" => Some(EV_KEY::KEY_WORDPROCESSOR), + "Editor" => Some(EV_KEY::KEY_EDITOR), + "Spreadsheet" => Some(EV_KEY::KEY_SPREADSHEET), + "Graphicseditor" => Some(EV_KEY::KEY_GRAPHICSEDITOR), + "Presentation" => Some(EV_KEY::KEY_PRESENTATION), + "Database" => Some(EV_KEY::KEY_DATABASE), + "News" => Some(EV_KEY::KEY_NEWS), + "Voicemail" => Some(EV_KEY::KEY_VOICEMAIL), + "Addressbook" => Some(EV_KEY::KEY_ADDRESSBOOK), + "Messenger" => Some(EV_KEY::KEY_MESSENGER), + "Displaytoggle" => Some(EV_KEY::KEY_DISPLAYTOGGLE), + "Spellcheck" => Some(EV_KEY::KEY_SPELLCHECK), + "Logoff" => Some(EV_KEY::KEY_LOGOFF), + "Dollar" => Some(EV_KEY::KEY_DOLLAR), + "Euro" => Some(EV_KEY::KEY_EURO), + "Frameback" => Some(EV_KEY::KEY_FRAMEBACK), + "Frameforward" => Some(EV_KEY::KEY_FRAMEFORWARD), + "Context_menu" => Some(EV_KEY::KEY_CONTEXT_MENU), + "Media_repeat" => Some(EV_KEY::KEY_MEDIA_REPEAT), + "10channelsup" => Some(EV_KEY::KEY_10CHANNELSUP), + "10channelsdown" => Some(EV_KEY::KEY_10CHANNELSDOWN), + "Images" => Some(EV_KEY::KEY_IMAGES), + "Notification_center" => Some(EV_KEY::KEY_NOTIFICATION_CENTER), + "Pickup_phone" => Some(EV_KEY::KEY_PICKUP_PHONE), + "Hangup_phone" => Some(EV_KEY::KEY_HANGUP_PHONE), + "Del_eol" => Some(EV_KEY::KEY_DEL_EOL), + "Del_eos" => Some(EV_KEY::KEY_DEL_EOS), + "Ins_line" => Some(EV_KEY::KEY_INS_LINE), + "Del_line" => Some(EV_KEY::KEY_DEL_LINE), + "Fn" => Some(EV_KEY::KEY_FN), + "Fn_esc" => Some(EV_KEY::KEY_FN_ESC), + "Fn_f1" => Some(EV_KEY::KEY_FN_F1), + "Fn_f2" => Some(EV_KEY::KEY_FN_F2), + "Fn_f3" => Some(EV_KEY::KEY_FN_F3), + "Fn_f4" => Some(EV_KEY::KEY_FN_F4), + "Fn_f5" => Some(EV_KEY::KEY_FN_F5), + "Fn_f6" => Some(EV_KEY::KEY_FN_F6), + "Fn_f7" => Some(EV_KEY::KEY_FN_F7), + "Fn_f8" => Some(EV_KEY::KEY_FN_F8), + "Fn_f9" => Some(EV_KEY::KEY_FN_F9), + "Fn_f10" => Some(EV_KEY::KEY_FN_F10), + "Fn_f11" => Some(EV_KEY::KEY_FN_F11), + "Fn_f12" => Some(EV_KEY::KEY_FN_F12), + "Fn_1" => Some(EV_KEY::KEY_FN_1), + "Fn_2" => Some(EV_KEY::KEY_FN_2), + "Fn_d" => Some(EV_KEY::KEY_FN_D), + "Fn_e" => Some(EV_KEY::KEY_FN_E), + "Fn_f" => Some(EV_KEY::KEY_FN_F), + "Fn_s" => Some(EV_KEY::KEY_FN_S), + "Fn_b" => Some(EV_KEY::KEY_FN_B), + "Fn_right_shift" => Some(EV_KEY::KEY_FN_RIGHT_SHIFT), + "Brl_dot1" => Some(EV_KEY::KEY_BRL_DOT1), + "Brl_dot2" => Some(EV_KEY::KEY_BRL_DOT2), + "Brl_dot3" => Some(EV_KEY::KEY_BRL_DOT3), + "Brl_dot4" => Some(EV_KEY::KEY_BRL_DOT4), + "Brl_dot5" => Some(EV_KEY::KEY_BRL_DOT5), + "Brl_dot6" => Some(EV_KEY::KEY_BRL_DOT6), + "Brl_dot7" => Some(EV_KEY::KEY_BRL_DOT7), + "Brl_dot8" => Some(EV_KEY::KEY_BRL_DOT8), + "Brl_dot9" => Some(EV_KEY::KEY_BRL_DOT9), + "Brl_dot10" => Some(EV_KEY::KEY_BRL_DOT10), + "Numeric_0" => Some(EV_KEY::KEY_NUMERIC_0), + "Numeric_1" => Some(EV_KEY::KEY_NUMERIC_1), + "Numeric_2" => Some(EV_KEY::KEY_NUMERIC_2), + "Numeric_3" => Some(EV_KEY::KEY_NUMERIC_3), + "Numeric_4" => Some(EV_KEY::KEY_NUMERIC_4), + "Numeric_5" => Some(EV_KEY::KEY_NUMERIC_5), + "Numeric_6" => Some(EV_KEY::KEY_NUMERIC_6), + "Numeric_7" => Some(EV_KEY::KEY_NUMERIC_7), + "Numeric_8" => Some(EV_KEY::KEY_NUMERIC_8), + "Numeric_9" => Some(EV_KEY::KEY_NUMERIC_9), + "Numeric_star" => Some(EV_KEY::KEY_NUMERIC_STAR), + "Numeric_pound" => Some(EV_KEY::KEY_NUMERIC_POUND), + "Numeric_a" => Some(EV_KEY::KEY_NUMERIC_A), + "Numeric_b" => Some(EV_KEY::KEY_NUMERIC_B), + "Numeric_c" => Some(EV_KEY::KEY_NUMERIC_C), + "Numeric_d" => Some(EV_KEY::KEY_NUMERIC_D), + "Camera_focus" => Some(EV_KEY::KEY_CAMERA_FOCUS), + "Wps_button" => Some(EV_KEY::KEY_WPS_BUTTON), + "Touchpad_toggle" => Some(EV_KEY::KEY_TOUCHPAD_TOGGLE), + "Touchpad_on" => Some(EV_KEY::KEY_TOUCHPAD_ON), + "Touchpad_off" => Some(EV_KEY::KEY_TOUCHPAD_OFF), + "Camera_zoomin" => Some(EV_KEY::KEY_CAMERA_ZOOMIN), + "Camera_zoomout" => Some(EV_KEY::KEY_CAMERA_ZOOMOUT), + "Camera_up" => Some(EV_KEY::KEY_CAMERA_UP), + "Camera_down" => Some(EV_KEY::KEY_CAMERA_DOWN), + "Camera_left" => Some(EV_KEY::KEY_CAMERA_LEFT), + "Camera_right" => Some(EV_KEY::KEY_CAMERA_RIGHT), + "Attendant_on" => Some(EV_KEY::KEY_ATTENDANT_ON), + "Attendant_off" => Some(EV_KEY::KEY_ATTENDANT_OFF), + "Attendant_toggle" => Some(EV_KEY::KEY_ATTENDANT_TOGGLE), + "Lights_toggle" => Some(EV_KEY::KEY_LIGHTS_TOGGLE), + "Als_toggle" => Some(EV_KEY::KEY_ALS_TOGGLE), + "Rotate_lock_toggle" => Some(EV_KEY::KEY_ROTATE_LOCK_TOGGLE), + "Buttonconfig" => Some(EV_KEY::KEY_BUTTONCONFIG), + "Taskmanager" => Some(EV_KEY::KEY_TASKMANAGER), + "Journal" => Some(EV_KEY::KEY_JOURNAL), + "Controlpanel" => Some(EV_KEY::KEY_CONTROLPANEL), + "Appselect" => Some(EV_KEY::KEY_APPSELECT), + "Screensaver" => Some(EV_KEY::KEY_SCREENSAVER), + "Voicecommand" => Some(EV_KEY::KEY_VOICECOMMAND), + "Assistant" => Some(EV_KEY::KEY_ASSISTANT), + "Kbd_layout_next" => Some(EV_KEY::KEY_KBD_LAYOUT_NEXT), + "Emoji_picker" => Some(EV_KEY::KEY_EMOJI_PICKER), + "Dictate" => Some(EV_KEY::KEY_DICTATE), + "Camera_access_enable" => Some(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), + "Camera_access_disable" => Some(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), + "Camera_access_toggle" => Some(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), + "Brightness_min" => Some(EV_KEY::KEY_BRIGHTNESS_MIN), + "Brightness_max" => Some(EV_KEY::KEY_BRIGHTNESS_MAX), + "Kbdinputassist_prev" => Some(EV_KEY::KEY_KBDINPUTASSIST_PREV), + "Kbdinputassist_next" => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXT), + "Kbdinputassist_prevgroup" => Some(EV_KEY::KEY_KBDINPUTASSIST_PREVGROUP), + "Kbdinputassist_nextgroup" => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXTGROUP), + "Kbdinputassist_accept" => Some(EV_KEY::KEY_KBDINPUTASSIST_ACCEPT), + "Kbdinputassist_cancel" => Some(EV_KEY::KEY_KBDINPUTASSIST_CANCEL), + "Right_up" => Some(EV_KEY::KEY_RIGHT_UP), + "Right_down" => Some(EV_KEY::KEY_RIGHT_DOWN), + "Left_up" => Some(EV_KEY::KEY_LEFT_UP), + "Left_down" => Some(EV_KEY::KEY_LEFT_DOWN), + "Root_menu" => Some(EV_KEY::KEY_ROOT_MENU), + "Media_top_menu" => Some(EV_KEY::KEY_MEDIA_TOP_MENU), + "Numeric_11" => Some(EV_KEY::KEY_NUMERIC_11), + "Numeric_12" => Some(EV_KEY::KEY_NUMERIC_12), + "Audio_desc" => Some(EV_KEY::KEY_AUDIO_DESC), + "3d_mode" => Some(EV_KEY::KEY_3D_MODE), + "Next_favorite" => Some(EV_KEY::KEY_NEXT_FAVORITE), + "Stop_record" => Some(EV_KEY::KEY_STOP_RECORD), + "Pause_record" => Some(EV_KEY::KEY_PAUSE_RECORD), + "Vod" => Some(EV_KEY::KEY_VOD), + "Unmute" => Some(EV_KEY::KEY_UNMUTE), + "Fastreverse" => Some(EV_KEY::KEY_FASTREVERSE), + "Slowreverse" => Some(EV_KEY::KEY_SLOWREVERSE), + "Data" => Some(EV_KEY::KEY_DATA), + "Onscreen_keyboard" => Some(EV_KEY::KEY_ONSCREEN_KEYBOARD), + "Privacy_screen_toggle" => Some(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), + "Selective_screenshot" => Some(EV_KEY::KEY_SELECTIVE_SCREENSHOT), + "Next_element" => Some(EV_KEY::KEY_NEXT_ELEMENT), + "Previous_element" => Some(EV_KEY::KEY_PREVIOUS_ELEMENT), + "Autopilot_engage_toggle" => Some(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), + "Mark_waypoint" => Some(EV_KEY::KEY_MARK_WAYPOINT), + "Sos" => Some(EV_KEY::KEY_SOS), + "Nav_chart" => Some(EV_KEY::KEY_NAV_CHART), + "Fishing_chart" => Some(EV_KEY::KEY_FISHING_CHART), + "Single_range_radar" => Some(EV_KEY::KEY_SINGLE_RANGE_RADAR), + "Dual_range_radar" => Some(EV_KEY::KEY_DUAL_RANGE_RADAR), + "Radar_overlay" => Some(EV_KEY::KEY_RADAR_OVERLAY), + "Traditional_sonar" => Some(EV_KEY::KEY_TRADITIONAL_SONAR), + "Clearvu_sonar" => Some(EV_KEY::KEY_CLEARVU_SONAR), + "Sidevu_sonar" => Some(EV_KEY::KEY_SIDEVU_SONAR), + "Nav_info" => Some(EV_KEY::KEY_NAV_INFO), + "Brightness_menu" => Some(EV_KEY::KEY_BRIGHTNESS_MENU), + "Macro1" => Some(EV_KEY::KEY_MACRO1), + "Macro2" => Some(EV_KEY::KEY_MACRO2), + "Macro3" => Some(EV_KEY::KEY_MACRO3), + "Macro4" => Some(EV_KEY::KEY_MACRO4), + "Macro5" => Some(EV_KEY::KEY_MACRO5), + "Macro6" => Some(EV_KEY::KEY_MACRO6), + "Macro7" => Some(EV_KEY::KEY_MACRO7), + "Macro8" => Some(EV_KEY::KEY_MACRO8), + "Macro9" => Some(EV_KEY::KEY_MACRO9), + "Macro10" => Some(EV_KEY::KEY_MACRO10), + "Macro11" => Some(EV_KEY::KEY_MACRO11), + "Macro12" => Some(EV_KEY::KEY_MACRO12), + "Macro13" => Some(EV_KEY::KEY_MACRO13), + "Macro14" => Some(EV_KEY::KEY_MACRO14), + "Macro15" => Some(EV_KEY::KEY_MACRO15), + "Macro16" => Some(EV_KEY::KEY_MACRO16), + "Macro17" => Some(EV_KEY::KEY_MACRO17), + "Macro18" => Some(EV_KEY::KEY_MACRO18), + "Macro19" => Some(EV_KEY::KEY_MACRO19), + "Macro20" => Some(EV_KEY::KEY_MACRO20), + "Macro21" => Some(EV_KEY::KEY_MACRO21), + "Macro22" => Some(EV_KEY::KEY_MACRO22), + "Macro23" => Some(EV_KEY::KEY_MACRO23), + "Macro24" => Some(EV_KEY::KEY_MACRO24), + "Macro25" => Some(EV_KEY::KEY_MACRO25), + "Macro26" => Some(EV_KEY::KEY_MACRO26), + "Macro27" => Some(EV_KEY::KEY_MACRO27), + "Macro28" => Some(EV_KEY::KEY_MACRO28), + "Macro29" => Some(EV_KEY::KEY_MACRO29), + "Macro30" => Some(EV_KEY::KEY_MACRO30), + "Macro_record_start" => Some(EV_KEY::KEY_MACRO_RECORD_START), + "Macro_record_stop" => Some(EV_KEY::KEY_MACRO_RECORD_STOP), + "Macro_preset_cycle" => Some(EV_KEY::KEY_MACRO_PRESET_CYCLE), + "Macro_preset1" => Some(EV_KEY::KEY_MACRO_PRESET1), + "Macro_preset2" => Some(EV_KEY::KEY_MACRO_PRESET2), + "Macro_preset3" => Some(EV_KEY::KEY_MACRO_PRESET3), + "Kbd_lcd_menu1" => Some(EV_KEY::KEY_KBD_LCD_MENU1), + "Kbd_lcd_menu2" => Some(EV_KEY::KEY_KBD_LCD_MENU2), + "Kbd_lcd_menu3" => Some(EV_KEY::KEY_KBD_LCD_MENU3), + "Kbd_lcd_menu4" => Some(EV_KEY::KEY_KBD_LCD_MENU4), + "Kbd_lcd_menu5" => Some(EV_KEY::KEY_KBD_LCD_MENU5), + "Max" => Some(EV_KEY::KEY_MAX), + "Btn_0" => Some(EV_KEY::BTN_0), + "Btn_1" => Some(EV_KEY::BTN_1), + "Btn_2" => Some(EV_KEY::BTN_2), + "Btn_3" => Some(EV_KEY::BTN_3), + "Btn_4" => Some(EV_KEY::BTN_4), + "Btn_5" => Some(EV_KEY::BTN_5), + "Btn_6" => Some(EV_KEY::BTN_6), + "Btn_7" => Some(EV_KEY::BTN_7), + "Btn_8" => Some(EV_KEY::BTN_8), + "Btn_9" => Some(EV_KEY::BTN_9), + "Btn_left" => Some(EV_KEY::BTN_LEFT), + "Btn_right" => Some(EV_KEY::BTN_RIGHT), + "Btn_middle" => Some(EV_KEY::BTN_MIDDLE), + "Btn_side" => Some(EV_KEY::BTN_SIDE), + "Btn_extra" => Some(EV_KEY::BTN_EXTRA), + "Btn_forward" => Some(EV_KEY::BTN_FORWARD), + "Btn_back" => Some(EV_KEY::BTN_BACK), + "Btn_task" => Some(EV_KEY::BTN_TASK), + "Btn_trigger" => Some(EV_KEY::BTN_TRIGGER), + "Btn_thumb" => Some(EV_KEY::BTN_THUMB), + "Btn_thumb2" => Some(EV_KEY::BTN_THUMB2), + "Btn_top" => Some(EV_KEY::BTN_TOP), + "Btn_top2" => Some(EV_KEY::BTN_TOP2), + "Btn_pinkie" => Some(EV_KEY::BTN_PINKIE), + "Btn_base" => Some(EV_KEY::BTN_BASE), + "Btn_base2" => Some(EV_KEY::BTN_BASE2), + "Btn_base3" => Some(EV_KEY::BTN_BASE3), + "Btn_base4" => Some(EV_KEY::BTN_BASE4), + "Btn_base5" => Some(EV_KEY::BTN_BASE5), + "Btn_base6" => Some(EV_KEY::BTN_BASE6), + "Btn_dead" => Some(EV_KEY::BTN_DEAD), + "Btn_south" => Some(EV_KEY::BTN_SOUTH), + "Btn_east" => Some(EV_KEY::BTN_EAST), + "Btn_c" => Some(EV_KEY::BTN_C), + "Btn_north" => Some(EV_KEY::BTN_NORTH), + "Btn_west" => Some(EV_KEY::BTN_WEST), + "Btn_z" => Some(EV_KEY::BTN_Z), + "Btn_tl" => Some(EV_KEY::BTN_TL), + "Btn_tr" => Some(EV_KEY::BTN_TR), + "Btn_tl2" => Some(EV_KEY::BTN_TL2), + "Btn_tr2" => Some(EV_KEY::BTN_TR2), + "Btn_select" => Some(EV_KEY::BTN_SELECT), + "Btn_start" => Some(EV_KEY::BTN_START), + "Btn_mode" => Some(EV_KEY::BTN_MODE), + "Btn_thumbl" => Some(EV_KEY::BTN_THUMBL), + "Btn_thumbr" => Some(EV_KEY::BTN_THUMBR), + "Btn_tool_pen" => Some(EV_KEY::BTN_TOOL_PEN), + "Btn_tool_rubber" => Some(EV_KEY::BTN_TOOL_RUBBER), + "Btn_tool_brush" => Some(EV_KEY::BTN_TOOL_BRUSH), + "Btn_tool_pencil" => Some(EV_KEY::BTN_TOOL_PENCIL), + "Btn_tool_airbrush" => Some(EV_KEY::BTN_TOOL_AIRBRUSH), + "Btn_tool_finger" => Some(EV_KEY::BTN_TOOL_FINGER), + "Btn_tool_mouse" => Some(EV_KEY::BTN_TOOL_MOUSE), + "Btn_tool_lens" => Some(EV_KEY::BTN_TOOL_LENS), + "Btn_tool_quinttap" => Some(EV_KEY::BTN_TOOL_QUINTTAP), + "Btn_stylus3" => Some(EV_KEY::BTN_STYLUS3), + "Btn_touch" => Some(EV_KEY::BTN_TOUCH), + "Btn_stylus" => Some(EV_KEY::BTN_STYLUS), + "Btn_stylus2" => Some(EV_KEY::BTN_STYLUS2), + "Btn_tool_doubletap" => Some(EV_KEY::BTN_TOOL_DOUBLETAP), + "Btn_tool_tripletap" => Some(EV_KEY::BTN_TOOL_TRIPLETAP), + "Btn_tool_quadtap" => Some(EV_KEY::BTN_TOOL_QUADTAP), + "Btn_gear_down" => Some(EV_KEY::BTN_GEAR_DOWN), + "Btn_gear_up" => Some(EV_KEY::BTN_GEAR_UP), + "Btn_dpad_up" => Some(EV_KEY::BTN_DPAD_UP), + "Btn_dpad_down" => Some(EV_KEY::BTN_DPAD_DOWN), + "Btn_dpad_left" => Some(EV_KEY::BTN_DPAD_LEFT), + "Btn_dpad_right" => Some(EV_KEY::BTN_DPAD_RIGHT), + "Btn_trigger_happy1" => Some(EV_KEY::BTN_TRIGGER_HAPPY1), + "Btn_trigger_happy2" => Some(EV_KEY::BTN_TRIGGER_HAPPY2), + "Btn_trigger_happy3" => Some(EV_KEY::BTN_TRIGGER_HAPPY3), + "Btn_trigger_happy4" => Some(EV_KEY::BTN_TRIGGER_HAPPY4), + "Btn_trigger_happy5" => Some(EV_KEY::BTN_TRIGGER_HAPPY5), + "Btn_trigger_happy6" => Some(EV_KEY::BTN_TRIGGER_HAPPY6), + "Btn_trigger_happy7" => Some(EV_KEY::BTN_TRIGGER_HAPPY7), + "Btn_trigger_happy8" => Some(EV_KEY::BTN_TRIGGER_HAPPY8), + "Btn_trigger_happy9" => Some(EV_KEY::BTN_TRIGGER_HAPPY9), + "Btn_trigger_happy10" => Some(EV_KEY::BTN_TRIGGER_HAPPY10), + "Btn_trigger_happy11" => Some(EV_KEY::BTN_TRIGGER_HAPPY11), + "Btn_trigger_happy12" => Some(EV_KEY::BTN_TRIGGER_HAPPY12), + "Btn_trigger_happy13" => Some(EV_KEY::BTN_TRIGGER_HAPPY13), + "Btn_trigger_happy14" => Some(EV_KEY::BTN_TRIGGER_HAPPY14), + "Btn_trigger_happy15" => Some(EV_KEY::BTN_TRIGGER_HAPPY15), + "Btn_trigger_happy16" => Some(EV_KEY::BTN_TRIGGER_HAPPY16), + "Btn_trigger_happy17" => Some(EV_KEY::BTN_TRIGGER_HAPPY17), + "Btn_trigger_happy18" => Some(EV_KEY::BTN_TRIGGER_HAPPY18), + "Btn_trigger_happy19" => Some(EV_KEY::BTN_TRIGGER_HAPPY19), + "Btn_trigger_happy20" => Some(EV_KEY::BTN_TRIGGER_HAPPY20), + "Btn_trigger_happy21" => Some(EV_KEY::BTN_TRIGGER_HAPPY21), + "Btn_trigger_happy22" => Some(EV_KEY::BTN_TRIGGER_HAPPY22), + "Btn_trigger_happy23" => Some(EV_KEY::BTN_TRIGGER_HAPPY23), + "Btn_trigger_happy24" => Some(EV_KEY::BTN_TRIGGER_HAPPY24), + "Btn_trigger_happy25" => Some(EV_KEY::BTN_TRIGGER_HAPPY25), + "Btn_trigger_happy26" => Some(EV_KEY::BTN_TRIGGER_HAPPY26), + "Btn_trigger_happy27" => Some(EV_KEY::BTN_TRIGGER_HAPPY27), + "Btn_trigger_happy28" => Some(EV_KEY::BTN_TRIGGER_HAPPY28), + "Btn_trigger_happy29" => Some(EV_KEY::BTN_TRIGGER_HAPPY29), + "Btn_trigger_happy30" => Some(EV_KEY::BTN_TRIGGER_HAPPY30), + "Btn_trigger_happy31" => Some(EV_KEY::BTN_TRIGGER_HAPPY31), + "Btn_trigger_happy32" => Some(EV_KEY::BTN_TRIGGER_HAPPY32), + "Btn_trigger_happy33" => Some(EV_KEY::BTN_TRIGGER_HAPPY33), + "Btn_trigger_happy34" => Some(EV_KEY::BTN_TRIGGER_HAPPY34), + "Btn_trigger_happy35" => Some(EV_KEY::BTN_TRIGGER_HAPPY35), + "Btn_trigger_happy36" => Some(EV_KEY::BTN_TRIGGER_HAPPY36), + "Btn_trigger_happy37" => Some(EV_KEY::BTN_TRIGGER_HAPPY37), + "Btn_trigger_happy38" => Some(EV_KEY::BTN_TRIGGER_HAPPY38), + "Btn_trigger_happy39" => Some(EV_KEY::BTN_TRIGGER_HAPPY39), + "Btn_trigger_happy40" => Some(EV_KEY::BTN_TRIGGER_HAPPY40), + _ => None, + } +} From 42ea013b77b9d22e0dfa2b188eb3333828731aa7 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Tue, 5 Mar 2024 15:54:39 +0000 Subject: [PATCH 04/16] Simplifiy evdev reading and it actually works --- lefthk-core/src/] | 104 ++++++++++++++++++++++ lefthk-core/src/errors.rs | 2 - lefthk-core/src/evdev.rs | 145 ++++++++----------------------- lefthk-core/src/keysym_lookup.rs | 45 ++++++---- lefthk-core/src/worker/mod.rs | 136 +++++++++++++++++------------ 5 files changed, 248 insertions(+), 184 deletions(-) create mode 100644 lefthk-core/src/] diff --git a/lefthk-core/src/] b/lefthk-core/src/] new file mode 100644 index 0000000..0be7bc3 --- /dev/null +++ b/lefthk-core/src/] @@ -0,0 +1,104 @@ +use evdev_rs::{Device, DeviceWrapper}; +use std::fs::File; +use std::future::Future; +use std::os::fd::AsRawFd; +use std::path::PathBuf; +use std::pin::Pin; +use std::sync::Arc; +use tokio::sync::{oneshot, Notify}; +use tokio::time::Duration; + +use crate::errors::{self, LeftError, Result}; + +pub struct EvDev { + pub devices: Vec, + pub task_notify: Arc, + _task_guards: Vec>, +} + +impl EvDev { + pub fn new() -> Self { + let task_notify = Arc::new(Notify::new()); + + let mut task_guards: Vec> = vec![]; + let mut devices = vec![]; + for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { + let entry = errors::exit_on_error!(entry); + + if !entry + .file_name() + .to_str() + .unwrap_or("") + .starts_with("event") + { + continue; + } + let path = entry.path(); + if path.is_dir() { + continue; + } + + match device_with_path(path) { + Ok(item) => devices.push(item), + Err(err) => tracing::error!("{:#}", err), + } + } + devices + .iter() + .filter(|device| { + device.has(evdev_rs::enums::EventType::EV_KEY) + && device.phys().unwrap().contains("input0") + }) + .for_each(|device| { + let (guard, task_guard) = oneshot::channel(); + let notify = task_notify.clone(); + const SERVER: mio::Token = mio::Token(0); + let fd = device.file().as_raw_fd(); + let mut poll = errors::exit_on_error!(mio::Poll::new()); + let mut events = mio::Events::with_capacity(1); + errors::exit_on_error!(poll.registry().register( + &mut mio::unix::SourceFd(&fd), + SERVER, + mio::Interest::READABLE, + )); + let timeout = Duration::from_millis(100); + tokio::task::spawn_blocking(move || loop { + if guard.is_closed() { + println!("Bye"); + return; + } + + if let Err(err) = poll.poll(&mut events, Some(timeout)) { + tracing::warn!("Xlib socket poll failed with {:?}", err); + continue; + } + + events + .iter() + .filter(|event| SERVER == event.token()) + .for_each(|_| notify.notify_one()); + }); + task_guards.push(task_guard); + }); + + println!("Setup keyboard watcher"); + + Self { + devices: files, + task_notify, + _task_guards: task_guards, + } + } + + pub fn wait_readable(&mut self) -> Pin>> { + let task_notify = self.task_notify.clone(); + Box::pin(async move { + task_notify.notified().await; + }) + } +} + +pub fn device_with_path(path: PathBuf) -> Result<(Device, File)> { + let f = std::fs::File::open(&path)?; + Ok((Device::new_from_path(path)?, f)) +} diff --git a/lefthk-core/src/errors.rs b/lefthk-core/src/errors.rs index ac53c98..b6ef807 100644 --- a/lefthk-core/src/errors.rs +++ b/lefthk-core/src/errors.rs @@ -33,8 +33,6 @@ pub enum LeftError { IoError(#[from] std::io::Error), #[error("Nix errno: {0}.")] NixErrno(#[from] nix::errno::Errno), - #[error("Xlib error: {0}.")] - XlibError(#[from] x11_dl::error::OpenError), #[error("XDG error: {0}.")] XdgBaseDirError(#[from] xdg::BaseDirectoriesError), diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index cabdc4f..250fafa 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,39 +1,21 @@ -use evdev_rs::{Device, DeviceWrapper}; -use std::cmp::Ordering; -use std::future::Future; +use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; use std::os::fd::AsRawFd; use std::path::PathBuf; -use std::pin::Pin; -use std::ptr; -use std::sync::Arc; -use std::task::{Context, Waker}; -use tokio::sync::{oneshot, Notify}; +use tokio::sync::{mpsc, oneshot}; use tokio::time::Duration; -use crate::errors::{self, Error, LeftError, Result}; +use crate::errors::{self, LeftError}; pub struct EvDev { - pub devices: Vec, - pub task_notify: Arc, + pub task_receiver: mpsc::Receiver, _task_guards: Vec>, } -// impl From<(PathBuf, Device)> for EvDev { -// fn from(value: (PathBuf, Device)) -> Self { -// Self { -// name: value.1.name().unwrap_or("").to_string(), -// phys: value.1.physical_path().unwrap_or("").to_string(), -// path: value.0, -// } -// } -// } - impl EvDev { pub fn new() -> Self { - let task_notify = Arc::new(Notify::new()); + let (tx, task_receiver) = mpsc::channel(100); let mut task_guards: Vec> = vec![]; - let mut devices = vec![]; for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { let entry = errors::exit_on_error!(entry); @@ -49,21 +31,9 @@ impl EvDev { if path.is_dir() { continue; } - - match device_with_path(path) { - Ok(item) => devices.push(item), - Err(err) => tracing::error!("{:#}", err), - } - } - devices - .iter() - .filter(|device| { - device.has(evdev_rs::enums::EventType::EV_KEY) - && device.phys().unwrap().contains("input0") - }) - .for_each(|device| { + if let Some(device) = device_with_path(path) { let (guard, task_guard) = oneshot::channel(); - let notify = task_notify.clone(); + let transmitter = tx.clone(); const SERVER: mio::Token = mio::Token(0); let fd = device.file().as_raw_fd(); let mut poll = errors::exit_on_error!(mio::Poll::new()); @@ -74,85 +44,46 @@ impl EvDev { mio::Interest::READABLE, )); let timeout = Duration::from_millis(100); - tokio::task::spawn_blocking(move || loop { - if guard.is_closed() { - println!("Bye"); - return; - } + tokio::task::spawn(async move { + loop { + if guard.is_closed() { + println!("Bye"); + return; + } - if let Err(err) = poll.poll(&mut events, Some(timeout)) { - tracing::warn!("Xlib socket poll failed with {:?}", err); - continue; - } + if let Err(err) = poll.poll(&mut events, Some(timeout)) { + tracing::warn!("Evdev device poll failed with {:?}", err); + continue; + } - events - .iter() - .filter(|event| SERVER == event.token()) - .for_each(|_| notify.notify_one()); + while device.has_event_pending() { + match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { + Ok((status, event)) if status == ReadStatus::Success => { + transmitter.send(event).await.unwrap(); + } + Err(_) => println!("Boo"), + _ => {} + } + } + } }); task_guards.push(task_guard); - }); + } + } Self { - devices, - task_notify, + task_receiver, _task_guards: task_guards, } } - - pub fn wait_readable(&mut self) -> Pin>> { - let task_notify = self.task_notify.clone(); - Box::pin(async move { - task_notify.notified().await; - }) - } - - // fn obtain_device_list() -> Result> { - // let mut devices: Vec = evdev::enumerate() - // .filter(|(_, device)| { - // device - // .supported_keys() - // .map_or(false, |keys| keys.contains(evdev::Key::KEY_ENTER)) - // }) - // .map(|device| Self::from(device)) - // .collect(); - // - // // Order by name, but when multiple devices have the same name, - // // order by the event device unit number - // devices.sort_by(|a, b| { - // match event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) { - // Ordering::Equal => { - // event_number_from_path(&a.path).cmp(&event_number_from_path(&b.path)) - // } - // different => different, - // } - // }); - // Ok(devices) - // } } -pub fn device_with_path(path: PathBuf) -> Result { - let f = std::fs::File::open(&path)?; - Ok(Device::new_from_path(path)?) +pub fn device_with_path(path: PathBuf) -> Option { + let device = Device::new_from_path(path).ok()?; + if device.has(evdev_rs::enums::EventType::EV_KEY) + && device.phys().unwrap_or("").contains("input0") + { + return Some(device); + } + None } - -// fn event_number_from_path(path: &PathBuf) -> u32 { -// match path.to_str() { -// Some(s) => match s.rfind("event") { -// Some(idx) => s[idx + 5..].parse().unwrap_or(0), -// None => 0, -// }, -// None => 0, -// } -// } -// -// pub fn list_devices() -> Error { -// let devices = EvDev::obtain_device_list()?; -// for item in &devices { -// println!("Name: {}", item.name); -// println!("Path: {}", item.path.display()); -// println!("Phys: {}", item.phys); -// println!(); -// } -// Ok(()) -// } diff --git a/lefthk-core/src/keysym_lookup.rs b/lefthk-core/src/keysym_lookup.rs index ea56202..a02121b 100644 --- a/lefthk-core/src/keysym_lookup.rs +++ b/lefthk-core/src/keysym_lookup.rs @@ -1,5 +1,32 @@ use evdev_rs::enums::EV_KEY; +pub fn is_modifier(key: &EV_KEY) -> bool { + matches!( + key, + EV_KEY::KEY_LEFTSHIFT + | EV_KEY::KEY_RIGHTSHIFT + | EV_KEY::KEY_LEFTCTRL + | EV_KEY::KEY_RIGHTCTRL + | EV_KEY::KEY_LEFTALT + | EV_KEY::KEY_RIGHTALT + | EV_KEY::KEY_LEFTMETA + | EV_KEY::KEY_RIGHTMETA + | EV_KEY::KEY_CAPSLOCK + | EV_KEY::KEY_NUMLOCK + | EV_KEY::KEY_SCROLLLOCK + ) +} + +pub fn into_keys(keys: &[String]) -> Vec { + let mut result = Vec::new(); + for key in keys { + if let Some(key) = into_key(key) { + result.push(key); + } + } + result +} + // We allow this because this function is simply a mapping wrapper. #[allow(clippy::too_many_lines)] #[must_use] @@ -422,9 +449,6 @@ pub fn into_key(key: &str) -> Option { "Kbd_layout_next" => Some(EV_KEY::KEY_KBD_LAYOUT_NEXT), "Emoji_picker" => Some(EV_KEY::KEY_EMOJI_PICKER), "Dictate" => Some(EV_KEY::KEY_DICTATE), - "Camera_access_enable" => Some(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), - "Camera_access_disable" => Some(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), - "Camera_access_toggle" => Some(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), "Brightness_min" => Some(EV_KEY::KEY_BRIGHTNESS_MIN), "Brightness_max" => Some(EV_KEY::KEY_BRIGHTNESS_MAX), "Kbdinputassist_prev" => Some(EV_KEY::KEY_KBDINPUTASSIST_PREV), @@ -454,21 +478,6 @@ pub fn into_key(key: &str) -> Option { "Onscreen_keyboard" => Some(EV_KEY::KEY_ONSCREEN_KEYBOARD), "Privacy_screen_toggle" => Some(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), "Selective_screenshot" => Some(EV_KEY::KEY_SELECTIVE_SCREENSHOT), - "Next_element" => Some(EV_KEY::KEY_NEXT_ELEMENT), - "Previous_element" => Some(EV_KEY::KEY_PREVIOUS_ELEMENT), - "Autopilot_engage_toggle" => Some(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), - "Mark_waypoint" => Some(EV_KEY::KEY_MARK_WAYPOINT), - "Sos" => Some(EV_KEY::KEY_SOS), - "Nav_chart" => Some(EV_KEY::KEY_NAV_CHART), - "Fishing_chart" => Some(EV_KEY::KEY_FISHING_CHART), - "Single_range_radar" => Some(EV_KEY::KEY_SINGLE_RANGE_RADAR), - "Dual_range_radar" => Some(EV_KEY::KEY_DUAL_RANGE_RADAR), - "Radar_overlay" => Some(EV_KEY::KEY_RADAR_OVERLAY), - "Traditional_sonar" => Some(EV_KEY::KEY_TRADITIONAL_SONAR), - "Clearvu_sonar" => Some(EV_KEY::KEY_CLEARVU_SONAR), - "Sidevu_sonar" => Some(EV_KEY::KEY_SIDEVU_SONAR), - "Nav_info" => Some(EV_KEY::KEY_NAV_INFO), - "Brightness_menu" => Some(EV_KEY::KEY_BRIGHTNESS_MENU), "Macro1" => Some(EV_KEY::KEY_MACRO1), "Macro2" => Some(EV_KEY::KEY_MACRO2), "Macro3" => Some(EV_KEY::KEY_MACRO3), diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 49991c9..0a750b5 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -1,17 +1,34 @@ pub mod context; -use std::path::Path; - use crate::child::Children; use crate::config::{command, Keybind}; -use crate::errors::{self, Error, LeftError}; -use crate::evdev; +use crate::errors::{self, LeftError}; use crate::evdev::EvDev; use crate::ipc::Pipe; -use evdev_rs::enums::EV_KEY; -use evdev_rs::ReadFlag; +use crate::keysym_lookup; +use evdev_rs::enums::{EventCode, EV_KEY}; +use evdev_rs::InputEvent; use xdg::BaseDirectories; +#[derive(Clone, Copy, Debug)] +enum KeyEventType { + Release, + Press, + Repeat, + Unknown, +} + +impl From for KeyEventType { + fn from(value: i32) -> Self { + match value { + 0 => Self::Release, + 1 => Self::Press, + 2 => Self::Repeat, + _ => Self::Unknown, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Status { Reload, @@ -54,18 +71,11 @@ impl Worker { tokio::select! { () = self.children.wait_readable() => { + println!("Reaping children"); self.children.reap(); } - () = self.evdev.wait_readable() => { - for device in &self.evdev.devices { - while device.has_event_pending() { - match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { - Ok((_, event)) if event.value == 1 => println!("{:?}", event), - Err(_) => println!("Boo"), - _ => {}, - } - } - } + Some(event) = self.evdev.task_receiver.recv() => { + self.handle_event(&event); } Some(command) = pipe.get_next_command() => { errors::log_on_error!(command.execute(&mut self)); @@ -82,47 +92,59 @@ impl Worker { errors::exit_on_error!(Pipe::new(pipe_file).await) } - // fn handle_event(&mut self, xlib_event: &xlib::XEvent) { - // let error = match xlib_event.get_type() { - // xlib::KeyPress => self.handle_key_press(&xlib::XKeyEvent::from(xlib_event)), - // xlib::MappingNotify => { - // self.handle_mapping_notify(&mut xlib::XMappingEvent::from(xlib_event)) - // } - // _ => return, - // }; - // errors::log_on_error!(error); - // } - // - // fn handle_key_press(&mut self, event: &xlib::XKeyEvent) -> Error { - // let key = self.xwrap.keycode_to_keysym(event.keycode); - // let mask = xkeysym_lookup::clean_mask(event.state); - // if let Some(keybind) = self.get_keybind((mask, key)) { - // if let Ok(command) = command::denormalize(&keybind.command) { - // return command.execute(self); - // } - // } else { - // return Err(LeftError::CommandNotFound); - // } - // Ok(()) - // } - // - // fn get_keybind(&self, mask_key_pair: (u32, u32)) -> Option { - // let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds { - // keybinds - // } else { - // &self.keybinds - // }; - // keybinds - // .iter() - // .find(|keybind| { - // if let Some(key) = xkeysym_lookup::into_keysym(&keybind.key) { - // let mask = xkeysym_lookup::into_modmask(&keybind.modifier); - // return mask_key_pair == (mask, key); - // } - // false - // }) - // .cloned() - // } + fn handle_event(&mut self, event: &InputEvent) { + let r#type = KeyEventType::from(event.value); + match r#type { + KeyEventType::Release => { + if let EventCode::EV_KEY(key) = event.event_code { + if let Some(index) = self.keys_pressed.iter().position(|&k| k == key) { + self.keys_pressed.remove(index); + } + } + } + KeyEventType::Press => { + let mut new_key = false; + if let EventCode::EV_KEY(key) = event.event_code { + if !self.keys_pressed.contains(&key) { + self.keys_pressed.push(key); + new_key = true; + } + } + if new_key { + println!("Keys: {:?}", self.keys_pressed); + if let Some(keybind) = self.check_for_keybind() { + if let Ok(command) = command::denormalize(&keybind.command) { + let _ = command.execute(self); + } else { + errors::log_on_error!(Err(LeftError::CommandNotFound)); + } + } + } + } + KeyEventType::Repeat => {} + KeyEventType::Unknown => {} + } + } + + fn check_for_keybind(&self) -> Option { + let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds { + keybinds + } else { + &self.keybinds + }; + keybinds + .iter() + .find(|keybind| { + if let Some(key) = keysym_lookup::into_key(&keybind.key) { + let modifiers = keysym_lookup::into_keys(&keybind.modifier); + let keys: Vec = + modifiers.into_iter().chain(std::iter::once(key)).collect(); + return keys.iter().all(|key| self.keys_pressed.contains(key)); + } + false + }) + .cloned() + } // // fn handle_mapping_notify(&self, event: &mut xlib::XMappingEvent) -> Error { // if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard { From a77b38ece2ee4b4fdf3e5676e46384a171dd6ff7 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Tue, 5 Mar 2024 22:26:10 +0000 Subject: [PATCH 05/16] Add mod masks, gather keyboards more reliably --- Cargo.lock | 53 +++++++++ lefthk-core/Cargo.toml | 1 + lefthk-core/src/evdev.rs | 194 +++++++++++++++++++++---------- lefthk-core/src/keysym_lookup.rs | 104 ++++++++++++----- lefthk-core/src/worker/mod.rs | 54 ++++++--- 5 files changed, 301 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd25a26..ef2258e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,12 +199,43 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "input" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" +dependencies = [ + "bitflags 1.3.2", + "input-sys", + "io-lifetimes", + "libc", + "log", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" + [[package]] name = "inventory" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -232,6 +263,7 @@ name = "lefthk-core" version = "0.2.1" dependencies = [ "evdev-rs", + "input", "inventory", "mio", "nix", @@ -251,6 +283,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -689,6 +731,17 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "udev" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" +dependencies = [ + "libc", + "libudev-sys", + "pkg-config", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/lefthk-core/Cargo.toml b/lefthk-core/Cargo.toml index 23a912f..3ed88ce 100644 --- a/lefthk-core/Cargo.toml +++ b/lefthk-core/Cargo.toml @@ -9,6 +9,7 @@ description = "A hotkey daemon for Adventurers" [dependencies] evdev-rs = { version = "0.6.1", features = ["serde"] } +input = "0.8" mio = "0.8.0" nix = { version = "0.27.1", features = ["fs", "signal"] } signal-hook = "0.3.4" diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index 250fafa..0df9086 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,84 +1,143 @@ use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; -use std::os::fd::AsRawFd; -use std::path::PathBuf; +use input::event::{DeviceEvent, EventTrait}; +use input::{Event, Libinput, LibinputInterface}; +use nix::libc::{O_RDWR, O_WRONLY}; +use std::ffi::OsStr; +use std::fs::{File, OpenOptions}; +use std::os::fd::{AsRawFd, OwnedFd}; +use std::os::unix::fs::OpenOptionsExt; +use std::path::{Path, PathBuf}; use tokio::sync::{mpsc, oneshot}; use tokio::time::Duration; use crate::errors::{self, LeftError}; +#[derive(Debug)] +pub enum Task { + KeyboardEvent(InputEvent), + KeyboardAdded(String), +} + pub struct EvDev { - pub task_receiver: mpsc::Receiver, - _task_guards: Vec>, + pub task_receiver: mpsc::Receiver, + task_transmitter: mpsc::Sender, + task_guards: Vec>, + _keyboard_watcher: KeyboardWatcher, +} + +impl Default for EvDev { + fn default() -> Self { + let (task_transmitter, task_receiver) = mpsc::channel(100); + + let keyboard_watcher = KeyboardWatcher::new(task_transmitter.clone()); + + let task_guards: Vec> = vec![]; + + let devices = find_keyboards(); + + let mut evdev = EvDev { + task_receiver, + task_transmitter, + task_guards, + _keyboard_watcher: keyboard_watcher, + }; + for device in devices { + evdev.add_device(device); + } + + evdev + } } impl EvDev { - pub fn new() -> Self { - let (tx, task_receiver) = mpsc::channel(100); - - let mut task_guards: Vec> = vec![]; - for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { - let entry = errors::exit_on_error!(entry); - - if !entry - .file_name() - .to_str() - .unwrap_or("") - .starts_with("event") - { - continue; - } - let path = entry.path(); - if path.is_dir() { - continue; - } - if let Some(device) = device_with_path(path) { - let (guard, task_guard) = oneshot::channel(); - let transmitter = tx.clone(); - const SERVER: mio::Token = mio::Token(0); - let fd = device.file().as_raw_fd(); - let mut poll = errors::exit_on_error!(mio::Poll::new()); - let mut events = mio::Events::with_capacity(1); - errors::exit_on_error!(poll.registry().register( - &mut mio::unix::SourceFd(&fd), - SERVER, - mio::Interest::READABLE, - )); - let timeout = Duration::from_millis(100); - tokio::task::spawn(async move { - loop { - if guard.is_closed() { - println!("Bye"); - return; - } + pub fn add_device(&mut self, path: PathBuf) { + if let Some(device) = device_with_path(path) { + let (guard, task_guard) = oneshot::channel(); + let transmitter = self.task_transmitter.clone(); + const SERVER: mio::Token = mio::Token(0); + let fd = device.file().as_raw_fd(); + let mut poll = errors::exit_on_error!(mio::Poll::new()); + let mut events = mio::Events::with_capacity(1); + errors::exit_on_error!(poll.registry().register( + &mut mio::unix::SourceFd(&fd), + SERVER, + mio::Interest::READABLE, + )); + let timeout = Duration::from_millis(100); + tokio::task::spawn(async move { + loop { + if guard.is_closed() { + println!("Bye"); + return; + } - if let Err(err) = poll.poll(&mut events, Some(timeout)) { - tracing::warn!("Evdev device poll failed with {:?}", err); - continue; - } + if let Err(err) = poll.poll(&mut events, Some(timeout)) { + tracing::warn!("Evdev device poll failed with {:?}", err); + continue; + } - while device.has_event_pending() { - match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { - Ok((status, event)) if status == ReadStatus::Success => { - transmitter.send(event).await.unwrap(); - } - Err(_) => println!("Boo"), - _ => {} + while device.has_event_pending() { + match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { + Ok((ReadStatus::Success, event)) => { + transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); } + Err(_) => println!("Boo"), + _ => {} } } - }); - task_guards.push(task_guard); - } + } + }); + self.task_guards.push(task_guard); } + } +} - Self { - task_receiver, - _task_guards: task_guards, +struct Interface; + +impl LibinputInterface for Interface { + fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { + OpenOptions::new() + .custom_flags(flags) + .read((flags != 0) | (flags & O_RDWR != 0)) + .write((flags & O_WRONLY != 0) | (flags & O_RDWR != 0)) + .open(path) + .map(|file| file.into()) + .map_err(|err| err.raw_os_error().unwrap()) + } + fn close_restricted(&mut self, fd: OwnedFd) { + let _ = File::from(fd); + } +} + +fn find_keyboards() -> Vec { + let mut context = Libinput::new_with_udev(Interface); + context.udev_assign_seat("seat0").unwrap(); + context.dispatch().unwrap(); + let mut devices = vec![]; + for event in &mut context { + if let Event::Device(DeviceEvent::Added(_)) = &event { + unsafe { + if let Some(device) = event.device().udev_device() { + let is_keyboard = device + .property_value("ID_INPUT_KEYBOARD") + .unwrap_or(OsStr::new("0")) + == "1" + && device + .property_value("ID_INPUT_MOUSE") + .unwrap_or(OsStr::new("0")) + == "0"; + if is_keyboard { + let path = device.property_value("DEVNAME").unwrap_or(OsStr::new("")); + devices.push(PathBuf::from(path)) + } + } + } } } + devices } -pub fn device_with_path(path: PathBuf) -> Option { +fn device_with_path(path: PathBuf) -> Option { let device = Device::new_from_path(path).ok()?; if device.has(evdev_rs::enums::EventType::EV_KEY) && device.phys().unwrap_or("").contains("input0") @@ -87,3 +146,18 @@ pub fn device_with_path(path: PathBuf) -> Option { } None } + +#[derive(Debug)] +struct KeyboardWatcher { + _task_guard: oneshot::Receiver<()>, +} + +impl KeyboardWatcher { + pub fn new(_task_transmitter: mpsc::Sender) -> Self { + let (_guard, task_guard) = oneshot::channel(); + tokio::task::spawn(async move {}); + Self { + _task_guard: task_guard, + } + } +} diff --git a/lefthk-core/src/keysym_lookup.rs b/lefthk-core/src/keysym_lookup.rs index a02121b..d203cdf 100644 --- a/lefthk-core/src/keysym_lookup.rs +++ b/lefthk-core/src/keysym_lookup.rs @@ -1,5 +1,34 @@ use evdev_rs::enums::EV_KEY; +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum MOD_MASK { + MASK_SHIFT, + MASK_CTRL, + MASK_ALT, + MASK_META, + MASK_CAPSLOCK, + MASK_NUMLOCK, + MASK_SCROLLLOCK, +} + +impl TryFrom for MOD_MASK { + type Error = (); + + fn try_from(key: EV_KEY) -> Result { + match key { + EV_KEY::KEY_LEFTSHIFT | EV_KEY::KEY_RIGHTSHIFT => Ok(Self::MASK_SHIFT), + EV_KEY::KEY_LEFTCTRL | EV_KEY::KEY_RIGHTCTRL => Ok(Self::MASK_CTRL), + EV_KEY::KEY_LEFTALT | EV_KEY::KEY_RIGHTALT => Ok(Self::MASK_ALT), + EV_KEY::KEY_LEFTMETA | EV_KEY::KEY_RIGHTMETA => Ok(Self::MASK_META), + EV_KEY::KEY_CAPSLOCK => Ok(Self::MASK_CAPSLOCK), + EV_KEY::KEY_NUMLOCK => Ok(Self::MASK_NUMLOCK), + EV_KEY::KEY_SCROLLLOCK => Ok(Self::MASK_SCROLLLOCK), + _ => Err(()), + } + } +} + pub fn is_modifier(key: &EV_KEY) -> bool { matches!( key, @@ -17,6 +46,29 @@ pub fn is_modifier(key: &EV_KEY) -> bool { ) } +pub fn into_mods(keys: &[String]) -> Vec { + let mut result = Vec::new(); + for key in keys { + if let Some(key) = into_mod(key) { + result.push(key); + } + } + result +} + +pub fn into_mod(key: &str) -> Option { + match key { + "Shift" => Some(MOD_MASK::MASK_SHIFT), + "Ctrl" => Some(MOD_MASK::MASK_CTRL), + "Alt" => Some(MOD_MASK::MASK_ALT), + "Meta" => Some(MOD_MASK::MASK_META), + "Capslock" => Some(MOD_MASK::MASK_CAPSLOCK), + "Numlock" => Some(MOD_MASK::MASK_NUMLOCK), + "Scrolllock" => Some(MOD_MASK::MASK_SCROLLLOCK), + _ => None, + } +} + pub fn into_keys(keys: &[String]) -> Vec { let mut result = Vec::new(); for key in keys { @@ -48,41 +100,41 @@ pub fn into_key(key: &str) -> Option { "Equal" => Some(EV_KEY::KEY_EQUAL), "Backspace" => Some(EV_KEY::KEY_BACKSPACE), "Tab" => Some(EV_KEY::KEY_TAB), - "Q" => Some(EV_KEY::KEY_Q), - "W" => Some(EV_KEY::KEY_W), - "E" => Some(EV_KEY::KEY_E), - "R" => Some(EV_KEY::KEY_R), - "T" => Some(EV_KEY::KEY_T), - "Y" => Some(EV_KEY::KEY_Y), - "U" => Some(EV_KEY::KEY_U), - "I" => Some(EV_KEY::KEY_I), - "O" => Some(EV_KEY::KEY_O), - "P" => Some(EV_KEY::KEY_P), + "q" => Some(EV_KEY::KEY_Q), + "w" => Some(EV_KEY::KEY_W), + "e" => Some(EV_KEY::KEY_E), + "r" => Some(EV_KEY::KEY_R), + "t" => Some(EV_KEY::KEY_T), + "y" => Some(EV_KEY::KEY_Y), + "u" => Some(EV_KEY::KEY_U), + "i" => Some(EV_KEY::KEY_I), + "o" => Some(EV_KEY::KEY_O), + "p" => Some(EV_KEY::KEY_P), "Leftbrace" => Some(EV_KEY::KEY_LEFTBRACE), "Rightbrace" => Some(EV_KEY::KEY_RIGHTBRACE), "Enter" => Some(EV_KEY::KEY_ENTER), "Leftctrl" => Some(EV_KEY::KEY_LEFTCTRL), - "A" => Some(EV_KEY::KEY_A), - "S" => Some(EV_KEY::KEY_S), - "D" => Some(EV_KEY::KEY_D), - "F" => Some(EV_KEY::KEY_F), - "G" => Some(EV_KEY::KEY_G), - "H" => Some(EV_KEY::KEY_H), - "J" => Some(EV_KEY::KEY_J), - "K" => Some(EV_KEY::KEY_K), - "L" => Some(EV_KEY::KEY_L), + "a" => Some(EV_KEY::KEY_A), + "s" => Some(EV_KEY::KEY_S), + "d" => Some(EV_KEY::KEY_D), + "f" => Some(EV_KEY::KEY_F), + "g" => Some(EV_KEY::KEY_G), + "h" => Some(EV_KEY::KEY_H), + "j" => Some(EV_KEY::KEY_J), + "k" => Some(EV_KEY::KEY_K), + "l" => Some(EV_KEY::KEY_L), "Semicolon" => Some(EV_KEY::KEY_SEMICOLON), "Apostrophe" => Some(EV_KEY::KEY_APOSTROPHE), "Grave" => Some(EV_KEY::KEY_GRAVE), "Leftshift" => Some(EV_KEY::KEY_LEFTSHIFT), "Backslash" => Some(EV_KEY::KEY_BACKSLASH), - "Z" => Some(EV_KEY::KEY_Z), - "X" => Some(EV_KEY::KEY_X), - "C" => Some(EV_KEY::KEY_C), - "V" => Some(EV_KEY::KEY_V), - "B" => Some(EV_KEY::KEY_B), - "N" => Some(EV_KEY::KEY_N), - "M" => Some(EV_KEY::KEY_M), + "z" => Some(EV_KEY::KEY_Z), + "x" => Some(EV_KEY::KEY_X), + "c" => Some(EV_KEY::KEY_C), + "v" => Some(EV_KEY::KEY_V), + "b" => Some(EV_KEY::KEY_B), + "n" => Some(EV_KEY::KEY_N), + "m" => Some(EV_KEY::KEY_M), "Comma" => Some(EV_KEY::KEY_COMMA), "Dot" => Some(EV_KEY::KEY_DOT), "Slash" => Some(EV_KEY::KEY_SLASH), diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 0a750b5..9d402c9 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -3,9 +3,9 @@ pub mod context; use crate::child::Children; use crate::config::{command, Keybind}; use crate::errors::{self, LeftError}; -use crate::evdev::EvDev; +use crate::evdev::{EvDev, Task}; use crate::ipc::Pipe; -use crate::keysym_lookup; +use crate::keysym_lookup::{self, is_modifier, MOD_MASK}; use evdev_rs::enums::{EventCode, EV_KEY}; use evdev_rs::InputEvent; use xdg::BaseDirectories; @@ -41,6 +41,7 @@ pub struct Worker { base_directory: BaseDirectories, keys_pressed: Vec, + mods_pressed: Vec, pub evdev: EvDev, pub children: Children, @@ -57,7 +58,8 @@ impl Worker { keybinds, base_directory, keys_pressed: Vec::new(), - evdev: EvDev::new(), + mods_pressed: Vec::new(), + evdev: EvDev::default(), children: Children::default(), chord_ctx: context::Chord::new(), } @@ -74,8 +76,15 @@ impl Worker { println!("Reaping children"); self.children.reap(); } - Some(event) = self.evdev.task_receiver.recv() => { - self.handle_event(&event); + Some(task) = self.evdev.task_receiver.recv() => { + match task { + Task::KeyboardEvent(event) => { + self.handle_event(&event); + } + Task::KeyboardAdded(path) => { + self.evdev.add_device(path.into()); + } + } } Some(command) = pipe.get_next_command() => { errors::log_on_error!(command.execute(&mut self)); @@ -97,7 +106,11 @@ impl Worker { match r#type { KeyEventType::Release => { if let EventCode::EV_KEY(key) = event.event_code { - if let Some(index) = self.keys_pressed.iter().position(|&k| k == key) { + if is_modifier(&key) { + if let Ok(modifier) = key.try_into() { + self.mods_pressed.retain(|&m| m != modifier); + } + } else if let Some(index) = self.keys_pressed.iter().position(|&k| k == key) { self.keys_pressed.remove(index); } } @@ -105,13 +118,20 @@ impl Worker { KeyEventType::Press => { let mut new_key = false; if let EventCode::EV_KEY(key) = event.event_code { - if !self.keys_pressed.contains(&key) { + if is_modifier(&key) { + match key.try_into() { + Ok(modifier) if !self.mods_pressed.contains(&modifier) => { + self.mods_pressed.push(modifier); + new_key = true; + } + _ => {} + } + } else if !self.keys_pressed.contains(&key) { self.keys_pressed.push(key); new_key = true; } } if new_key { - println!("Keys: {:?}", self.keys_pressed); if let Some(keybind) = self.check_for_keybind() { if let Ok(command) = command::denormalize(&keybind.command) { let _ = command.execute(self); @@ -136,20 +156,16 @@ impl Worker { .iter() .find(|keybind| { if let Some(key) = keysym_lookup::into_key(&keybind.key) { - let modifiers = keysym_lookup::into_keys(&keybind.modifier); - let keys: Vec = - modifiers.into_iter().chain(std::iter::once(key)).collect(); - return keys.iter().all(|key| self.keys_pressed.contains(key)); + let modifiers = keysym_lookup::into_mods(&keybind.modifier); + let matching = modifiers.is_empty() && self.mods_pressed.is_empty() + || (!self.mods_pressed.is_empty() + && self.mods_pressed.iter().all(|m| modifiers.contains(m))) + && (!modifiers.is_empty() + && modifiers.iter().all(|m| self.mods_pressed.contains(m))); + return self.keys_pressed == vec![key] && matching; } false }) .cloned() } - // - // fn handle_mapping_notify(&self, event: &mut xlib::XMappingEvent) -> Error { - // if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard { - // return self.xwrap.refresh_keyboard(event); - // } - // Ok(()) - // } } From d9be9ff49a9c0e8618ea5ce35ecb447886a18f33 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Tue, 5 Mar 2024 22:30:00 +0000 Subject: [PATCH 06/16] Cleanup --- lefthk-core/src/] | 104 --- lefthk-core/src/xkeysym_lookup.rs | 1366 ----------------------------- lefthk-core/src/xwrap.rs | 205 ----- 3 files changed, 1675 deletions(-) delete mode 100644 lefthk-core/src/] delete mode 100644 lefthk-core/src/xkeysym_lookup.rs delete mode 100644 lefthk-core/src/xwrap.rs diff --git a/lefthk-core/src/] b/lefthk-core/src/] deleted file mode 100644 index 0be7bc3..0000000 --- a/lefthk-core/src/] +++ /dev/null @@ -1,104 +0,0 @@ -use evdev_rs::{Device, DeviceWrapper}; -use std::fs::File; -use std::future::Future; -use std::os::fd::AsRawFd; -use std::path::PathBuf; -use std::pin::Pin; -use std::sync::Arc; -use tokio::sync::{oneshot, Notify}; -use tokio::time::Duration; - -use crate::errors::{self, LeftError, Result}; - -pub struct EvDev { - pub devices: Vec, - pub task_notify: Arc, - _task_guards: Vec>, -} - -impl EvDev { - pub fn new() -> Self { - let task_notify = Arc::new(Notify::new()); - - let mut task_guards: Vec> = vec![]; - let mut devices = vec![]; - for entry in errors::exit_on_error!(std::fs::read_dir("/dev/input")) { - let entry = errors::exit_on_error!(entry); - - if !entry - .file_name() - .to_str() - .unwrap_or("") - .starts_with("event") - { - continue; - } - let path = entry.path(); - if path.is_dir() { - continue; - } - - match device_with_path(path) { - Ok(item) => devices.push(item), - Err(err) => tracing::error!("{:#}", err), - } - } - devices - .iter() - .filter(|device| { - device.has(evdev_rs::enums::EventType::EV_KEY) - && device.phys().unwrap().contains("input0") - }) - .for_each(|device| { - let (guard, task_guard) = oneshot::channel(); - let notify = task_notify.clone(); - const SERVER: mio::Token = mio::Token(0); - let fd = device.file().as_raw_fd(); - let mut poll = errors::exit_on_error!(mio::Poll::new()); - let mut events = mio::Events::with_capacity(1); - errors::exit_on_error!(poll.registry().register( - &mut mio::unix::SourceFd(&fd), - SERVER, - mio::Interest::READABLE, - )); - let timeout = Duration::from_millis(100); - tokio::task::spawn_blocking(move || loop { - if guard.is_closed() { - println!("Bye"); - return; - } - - if let Err(err) = poll.poll(&mut events, Some(timeout)) { - tracing::warn!("Xlib socket poll failed with {:?}", err); - continue; - } - - events - .iter() - .filter(|event| SERVER == event.token()) - .for_each(|_| notify.notify_one()); - }); - task_guards.push(task_guard); - }); - - println!("Setup keyboard watcher"); - - Self { - devices: files, - task_notify, - _task_guards: task_guards, - } - } - - pub fn wait_readable(&mut self) -> Pin>> { - let task_notify = self.task_notify.clone(); - Box::pin(async move { - task_notify.notified().await; - }) - } -} - -pub fn device_with_path(path: PathBuf) -> Result<(Device, File)> { - let f = std::fs::File::open(&path)?; - Ok((Device::new_from_path(path)?, f)) -} diff --git a/lefthk-core/src/xkeysym_lookup.rs b/lefthk-core/src/xkeysym_lookup.rs deleted file mode 100644 index c479219..0000000 --- a/lefthk-core/src/xkeysym_lookup.rs +++ /dev/null @@ -1,1366 +0,0 @@ -#![allow(clippy::wildcard_imports)] -use std::os::raw::c_uint; -use x11_dl::keysym::*; -use x11_dl::xlib; - -pub type XKeysym = c_uint; -pub type ModMask = c_uint; -pub type Button = c_uint; - -pub fn clean_mask(mut mask: ModMask) -> ModMask { - mask &= !(xlib::Mod2Mask | xlib::LockMask); - mask & (xlib::ShiftMask - | xlib::ControlMask - | xlib::Mod1Mask - | xlib::Mod3Mask - | xlib::Mod4Mask - | xlib::Mod5Mask) -} - -#[must_use] -pub fn into_modmask(keys: &[String]) -> ModMask { - let mut mask = 0; - for s in keys { - mask |= into_mod(s); - } - clean_mask(mask) -} - -#[must_use] -pub fn into_mod(key: &str) -> ModMask { - match key { - "None" => xlib::AnyModifier, - "Shift" => xlib::ShiftMask, - "Control" => xlib::ControlMask, - "Mod1" | "Alt" => xlib::Mod1Mask, - //"Mod2" => xlib::Mod2Mask, // NOTE: we are ignoring the state of Numlock - //"NumLock" => xlib::Mod2Mask, // this is left here as a reminder - "Mod3" => xlib::Mod3Mask, - "Mod4" | "Super" => xlib::Mod4Mask, - "Mod5" => xlib::Mod5Mask, - _ => 0, - } -} - -// We allow this because this function is simply a mapping wrapper. -#[allow(clippy::too_many_lines)] -#[must_use] -pub fn into_keysym(key: &str) -> Option { - match key { - "BackSpace" => Some(XK_BackSpace), - "Tab" => Some(XK_Tab), - "Linefeed" => Some(XK_Linefeed), - "Clear" => Some(XK_Clear), - "Return" => Some(XK_Return), - "Pause" => Some(XK_Pause), - "Scroll_Lock" => Some(XK_Scroll_Lock), - "Sys_Req" => Some(XK_Sys_Req), - "Escape" => Some(XK_Escape), - "Delete" => Some(XK_Delete), - "Multi_key" => Some(XK_Multi_key), - "Kanji" => Some(XK_Kanji), - "Muhenkan" => Some(XK_Muhenkan), - "Henkan_Mode" => Some(XK_Henkan_Mode), - "Henkan" => Some(XK_Henkan), - "Romaji" => Some(XK_Romaji), - "Hiragana" => Some(XK_Hiragana), - "Katakana" => Some(XK_Katakana), - "Hiragana_Katakana" => Some(XK_Hiragana_Katakana), - "Zenkaku" => Some(XK_Zenkaku), - "Hankaku" => Some(XK_Hankaku), - "Zenkaku_Hankaku" => Some(XK_Zenkaku_Hankaku), - "Touroku" => Some(XK_Touroku), - "Massyo" => Some(XK_Massyo), - "Kana_Lock" => Some(XK_Kana_Lock), - "Kana_Shift" => Some(XK_Kana_Shift), - "Eisu_Shift" => Some(XK_Eisu_Shift), - "Eisu_toggle" => Some(XK_Eisu_toggle), - "Home" => Some(XK_Home), - "Left" => Some(XK_Left), - "Up" => Some(XK_Up), - "Right" => Some(XK_Right), - "Down" => Some(XK_Down), - "Prior" => Some(XK_Prior), - "Page_Up" => Some(XK_Page_Up), - "Next" => Some(XK_Next), - "Page_Down" => Some(XK_Page_Down), - "End" => Some(XK_End), - "Begin" => Some(XK_Begin), - "Win_L" => Some(XK_Win_L), - "Win_R" => Some(XK_Win_R), - "App" => Some(XK_App), - "Select" => Some(XK_Select), - "Print" => Some(XK_Print), - "Execute" => Some(XK_Execute), - "Insert" => Some(XK_Insert), - "Undo" => Some(XK_Undo), - "Redo" => Some(XK_Redo), - "Menu" => Some(XK_Menu), - "Find" => Some(XK_Find), - "Cancel" => Some(XK_Cancel), - "Help" => Some(XK_Help), - "Break" => Some(XK_Break), - "Mode_switch" => Some(XK_Mode_switch), - "script_switch" => Some(XK_script_switch), - "Num_Lock" => Some(XK_Num_Lock), - "KP_Space" => Some(XK_KP_Space), - "KP_Tab" => Some(XK_KP_Tab), - "KP_Enter" => Some(XK_KP_Enter), - "KP_F1" => Some(XK_KP_F1), - "KP_F2" => Some(XK_KP_F2), - "KP_F3" => Some(XK_KP_F3), - "KP_F4" => Some(XK_KP_F4), - "KP_Home" => Some(XK_KP_Home), - "KP_Left" => Some(XK_KP_Left), - "KP_Up" => Some(XK_KP_Up), - "KP_Right" => Some(XK_KP_Right), - "KP_Down" => Some(XK_KP_Down), - "KP_Prior" => Some(XK_KP_Prior), - "KP_Page_Up" => Some(XK_KP_Page_Up), - "KP_Next" => Some(XK_KP_Next), - "KP_Page_Down" => Some(XK_KP_Page_Down), - "KP_End" => Some(XK_KP_End), - "KP_Begin" => Some(XK_KP_Begin), - "KP_Insert" => Some(XK_KP_Insert), - "KP_Delete" => Some(XK_KP_Delete), - "KP_Equal" => Some(XK_KP_Equal), - "KP_Multiply" => Some(XK_KP_Multiply), - "KP_Add" => Some(XK_KP_Add), - "KP_Separator" => Some(XK_KP_Separator), - "KP_Subtract" => Some(XK_KP_Subtract), - "KP_Decimal" => Some(XK_KP_Decimal), - "KP_Divide" => Some(XK_KP_Divide), - "KP_0" => Some(XK_KP_0), - "KP_1" => Some(XK_KP_1), - "KP_2" => Some(XK_KP_2), - "KP_3" => Some(XK_KP_3), - "KP_4" => Some(XK_KP_4), - "KP_5" => Some(XK_KP_5), - "KP_6" => Some(XK_KP_6), - "KP_7" => Some(XK_KP_7), - "KP_8" => Some(XK_KP_8), - "KP_9" => Some(XK_KP_9), - "F1" => Some(XK_F1), - "F2" => Some(XK_F2), - "F3" => Some(XK_F3), - "F4" => Some(XK_F4), - "F5" => Some(XK_F5), - "F6" => Some(XK_F6), - "F7" => Some(XK_F7), - "F8" => Some(XK_F8), - "F9" => Some(XK_F9), - "F10" => Some(XK_F10), - "F11" => Some(XK_F11), - "L1" => Some(XK_L1), - "F12" => Some(XK_F12), - "L2" => Some(XK_L2), - "F13" => Some(XK_F13), - "L3" => Some(XK_L3), - "F14" => Some(XK_F14), - "L4" => Some(XK_L4), - "F15" => Some(XK_F15), - "L5" => Some(XK_L5), - "F16" => Some(XK_F16), - "L6" => Some(XK_L6), - "F17" => Some(XK_F17), - "L7" => Some(XK_L7), - "F18" => Some(XK_F18), - "L8" => Some(XK_L8), - "F19" => Some(XK_F19), - "L9" => Some(XK_L9), - "F20" => Some(XK_F20), - "L10" => Some(XK_L10), - "F21" => Some(XK_F21), - "R1" => Some(XK_R1), - "F22" => Some(XK_F22), - "R2" => Some(XK_R2), - "F23" => Some(XK_F23), - "R3" => Some(XK_R3), - "F24" => Some(XK_F24), - "R4" => Some(XK_R4), - "F25" => Some(XK_F25), - "R5" => Some(XK_R5), - "F26" => Some(XK_F26), - "R6" => Some(XK_R6), - "F27" => Some(XK_F27), - "R7" => Some(XK_R7), - "F28" => Some(XK_F28), - "R8" => Some(XK_R8), - "F29" => Some(XK_F29), - "R9" => Some(XK_R9), - "F30" => Some(XK_F30), - "R10" => Some(XK_R10), - "F31" => Some(XK_F31), - "R11" => Some(XK_R11), - "F32" => Some(XK_F32), - "R12" => Some(XK_R12), - "F33" => Some(XK_F33), - "R13" => Some(XK_R13), - "F34" => Some(XK_F34), - "R14" => Some(XK_R14), - "F35" => Some(XK_F35), - "R15" => Some(XK_R15), - "Shift_L" => Some(XK_Shift_L), - "Shift_R" => Some(XK_Shift_R), - "Control_L" => Some(XK_Control_L), - "Control_R" => Some(XK_Control_R), - "Caps_Lock" => Some(XK_Caps_Lock), - "Shift_Lock" => Some(XK_Shift_Lock), - "Meta_L" => Some(XK_Meta_L), - "Meta_R" => Some(XK_Meta_R), - "Alt_L" => Some(XK_Alt_L), - "Alt_R" => Some(XK_Alt_R), - "Super_L" => Some(XK_Super_L), - "Super_R" => Some(XK_Super_R), - "Hyper_L" => Some(XK_Hyper_L), - "Hyper_R" => Some(XK_Hyper_R), - "space" => Some(XK_space), - "exclam" => Some(XK_exclam), - "quotedbl" => Some(XK_quotedbl), - "numbersign" => Some(XK_numbersign), - "dollar" => Some(XK_dollar), - "percent" => Some(XK_percent), - "ampersand" => Some(XK_ampersand), - "apostrophe" => Some(XK_apostrophe), - "quoteright" => Some(XK_quoteright), - "parenleft" => Some(XK_parenleft), - "parenright" => Some(XK_parenright), - "asterisk" => Some(XK_asterisk), - "plus" => Some(XK_plus), - "comma" => Some(XK_comma), - "minus" => Some(XK_minus), - "period" => Some(XK_period), - "slash" => Some(XK_slash), - "0" => Some(XK_0), - "1" => Some(XK_1), - "2" => Some(XK_2), - "3" => Some(XK_3), - "4" => Some(XK_4), - "5" => Some(XK_5), - "6" => Some(XK_6), - "7" => Some(XK_7), - "8" => Some(XK_8), - "9" => Some(XK_9), - "colon" => Some(XK_colon), - "semicolon" => Some(XK_semicolon), - "less" => Some(XK_less), - "equal" => Some(XK_equal), - "greater" => Some(XK_greater), - "question" => Some(XK_question), - "at" => Some(XK_at), - "A" => Some(XK_A), - "B" => Some(XK_B), - "C" => Some(XK_C), - "D" => Some(XK_D), - "E" => Some(XK_E), - "F" => Some(XK_F), - "G" => Some(XK_G), - "H" => Some(XK_H), - "I" => Some(XK_I), - "J" => Some(XK_J), - "K" => Some(XK_K), - "L" => Some(XK_L), - "M" => Some(XK_M), - "N" => Some(XK_N), - "O" => Some(XK_O), - "P" => Some(XK_P), - "Q" => Some(XK_Q), - "R" => Some(XK_R), - "S" => Some(XK_S), - "T" => Some(XK_T), - "U" => Some(XK_U), - "V" => Some(XK_V), - "W" => Some(XK_W), - "X" => Some(XK_X), - "Y" => Some(XK_Y), - "Z" => Some(XK_Z), - "bracketleft" => Some(XK_bracketleft), - "backslash" => Some(XK_backslash), - "bracketright" => Some(XK_bracketright), - "asciicircum" => Some(XK_asciicircum), - "underscore" => Some(XK_underscore), - "grave" => Some(XK_grave), - "quoteleft" => Some(XK_quoteleft), - "a" => Some(XK_a), - "b" => Some(XK_b), - "c" => Some(XK_c), - "d" => Some(XK_d), - "e" => Some(XK_e), - "f" => Some(XK_f), - "g" => Some(XK_g), - "h" => Some(XK_h), - "i" => Some(XK_i), - "j" => Some(XK_j), - "k" => Some(XK_k), - "l" => Some(XK_l), - "m" => Some(XK_m), - "n" => Some(XK_n), - "o" => Some(XK_o), - "p" => Some(XK_p), - "q" => Some(XK_q), - "r" => Some(XK_r), - "s" => Some(XK_s), - "t" => Some(XK_t), - "u" => Some(XK_u), - "v" => Some(XK_v), - "w" => Some(XK_w), - "x" => Some(XK_x), - "y" => Some(XK_y), - "z" => Some(XK_z), - "braceleft" => Some(XK_braceleft), - "bar" => Some(XK_bar), - "braceright" => Some(XK_braceright), - "asciitilde" => Some(XK_asciitilde), - "nobreakspace" => Some(XK_nobreakspace), - "exclamdown" => Some(XK_exclamdown), - "cent" => Some(XK_cent), - "sterling" => Some(XK_sterling), - "currency" => Some(XK_currency), - "yen" => Some(XK_yen), - "brokenbar" => Some(XK_brokenbar), - "section" => Some(XK_section), - "diaeresis" => Some(XK_diaeresis), - "copyright" => Some(XK_copyright), - "ordfeminine" => Some(XK_ordfeminine), - "guillemotleft" => Some(XK_guillemotleft), - "notsign" => Some(XK_notsign), - "hyphen" => Some(XK_hyphen), - "registered" => Some(XK_registered), - "macron" => Some(XK_macron), - "degree" => Some(XK_degree), - "plusminus" => Some(XK_plusminus), - "twosuperior" => Some(XK_twosuperior), - "threesuperior" => Some(XK_threesuperior), - "acute" => Some(XK_acute), - "mu" => Some(XK_mu), - "paragraph" => Some(XK_paragraph), - "periodcentered" => Some(XK_periodcentered), - "cedilla" => Some(XK_cedilla), - "onesuperior" => Some(XK_onesuperior), - "masculine" => Some(XK_masculine), - "guillemotright" => Some(XK_guillemotright), - "onequarter" => Some(XK_onequarter), - "onehalf" => Some(XK_onehalf), - "threequarters" => Some(XK_threequarters), - "questiondown" => Some(XK_questiondown), - "Agrave" => Some(XK_Agrave), - "Aacute" => Some(XK_Aacute), - "Acircumflex" => Some(XK_Acircumflex), - "Atilde" => Some(XK_Atilde), - "Adiaeresis" => Some(XK_Adiaeresis), - "Aring" => Some(XK_Aring), - "AE" => Some(XK_AE), - "Ccedilla" => Some(XK_Ccedilla), - "Egrave" => Some(XK_Egrave), - "Eacute" => Some(XK_Eacute), - "Ecircumflex" => Some(XK_Ecircumflex), - "Ediaeresis" => Some(XK_Ediaeresis), - "Igrave" => Some(XK_Igrave), - "Iacute" => Some(XK_Iacute), - "Icircumflex" => Some(XK_Icircumflex), - "Idiaeresis" => Some(XK_Idiaeresis), - "ETH" => Some(XK_ETH), - "Eth" => Some(XK_Eth), - "Ntilde" => Some(XK_Ntilde), - "Ograve" => Some(XK_Ograve), - "Oacute" => Some(XK_Oacute), - "Ocircumflex" => Some(XK_Ocircumflex), - "Otilde" => Some(XK_Otilde), - "Odiaeresis" => Some(XK_Odiaeresis), - "multiply" => Some(XK_multiply), - "Ooblique" => Some(XK_Ooblique), - "Ugrave" => Some(XK_Ugrave), - "Uacute" => Some(XK_Uacute), - "Ucircumflex" => Some(XK_Ucircumflex), - "Udiaeresis" => Some(XK_Udiaeresis), - "Yacute" => Some(XK_Yacute), - "THORN" => Some(XK_THORN), - "Thorn" => Some(XK_Thorn), - "ssharp" => Some(XK_ssharp), - "agrave" => Some(XK_agrave), - "aacute" => Some(XK_aacute), - "acircumflex" => Some(XK_acircumflex), - "atilde" => Some(XK_atilde), - "adiaeresis" => Some(XK_adiaeresis), - "aring" => Some(XK_aring), - "ae" => Some(XK_ae), - "ccedilla" => Some(XK_ccedilla), - "egrave" => Some(XK_egrave), - "eacute" => Some(XK_eacute), - "ecircumflex" => Some(XK_ecircumflex), - "ediaeresis" => Some(XK_ediaeresis), - "igrave" => Some(XK_igrave), - "iacute" => Some(XK_iacute), - "icircumflex" => Some(XK_icircumflex), - "idiaeresis" => Some(XK_idiaeresis), - "eth" => Some(XK_eth), - "ntilde" => Some(XK_ntilde), - "ograve" => Some(XK_ograve), - "oacute" => Some(XK_oacute), - "ocircumflex" => Some(XK_ocircumflex), - "otilde" => Some(XK_otilde), - "odiaeresis" => Some(XK_odiaeresis), - "division" => Some(XK_division), - "oslash" => Some(XK_oslash), - "ugrave" => Some(XK_ugrave), - "uacute" => Some(XK_uacute), - "ucircumflex" => Some(XK_ucircumflex), - "udiaeresis" => Some(XK_udiaeresis), - "yacute" => Some(XK_yacute), - "thorn" => Some(XK_thorn), - "ydiaeresis" => Some(XK_ydiaeresis), - "Aogonek" => Some(XK_Aogonek), - "breve" => Some(XK_breve), - "Lstroke" => Some(XK_Lstroke), - "Lcaron" => Some(XK_Lcaron), - "Sacute" => Some(XK_Sacute), - "Scaron" => Some(XK_Scaron), - "Scedilla" => Some(XK_Scedilla), - "Tcaron" => Some(XK_Tcaron), - "Zacute" => Some(XK_Zacute), - "Zcaron" => Some(XK_Zcaron), - "Zabovedot" => Some(XK_Zabovedot), - "aogonek" => Some(XK_aogonek), - "ogonek" => Some(XK_ogonek), - "lstroke" => Some(XK_lstroke), - "lcaron" => Some(XK_lcaron), - "sacute" => Some(XK_sacute), - "caron" => Some(XK_caron), - "scaron" => Some(XK_scaron), - "scedilla" => Some(XK_scedilla), - "tcaron" => Some(XK_tcaron), - "zacute" => Some(XK_zacute), - "doubleacute" => Some(XK_doubleacute), - "zcaron" => Some(XK_zcaron), - "zabovedot" => Some(XK_zabovedot), - "Racute" => Some(XK_Racute), - "Abreve" => Some(XK_Abreve), - "Lacute" => Some(XK_Lacute), - "Cacute" => Some(XK_Cacute), - "Ccaron" => Some(XK_Ccaron), - "Eogonek" => Some(XK_Eogonek), - "Ecaron" => Some(XK_Ecaron), - "Dcaron" => Some(XK_Dcaron), - "Dstroke" => Some(XK_Dstroke), - "Nacute" => Some(XK_Nacute), - "Ncaron" => Some(XK_Ncaron), - "Odoubleacute" => Some(XK_Odoubleacute), - "Rcaron" => Some(XK_Rcaron), - "Uring" => Some(XK_Uring), - "Udoubleacute" => Some(XK_Udoubleacute), - "Tcedilla" => Some(XK_Tcedilla), - "racute" => Some(XK_racute), - "abreve" => Some(XK_abreve), - "lacute" => Some(XK_lacute), - "cacute" => Some(XK_cacute), - "ccaron" => Some(XK_ccaron), - "eogonek" => Some(XK_eogonek), - "ecaron" => Some(XK_ecaron), - "dcaron" => Some(XK_dcaron), - "dstroke" => Some(XK_dstroke), - "nacute" => Some(XK_nacute), - "ncaron" => Some(XK_ncaron), - "odoubleacute" => Some(XK_odoubleacute), - "udoubleacute" => Some(XK_udoubleacute), - "rcaron" => Some(XK_rcaron), - "uring" => Some(XK_uring), - "tcedilla" => Some(XK_tcedilla), - "abovedot" => Some(XK_abovedot), - "Hstroke" => Some(XK_Hstroke), - "Hcircumflex" => Some(XK_Hcircumflex), - "Iabovedot" => Some(XK_Iabovedot), - "Gbreve" => Some(XK_Gbreve), - "Jcircumflex" => Some(XK_Jcircumflex), - "hstroke" => Some(XK_hstroke), - "hcircumflex" => Some(XK_hcircumflex), - "idotless" => Some(XK_idotless), - "gbreve" => Some(XK_gbreve), - "jcircumflex" => Some(XK_jcircumflex), - "Cabovedot" => Some(XK_Cabovedot), - "Ccircumflex" => Some(XK_Ccircumflex), - "Gabovedot" => Some(XK_Gabovedot), - "Gcircumflex" => Some(XK_Gcircumflex), - "Ubreve" => Some(XK_Ubreve), - "Scircumflex" => Some(XK_Scircumflex), - "cabovedot" => Some(XK_cabovedot), - "ccircumflex" => Some(XK_ccircumflex), - "gabovedot" => Some(XK_gabovedot), - "gcircumflex" => Some(XK_gcircumflex), - "ubreve" => Some(XK_ubreve), - "scircumflex" => Some(XK_scircumflex), - "kra" => Some(XK_kra), - "kappa" => Some(XK_kappa), - "Rcedilla" => Some(XK_Rcedilla), - "Itilde" => Some(XK_Itilde), - "Lcedilla" => Some(XK_Lcedilla), - "Emacron" => Some(XK_Emacron), - "Gcedilla" => Some(XK_Gcedilla), - "Tslash" => Some(XK_Tslash), - "rcedilla" => Some(XK_rcedilla), - "itilde" => Some(XK_itilde), - "lcedilla" => Some(XK_lcedilla), - "emacron" => Some(XK_emacron), - "gcedilla" => Some(XK_gcedilla), - "tslash" => Some(XK_tslash), - "ENG" => Some(XK_ENG), - "eng" => Some(XK_eng), - "Amacron" => Some(XK_Amacron), - "Iogonek" => Some(XK_Iogonek), - "Eabovedot" => Some(XK_Eabovedot), - "Imacron" => Some(XK_Imacron), - "Ncedilla" => Some(XK_Ncedilla), - "Omacron" => Some(XK_Omacron), - "Kcedilla" => Some(XK_Kcedilla), - "Uogonek" => Some(XK_Uogonek), - "Utilde" => Some(XK_Utilde), - "Umacron" => Some(XK_Umacron), - "amacron" => Some(XK_amacron), - "iogonek" => Some(XK_iogonek), - "eabovedot" => Some(XK_eabovedot), - "imacron" => Some(XK_imacron), - "ncedilla" => Some(XK_ncedilla), - "omacron" => Some(XK_omacron), - "kcedilla" => Some(XK_kcedilla), - "uogonek" => Some(XK_uogonek), - "utilde" => Some(XK_utilde), - "umacron" => Some(XK_umacron), - "overline" => Some(XK_overline), - "kana_fullstop" => Some(XK_kana_fullstop), - "kana_openingbracket" => Some(XK_kana_openingbracket), - "kana_closingbracket" => Some(XK_kana_closingbracket), - "kana_comma" => Some(XK_kana_comma), - "kana_conjunctive" => Some(XK_kana_conjunctive), - "kana_middledot" => Some(XK_kana_middledot), - "kana_WO" => Some(XK_kana_WO), - "kana_a" => Some(XK_kana_a), - "kana_i" => Some(XK_kana_i), - "kana_u" => Some(XK_kana_u), - "kana_e" => Some(XK_kana_e), - "kana_o" => Some(XK_kana_o), - "kana_ya" => Some(XK_kana_ya), - "kana_yu" => Some(XK_kana_yu), - "kana_yo" => Some(XK_kana_yo), - "kana_tsu" => Some(XK_kana_tsu), - "kana_tu" => Some(XK_kana_tu), - "prolongedsound" => Some(XK_prolongedsound), - "kana_A" => Some(XK_kana_A), - "kana_I" => Some(XK_kana_I), - "kana_U" => Some(XK_kana_U), - "kana_E" => Some(XK_kana_E), - "kana_O" => Some(XK_kana_O), - "kana_KA" => Some(XK_kana_KA), - "kana_KI" => Some(XK_kana_KI), - "kana_KU" => Some(XK_kana_KU), - "kana_KE" => Some(XK_kana_KE), - "kana_KO" => Some(XK_kana_KO), - "kana_SA" => Some(XK_kana_SA), - "kana_SHI" => Some(XK_kana_SHI), - "kana_SU" => Some(XK_kana_SU), - "kana_SE" => Some(XK_kana_SE), - "kana_SO" => Some(XK_kana_SO), - "kana_TA" => Some(XK_kana_TA), - "kana_CHI" => Some(XK_kana_CHI), - "kana_TI" => Some(XK_kana_TI), - "kana_TSU" => Some(XK_kana_TSU), - "kana_TU" => Some(XK_kana_TU), - "kana_TE" => Some(XK_kana_TE), - "kana_TO" => Some(XK_kana_TO), - "kana_NA" => Some(XK_kana_NA), - "kana_NI" => Some(XK_kana_NI), - "kana_NU" => Some(XK_kana_NU), - "kana_NE" => Some(XK_kana_NE), - "kana_NO" => Some(XK_kana_NO), - "kana_HA" => Some(XK_kana_HA), - "kana_HI" => Some(XK_kana_HI), - "kana_FU" => Some(XK_kana_FU), - "kana_HU" => Some(XK_kana_HU), - "kana_HE" => Some(XK_kana_HE), - "kana_HO" => Some(XK_kana_HO), - "kana_MA" => Some(XK_kana_MA), - "kana_MI" => Some(XK_kana_MI), - "kana_MU" => Some(XK_kana_MU), - "kana_ME" => Some(XK_kana_ME), - "kana_MO" => Some(XK_kana_MO), - "kana_YA" => Some(XK_kana_YA), - "kana_YU" => Some(XK_kana_YU), - "kana_YO" => Some(XK_kana_YO), - "kana_RA" => Some(XK_kana_RA), - "kana_RI" => Some(XK_kana_RI), - "kana_RU" => Some(XK_kana_RU), - "kana_RE" => Some(XK_kana_RE), - "kana_RO" => Some(XK_kana_RO), - "kana_WA" => Some(XK_kana_WA), - "kana_N" => Some(XK_kana_N), - "voicedsound" => Some(XK_voicedsound), - "semivoicedsound" => Some(XK_semivoicedsound), - "kana_switch" => Some(XK_kana_switch), - "Arabic_comma" => Some(XK_Arabic_comma), - "Arabic_semicolon" => Some(XK_Arabic_semicolon), - "Arabic_question_mark" => Some(XK_Arabic_question_mark), - "Arabic_hamza" => Some(XK_Arabic_hamza), - "Arabic_maddaonalef" => Some(XK_Arabic_maddaonalef), - "Arabic_hamzaonalef" => Some(XK_Arabic_hamzaonalef), - "Arabic_hamzaonwaw" => Some(XK_Arabic_hamzaonwaw), - "Arabic_hamzaunderalef" => Some(XK_Arabic_hamzaunderalef), - "Arabic_hamzaonyeh" => Some(XK_Arabic_hamzaonyeh), - "Arabic_alef" => Some(XK_Arabic_alef), - "Arabic_beh" => Some(XK_Arabic_beh), - "Arabic_tehmarbuta" => Some(XK_Arabic_tehmarbuta), - "Arabic_teh" => Some(XK_Arabic_teh), - "Arabic_theh" => Some(XK_Arabic_theh), - "Arabic_jeem" => Some(XK_Arabic_jeem), - "Arabic_hah" => Some(XK_Arabic_hah), - "Arabic_khah" => Some(XK_Arabic_khah), - "Arabic_dal" => Some(XK_Arabic_dal), - "Arabic_thal" => Some(XK_Arabic_thal), - "Arabic_ra" => Some(XK_Arabic_ra), - "Arabic_zain" => Some(XK_Arabic_zain), - "Arabic_seen" => Some(XK_Arabic_seen), - "Arabic_sheen" => Some(XK_Arabic_sheen), - "Arabic_sad" => Some(XK_Arabic_sad), - "Arabic_dad" => Some(XK_Arabic_dad), - "Arabic_tah" => Some(XK_Arabic_tah), - "Arabic_zah" => Some(XK_Arabic_zah), - "Arabic_ain" => Some(XK_Arabic_ain), - "Arabic_ghain" => Some(XK_Arabic_ghain), - "Arabic_tatweel" => Some(XK_Arabic_tatweel), - "Arabic_feh" => Some(XK_Arabic_feh), - "Arabic_qaf" => Some(XK_Arabic_qaf), - "Arabic_kaf" => Some(XK_Arabic_kaf), - "Arabic_lam" => Some(XK_Arabic_lam), - "Arabic_meem" => Some(XK_Arabic_meem), - "Arabic_noon" => Some(XK_Arabic_noon), - "Arabic_ha" => Some(XK_Arabic_ha), - "Arabic_heh" => Some(XK_Arabic_heh), - "Arabic_waw" => Some(XK_Arabic_waw), - "Arabic_alefmaksura" => Some(XK_Arabic_alefmaksura), - "Arabic_yeh" => Some(XK_Arabic_yeh), - "Arabic_fathatan" => Some(XK_Arabic_fathatan), - "Arabic_dammatan" => Some(XK_Arabic_dammatan), - "Arabic_kasratan" => Some(XK_Arabic_kasratan), - "Arabic_fatha" => Some(XK_Arabic_fatha), - "Arabic_damma" => Some(XK_Arabic_damma), - "Arabic_kasra" => Some(XK_Arabic_kasra), - "Arabic_shadda" => Some(XK_Arabic_shadda), - "Arabic_sukun" => Some(XK_Arabic_sukun), - "Arabic_switch" => Some(XK_Arabic_switch), - "Serbian_dje" => Some(XK_Serbian_dje), - "Macedonia_gje" => Some(XK_Macedonia_gje), - "Cyrillic_io" => Some(XK_Cyrillic_io), - "Ukrainian_ie" => Some(XK_Ukrainian_ie), - "Ukranian_je" => Some(XK_Ukranian_je), - "Macedonia_dse" => Some(XK_Macedonia_dse), - "Ukrainian_i" => Some(XK_Ukrainian_i), - "Ukranian_i" => Some(XK_Ukranian_i), - "Ukrainian_yi" => Some(XK_Ukrainian_yi), - "Ukranian_yi" => Some(XK_Ukranian_yi), - "Cyrillic_je" => Some(XK_Cyrillic_je), - "Serbian_je" => Some(XK_Serbian_je), - "Cyrillic_lje" => Some(XK_Cyrillic_lje), - "Serbian_lje" => Some(XK_Serbian_lje), - "Cyrillic_nje" => Some(XK_Cyrillic_nje), - "Serbian_nje" => Some(XK_Serbian_nje), - "Serbian_tshe" => Some(XK_Serbian_tshe), - "Macedonia_kje" => Some(XK_Macedonia_kje), - "Byelorussian_shortu" => Some(XK_Byelorussian_shortu), - "Cyrillic_dzhe" => Some(XK_Cyrillic_dzhe), - "Serbian_dze" => Some(XK_Serbian_dze), - "numerosign" => Some(XK_numerosign), - "Serbian_DJE" => Some(XK_Serbian_DJE), - "Macedonia_GJE" => Some(XK_Macedonia_GJE), - "Cyrillic_IO" => Some(XK_Cyrillic_IO), - "Ukrainian_IE" => Some(XK_Ukrainian_IE), - "Ukranian_JE" => Some(XK_Ukranian_JE), - "Macedonia_DSE" => Some(XK_Macedonia_DSE), - "Ukrainian_I" => Some(XK_Ukrainian_I), - "Ukranian_I" => Some(XK_Ukranian_I), - "Ukrainian_YI" => Some(XK_Ukrainian_YI), - "Ukranian_YI" => Some(XK_Ukranian_YI), - "Cyrillic_JE" => Some(XK_Cyrillic_JE), - "Serbian_JE" => Some(XK_Serbian_JE), - "Cyrillic_LJE" => Some(XK_Cyrillic_LJE), - "Serbian_LJE" => Some(XK_Serbian_LJE), - "Cyrillic_NJE" => Some(XK_Cyrillic_NJE), - "Serbian_NJE" => Some(XK_Serbian_NJE), - "Serbian_TSHE" => Some(XK_Serbian_TSHE), - "Macedonia_KJE" => Some(XK_Macedonia_KJE), - "Byelorussian_SHORTU" => Some(XK_Byelorussian_SHORTU), - "Cyrillic_DZHE" => Some(XK_Cyrillic_DZHE), - "Serbian_DZE" => Some(XK_Serbian_DZE), - "Cyrillic_yu" => Some(XK_Cyrillic_yu), - "Cyrillic_a" => Some(XK_Cyrillic_a), - "Cyrillic_be" => Some(XK_Cyrillic_be), - "Cyrillic_tse" => Some(XK_Cyrillic_tse), - "Cyrillic_de" => Some(XK_Cyrillic_de), - "Cyrillic_ie" => Some(XK_Cyrillic_ie), - "Cyrillic_ef" => Some(XK_Cyrillic_ef), - "Cyrillic_ghe" => Some(XK_Cyrillic_ghe), - "Cyrillic_ha" => Some(XK_Cyrillic_ha), - "Cyrillic_i" => Some(XK_Cyrillic_i), - "Cyrillic_shorti" => Some(XK_Cyrillic_shorti), - "Cyrillic_ka" => Some(XK_Cyrillic_ka), - "Cyrillic_el" => Some(XK_Cyrillic_el), - "Cyrillic_em" => Some(XK_Cyrillic_em), - "Cyrillic_en" => Some(XK_Cyrillic_en), - "Cyrillic_o" => Some(XK_Cyrillic_o), - "Cyrillic_pe" => Some(XK_Cyrillic_pe), - "Cyrillic_ya" => Some(XK_Cyrillic_ya), - "Cyrillic_er" => Some(XK_Cyrillic_er), - "Cyrillic_es" => Some(XK_Cyrillic_es), - "Cyrillic_te" => Some(XK_Cyrillic_te), - "Cyrillic_u" => Some(XK_Cyrillic_u), - "Cyrillic_zhe" => Some(XK_Cyrillic_zhe), - "Cyrillic_ve" => Some(XK_Cyrillic_ve), - "Cyrillic_softsign" => Some(XK_Cyrillic_softsign), - "Cyrillic_yeru" => Some(XK_Cyrillic_yeru), - "Cyrillic_ze" => Some(XK_Cyrillic_ze), - "Cyrillic_sha" => Some(XK_Cyrillic_sha), - "Cyrillic_e" => Some(XK_Cyrillic_e), - "Cyrillic_shcha" => Some(XK_Cyrillic_shcha), - "Cyrillic_che" => Some(XK_Cyrillic_che), - "Cyrillic_hardsign" => Some(XK_Cyrillic_hardsign), - "Cyrillic_YU" => Some(XK_Cyrillic_YU), - "Cyrillic_A" => Some(XK_Cyrillic_A), - "Cyrillic_BE" => Some(XK_Cyrillic_BE), - "Cyrillic_TSE" => Some(XK_Cyrillic_TSE), - "Cyrillic_DE" => Some(XK_Cyrillic_DE), - "Cyrillic_IE" => Some(XK_Cyrillic_IE), - "Cyrillic_EF" => Some(XK_Cyrillic_EF), - "Cyrillic_GHE" => Some(XK_Cyrillic_GHE), - "Cyrillic_HA" => Some(XK_Cyrillic_HA), - "Cyrillic_I" => Some(XK_Cyrillic_I), - "Cyrillic_SHORTI" => Some(XK_Cyrillic_SHORTI), - "Cyrillic_KA" => Some(XK_Cyrillic_KA), - "Cyrillic_EL" => Some(XK_Cyrillic_EL), - "Cyrillic_EM" => Some(XK_Cyrillic_EM), - "Cyrillic_EN" => Some(XK_Cyrillic_EN), - "Cyrillic_O" => Some(XK_Cyrillic_O), - "Cyrillic_PE" => Some(XK_Cyrillic_PE), - "Cyrillic_YA" => Some(XK_Cyrillic_YA), - "Cyrillic_ER" => Some(XK_Cyrillic_ER), - "Cyrillic_ES" => Some(XK_Cyrillic_ES), - "Cyrillic_TE" => Some(XK_Cyrillic_TE), - "Cyrillic_U" => Some(XK_Cyrillic_U), - "Cyrillic_ZHE" => Some(XK_Cyrillic_ZHE), - "Cyrillic_VE" => Some(XK_Cyrillic_VE), - "Cyrillic_SOFTSIGN" => Some(XK_Cyrillic_SOFTSIGN), - "Cyrillic_YERU" => Some(XK_Cyrillic_YERU), - "Cyrillic_ZE" => Some(XK_Cyrillic_ZE), - "Cyrillic_SHA" => Some(XK_Cyrillic_SHA), - "Cyrillic_E" => Some(XK_Cyrillic_E), - "Cyrillic_SHCHA" => Some(XK_Cyrillic_SHCHA), - "Cyrillic_CHE" => Some(XK_Cyrillic_CHE), - "Cyrillic_HARDSIGN" => Some(XK_Cyrillic_HARDSIGN), - "Greek_ALPHAaccent" => Some(XK_Greek_ALPHAaccent), - "Greek_EPSILONaccent" => Some(XK_Greek_EPSILONaccent), - "Greek_ETAaccent" => Some(XK_Greek_ETAaccent), - "Greek_IOTAaccent" => Some(XK_Greek_IOTAaccent), - "Greek_IOTAdiaeresis" => Some(XK_Greek_IOTAdiaeresis), - "Greek_OMICRONaccent" => Some(XK_Greek_OMICRONaccent), - "Greek_UPSILONaccent" => Some(XK_Greek_UPSILONaccent), - "Greek_UPSILONdieresis" => Some(XK_Greek_UPSILONdieresis), - "Greek_OMEGAaccent" => Some(XK_Greek_OMEGAaccent), - "Greek_accentdieresis" => Some(XK_Greek_accentdieresis), - "Greek_horizbar" => Some(XK_Greek_horizbar), - "Greek_alphaaccent" => Some(XK_Greek_alphaaccent), - "Greek_epsilonaccent" => Some(XK_Greek_epsilonaccent), - "Greek_etaaccent" => Some(XK_Greek_etaaccent), - "Greek_iotaaccent" => Some(XK_Greek_iotaaccent), - "Greek_iotadieresis" => Some(XK_Greek_iotadieresis), - "Greek_iotaaccentdieresis" => Some(XK_Greek_iotaaccentdieresis), - "Greek_omicronaccent" => Some(XK_Greek_omicronaccent), - "Greek_upsilonaccent" => Some(XK_Greek_upsilonaccent), - "Greek_upsilondieresis" => Some(XK_Greek_upsilondieresis), - "Greek_upsilonaccentdieresis" => Some(XK_Greek_upsilonaccentdieresis), - "Greek_omegaaccent" => Some(XK_Greek_omegaaccent), - "Greek_ALPHA" => Some(XK_Greek_ALPHA), - "Greek_BETA" => Some(XK_Greek_BETA), - "Greek_GAMMA" => Some(XK_Greek_GAMMA), - "Greek_DELTA" => Some(XK_Greek_DELTA), - "Greek_EPSILON" => Some(XK_Greek_EPSILON), - "Greek_ZETA" => Some(XK_Greek_ZETA), - "Greek_ETA" => Some(XK_Greek_ETA), - "Greek_THETA" => Some(XK_Greek_THETA), - "Greek_IOTA" => Some(XK_Greek_IOTA), - "Greek_KAPPA" => Some(XK_Greek_KAPPA), - "Greek_LAMDA" => Some(XK_Greek_LAMDA), - "Greek_LAMBDA" => Some(XK_Greek_LAMBDA), - "Greek_MU" => Some(XK_Greek_MU), - "Greek_NU" => Some(XK_Greek_NU), - "Greek_XI" => Some(XK_Greek_XI), - "Greek_OMICRON" => Some(XK_Greek_OMICRON), - "Greek_PI" => Some(XK_Greek_PI), - "Greek_RHO" => Some(XK_Greek_RHO), - "Greek_SIGMA" => Some(XK_Greek_SIGMA), - "Greek_TAU" => Some(XK_Greek_TAU), - "Greek_UPSILON" => Some(XK_Greek_UPSILON), - "Greek_PHI" => Some(XK_Greek_PHI), - "Greek_CHI" => Some(XK_Greek_CHI), - "Greek_PSI" => Some(XK_Greek_PSI), - "Greek_OMEGA" => Some(XK_Greek_OMEGA), - "Greek_alpha" => Some(XK_Greek_alpha), - "Greek_beta" => Some(XK_Greek_beta), - "Greek_gamma" => Some(XK_Greek_gamma), - "Greek_delta" => Some(XK_Greek_delta), - "Greek_epsilon" => Some(XK_Greek_epsilon), - "Greek_zeta" => Some(XK_Greek_zeta), - "Greek_eta" => Some(XK_Greek_eta), - "Greek_theta" => Some(XK_Greek_theta), - "Greek_iota" => Some(XK_Greek_iota), - "Greek_kappa" => Some(XK_Greek_kappa), - "Greek_lamda" => Some(XK_Greek_lamda), - "Greek_lambda" => Some(XK_Greek_lambda), - "Greek_mu" => Some(XK_Greek_mu), - "Greek_nu" => Some(XK_Greek_nu), - "Greek_xi" => Some(XK_Greek_xi), - "Greek_omicron" => Some(XK_Greek_omicron), - "Greek_pi" => Some(XK_Greek_pi), - "Greek_rho" => Some(XK_Greek_rho), - "Greek_sigma" => Some(XK_Greek_sigma), - "Greek_finalsmallsigma" => Some(XK_Greek_finalsmallsigma), - "Greek_tau" => Some(XK_Greek_tau), - "Greek_upsilon" => Some(XK_Greek_upsilon), - "Greek_phi" => Some(XK_Greek_phi), - "Greek_chi" => Some(XK_Greek_chi), - "Greek_psi" => Some(XK_Greek_psi), - "Greek_omega" => Some(XK_Greek_omega), - "Greek_switch" => Some(XK_Greek_switch), - "leftradical" => Some(XK_leftradical), - "topleftradical" => Some(XK_topleftradical), - "horizconnector" => Some(XK_horizconnector), - "topintegral" => Some(XK_topintegral), - "botintegral" => Some(XK_botintegral), - "vertconnector" => Some(XK_vertconnector), - "topleftsqbracket" => Some(XK_topleftsqbracket), - "botleftsqbracket" => Some(XK_botleftsqbracket), - "toprightsqbracket" => Some(XK_toprightsqbracket), - "botrightsqbracket" => Some(XK_botrightsqbracket), - "topleftparens" => Some(XK_topleftparens), - "botleftparens" => Some(XK_botleftparens), - "toprightparens" => Some(XK_toprightparens), - "botrightparens" => Some(XK_botrightparens), - "leftmiddlecurlybrace" => Some(XK_leftmiddlecurlybrace), - "rightmiddlecurlybrace" => Some(XK_rightmiddlecurlybrace), - "topleftsummation" => Some(XK_topleftsummation), - "botleftsummation" => Some(XK_botleftsummation), - "topvertsummationconnector" => Some(XK_topvertsummationconnector), - "botvertsummationconnector" => Some(XK_botvertsummationconnector), - "toprightsummation" => Some(XK_toprightsummation), - "botrightsummation" => Some(XK_botrightsummation), - "rightmiddlesummation" => Some(XK_rightmiddlesummation), - "lessthanequal" => Some(XK_lessthanequal), - "notequal" => Some(XK_notequal), - "greaterthanequal" => Some(XK_greaterthanequal), - "integral" => Some(XK_integral), - "therefore" => Some(XK_therefore), - "variation" => Some(XK_variation), - "infinity" => Some(XK_infinity), - "nabla" => Some(XK_nabla), - "approximate" => Some(XK_approximate), - "similarequal" => Some(XK_similarequal), - "ifonlyif" => Some(XK_ifonlyif), - "implies" => Some(XK_implies), - "identical" => Some(XK_identical), - "radical" => Some(XK_radical), - "includedin" => Some(XK_includedin), - "includes" => Some(XK_includes), - "intersection" => Some(XK_intersection), - "union" => Some(XK_union), - "logicaland" => Some(XK_logicaland), - "logicalor" => Some(XK_logicalor), - "partialderivative" => Some(XK_partialderivative), - "function" => Some(XK_function), - "leftarrow" => Some(XK_leftarrow), - "uparrow" => Some(XK_uparrow), - "rightarrow" => Some(XK_rightarrow), - "downarrow" => Some(XK_downarrow), - "blank" => Some(XK_blank), - "soliddiamond" => Some(XK_soliddiamond), - "checkerboard" => Some(XK_checkerboard), - "ht" => Some(XK_ht), - "ff" => Some(XK_ff), - "cr" => Some(XK_cr), - "lf" => Some(XK_lf), - "nl" => Some(XK_nl), - "vt" => Some(XK_vt), - "lowrightcorner" => Some(XK_lowrightcorner), - "uprightcorner" => Some(XK_uprightcorner), - "upleftcorner" => Some(XK_upleftcorner), - "lowleftcorner" => Some(XK_lowleftcorner), - "crossinglines" => Some(XK_crossinglines), - "horizlinescan1" => Some(XK_horizlinescan1), - "horizlinescan3" => Some(XK_horizlinescan3), - "horizlinescan5" => Some(XK_horizlinescan5), - "horizlinescan7" => Some(XK_horizlinescan7), - "horizlinescan9" => Some(XK_horizlinescan9), - "leftt" => Some(XK_leftt), - "rightt" => Some(XK_rightt), - "bott" => Some(XK_bott), - "topt" => Some(XK_topt), - "vertbar" => Some(XK_vertbar), - "emspace" => Some(XK_emspace), - "enspace" => Some(XK_enspace), - "em3space" => Some(XK_em3space), - "em4space" => Some(XK_em4space), - "digitspace" => Some(XK_digitspace), - "punctspace" => Some(XK_punctspace), - "thinspace" => Some(XK_thinspace), - "hairspace" => Some(XK_hairspace), - "emdash" => Some(XK_emdash), - "endash" => Some(XK_endash), - "signifblank" => Some(XK_signifblank), - "ellipsis" => Some(XK_ellipsis), - "doubbaselinedot" => Some(XK_doubbaselinedot), - "onethird" => Some(XK_onethird), - "twothirds" => Some(XK_twothirds), - "onefifth" => Some(XK_onefifth), - "twofifths" => Some(XK_twofifths), - "threefifths" => Some(XK_threefifths), - "fourfifths" => Some(XK_fourfifths), - "onesixth" => Some(XK_onesixth), - "fivesixths" => Some(XK_fivesixths), - "careof" => Some(XK_careof), - "figdash" => Some(XK_figdash), - "leftanglebracket" => Some(XK_leftanglebracket), - "decimalpoint" => Some(XK_decimalpoint), - "rightanglebracket" => Some(XK_rightanglebracket), - "marker" => Some(XK_marker), - "oneeighth" => Some(XK_oneeighth), - "threeeighths" => Some(XK_threeeighths), - "fiveeighths" => Some(XK_fiveeighths), - "seveneighths" => Some(XK_seveneighths), - "trademark" => Some(XK_trademark), - "signaturemark" => Some(XK_signaturemark), - "trademarkincircle" => Some(XK_trademarkincircle), - "leftopentriangle" => Some(XK_leftopentriangle), - "rightopentriangle" => Some(XK_rightopentriangle), - "emopencircle" => Some(XK_emopencircle), - "emopenrectangle" => Some(XK_emopenrectangle), - "leftsinglequotemark" => Some(XK_leftsinglequotemark), - "rightsinglequotemark" => Some(XK_rightsinglequotemark), - "leftdoublequotemark" => Some(XK_leftdoublequotemark), - "rightdoublequotemark" => Some(XK_rightdoublequotemark), - "prescription" => Some(XK_prescription), - "minutes" => Some(XK_minutes), - "seconds" => Some(XK_seconds), - "latincross" => Some(XK_latincross), - "hexagram" => Some(XK_hexagram), - "filledrectbullet" => Some(XK_filledrectbullet), - "filledlefttribullet" => Some(XK_filledlefttribullet), - "filledrighttribullet" => Some(XK_filledrighttribullet), - "emfilledcircle" => Some(XK_emfilledcircle), - "emfilledrect" => Some(XK_emfilledrect), - "enopencircbullet" => Some(XK_enopencircbullet), - "enopensquarebullet" => Some(XK_enopensquarebullet), - "openrectbullet" => Some(XK_openrectbullet), - "opentribulletup" => Some(XK_opentribulletup), - "opentribulletdown" => Some(XK_opentribulletdown), - "openstar" => Some(XK_openstar), - "enfilledcircbullet" => Some(XK_enfilledcircbullet), - "enfilledsqbullet" => Some(XK_enfilledsqbullet), - "filledtribulletup" => Some(XK_filledtribulletup), - "filledtribulletdown" => Some(XK_filledtribulletdown), - "leftpointer" => Some(XK_leftpointer), - "rightpointer" => Some(XK_rightpointer), - "club" => Some(XK_club), - "diamond" => Some(XK_diamond), - "heart" => Some(XK_heart), - "maltesecross" => Some(XK_maltesecross), - "dagger" => Some(XK_dagger), - "doubledagger" => Some(XK_doubledagger), - "checkmark" => Some(XK_checkmark), - "ballotcross" => Some(XK_ballotcross), - "musicalsharp" => Some(XK_musicalsharp), - "musicalflat" => Some(XK_musicalflat), - "malesymbol" => Some(XK_malesymbol), - "femalesymbol" => Some(XK_femalesymbol), - "telephone" => Some(XK_telephone), - "telephonerecorder" => Some(XK_telephonerecorder), - "phonographcopyright" => Some(XK_phonographcopyright), - "caret" => Some(XK_caret), - "singlelowquotemark" => Some(XK_singlelowquotemark), - "doublelowquotemark" => Some(XK_doublelowquotemark), - "cursor" => Some(XK_cursor), - "leftcaret" => Some(XK_leftcaret), - "rightcaret" => Some(XK_rightcaret), - "downcaret" => Some(XK_downcaret), - "upcaret" => Some(XK_upcaret), - "overbar" => Some(XK_overbar), - "downtack" => Some(XK_downtack), - "upshoe" => Some(XK_upshoe), - "downstile" => Some(XK_downstile), - "underbar" => Some(XK_underbar), - "jot" => Some(XK_jot), - "quad" => Some(XK_quad), - "uptack" => Some(XK_uptack), - "circle" => Some(XK_circle), - "upstile" => Some(XK_upstile), - "downshoe" => Some(XK_downshoe), - "rightshoe" => Some(XK_rightshoe), - "leftshoe" => Some(XK_leftshoe), - "lefttack" => Some(XK_lefttack), - "righttack" => Some(XK_righttack), - "hebrew_doublelowline" => Some(XK_hebrew_doublelowline), - "hebrew_aleph" => Some(XK_hebrew_aleph), - "hebrew_bet" => Some(XK_hebrew_bet), - "hebrew_beth" => Some(XK_hebrew_beth), - "hebrew_gimel" => Some(XK_hebrew_gimel), - "hebrew_gimmel" => Some(XK_hebrew_gimmel), - "hebrew_dalet" => Some(XK_hebrew_dalet), - "hebrew_daleth" => Some(XK_hebrew_daleth), - "hebrew_he" => Some(XK_hebrew_he), - "hebrew_waw" => Some(XK_hebrew_waw), - "hebrew_zain" => Some(XK_hebrew_zain), - "hebrew_zayin" => Some(XK_hebrew_zayin), - "hebrew_chet" => Some(XK_hebrew_chet), - "hebrew_het" => Some(XK_hebrew_het), - "hebrew_tet" => Some(XK_hebrew_tet), - "hebrew_teth" => Some(XK_hebrew_teth), - "hebrew_yod" => Some(XK_hebrew_yod), - "hebrew_finalkaph" => Some(XK_hebrew_finalkaph), - "hebrew_kaph" => Some(XK_hebrew_kaph), - "hebrew_lamed" => Some(XK_hebrew_lamed), - "hebrew_finalmem" => Some(XK_hebrew_finalmem), - "hebrew_mem" => Some(XK_hebrew_mem), - "hebrew_finalnun" => Some(XK_hebrew_finalnun), - "hebrew_nun" => Some(XK_hebrew_nun), - "hebrew_samech" => Some(XK_hebrew_samech), - "hebrew_samekh" => Some(XK_hebrew_samekh), - "hebrew_ayin" => Some(XK_hebrew_ayin), - "hebrew_finalpe" => Some(XK_hebrew_finalpe), - "hebrew_pe" => Some(XK_hebrew_pe), - "hebrew_finalzade" => Some(XK_hebrew_finalzade), - "hebrew_finalzadi" => Some(XK_hebrew_finalzadi), - "hebrew_zade" => Some(XK_hebrew_zade), - "hebrew_zadi" => Some(XK_hebrew_zadi), - "hebrew_qoph" => Some(XK_hebrew_qoph), - "hebrew_kuf" => Some(XK_hebrew_kuf), - "hebrew_resh" => Some(XK_hebrew_resh), - "hebrew_shin" => Some(XK_hebrew_shin), - "hebrew_taw" => Some(XK_hebrew_taw), - "hebrew_taf" => Some(XK_hebrew_taf), - "Hebrew_switch" => Some(XK_Hebrew_switch), - "XF86XK_ModeLock" | "XF86ModeLock" => Some(XF86XK_ModeLock), - "XF86XK_MonBrightnessUp" | "XF86MonBrightnessUp" => Some(XF86XK_MonBrightnessUp), - "XF86XK_MonBrightnessDown" | "XF86MonBrightnessDown" => Some(XF86XK_MonBrightnessDown), - "XF86XK_KbdLightOnOff" | "XF86KbdLightOnOff" => Some(XF86XK_KbdLightOnOff), - "XF86XK_KbdBrightnessUp" | "XF86KbdBrightnessUp" => Some(XF86XK_KbdBrightnessUp), - "XF86XK_KbdBrightnessDown" | "XF86KbdBrightnessDown" => Some(XF86XK_KbdBrightnessDown), - "XF86XK_Standby" | "XF86Standby" => Some(XF86XK_Standby), - "XF86XK_AudioLowerVolume" | "XF86AudioLowerVolume" => Some(XF86XK_AudioLowerVolume), - "XF86XK_AudioMute" | "XF86AudioMute" => Some(XF86XK_AudioMute), - "XF86XK_AudioRaiseVolume" | "XF86AudioRaiseVolume" => Some(XF86XK_AudioRaiseVolume), - "XF86XK_AudioPlay" | "XF86AudioPlay" => Some(XF86XK_AudioPlay), - "XF86XK_AudioStop" | "XF86AudioStop" => Some(XF86XK_AudioStop), - "XF86XK_AudioPrev" | "XF86AudioPrev" => Some(XF86XK_AudioPrev), - "XF86XK_AudioNext" | "XF86AudioNext" => Some(XF86XK_AudioNext), - "XF86XK_HomePage" | "XF86HomePage" => Some(XF86XK_HomePage), - "XF86XK_Mail" | "XF86Mail" => Some(XF86XK_Mail), - "XF86XK_Start" | "XF86Start" => Some(XF86XK_Start), - "XF86XK_Search" | "XF86Search" => Some(XF86XK_Search), - "XF86XK_AudioRecord" | "XF86AudioRecord" => Some(XF86XK_AudioRecord), - "XF86XK_Calculator" | "XF86Calculator" => Some(XF86XK_Calculator), - "XF86XK_Memo" | "XF86Memo" => Some(XF86XK_Memo), - "XF86XK_ToDoList" | "XF86ToDoList" => Some(XF86XK_ToDoList), - "XF86XK_Calendar" | "XF86Calendar" => Some(XF86XK_Calendar), - "XF86XK_PowerDown" | "XF86PowerDown" => Some(XF86XK_PowerDown), - "XF86XK_ContrastAdjust" | "XF86ContrastAdjust" => Some(XF86XK_ContrastAdjust), - "XF86XK_RockerUp" | "XF86RockerUp" => Some(XF86XK_RockerUp), - "XF86XK_RockerDown" | "XF86RockerDown" => Some(XF86XK_RockerDown), - "XF86XK_RockerEnter" | "XF86RockerEnter" => Some(XF86XK_RockerEnter), - "XF86XK_Back" | "XF86Back" => Some(XF86XK_Back), - "XF86XK_Forward" | "XF86Forward" => Some(XF86XK_Forward), - "XF86XK_Stop" | "XF86Stop" => Some(XF86XK_Stop), - "XF86XK_Refresh" | "XF86Refresh" => Some(XF86XK_Refresh), - "XF86XK_PowerOff" | "XF86PowerOff" => Some(XF86XK_PowerOff), - "XF86XK_WakeUp" | "XF86WakeUp" => Some(XF86XK_WakeUp), - "XF86XK_Eject" | "XF86Eject" => Some(XF86XK_Eject), - "XF86XK_ScreenSaver" | "XF86ScreenSaver" => Some(XF86XK_ScreenSaver), - "XF86XK_WWW" | "XF86WWW" => Some(XF86XK_WWW), - "XF86XK_Sleep" | "XF86Sleep" => Some(XF86XK_Sleep), - "XF86XK_Favorites" | "XF86Favorites" => Some(XF86XK_Favorites), - "XF86XK_AudioPause" | "XF86AudioPause" => Some(XF86XK_AudioPause), - "XF86XK_AudioMedia" | "XF86AudioMedia" => Some(XF86XK_AudioMedia), - "XF86XK_MyComputer" | "XF86MyComputer" => Some(XF86XK_MyComputer), - "XF86XK_VendorHome" | "XF86VendorHome" => Some(XF86XK_VendorHome), - "XF86XK_LightBulb" | "XF86LightBulb" => Some(XF86XK_LightBulb), - "XF86XK_Shop" | "XF86Shop" => Some(XF86XK_Shop), - "XF86XK_History" | "XF86History" => Some(XF86XK_History), - "XF86XK_OpenURL" | "XF86OpenURL" => Some(XF86XK_OpenURL), - "XF86XK_AddFavorite" | "XF86AddFavorite" => Some(XF86XK_AddFavorite), - "XF86XK_HotLinks" | "XF86HotLinks" => Some(XF86XK_HotLinks), - "XF86XK_BrightnessAdjust" | "XF86BrightnessAdjust" => Some(XF86XK_BrightnessAdjust), - "XF86XK_Finance" | "XF86Finance" => Some(XF86XK_Finance), - "XF86XK_Community" | "XF86Community" => Some(XF86XK_Community), - "XF86XK_AudioRewind" | "XF86AudioRewind" => Some(XF86XK_AudioRewind), - "XF86XK_BackForward" | "XF86BackForward" => Some(XF86XK_BackForward), - "XF86XK_Launch0" | "XF86Launch0" => Some(XF86XK_Launch0), - "XF86XK_Launch1" | "XF86Launch1" => Some(XF86XK_Launch1), - "XF86XK_Launch2" | "XF86Launch2" => Some(XF86XK_Launch2), - "XF86XK_Launch3" | "XF86Launch3" => Some(XF86XK_Launch3), - "XF86XK_Launch4" | "XF86Launch4" => Some(XF86XK_Launch4), - "XF86XK_Launch5" | "XF86Launch5" => Some(XF86XK_Launch5), - "XF86XK_Launch6" | "XF86Launch6" => Some(XF86XK_Launch6), - "XF86XK_Launch7" | "XF86Launch7" => Some(XF86XK_Launch7), - "XF86XK_Launch8" | "XF86Launch8" => Some(XF86XK_Launch8), - "XF86XK_Launch9" | "XF86Launch9" => Some(XF86XK_Launch9), - "XF86XK_LaunchA" | "XF86LaunchA" => Some(XF86XK_LaunchA), - "XF86XK_LaunchB" | "XF86LaunchB" => Some(XF86XK_LaunchB), - "XF86XK_LaunchC" | "XF86LaunchC" => Some(XF86XK_LaunchC), - "XF86XK_LaunchD" | "XF86LaunchD" => Some(XF86XK_LaunchD), - "XF86XK_LaunchE" | "XF86LaunchE" => Some(XF86XK_LaunchE), - "XF86XK_LaunchF" | "XF86LaunchF" => Some(XF86XK_LaunchF), - "XF86XK_ApplicationLeft" | "XF86ApplicationLeft" => Some(XF86XK_ApplicationLeft), - "XF86XK_ApplicationRight" | "XF86ApplicationRight" => Some(XF86XK_ApplicationRight), - "XF86XK_Book" | "XF86Book" => Some(XF86XK_Book), - "XF86XK_CD" | "XF86CD" => Some(XF86XK_CD), - "XF86XK_Calculater" | "XF86Calculater" => Some(XF86XK_Calculater), - "XF86XK_Clear" | "XF86Clear" => Some(XF86XK_Clear), - "XF86XK_Close" | "XF86Close" => Some(XF86XK_Close), - "XF86XK_Copy" | "XF86Copy" => Some(XF86XK_Copy), - "XF86XK_Cut" | "XF86Cut" => Some(XF86XK_Cut), - "XF86XK_Display" | "XF86Display" => Some(XF86XK_Display), - "XF86XK_DOS" | "XF86DOS" => Some(XF86XK_DOS), - "XF86XK_Documents" | "XF86Documents" => Some(XF86XK_Documents), - "XF86XK_Excel" | "XF86Excel" => Some(XF86XK_Excel), - "XF86XK_Explorer" | "XF86Explorer" => Some(XF86XK_Explorer), - "XF86XK_Game" | "XF86Game" => Some(XF86XK_Game), - "XF86XK_Go" | "XF86Go" => Some(XF86XK_Go), - "XF86XK_iTouch" | "XF86iTouch" => Some(XF86XK_iTouch), - "XF86XK_LogOff" | "XF86LogOff" => Some(XF86XK_LogOff), - "XF86XK_Market" | "XF86Market" => Some(XF86XK_Market), - "XF86XK_Meeting" | "XF86Meeting" => Some(XF86XK_Meeting), - "XF86XK_MenuKB" | "XF86MenuKB" => Some(XF86XK_MenuKB), - "XF86XK_MenuPB" | "XF86MenuPB" => Some(XF86XK_MenuPB), - "XF86XK_MySites" | "XF86MySites" => Some(XF86XK_MySites), - "XF86XK_New" | "XF86New" => Some(XF86XK_New), - "XF86XK_News" | "XF86News" => Some(XF86XK_News), - "XF86XK_OfficeHome" | "XF86OfficeHome" => Some(XF86XK_OfficeHome), - "XF86XK_Open" | "XF86Open" => Some(XF86XK_Open), - "XF86XK_Option" | "XF86Option" => Some(XF86XK_Option), - "XF86XK_Paste" | "XF86Paste" => Some(XF86XK_Paste), - "XF86XK_Phone" | "XF86Phone" => Some(XF86XK_Phone), - "XF86XK_Q" | "XF86Q" => Some(XF86XK_Q), - "XF86XK_Reply" | "XF86Reply" => Some(XF86XK_Reply), - "XF86XK_Reload" | "XF86Reload" => Some(XF86XK_Reload), - "XF86XK_RotateWindows" | "XF86RotateWindows" => Some(XF86XK_RotateWindows), - "XF86XK_RotationPB" | "XF86RotationPB" => Some(XF86XK_RotationPB), - "XF86XK_RotationKB" | "XF86RotationKB" => Some(XF86XK_RotationKB), - "XF86XK_Save" | "XF86Save" => Some(XF86XK_Save), - "XF86XK_ScrollUp" | "XF86ScrollUp" => Some(XF86XK_ScrollUp), - "XF86XK_ScrollDown" | "XF86ScrollDown" => Some(XF86XK_ScrollDown), - "XF86XK_ScrollClick" | "XF86ScrollClick" => Some(XF86XK_ScrollClick), - "XF86XK_Send" | "XF86Send" => Some(XF86XK_Send), - "XF86XK_Spell" | "XF86Spell" => Some(XF86XK_Spell), - "XF86XK_SplitScreen" | "XF86SplitScreen" => Some(XF86XK_SplitScreen), - "XF86XK_Support" | "XF86Support" => Some(XF86XK_Support), - "XF86XK_TaskPane" | "XF86TaskPane" => Some(XF86XK_TaskPane), - "XF86XK_Terminal" | "XF86Terminal" => Some(XF86XK_Terminal), - "XF86XK_Tools" | "XF86Tools" => Some(XF86XK_Tools), - "XF86XK_Travel" | "XF86Travel" => Some(XF86XK_Travel), - "XF86XK_UserPB" | "XF86UserPB" => Some(XF86XK_UserPB), - "XF86XK_User1KB" | "XF86User1KB" => Some(XF86XK_User1KB), - "XF86XK_User2KB" | "XF86User2KB" => Some(XF86XK_User2KB), - "XF86XK_Video" | "XF86Video" => Some(XF86XK_Video), - "XF86XK_WheelButton" | "XF86WheelButton" => Some(XF86XK_WheelButton), - "XF86XK_Word" | "XF86Word" => Some(XF86XK_Word), - "XF86XK_Xfer" | "XF86Xfer" => Some(XF86XK_Xfer), - "XF86XK_ZoomIn" | "XF86ZoomIn" => Some(XF86XK_ZoomIn), - "XF86XK_ZoomOut" | "XF86ZoomOut" => Some(XF86XK_ZoomOut), - "XF86XK_Away" | "XF86Away" => Some(XF86XK_Away), - "XF86XK_Messenger" | "XF86Messenger" => Some(XF86XK_Messenger), - "XF86XK_WebCam" | "XF86WebCam" => Some(XF86XK_WebCam), - "XF86XK_MailForward" | "XF86MailForward" => Some(XF86XK_MailForward), - "XF86XK_Pictures" | "XF86Pictures" => Some(XF86XK_Pictures), - "XF86XK_Music" | "XF86Music" => Some(XF86XK_Music), - "XF86XK_Battery" | "XF86Battery" => Some(XF86XK_Battery), - "XF86XK_Bluetooth" | "XF86Bluetooth" => Some(XF86XK_Bluetooth), - "XF86XK_WLAN" | "XF86WLAN" => Some(XF86XK_WLAN), - "XF86XK_UWB" | "XF86UWB" => Some(XF86XK_UWB), - "XF86XK_AudioForward" | "XF86AudioForward" => Some(XF86XK_AudioForward), - "XF86XK_AudioRepeat" | "XF86AudioRepeat" => Some(XF86XK_AudioRepeat), - "XF86XK_AudioRandomPlay" | "XF86AudioRandomPlay" => Some(XF86XK_AudioRandomPlay), - "XF86XK_Subtitle" | "XF86Subtitle" => Some(XF86XK_Subtitle), - "XF86XK_AudioCycleTrack" | "XF86AudioCycleTrack" => Some(XF86XK_AudioCycleTrack), - "XF86XK_CycleAngle" | "XF86CycleAngle" => Some(XF86XK_CycleAngle), - "XF86XK_FrameBack" | "XF86FrameBack" => Some(XF86XK_FrameBack), - "XF86XK_FrameForward" | "XF86FrameForward" => Some(XF86XK_FrameForward), - "XF86XK_Time" | "XF86Time" => Some(XF86XK_Time), - "XF86XK_Select" | "XF86Select" => Some(XF86XK_Select), - "XF86XK_View" | "XF86View" => Some(XF86XK_View), - "XF86XK_TopMenu" | "XF86TopMenu" => Some(XF86XK_TopMenu), - "XF86XK_Red" | "XF86Red" => Some(XF86XK_Red), - "XF86XK_Green" | "XF86Green" => Some(XF86XK_Green), - "XF86XK_Yellow" | "XF86Yellow" => Some(XF86XK_Yellow), - "XF86XK_Blue" | "XF86Blue" => Some(XF86XK_Blue), - "XF86XK_Suspend" | "XF86Suspend" => Some(XF86XK_Suspend), - "XF86XK_Hibernate" | "XF86Hibernate" => Some(XF86XK_Hibernate), - "XF86XK_TouchpadToggle" | "XF86TouchpadToggle" => Some(XF86XK_TouchpadToggle), - "XF86XK_TouchpadOn" | "XF86TouchpadOn" => Some(XF86XK_TouchpadOn), - "XF86XK_TouchpadOff" | "XF86TouchpadOff" => Some(XF86XK_TouchpadOff), - "XF86XK_AudioMicMute" | "XF86AudioMicMute" => Some(XF86XK_AudioMicMute), - "XF86XK_Switch_VT_1" | "XF86Switch_VT_1" => Some(XF86XK_Switch_VT_1), - "XF86XK_Switch_VT_2" | "XF86Switch_VT_2" => Some(XF86XK_Switch_VT_2), - "XF86XK_Switch_VT_3" | "XF86Switch_VT_3" => Some(XF86XK_Switch_VT_3), - "XF86XK_Switch_VT_4" | "XF86Switch_VT_4" => Some(XF86XK_Switch_VT_4), - "XF86XK_Switch_VT_5" | "XF86Switch_VT_5" => Some(XF86XK_Switch_VT_5), - "XF86XK_Switch_VT_6" | "XF86Switch_VT_6" => Some(XF86XK_Switch_VT_6), - "XF86XK_Switch_VT_7" | "XF86Switch_VT_7" => Some(XF86XK_Switch_VT_7), - "XF86XK_Switch_VT_8" | "XF86Switch_VT_8" => Some(XF86XK_Switch_VT_8), - "XF86XK_Switch_VT_9" | "XF86Switch_VT_9" => Some(XF86XK_Switch_VT_9), - "XF86XK_Switch_VT_10" | "XF86Switch_VT_10" => Some(XF86XK_Switch_VT_10), - "XF86XK_Switch_VT_11" | "XF86Switch_VT_11" => Some(XF86XK_Switch_VT_11), - "XF86XK_Switch_VT_12" | "XF86Switch_VT_12" => Some(XF86XK_Switch_VT_12), - "XF86XK_Ungrab" | "XF86Ungrab" => Some(XF86XK_Ungrab), - "XF86XK_ClearGrab" | "XF86ClearGrab" => Some(XF86XK_ClearGrab), - "XF86XK_Next_VMode" | "XF86Next_VMode" => Some(XF86XK_Next_VMode), - "XF86XK_Prev_VMode" | "XF86Prev_VMode" => Some(XF86XK_Prev_VMode), - "XF86XK_LogWindowTree" | "XF86LogWindowTree" => Some(XF86XK_LogWindowTree), - "XF86XK_LogGrabInfo" | "XF86LogGrabInfo" => Some(XF86XK_LogGrabInfo), - "ISO_Lock" => Some(XK_ISO_Lock), - "ISO_Level2_Latch" => Some(XK_ISO_Level2_Latch), - "ISO_Level3_Shift" => Some(XK_ISO_Level3_Shift), - "ISO_Level3_Latch" => Some(XK_ISO_Level3_Latch), - "ISO_Level3_Lock" => Some(XK_ISO_Level3_Lock), - "ISO_Level5_Shift" => Some(XK_ISO_Level5_Shift), - "ISO_Level5_Latch" => Some(XK_ISO_Level5_Latch), - "ISO_Level5_Lock" => Some(XK_ISO_Level5_Lock), - "ISO_Group_Shift" => Some(XK_ISO_Group_Shift), - "ISO_Group_Latch" => Some(XK_ISO_Group_Latch), - "ISO_Group_Lock" => Some(XK_ISO_Group_Lock), - "ISO_Next_Group" => Some(XK_ISO_Next_Group), - "ISO_Next_Group_Lock" => Some(XK_ISO_Next_Group_Lock), - "ISO_Prev_Group" => Some(XK_ISO_Prev_Group), - "ISO_Prev_Group_Lock" => Some(XK_ISO_Prev_Group_Lock), - "ISO_First_Group" => Some(XK_ISO_First_Group), - "ISO_First_Group_Lock" => Some(XK_ISO_First_Group_Lock), - "ISO_Last_Group" => Some(XK_ISO_Last_Group), - "ISO_Last_Group_Lock" => Some(XK_ISO_Last_Group_Lock), - "ISO_Left_Tab" => Some(XK_ISO_Left_Tab), - "ISO_Move_Line_Up" => Some(XK_ISO_Move_Line_Up), - "ISO_Move_Line_Down" => Some(XK_ISO_Move_Line_Down), - "ISO_Partial_Line_Up" => Some(XK_ISO_Partial_Line_Up), - "ISO_Partial_Line_Down" => Some(XK_ISO_Partial_Line_Down), - "ISO_Partial_Space_Left" => Some(XK_ISO_Partial_Space_Left), - "ISO_Partial_Space_Right" => Some(XK_ISO_Partial_Space_Right), - "ISO_Set_Margin_Left" => Some(XK_ISO_Set_Margin_Left), - "ISO_Set_Margin_Right" => Some(XK_ISO_Set_Margin_Right), - "ISO_Release_Margin_Left" => Some(XK_ISO_Release_Margin_Left), - "ISO_Release_Margin_Right" => Some(XK_ISO_Release_Margin_Right), - "ISO_Release_Both_Margins" => Some(XK_ISO_Release_Both_Margins), - "ISO_Fast_Cursor_Left" => Some(XK_ISO_Fast_Cursor_Left), - "ISO_Fast_Cursor_Right" => Some(XK_ISO_Fast_Cursor_Right), - "ISO_Fast_Cursor_Up" => Some(XK_ISO_Fast_Cursor_Up), - "ISO_Fast_Cursor_Down" => Some(XK_ISO_Fast_Cursor_Down), - "ISO_Continuous_Underline" => Some(XK_ISO_Continuous_Underline), - "ISO_Discontinuous_Underline" => Some(XK_ISO_Discontinuous_Underline), - "ISO_Emphasize" => Some(XK_ISO_Emphasize), - "ISO_Center_Object" => Some(XK_ISO_Center_Object), - "ISO_Enter" => Some(XK_ISO_Enter), - "dead_grave" => Some(XK_dead_grave), - "dead_acute" => Some(XK_dead_acute), - "dead_circumflex" => Some(XK_dead_circumflex), - "dead_tilde" => Some(XK_dead_tilde), - "dead_perispomeni" => Some(XK_dead_perispomeni), - "dead_macron" => Some(XK_dead_macron), - "dead_breve" => Some(XK_dead_breve), - "dead_abovedot" => Some(XK_dead_abovedot), - "dead_diaeresis" => Some(XK_dead_diaeresis), - "dead_abovering" => Some(XK_dead_abovering), - "dead_doubleacute" => Some(XK_dead_doubleacute), - "dead_caron" => Some(XK_dead_caron), - "dead_cedilla" => Some(XK_dead_cedilla), - "dead_ogonek" => Some(XK_dead_ogonek), - "dead_iota" => Some(XK_dead_iota), - "dead_voiced_sound" => Some(XK_dead_voiced_sound), - "dead_semivoiced_sound" => Some(XK_dead_semivoiced_sound), - "dead_belowdot" => Some(XK_dead_belowdot), - "dead_hook" => Some(XK_dead_hook), - "dead_horn" => Some(XK_dead_horn), - "dead_stroke" => Some(XK_dead_stroke), - "dead_abovecomma" => Some(XK_dead_abovecomma), - "dead_psili" => Some(XK_dead_psili), - "dead_abovereversedcomma" => Some(XK_dead_abovereversedcomma), - "dead_dasia" => Some(XK_dead_dasia), - "dead_doublegrave" => Some(XK_dead_doublegrave), - "dead_belowring" => Some(XK_dead_belowring), - "dead_belowmacron" => Some(XK_dead_belowmacron), - "dead_belowcircumflex" => Some(XK_dead_belowcircumflex), - "dead_belowtilde" => Some(XK_dead_belowtilde), - "dead_belowbreve" => Some(XK_dead_belowbreve), - "dead_belowdiaeresis" => Some(XK_dead_belowdiaeresis), - "dead_invertedbreve" => Some(XK_dead_invertedbreve), - "dead_belowcomma" => Some(XK_dead_belowcomma), - "dead_currency" => Some(XK_dead_currency), - "dead_lowline" => Some(XK_dead_lowline), - "dead_aboveverticalline" => Some(XK_dead_aboveverticalline), - "dead_belowverticalline" => Some(XK_dead_belowverticalline), - "dead_longsolidusoverlay" => Some(XK_dead_longsolidusoverlay), - "dead_a" => Some(XK_dead_a), - "dead_A" => Some(XK_dead_A), - "dead_e" => Some(XK_dead_e), - "dead_E" => Some(XK_dead_E), - "dead_i" => Some(XK_dead_i), - "dead_I" => Some(XK_dead_I), - "dead_o" => Some(XK_dead_o), - "dead_O" => Some(XK_dead_O), - "dead_u" => Some(XK_dead_u), - "dead_U" => Some(XK_dead_U), - "dead_small_schwa" => Some(XK_dead_small_schwa), - "dead_capital_schwa" => Some(XK_dead_capital_schwa), - "dead_greek" => Some(XK_dead_greek), - "First_Virtual_Screen" => Some(XK_First_Virtual_Screen), - "Prev_Virtual_Screen" => Some(XK_Prev_Virtual_Screen), - "Next_Virtual_Screen" => Some(XK_Next_Virtual_Screen), - "Last_Virtual_Screen" => Some(XK_Last_Virtual_Screen), - "Terminate_Server" => Some(XK_Terminate_Server), - "AccessX_Enable" => Some(XK_AccessX_Enable), - "AccessX_Feedback_Enable" => Some(XK_AccessX_Feedback_Enable), - "RepeatKeys_Enable" => Some(XK_RepeatKeys_Enable), - "SlowKeys_Enable" => Some(XK_SlowKeys_Enable), - "BounceKeys_Enable" => Some(XK_BounceKeys_Enable), - "StickyKeys_Enable" => Some(XK_StickyKeys_Enable), - "MouseKeys_Enable" => Some(XK_MouseKeys_Enable), - "MouseKeys_Accel_Enable" => Some(XK_MouseKeys_Accel_Enable), - "Overlay1_Enable" => Some(XK_Overlay1_Enable), - "Overlay2_Enable" => Some(XK_Overlay2_Enable), - "AudibleBell_Enable" => Some(XK_AudibleBell_Enable), - "Pointer_Left" => Some(XK_Pointer_Left), - "Pointer_Right" => Some(XK_Pointer_Right), - "Pointer_Up" => Some(XK_Pointer_Up), - "Pointer_Down" => Some(XK_Pointer_Down), - "Pointer_UpLeft" => Some(XK_Pointer_UpLeft), - "Pointer_UpRight" => Some(XK_Pointer_UpRight), - "Pointer_DownLeft" => Some(XK_Pointer_DownLeft), - "Pointer_DownRight" => Some(XK_Pointer_DownRight), - "Pointer_Button_Dflt" => Some(XK_Pointer_Button_Dflt), - "Pointer_Button1" => Some(XK_Pointer_Button1), - "Pointer_Button2" => Some(XK_Pointer_Button2), - "Pointer_Button3" => Some(XK_Pointer_Button3), - "Pointer_Button4" => Some(XK_Pointer_Button4), - "Pointer_Button5" => Some(XK_Pointer_Button5), - "Pointer_DblClick_Dflt" => Some(XK_Pointer_DblClick_Dflt), - "Pointer_DblClick1" => Some(XK_Pointer_DblClick1), - "Pointer_DblClick2" => Some(XK_Pointer_DblClick2), - "Pointer_DblClick3" => Some(XK_Pointer_DblClick3), - "Pointer_DblClick4" => Some(XK_Pointer_DblClick4), - "Pointer_DblClick5" => Some(XK_Pointer_DblClick5), - "Pointer_Drag_Dflt" => Some(XK_Pointer_Drag_Dflt), - "Pointer_Drag1" => Some(XK_Pointer_Drag1), - "Pointer_Drag2" => Some(XK_Pointer_Drag2), - "Pointer_Drag3" => Some(XK_Pointer_Drag3), - "Pointer_Drag4" => Some(XK_Pointer_Drag4), - "Pointer_Drag5" => Some(XK_Pointer_Drag5), - "Pointer_EnableKeys" => Some(XK_Pointer_EnableKeys), - "Pointer_Accelerate" => Some(XK_Pointer_Accelerate), - "Pointer_DfltBtnNext" => Some(XK_Pointer_DfltBtnNext), - "Pointer_DfltBtnPrev" => Some(XK_Pointer_DfltBtnPrev), - "ch" => Some(XK_ch), - "Ch" => Some(XK_Ch), - "CH" => Some(XK_CH), - "c_h" => Some(XK_c_h), - "C_h" => Some(XK_C_h), - "C_H" => Some(XK_C_H), - _ => None, - } -} diff --git a/lefthk-core/src/xwrap.rs b/lefthk-core/src/xwrap.rs deleted file mode 100644 index 258228a..0000000 --- a/lefthk-core/src/xwrap.rs +++ /dev/null @@ -1,205 +0,0 @@ -use crate::config::Keybind; -use crate::errors::{self, Error, LeftError}; -use crate::xkeysym_lookup; -use std::future::Future; -use std::os::raw::{c_int, c_ulong}; -use std::pin::Pin; -use std::ptr; -use std::sync::Arc; -use tokio::sync::{oneshot, Notify}; -use tokio::time::Duration; -use x11_dl::xlib; - -pub struct XWrap { - pub xlib: xlib::Xlib, - pub display: *mut xlib::Display, - pub root: xlib::Window, - pub task_notify: Arc, - _task_guard: oneshot::Receiver<()>, -} - -impl Default for XWrap { - fn default() -> Self { - Self::new() - } -} - -impl XWrap { - /// # Panics - /// - /// Panics if unable to contact xorg. - #[must_use] - #[allow(clippy::items_after_statements)] - pub fn new() -> Self { - const SERVER: mio::Token = mio::Token(0); - let xlib = errors::exit_on_error!(xlib::Xlib::open()); - let display = unsafe { (xlib.XOpenDisplay)(ptr::null()) }; - assert!(!display.is_null(), "Null pointer in display"); - - let fd = unsafe { (xlib.XConnectionNumber)(display) }; - let (guard, task_guard) = oneshot::channel(); - let notify = Arc::new(Notify::new()); - let task_notify = notify.clone(); - let mut poll = errors::exit_on_error!(mio::Poll::new()); - let mut events = mio::Events::with_capacity(1); - errors::exit_on_error!(poll.registry().register( - &mut mio::unix::SourceFd(&fd), - SERVER, - mio::Interest::READABLE, - )); - let timeout = Duration::from_millis(100); - tokio::task::spawn_blocking(move || loop { - if guard.is_closed() { - return; - } - - if let Err(err) = poll.poll(&mut events, Some(timeout)) { - tracing::warn!("Xlib socket poll failed with {:?}", err); - continue; - } - - events - .iter() - .filter(|event| SERVER == event.token()) - .for_each(|_| notify.notify_one()); - }); - let root = unsafe { (xlib.XDefaultRootWindow)(display) }; - - let xw = Self { - xlib, - display, - root, - task_notify, - _task_guard: task_guard, - }; - - // Setup cached keymap/modifier information, otherwise MappingNotify might never be called - // from: - // https://stackoverflow.com/questions/35569562/how-to-catch-keyboard-layout-change-event-and-get-current-new-keyboard-layout-on - xw.keysym_to_keycode(x11_dl::keysym::XK_F1); - - // This is allowed for now as const extern fns - // are not yet stable (1.56.0, 16 Sept 2021) - // see issue #64926 for more information - // also this is the reason for #[allow(clippy::items_after_statements)] above - #[allow(clippy::missing_const_for_fn)] - extern "C" fn on_error_from_xlib( - _: *mut xlib::Display, - er: *mut xlib::XErrorEvent, - ) -> c_int { - let err = unsafe { *er }; - //ignore bad window errors - if err.error_code == xlib::BadWindow { - return 0; - } - 1 - } - unsafe { - (xw.xlib.XSetErrorHandler)(Some(on_error_from_xlib)); - (xw.xlib.XSync)(xw.display, xlib::False); - }; - xw - } - - /// Shutdown connections to the xserver. - pub fn shutdown(&self) { - unsafe { - (self.xlib.XUngrabKey)(self.display, xlib::AnyKey, xlib::AnyModifier, self.root); - (self.xlib.XCloseDisplay)(self.display); - } - } - - /// Grabs a list of keybindings. - pub fn grab_keys(&self, keybinds: &[Keybind]) { - // Cleanup key grabs. - unsafe { - (self.xlib.XUngrabKey)(self.display, xlib::AnyKey, xlib::AnyModifier, self.root); - } - - // Grab all the key combos from the config file. - for kb in keybinds { - if let Some(keysym) = xkeysym_lookup::into_keysym(&kb.key) { - let modmask = xkeysym_lookup::into_modmask(&kb.modifier); - self.grab_key(self.root, keysym, modmask); - } - } - } - - /// Grabs the keysym with the modifier for a window. - pub fn grab_key(&self, root: xlib::Window, keysym: u32, modifiers: u32) { - let code = unsafe { (self.xlib.XKeysymToKeycode)(self.display, c_ulong::from(keysym)) }; - // Grab the keys with and without numlock (Mod2). - let mods: Vec = vec![ - modifiers, - modifiers | xlib::Mod2Mask, - modifiers | xlib::LockMask, - ]; - for m in mods { - unsafe { - (self.xlib.XGrabKey)( - self.display, - i32::from(code), - m, - root, - 1, - xlib::GrabModeAsync, - xlib::GrabModeAsync, - ); - } - } - } - - /// Updates the keyboard mapping. - /// # Errors - /// - /// Will error if updating the keyboard failed. - pub fn refresh_keyboard(&self, evt: &mut xlib::XMappingEvent) -> Error { - let status = unsafe { (self.xlib.XRefreshKeyboardMapping)(evt) }; - if status == 0 { - Err(LeftError::XFailedStatus) - } else { - Ok(()) - } - } - - /// Converts a keycode to a keysym. - #[must_use] - pub fn keycode_to_keysym(&self, keycode: u32) -> xkeysym_lookup::XKeysym { - // Not using XKeysymToKeycode because deprecated. - let sym = unsafe { (self.xlib.XkbKeycodeToKeysym)(self.display, keycode as u8, 0, 0) }; - sym as u32 - } - - /// Converts a keysym to a keycode. - pub fn keysym_to_keycode(&self, keysym: xkeysym_lookup::XKeysym) -> u32 { - let code = unsafe { (self.xlib.XKeysymToKeycode)(self.display, keysym.into()) }; - u32::from(code) - } - - /// Returns the next `Xevent` of the xserver. - #[must_use] - pub fn get_next_event(&self) -> xlib::XEvent { - unsafe { - let mut event: xlib::XEvent = std::mem::zeroed(); - (self.xlib.XNextEvent)(self.display, &mut event); - event - } - } - - /// Returns how many events are waiting. - #[must_use] - pub fn queue_len(&self) -> i32 { - unsafe { (self.xlib.XPending)(self.display) } - } - - pub fn flush(&self) { - unsafe { (self.xlib.XFlush)(self.display) }; - } - - pub fn wait_readable(&mut self) -> Pin>> { - let task_notify = self.task_notify.clone(); - Box::pin(async move { - task_notify.notified().await; - }) - } -} From cc8d3d35a78182fd55627d4d154474d9291ef438 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Fri, 8 Mar 2024 17:42:02 +0000 Subject: [PATCH 07/16] Robust keyboard finding with udev and respond to removed/added keyboards --- Cargo.lock | 32 ++----- lefthk-core/Cargo.toml | 4 +- lefthk-core/src/evdev.rs | 175 ++++++++++++++++++++++------------ lefthk-core/src/worker/mod.rs | 3 + 4 files changed, 125 insertions(+), 89 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef2258e..85dd747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,9 +87,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -199,26 +199,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "input" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" -dependencies = [ - "bitflags 1.3.2", - "input-sys", - "io-lifetimes", - "libc", - "log", - "udev", -] - -[[package]] -name = "input-sys" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" - [[package]] name = "inventory" version = "0.3.15" @@ -263,7 +243,6 @@ name = "lefthk-core" version = "0.2.1" dependencies = [ "evdev-rs", - "input", "inventory", "mio", "nix", @@ -274,6 +253,7 @@ dependencies = [ "thiserror", "tokio", "tracing", + "udev", "xdg", ] @@ -733,12 +713,14 @@ dependencies = [ [[package]] name = "udev" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" +checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1" dependencies = [ + "io-lifetimes", "libc", "libudev-sys", + "mio", "pkg-config", ] diff --git a/lefthk-core/Cargo.toml b/lefthk-core/Cargo.toml index 3ed88ce..44e4b6b 100644 --- a/lefthk-core/Cargo.toml +++ b/lefthk-core/Cargo.toml @@ -9,8 +9,7 @@ description = "A hotkey daemon for Adventurers" [dependencies] evdev-rs = { version = "0.6.1", features = ["serde"] } -input = "0.8" -mio = "0.8.0" +mio = "0.8.11" nix = { version = "0.27.1", features = ["fs", "signal"] } signal-hook = "0.3.4" thiserror = "1.0.30" @@ -23,6 +22,7 @@ tokio = { version = "1.14.0", features = [ "sync", "time", ] } +udev = {version = "0.8.0", features = ["mio"] } xdg = "2.4.0" ron = "0.8.0" serde = { version = "1.0.145", features = ["derive"] } diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index 0df9086..227f096 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,37 +1,32 @@ use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; -use input::event::{DeviceEvent, EventTrait}; -use input::{Event, Libinput, LibinputInterface}; -use nix::libc::{O_RDWR, O_WRONLY}; -use std::ffi::OsStr; -use std::fs::{File, OpenOptions}; -use std::os::fd::{AsRawFd, OwnedFd}; -use std::os::unix::fs::OpenOptionsExt; -use std::path::{Path, PathBuf}; +use std::os::fd::AsRawFd; +use std::path::PathBuf; +use std::{collections::HashMap, ffi::OsStr}; use tokio::sync::{mpsc, oneshot}; -use tokio::time::Duration; use crate::errors::{self, LeftError}; #[derive(Debug)] pub enum Task { KeyboardEvent(InputEvent), - KeyboardAdded(String), + KeyboardAdded(PathBuf), + KeyboardRemoved(PathBuf), } pub struct EvDev { pub task_receiver: mpsc::Receiver, task_transmitter: mpsc::Sender, - task_guards: Vec>, + task_guards: HashMap>, _keyboard_watcher: KeyboardWatcher, } impl Default for EvDev { fn default() -> Self { - let (task_transmitter, task_receiver) = mpsc::channel(100); + let (task_transmitter, task_receiver) = mpsc::channel(128); let keyboard_watcher = KeyboardWatcher::new(task_transmitter.clone()); - let task_guards: Vec> = vec![]; + let task_guards: HashMap> = HashMap::new(); let devices = find_keyboards(); @@ -41,17 +36,22 @@ impl Default for EvDev { task_guards, _keyboard_watcher: keyboard_watcher, }; - for device in devices { - evdev.add_device(device); + match devices { + Some(devices) => { + for device in devices { + evdev.add_device(device); + } + } + None => tracing::warn!("No devices found on intialization."), } - evdev } } impl EvDev { pub fn add_device(&mut self, path: PathBuf) { - if let Some(device) = device_with_path(path) { + tracing::info!("Adding device with path: {:?}", path); + if let Some(device) = device_with_path(path.clone()) { let (guard, task_guard) = oneshot::channel(); let transmitter = self.task_transmitter.clone(); const SERVER: mio::Token = mio::Token(0); @@ -63,7 +63,7 @@ impl EvDev { SERVER, mio::Interest::READABLE, )); - let timeout = Duration::from_millis(100); + tokio::task::spawn(async move { loop { if guard.is_closed() { @@ -71,7 +71,7 @@ impl EvDev { return; } - if let Err(err) = poll.poll(&mut events, Some(timeout)) { + if let Err(err) = poll.poll(&mut events, None) { tracing::warn!("Evdev device poll failed with {:?}", err); continue; } @@ -81,60 +81,43 @@ impl EvDev { Ok((ReadStatus::Success, event)) => { transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); } - Err(_) => println!("Boo"), + Err(_) => break, _ => {} } } } }); - self.task_guards.push(task_guard); + self.task_guards.insert(path, task_guard); } } -} - -struct Interface; - -impl LibinputInterface for Interface { - fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { - OpenOptions::new() - .custom_flags(flags) - .read((flags != 0) | (flags & O_RDWR != 0)) - .write((flags & O_WRONLY != 0) | (flags & O_RDWR != 0)) - .open(path) - .map(|file| file.into()) - .map_err(|err| err.raw_os_error().unwrap()) - } - fn close_restricted(&mut self, fd: OwnedFd) { - let _ = File::from(fd); + pub fn remove_device(&mut self, path: PathBuf) { + tracing::info!("Removing device with path: {:?}", path); + drop(self.task_guards.remove(&path)); } } -fn find_keyboards() -> Vec { - let mut context = Libinput::new_with_udev(Interface); - context.udev_assign_seat("seat0").unwrap(); - context.dispatch().unwrap(); +fn find_keyboards() -> Option> { let mut devices = vec![]; - for event in &mut context { - if let Event::Device(DeviceEvent::Added(_)) = &event { - unsafe { - if let Some(device) = event.device().udev_device() { - let is_keyboard = device - .property_value("ID_INPUT_KEYBOARD") - .unwrap_or(OsStr::new("0")) - == "1" - && device - .property_value("ID_INPUT_MOUSE") - .unwrap_or(OsStr::new("0")) - == "0"; - if is_keyboard { - let path = device.property_value("DEVNAME").unwrap_or(OsStr::new("")); - devices.push(PathBuf::from(path)) - } - } + let mut enumerator = udev::Enumerator::new().ok()?; + enumerator.match_is_initialized().ok()?; + enumerator.match_subsystem("input").ok()?; + let enum_devices = enumerator.scan_devices().ok()?; + for device in enum_devices { + if let Some(devnode) = device.devnode() { + let is_keyboard = device + .property_value("ID_INPUT_KEYBOARD") + .unwrap_or(OsStr::new("0")) + == "1" + && device + .property_value("ID_INPUT_MOUSE") + .unwrap_or(OsStr::new("0")) + == "0"; + if is_keyboard { + devices.push(PathBuf::from(devnode)); } } } - devices + Some(devices) } fn device_with_path(path: PathBuf) -> Option { @@ -153,9 +136,77 @@ struct KeyboardWatcher { } impl KeyboardWatcher { - pub fn new(_task_transmitter: mpsc::Sender) -> Self { - let (_guard, task_guard) = oneshot::channel(); - tokio::task::spawn(async move {}); + pub fn new(task_transmitter: mpsc::Sender) -> Self { + let (guard, task_guard) = oneshot::channel(); + + tokio::task::spawn_blocking(move || { + let mut socket = udev::MonitorBuilder::new() + .expect("Failed to create monitor") + .match_subsystem("input") + .expect("Failed to match subsystem") + .listen() + .expect("Failed to listen"); + const SERVER: mio::Token = mio::Token(0); + let mut poll = mio::Poll::new().expect("Failed to create poll"); + let mut events = mio::Events::with_capacity(1); + poll.registry() + .register(&mut socket, SERVER, mio::Interest::READABLE) + .expect("Failed to register"); + loop { + if guard.is_closed() { + println!("Bye"); + return; + } + if let Err(err) = poll.poll(&mut events, None) { + tracing::warn!("KeyboardWatcher poll failed with {:?}", err); + continue; + } + + for e in socket.iter() { + let device = e.device(); + let is_keyboard = device + .property_value("ID_INPUT_KEYBOARD") + .unwrap_or(OsStr::new("0")) + == "1" + && device + .property_value("ID_INPUT_MOUSE") + .unwrap_or(OsStr::new("0")) + == "0"; + if is_keyboard { + let path = device + .property_value("DEVNAME") + .unwrap_or(OsStr::new("")) + .to_owned(); + if path.is_empty() { + continue; + } + match e.event_type() { + udev::EventType::Add => { + if let Err(err) = task_transmitter + .try_send(Task::KeyboardAdded(PathBuf::from(path))) + { + tracing::warn!( + "Failed to send keyboard added event: {:?}", + err + ); + } + } + udev::EventType::Remove => { + if let Err(err) = task_transmitter + .try_send(Task::KeyboardRemoved(PathBuf::from(path))) + { + tracing::warn!( + "Failed to send keyboard removed event: {:?}", + err + ); + } + } + _ => {} + } + } + } + } + }); Self { _task_guard: task_guard, } diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 9d402c9..e56b842 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -84,6 +84,9 @@ impl Worker { Task::KeyboardAdded(path) => { self.evdev.add_device(path.into()); } + Task::KeyboardRemoved(path) => { + self.evdev.remove_device(path.into()); + } } } Some(command) = pipe.get_next_command() => { From 5c7097cf3ae6fd5223cb8d03e3408a1fbf99c4cd Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Fri, 8 Mar 2024 20:18:32 +0000 Subject: [PATCH 08/16] Make sure the thread is cleaned after remove and fix ci? --- .github/workflows/ci.yml | 8 ++++---- lefthk-core/src/evdev.rs | 24 ++++++++++-------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 644f6d8..4920ccd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,8 @@ jobs: - uses: actions/checkout@v2 - name: apt update run: sudo apt update - - name: apt install libsystemd-dev - run: sudo apt install -y --no-install-recommends libsystemd-dev + - name: apt install libsystemd-dev libudev-dev + run: sudo apt install -y --no-install-recommends libsystemd-dev libudev-dev - name: Build run: cargo build --all-targets --all-features - name: Run tests @@ -31,8 +31,8 @@ jobs: - uses: actions/checkout@v2 - name: apt update run: sudo apt update - - name: apt install libsystemd-dev - run: sudo apt install -y --no-install-recommends libsystemd-dev + - name: apt install libsystemd-dev libudev-dev + run: sudo apt install -y --no-install-recommends libsystemd-dev libudev-dev - name: Clippy run: cargo clippy --all-targets --all-features diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index 227f096..c418b97 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,4 +1,5 @@ use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; +use std::future::poll_fn; use std::os::fd::AsRawFd; use std::path::PathBuf; use std::{collections::HashMap, ffi::OsStr}; @@ -52,7 +53,7 @@ impl EvDev { pub fn add_device(&mut self, path: PathBuf) { tracing::info!("Adding device with path: {:?}", path); if let Some(device) = device_with_path(path.clone()) { - let (guard, task_guard) = oneshot::channel(); + let (mut guard, task_guard) = oneshot::channel(); let transmitter = self.task_transmitter.clone(); const SERVER: mio::Token = mio::Token(0); let fd = device.file().as_raw_fd(); @@ -65,12 +66,7 @@ impl EvDev { )); tokio::task::spawn(async move { - loop { - if guard.is_closed() { - println!("Bye"); - return; - } - + while !guard.is_closed() { if let Err(err) = poll.poll(&mut events, None) { tracing::warn!("Evdev device poll failed with {:?}", err); continue; @@ -81,18 +77,22 @@ impl EvDev { Ok((ReadStatus::Success, event)) => { transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); } - Err(_) => break, + Err(_) => { + poll_fn(|cx| guard.poll_closed(cx)).await; + break; + } _ => {} } } } + tracing::info!("Device loop has closed."); }); self.task_guards.insert(path, task_guard); } } pub fn remove_device(&mut self, path: PathBuf) { tracing::info!("Removing device with path: {:?}", path); - drop(self.task_guards.remove(&path)); + self.task_guards.remove(&path); } } @@ -152,11 +152,7 @@ impl KeyboardWatcher { poll.registry() .register(&mut socket, SERVER, mio::Interest::READABLE) .expect("Failed to register"); - loop { - if guard.is_closed() { - println!("Bye"); - return; - } + while !guard.is_closed() { if let Err(err) = poll.poll(&mut events, None) { tracing::warn!("KeyboardWatcher poll failed with {:?}", err); continue; From c2520e8e2a666be5a68c55d7233e7d6740ff2d73 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Fri, 8 Mar 2024 20:20:14 +0000 Subject: [PATCH 09/16] clippy --- lefthk-core/src/worker/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index e56b842..9622f8b 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -82,10 +82,10 @@ impl Worker { self.handle_event(&event); } Task::KeyboardAdded(path) => { - self.evdev.add_device(path.into()); + self.evdev.add_device(path); } Task::KeyboardRemoved(path) => { - self.evdev.remove_device(path.into()); + self.evdev.remove_device(path); } } } From 00b41edd7540a471087f0ecf664a96d7c7af81ce Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Sat, 9 Mar 2024 17:10:22 +0000 Subject: [PATCH 10/16] We now eat keybind keys, but causes a temp bug when grabbing --- lefthk-core/src/errors.rs | 25 +++++++++++++----- lefthk-core/src/evdev.rs | 49 ++++++++++++++++++++++++++-------- lefthk-core/src/worker/mod.rs | 50 ++++++++++++++++++++++++++++------- 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/lefthk-core/src/errors.rs b/lefthk-core/src/errors.rs index b6ef807..3a09f4c 100644 --- a/lefthk-core/src/errors.rs +++ b/lefthk-core/src/errors.rs @@ -1,6 +1,18 @@ use thiserror::Error; -macro_rules! log_on_error { +macro_rules! r#return { + ($a: expr) => { + match $a { + Ok(value) => value, + Err(err) => { + tracing::error!("{}", LeftError::from(err)); + return; + } + } + }; +} + +macro_rules! log { ($a: expr) => { match $a { Ok(value) => value, @@ -9,7 +21,7 @@ macro_rules! log_on_error { }; } -macro_rules! exit_on_error { +macro_rules! exit { ($a: expr) => { match $a { Ok(value) => value, @@ -21,8 +33,9 @@ macro_rules! exit_on_error { }; } -pub(crate) use exit_on_error; -pub(crate) use log_on_error; +pub(crate) use exit; +pub(crate) use log; +pub(crate) use r#return; pub type Result = std::result::Result; pub type Error = std::result::Result<(), LeftError>; @@ -48,6 +61,6 @@ pub enum LeftError { NoConfigFound, #[error("No value set for execution.")] ValueNotFound, - #[error("X failed status error.")] - XFailedStatus, + #[error("UInput device could not be found.")] + UInputNotFound, } diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index c418b97..ec0b242 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,4 +1,4 @@ -use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus}; +use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus, UInputDevice}; use std::future::poll_fn; use std::os::fd::AsRawFd; use std::path::PathBuf; @@ -9,12 +9,13 @@ use crate::errors::{self, LeftError}; #[derive(Debug)] pub enum Task { - KeyboardEvent(InputEvent), + KeyboardEvent((PathBuf, InputEvent)), KeyboardAdded(PathBuf), KeyboardRemoved(PathBuf), } pub struct EvDev { + pub devices: HashMap, pub task_receiver: mpsc::Receiver, task_transmitter: mpsc::Sender, task_guards: HashMap>, @@ -23,21 +24,23 @@ pub struct EvDev { impl Default for EvDev { fn default() -> Self { + let devices: HashMap = HashMap::new(); + let (task_transmitter, task_receiver) = mpsc::channel(128); let keyboard_watcher = KeyboardWatcher::new(task_transmitter.clone()); let task_guards: HashMap> = HashMap::new(); - let devices = find_keyboards(); - let mut evdev = EvDev { + devices, task_receiver, task_transmitter, task_guards, _keyboard_watcher: keyboard_watcher, }; - match devices { + + match find_keyboards() { Some(devices) => { for device in devices { evdev.add_device(device); @@ -45,6 +48,7 @@ impl Default for EvDev { } None => tracing::warn!("No devices found on intialization."), } + evdev } } @@ -52,19 +56,23 @@ impl Default for EvDev { impl EvDev { pub fn add_device(&mut self, path: PathBuf) { tracing::info!("Adding device with path: {:?}", path); - if let Some(device) = device_with_path(path.clone()) { + if let Some(mut device) = device_with_path(path.clone()) { + device.set_name(&format!("LeftHK virtual input for {:?}", path)); + let uinput = errors::r#return!(UInputDevice::create_from_device(&device)); + errors::r#return!(device.grab(evdev_rs::GrabMode::Grab)); + let (mut guard, task_guard) = oneshot::channel(); let transmitter = self.task_transmitter.clone(); const SERVER: mio::Token = mio::Token(0); let fd = device.file().as_raw_fd(); - let mut poll = errors::exit_on_error!(mio::Poll::new()); + let mut poll = errors::exit!(mio::Poll::new()); let mut events = mio::Events::with_capacity(1); - errors::exit_on_error!(poll.registry().register( + errors::exit!(poll.registry().register( &mut mio::unix::SourceFd(&fd), SERVER, mio::Interest::READABLE, )); - + let p = path.clone(); tokio::task::spawn(async move { while !guard.is_closed() { if let Err(err) = poll.poll(&mut events, None) { @@ -73,9 +81,12 @@ impl EvDev { } while device.has_event_pending() { - match device.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING) { + match device.next_event(ReadFlag::NORMAL) { Ok((ReadStatus::Success, event)) => { - transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); + transmitter + .send(Task::KeyboardEvent((p.clone(), event))) + .await + .unwrap(); } Err(_) => { poll_fn(|cx| guard.poll_closed(cx)).await; @@ -86,13 +97,17 @@ impl EvDev { } } tracing::info!("Device loop has closed."); + errors::r#return!(device.grab(evdev_rs::GrabMode::Ungrab)); }); + + self.devices.insert(path.clone(), uinput); self.task_guards.insert(path, task_guard); } } pub fn remove_device(&mut self, path: PathBuf) { tracing::info!("Removing device with path: {:?}", path); self.task_guards.remove(&path); + self.devices.remove(&path); } } @@ -160,6 +175,18 @@ impl KeyboardWatcher { for e in socket.iter() { let device = e.device(); + // for property in device.properties() { + // tracing::info!("Property: {:?}, {:?}", property.name(), property.value()); + // } + if device + .property_value("NAME") + .unwrap_or(OsStr::new("")) + .to_str() + .unwrap_or("") + .contains("LeftHK") + { + continue; + } let is_keyboard = device .property_value("ID_INPUT_KEYBOARD") .unwrap_or(OsStr::new("0")) diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 9622f8b..e91546e 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -1,5 +1,7 @@ pub mod context; +use std::path::PathBuf; + use crate::child::Children; use crate::config::{command, Keybind}; use crate::errors::{self, LeftError}; @@ -42,6 +44,8 @@ pub struct Worker { keys_pressed: Vec, mods_pressed: Vec, + eaten_keys: Vec, + eaten_mods: Vec, pub evdev: EvDev, pub children: Children, @@ -59,6 +63,8 @@ impl Worker { base_directory, keys_pressed: Vec::new(), mods_pressed: Vec::new(), + eaten_keys: Vec::new(), + eaten_mods: Vec::new(), evdev: EvDev::default(), children: Children::default(), chord_ctx: context::Chord::new(), @@ -78,8 +84,8 @@ impl Worker { } Some(task) = self.evdev.task_receiver.recv() => { match task { - Task::KeyboardEvent(event) => { - self.handle_event(&event); + Task::KeyboardEvent((path, event)) => { + self.handle_event(path, &event); } Task::KeyboardAdded(path) => { self.evdev.add_device(path); @@ -90,7 +96,7 @@ impl Worker { } } Some(command) = pipe.get_next_command() => { - errors::log_on_error!(command.execute(&mut self)); + errors::log!(command.execute(&mut self)); } }; } @@ -100,21 +106,30 @@ impl Worker { async fn get_pipe(&self) -> Pipe { let pipe_name = Pipe::pipe_name(); - let pipe_file = errors::exit_on_error!(self.base_directory.place_runtime_file(pipe_name)); - errors::exit_on_error!(Pipe::new(pipe_file).await) + let pipe_file = errors::exit!(self.base_directory.place_runtime_file(pipe_name)); + errors::exit!(Pipe::new(pipe_file).await) } - fn handle_event(&mut self, event: &InputEvent) { + fn handle_event(&mut self, path: PathBuf, event: &InputEvent) { let r#type = KeyEventType::from(event.value); + let mut eaten = false; match r#type { KeyEventType::Release => { if let EventCode::EV_KEY(key) = event.event_code { if is_modifier(&key) { if let Ok(modifier) = key.try_into() { self.mods_pressed.retain(|&m| m != modifier); + if self.eaten_mods.contains(&modifier) { + eaten = true; + self.eaten_mods.retain(|&m| m != modifier); + } } } else if let Some(index) = self.keys_pressed.iter().position(|&k| k == key) { self.keys_pressed.remove(index); + if self.eaten_keys.contains(&key) { + eaten = true; + self.eaten_keys.retain(|&k| k != key); + } } } } @@ -136,16 +151,33 @@ impl Worker { } if new_key { if let Some(keybind) = self.check_for_keybind() { + eaten = true; + self.keys_pressed + .iter() + .for_each(|&key| self.eaten_keys.push(key)); + self.mods_pressed + .iter() + .for_each(|&key| self.eaten_mods.push(key)); if let Ok(command) = command::denormalize(&keybind.command) { let _ = command.execute(self); } else { - errors::log_on_error!(Err(LeftError::CommandNotFound)); + errors::log!(Err(LeftError::CommandNotFound)); } } } } - KeyEventType::Repeat => {} - KeyEventType::Unknown => {} + KeyEventType::Repeat | KeyEventType::Unknown => {} + } + if !eaten { + self.pass_event(path, event); + } + } + + fn pass_event(&self, path: PathBuf, event: &InputEvent) { + // println!("Passing event: {:?}", event); + match self.evdev.devices.get(&path) { + Some(device) => errors::log!(device.write_event(event)), + None => errors::log!(Err(LeftError::UInputNotFound)), } } From 30352bbc1e6d1453153c1e6fe985e2d235452e3f Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Sun, 10 Mar 2024 13:32:03 +0000 Subject: [PATCH 11/16] Switch to evdev from evdev-rs --- Cargo.lock | 98 ++- lefthk-core/Cargo.toml | 2 +- lefthk-core/src/evdev.rs | 115 +-- lefthk-core/src/keysym_lookup.rs | 1190 ++++++++++++++---------------- lefthk-core/src/worker/mod.rs | 46 +- 5 files changed, 748 insertions(+), 703 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85dd747..9d7fc2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "bytes" version = "1.5.0" @@ -133,27 +145,18 @@ dependencies = [ ] [[package]] -name = "evdev-rs" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9812d5790fb6fcce449333eb6713dad335e8c979225ed98755c84a3987e06dba" -dependencies = [ - "bitflags 1.3.2", - "evdev-sys", - "libc", - "log", - "serde", -] - -[[package]] -name = "evdev-sys" -version = "0.2.5" +name = "evdev" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ead42b547b15d47089c1243d907bcf0eb94e457046d3b315a26ac9c9e9ea6d" +checksum = "2bed59fcc8cfd6b190814a509018388462d3b203cf6dd10db5c00087e72a83f3" dependencies = [ - "cc", + "bitvec", + "cfg-if", + "futures-core", "libc", - "pkg-config", + "nix 0.23.2", + "thiserror", + "tokio", ] [[package]] @@ -162,6 +165,18 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + [[package]] name = "gimli" version = "0.28.1" @@ -242,10 +257,10 @@ dependencies = [ name = "lefthk-core" version = "0.2.1" dependencies = [ - "evdev-rs", + "evdev", "inventory", "mio", - "nix", + "nix 0.27.1", "ron", "serde", "signal-hook", @@ -300,6 +315,15 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -321,6 +345,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nix" version = "0.27.1" @@ -409,6 +446,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "regex" version = "1.10.3" @@ -565,6 +608,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.10.1" @@ -905,6 +954,15 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xdg" version = "2.5.2" diff --git a/lefthk-core/Cargo.toml b/lefthk-core/Cargo.toml index 44e4b6b..644bea4 100644 --- a/lefthk-core/Cargo.toml +++ b/lefthk-core/Cargo.toml @@ -8,7 +8,7 @@ repository = "https://github.com/leftwm/lefthk" description = "A hotkey daemon for Adventurers" [dependencies] -evdev-rs = { version = "0.6.1", features = ["serde"] } +evdev = { version = "0.12.1", features = ["tokio"] } mio = "0.8.11" nix = { version = "0.27.1", features = ["fs", "signal"] } signal-hook = "0.3.4" diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index ec0b242..de2d75c 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -1,6 +1,5 @@ -use evdev_rs::{Device, DeviceWrapper, InputEvent, ReadFlag, ReadStatus, UInputDevice}; -use std::future::poll_fn; -use std::os::fd::AsRawFd; +use evdev::uinput::{VirtualDevice, VirtualDeviceBuilder}; +use evdev::{AttributeSet, BusType, Device, InputEvent, InputId, Key, RelativeAxisType}; use std::path::PathBuf; use std::{collections::HashMap, ffi::OsStr}; use tokio::sync::{mpsc, oneshot}; @@ -15,7 +14,7 @@ pub enum Task { } pub struct EvDev { - pub devices: HashMap, + pub device: VirtualDevice, pub task_receiver: mpsc::Receiver, task_transmitter: mpsc::Sender, task_guards: HashMap>, @@ -24,7 +23,39 @@ pub struct EvDev { impl Default for EvDev { fn default() -> Self { - let devices: HashMap = HashMap::new(); + let keys = AttributeSet::from_iter( + (Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code()).map(Key::new), + ); + let relative_axes = evdev::AttributeSet::from_iter([ + RelativeAxisType::REL_WHEEL, + RelativeAxisType::REL_HWHEEL, + RelativeAxisType::REL_X, + RelativeAxisType::REL_Y, + RelativeAxisType::REL_Z, + RelativeAxisType::REL_RX, + RelativeAxisType::REL_RY, + RelativeAxisType::REL_RZ, + RelativeAxisType::REL_DIAL, + RelativeAxisType::REL_MISC, + RelativeAxisType::REL_WHEEL_HI_RES, + RelativeAxisType::REL_HWHEEL_HI_RES, + ]); + + let builder = errors::exit!(VirtualDeviceBuilder::new()); + + let mut device = builder + .name("LeftHK Virtual Keyboard") + .input_id(InputId::new(BusType::BUS_I8042, 1, 1, 1)) + .with_keys(&keys) + .unwrap() + .with_relative_axes(&relative_axes) + .unwrap() + .build() + .unwrap(); + println!("Device: {:?}", device.get_syspath()); + + let devnode = device.enumerate_dev_nodes_blocking().unwrap().next(); + println!("Devnode: {:?}", devnode); let (task_transmitter, task_receiver) = mpsc::channel(128); @@ -33,7 +64,7 @@ impl Default for EvDev { let task_guards: HashMap> = HashMap::new(); let mut evdev = EvDev { - devices, + device, task_receiver, task_transmitter, task_guards, @@ -56,58 +87,43 @@ impl Default for EvDev { impl EvDev { pub fn add_device(&mut self, path: PathBuf) { tracing::info!("Adding device with path: {:?}", path); - if let Some(mut device) = device_with_path(path.clone()) { - device.set_name(&format!("LeftHK virtual input for {:?}", path)); - let uinput = errors::r#return!(UInputDevice::create_from_device(&device)); - errors::r#return!(device.grab(evdev_rs::GrabMode::Grab)); + if let Ok(mut device) = Device::open(path.clone()) { + wait_for_all_keys_unpressed(&device); + errors::r#return!(device.grab()); + errors::r#return!(device.ungrab()); + errors::r#return!(device.grab()); - let (mut guard, task_guard) = oneshot::channel(); + let (guard, task_guard) = oneshot::channel(); let transmitter = self.task_transmitter.clone(); - const SERVER: mio::Token = mio::Token(0); - let fd = device.file().as_raw_fd(); - let mut poll = errors::exit!(mio::Poll::new()); - let mut events = mio::Events::with_capacity(1); - errors::exit!(poll.registry().register( - &mut mio::unix::SourceFd(&fd), - SERVER, - mio::Interest::READABLE, - )); + + let mut stream = errors::r#return!(device.into_event_stream()); let p = path.clone(); tokio::task::spawn(async move { while !guard.is_closed() { - if let Err(err) = poll.poll(&mut events, None) { - tracing::warn!("Evdev device poll failed with {:?}", err); - continue; - } - - while device.has_event_pending() { - match device.next_event(ReadFlag::NORMAL) { - Ok((ReadStatus::Success, event)) => { - transmitter - .send(Task::KeyboardEvent((p.clone(), event))) - .await - .unwrap(); - } - Err(_) => { - poll_fn(|cx| guard.poll_closed(cx)).await; - break; - } - _ => {} + match stream.next_event().await { + Ok(event) => { + transmitter + .send(Task::KeyboardEvent((p.clone(), event))) + .await + .unwrap(); + } + Err(err) => { + tracing::warn!("Evdev device stream failed with {:?}", err); + // poll_fn(|cx| guard.poll_closed(cx)).await; + break; } } } tracing::info!("Device loop has closed."); - errors::r#return!(device.grab(evdev_rs::GrabMode::Ungrab)); + errors::r#return!(stream.device_mut().ungrab()); }); - self.devices.insert(path.clone(), uinput); self.task_guards.insert(path, task_guard); } } pub fn remove_device(&mut self, path: PathBuf) { tracing::info!("Removing device with path: {:?}", path); self.task_guards.remove(&path); - self.devices.remove(&path); } } @@ -135,14 +151,17 @@ fn find_keyboards() -> Option> { Some(devices) } -fn device_with_path(path: PathBuf) -> Option { - let device = Device::new_from_path(path).ok()?; - if device.has(evdev_rs::enums::EventType::EV_KEY) - && device.phys().unwrap_or("").contains("input0") - { - return Some(device); +fn wait_for_all_keys_unpressed(device: &Device) { + let mut pending_release = false; + loop { + match device.get_key_state() { + Ok(keys) if keys.iter().count() > 0 => pending_release = true, + _ => break, + } + } + if pending_release { + std::thread::sleep(std::time::Duration::from_micros(100)); } - None } #[derive(Debug)] diff --git a/lefthk-core/src/keysym_lookup.rs b/lefthk-core/src/keysym_lookup.rs index d203cdf..85ef765 100644 --- a/lefthk-core/src/keysym_lookup.rs +++ b/lefthk-core/src/keysym_lookup.rs @@ -1,4 +1,4 @@ -use evdev_rs::enums::EV_KEY; +use evdev::Key; #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -12,37 +12,37 @@ pub enum MOD_MASK { MASK_SCROLLLOCK, } -impl TryFrom for MOD_MASK { +impl TryFrom for MOD_MASK { type Error = (); - fn try_from(key: EV_KEY) -> Result { + fn try_from(key: Key) -> Result { match key { - EV_KEY::KEY_LEFTSHIFT | EV_KEY::KEY_RIGHTSHIFT => Ok(Self::MASK_SHIFT), - EV_KEY::KEY_LEFTCTRL | EV_KEY::KEY_RIGHTCTRL => Ok(Self::MASK_CTRL), - EV_KEY::KEY_LEFTALT | EV_KEY::KEY_RIGHTALT => Ok(Self::MASK_ALT), - EV_KEY::KEY_LEFTMETA | EV_KEY::KEY_RIGHTMETA => Ok(Self::MASK_META), - EV_KEY::KEY_CAPSLOCK => Ok(Self::MASK_CAPSLOCK), - EV_KEY::KEY_NUMLOCK => Ok(Self::MASK_NUMLOCK), - EV_KEY::KEY_SCROLLLOCK => Ok(Self::MASK_SCROLLLOCK), + Key::KEY_LEFTSHIFT | Key::KEY_RIGHTSHIFT => Ok(Self::MASK_SHIFT), + Key::KEY_LEFTCTRL | Key::KEY_RIGHTCTRL => Ok(Self::MASK_CTRL), + Key::KEY_LEFTALT | Key::KEY_RIGHTALT => Ok(Self::MASK_ALT), + Key::KEY_LEFTMETA | Key::KEY_RIGHTMETA => Ok(Self::MASK_META), + Key::KEY_CAPSLOCK => Ok(Self::MASK_CAPSLOCK), + Key::KEY_NUMLOCK => Ok(Self::MASK_NUMLOCK), + Key::KEY_SCROLLLOCK => Ok(Self::MASK_SCROLLLOCK), _ => Err(()), } } } -pub fn is_modifier(key: &EV_KEY) -> bool { +pub fn is_modifier(key: &Key) -> bool { matches!( key, - EV_KEY::KEY_LEFTSHIFT - | EV_KEY::KEY_RIGHTSHIFT - | EV_KEY::KEY_LEFTCTRL - | EV_KEY::KEY_RIGHTCTRL - | EV_KEY::KEY_LEFTALT - | EV_KEY::KEY_RIGHTALT - | EV_KEY::KEY_LEFTMETA - | EV_KEY::KEY_RIGHTMETA - | EV_KEY::KEY_CAPSLOCK - | EV_KEY::KEY_NUMLOCK - | EV_KEY::KEY_SCROLLLOCK + &Key::KEY_LEFTSHIFT + | &Key::KEY_RIGHTSHIFT + | &Key::KEY_LEFTCTRL + | &Key::KEY_RIGHTCTRL + | &Key::KEY_LEFTALT + | &Key::KEY_RIGHTALT + | &Key::KEY_LEFTMETA + | &Key::KEY_RIGHTMETA + | &Key::KEY_CAPSLOCK + | &Key::KEY_NUMLOCK + | &Key::KEY_SCROLLLOCK ) } @@ -69,7 +69,7 @@ pub fn into_mod(key: &str) -> Option { } } -pub fn into_keys(keys: &[String]) -> Vec { +pub fn into_keys(keys: &[String]) -> Vec { let mut result = Vec::new(); for key in keys { if let Some(key) = into_key(key) { @@ -82,604 +82,554 @@ pub fn into_keys(keys: &[String]) -> Vec { // We allow this because this function is simply a mapping wrapper. #[allow(clippy::too_many_lines)] #[must_use] -pub fn into_key(key: &str) -> Option { +pub fn into_key(key: &str) -> Option { match key { - "Reserved" => Some(EV_KEY::KEY_RESERVED), - "Escape" => Some(EV_KEY::KEY_ESC), - "1" => Some(EV_KEY::KEY_1), - "2" => Some(EV_KEY::KEY_2), - "3" => Some(EV_KEY::KEY_3), - "4" => Some(EV_KEY::KEY_4), - "5" => Some(EV_KEY::KEY_5), - "6" => Some(EV_KEY::KEY_6), - "7" => Some(EV_KEY::KEY_7), - "8" => Some(EV_KEY::KEY_8), - "9" => Some(EV_KEY::KEY_9), - "0" => Some(EV_KEY::KEY_0), - "Minus" => Some(EV_KEY::KEY_MINUS), - "Equal" => Some(EV_KEY::KEY_EQUAL), - "Backspace" => Some(EV_KEY::KEY_BACKSPACE), - "Tab" => Some(EV_KEY::KEY_TAB), - "q" => Some(EV_KEY::KEY_Q), - "w" => Some(EV_KEY::KEY_W), - "e" => Some(EV_KEY::KEY_E), - "r" => Some(EV_KEY::KEY_R), - "t" => Some(EV_KEY::KEY_T), - "y" => Some(EV_KEY::KEY_Y), - "u" => Some(EV_KEY::KEY_U), - "i" => Some(EV_KEY::KEY_I), - "o" => Some(EV_KEY::KEY_O), - "p" => Some(EV_KEY::KEY_P), - "Leftbrace" => Some(EV_KEY::KEY_LEFTBRACE), - "Rightbrace" => Some(EV_KEY::KEY_RIGHTBRACE), - "Enter" => Some(EV_KEY::KEY_ENTER), - "Leftctrl" => Some(EV_KEY::KEY_LEFTCTRL), - "a" => Some(EV_KEY::KEY_A), - "s" => Some(EV_KEY::KEY_S), - "d" => Some(EV_KEY::KEY_D), - "f" => Some(EV_KEY::KEY_F), - "g" => Some(EV_KEY::KEY_G), - "h" => Some(EV_KEY::KEY_H), - "j" => Some(EV_KEY::KEY_J), - "k" => Some(EV_KEY::KEY_K), - "l" => Some(EV_KEY::KEY_L), - "Semicolon" => Some(EV_KEY::KEY_SEMICOLON), - "Apostrophe" => Some(EV_KEY::KEY_APOSTROPHE), - "Grave" => Some(EV_KEY::KEY_GRAVE), - "Leftshift" => Some(EV_KEY::KEY_LEFTSHIFT), - "Backslash" => Some(EV_KEY::KEY_BACKSLASH), - "z" => Some(EV_KEY::KEY_Z), - "x" => Some(EV_KEY::KEY_X), - "c" => Some(EV_KEY::KEY_C), - "v" => Some(EV_KEY::KEY_V), - "b" => Some(EV_KEY::KEY_B), - "n" => Some(EV_KEY::KEY_N), - "m" => Some(EV_KEY::KEY_M), - "Comma" => Some(EV_KEY::KEY_COMMA), - "Dot" => Some(EV_KEY::KEY_DOT), - "Slash" => Some(EV_KEY::KEY_SLASH), - "Rightshift" => Some(EV_KEY::KEY_RIGHTSHIFT), - "Kpasterisk" => Some(EV_KEY::KEY_KPASTERISK), - "Leftalt" => Some(EV_KEY::KEY_LEFTALT), - "Space" => Some(EV_KEY::KEY_SPACE), - "Capslock" => Some(EV_KEY::KEY_CAPSLOCK), - "F1" => Some(EV_KEY::KEY_F1), - "F2" => Some(EV_KEY::KEY_F2), - "F3" => Some(EV_KEY::KEY_F3), - "F4" => Some(EV_KEY::KEY_F4), - "F5" => Some(EV_KEY::KEY_F5), - "F6" => Some(EV_KEY::KEY_F6), - "F7" => Some(EV_KEY::KEY_F7), - "F8" => Some(EV_KEY::KEY_F8), - "F9" => Some(EV_KEY::KEY_F9), - "F10" => Some(EV_KEY::KEY_F10), - "Numlock" => Some(EV_KEY::KEY_NUMLOCK), - "Scrolllock" => Some(EV_KEY::KEY_SCROLLLOCK), - "Kp7" => Some(EV_KEY::KEY_KP7), - "Kp8" => Some(EV_KEY::KEY_KP8), - "Kp9" => Some(EV_KEY::KEY_KP9), - "Kpminus" => Some(EV_KEY::KEY_KPMINUS), - "Kp4" => Some(EV_KEY::KEY_KP4), - "Kp5" => Some(EV_KEY::KEY_KP5), - "Kp6" => Some(EV_KEY::KEY_KP6), - "Kpplus" => Some(EV_KEY::KEY_KPPLUS), - "Kp1" => Some(EV_KEY::KEY_KP1), - "Kp2" => Some(EV_KEY::KEY_KP2), - "Kp3" => Some(EV_KEY::KEY_KP3), - "Kp0" => Some(EV_KEY::KEY_KP0), - "Kpdot" => Some(EV_KEY::KEY_KPDOT), - "Zenkakuhankaku" => Some(EV_KEY::KEY_ZENKAKUHANKAKU), - "102nd" => Some(EV_KEY::KEY_102ND), - "F11" => Some(EV_KEY::KEY_F11), - "F12" => Some(EV_KEY::KEY_F12), - "Ro" => Some(EV_KEY::KEY_RO), - "Katakana" => Some(EV_KEY::KEY_KATAKANA), - "Hiragana" => Some(EV_KEY::KEY_HIRAGANA), - "Henkan" => Some(EV_KEY::KEY_HENKAN), - "Katakanahiragana" => Some(EV_KEY::KEY_KATAKANAHIRAGANA), - "Muhenkan" => Some(EV_KEY::KEY_MUHENKAN), - "Kpjpcomma" => Some(EV_KEY::KEY_KPJPCOMMA), - "Kpenter" => Some(EV_KEY::KEY_KPENTER), - "Rightctrl" => Some(EV_KEY::KEY_RIGHTCTRL), - "Kpslash" => Some(EV_KEY::KEY_KPSLASH), - "Sysrq" => Some(EV_KEY::KEY_SYSRQ), - "Rightalt" => Some(EV_KEY::KEY_RIGHTALT), - "Linefeed" => Some(EV_KEY::KEY_LINEFEED), - "Home" => Some(EV_KEY::KEY_HOME), - "Up" => Some(EV_KEY::KEY_UP), - "Pageup" => Some(EV_KEY::KEY_PAGEUP), - "Left" => Some(EV_KEY::KEY_LEFT), - "Right" => Some(EV_KEY::KEY_RIGHT), - "End" => Some(EV_KEY::KEY_END), - "Down" => Some(EV_KEY::KEY_DOWN), - "Pagedown" => Some(EV_KEY::KEY_PAGEDOWN), - "Insert" => Some(EV_KEY::KEY_INSERT), - "Delete" => Some(EV_KEY::KEY_DELETE), - "Macro" => Some(EV_KEY::KEY_MACRO), - "Mute" => Some(EV_KEY::KEY_MUTE), - "Volumedown" => Some(EV_KEY::KEY_VOLUMEDOWN), - "Volumeup" => Some(EV_KEY::KEY_VOLUMEUP), - "Power" => Some(EV_KEY::KEY_POWER), - "Kpequal" => Some(EV_KEY::KEY_KPEQUAL), - "Kpplusminus" => Some(EV_KEY::KEY_KPPLUSMINUS), - "Pause" => Some(EV_KEY::KEY_PAUSE), - "Scale" => Some(EV_KEY::KEY_SCALE), - "Kpcomma" => Some(EV_KEY::KEY_KPCOMMA), - "Hangeul" => Some(EV_KEY::KEY_HANGEUL), - "Hanja" => Some(EV_KEY::KEY_HANJA), - "Yen" => Some(EV_KEY::KEY_YEN), - "Leftmeta" => Some(EV_KEY::KEY_LEFTMETA), - "Rightmeta" => Some(EV_KEY::KEY_RIGHTMETA), - "Compose" => Some(EV_KEY::KEY_COMPOSE), - "Stop" => Some(EV_KEY::KEY_STOP), - "Again" => Some(EV_KEY::KEY_AGAIN), - "Props" => Some(EV_KEY::KEY_PROPS), - "Undo" => Some(EV_KEY::KEY_UNDO), - "Front" => Some(EV_KEY::KEY_FRONT), - "Copy" => Some(EV_KEY::KEY_COPY), - "Open" => Some(EV_KEY::KEY_OPEN), - "Paste" => Some(EV_KEY::KEY_PASTE), - "Find" => Some(EV_KEY::KEY_FIND), - "Cut" => Some(EV_KEY::KEY_CUT), - "Help" => Some(EV_KEY::KEY_HELP), - "Menu" => Some(EV_KEY::KEY_MENU), - "Calc" => Some(EV_KEY::KEY_CALC), - "Setup" => Some(EV_KEY::KEY_SETUP), - "Sleep" => Some(EV_KEY::KEY_SLEEP), - "Wakeup" => Some(EV_KEY::KEY_WAKEUP), - "File" => Some(EV_KEY::KEY_FILE), - "Sendfile" => Some(EV_KEY::KEY_SENDFILE), - "Deletefile" => Some(EV_KEY::KEY_DELETEFILE), - "Xfer" => Some(EV_KEY::KEY_XFER), - "Prog1" => Some(EV_KEY::KEY_PROG1), - "Prog2" => Some(EV_KEY::KEY_PROG2), - "Www" => Some(EV_KEY::KEY_WWW), - "Msdos" => Some(EV_KEY::KEY_MSDOS), - "Coffee" => Some(EV_KEY::KEY_COFFEE), - "Rotate_display" => Some(EV_KEY::KEY_ROTATE_DISPLAY), - "Cyclewindows" => Some(EV_KEY::KEY_CYCLEWINDOWS), - "Mail" => Some(EV_KEY::KEY_MAIL), - "Bookmarks" => Some(EV_KEY::KEY_BOOKMARKS), - "Computer" => Some(EV_KEY::KEY_COMPUTER), - "Back" => Some(EV_KEY::KEY_BACK), - "Forward" => Some(EV_KEY::KEY_FORWARD), - "Closecd" => Some(EV_KEY::KEY_CLOSECD), - "Ejectcd" => Some(EV_KEY::KEY_EJECTCD), - "Ejectclosecd" => Some(EV_KEY::KEY_EJECTCLOSECD), - "Nextsong" => Some(EV_KEY::KEY_NEXTSONG), - "Playpause" => Some(EV_KEY::KEY_PLAYPAUSE), - "Previoussong" => Some(EV_KEY::KEY_PREVIOUSSONG), - "Stopcd" => Some(EV_KEY::KEY_STOPCD), - "Record" => Some(EV_KEY::KEY_RECORD), - "Rewind" => Some(EV_KEY::KEY_REWIND), - "Phone" => Some(EV_KEY::KEY_PHONE), - "Iso" => Some(EV_KEY::KEY_ISO), - "Config" => Some(EV_KEY::KEY_CONFIG), - "Homepage" => Some(EV_KEY::KEY_HOMEPAGE), - "Refresh" => Some(EV_KEY::KEY_REFRESH), - "Exit" => Some(EV_KEY::KEY_EXIT), - "Move" => Some(EV_KEY::KEY_MOVE), - "Edit" => Some(EV_KEY::KEY_EDIT), - "Scrollup" => Some(EV_KEY::KEY_SCROLLUP), - "Scrolldown" => Some(EV_KEY::KEY_SCROLLDOWN), - "Kpleftparen" => Some(EV_KEY::KEY_KPLEFTPAREN), - "Kprightparen" => Some(EV_KEY::KEY_KPRIGHTPAREN), - "New" => Some(EV_KEY::KEY_NEW), - "Redo" => Some(EV_KEY::KEY_REDO), - "F13" => Some(EV_KEY::KEY_F13), - "F14" => Some(EV_KEY::KEY_F14), - "F15" => Some(EV_KEY::KEY_F15), - "F16" => Some(EV_KEY::KEY_F16), - "F17" => Some(EV_KEY::KEY_F17), - "F18" => Some(EV_KEY::KEY_F18), - "F19" => Some(EV_KEY::KEY_F19), - "F20" => Some(EV_KEY::KEY_F20), - "F21" => Some(EV_KEY::KEY_F21), - "F22" => Some(EV_KEY::KEY_F22), - "F23" => Some(EV_KEY::KEY_F23), - "F24" => Some(EV_KEY::KEY_F24), - "Playcd" => Some(EV_KEY::KEY_PLAYCD), - "Pausecd" => Some(EV_KEY::KEY_PAUSECD), - "Prog3" => Some(EV_KEY::KEY_PROG3), - "Prog4" => Some(EV_KEY::KEY_PROG4), - "All_applications" => Some(EV_KEY::KEY_ALL_APPLICATIONS), - "Suspend" => Some(EV_KEY::KEY_SUSPEND), - "Close" => Some(EV_KEY::KEY_CLOSE), - "Play" => Some(EV_KEY::KEY_PLAY), - "Fastforward" => Some(EV_KEY::KEY_FASTFORWARD), - "Bassboost" => Some(EV_KEY::KEY_BASSBOOST), - "Print" => Some(EV_KEY::KEY_PRINT), - "Hp" => Some(EV_KEY::KEY_HP), - "Camera" => Some(EV_KEY::KEY_CAMERA), - "Sound" => Some(EV_KEY::KEY_SOUND), - "Question" => Some(EV_KEY::KEY_QUESTION), - "Email" => Some(EV_KEY::KEY_EMAIL), - "Chat" => Some(EV_KEY::KEY_CHAT), - "Search" => Some(EV_KEY::KEY_SEARCH), - "Connect" => Some(EV_KEY::KEY_CONNECT), - "Finance" => Some(EV_KEY::KEY_FINANCE), - "Sport" => Some(EV_KEY::KEY_SPORT), - "Shop" => Some(EV_KEY::KEY_SHOP), - "Alterase" => Some(EV_KEY::KEY_ALTERASE), - "Cancel" => Some(EV_KEY::KEY_CANCEL), - "Brightnessdown" => Some(EV_KEY::KEY_BRIGHTNESSDOWN), - "Brightnessup" => Some(EV_KEY::KEY_BRIGHTNESSUP), - "Media" => Some(EV_KEY::KEY_MEDIA), - "Switchvideomode" => Some(EV_KEY::KEY_SWITCHVIDEOMODE), - "Kbdillumtoggle" => Some(EV_KEY::KEY_KBDILLUMTOGGLE), - "Kbdillumdown" => Some(EV_KEY::KEY_KBDILLUMDOWN), - "Kbdillumup" => Some(EV_KEY::KEY_KBDILLUMUP), - "Send" => Some(EV_KEY::KEY_SEND), - "Reply" => Some(EV_KEY::KEY_REPLY), - "Forwardmail" => Some(EV_KEY::KEY_FORWARDMAIL), - "Save" => Some(EV_KEY::KEY_SAVE), - "Documents" => Some(EV_KEY::KEY_DOCUMENTS), - "Battery" => Some(EV_KEY::KEY_BATTERY), - "Bluetooth" => Some(EV_KEY::KEY_BLUETOOTH), - "Wlan" => Some(EV_KEY::KEY_WLAN), - "Uwb" => Some(EV_KEY::KEY_UWB), - "Unknown" => Some(EV_KEY::KEY_UNKNOWN), - "Video_next" => Some(EV_KEY::KEY_VIDEO_NEXT), - "Video_prev" => Some(EV_KEY::KEY_VIDEO_PREV), - "Brightness_cycle" => Some(EV_KEY::KEY_BRIGHTNESS_CYCLE), - "Brightness_auto" => Some(EV_KEY::KEY_BRIGHTNESS_AUTO), - "Display_off" => Some(EV_KEY::KEY_DISPLAY_OFF), - "Wwan" => Some(EV_KEY::KEY_WWAN), - "Rfkill" => Some(EV_KEY::KEY_RFKILL), - "Micmute" => Some(EV_KEY::KEY_MICMUTE), - "Ok" => Some(EV_KEY::KEY_OK), - "Select" => Some(EV_KEY::KEY_SELECT), - "Goto" => Some(EV_KEY::KEY_GOTO), - "Clear" => Some(EV_KEY::KEY_CLEAR), - "Power2" => Some(EV_KEY::KEY_POWER2), - "Option" => Some(EV_KEY::KEY_OPTION), - "Info" => Some(EV_KEY::KEY_INFO), - "Time" => Some(EV_KEY::KEY_TIME), - "Vendor" => Some(EV_KEY::KEY_VENDOR), - "Archive" => Some(EV_KEY::KEY_ARCHIVE), - "Program" => Some(EV_KEY::KEY_PROGRAM), - "Channel" => Some(EV_KEY::KEY_CHANNEL), - "Favorites" => Some(EV_KEY::KEY_FAVORITES), - "Epg" => Some(EV_KEY::KEY_EPG), - "Pvr" => Some(EV_KEY::KEY_PVR), - "Mhp" => Some(EV_KEY::KEY_MHP), - "Language" => Some(EV_KEY::KEY_LANGUAGE), - "Title" => Some(EV_KEY::KEY_TITLE), - "Subtitle" => Some(EV_KEY::KEY_SUBTITLE), - "Angle" => Some(EV_KEY::KEY_ANGLE), - "Full_screen" => Some(EV_KEY::KEY_FULL_SCREEN), - "Mode" => Some(EV_KEY::KEY_MODE), - "Keyboard" => Some(EV_KEY::KEY_KEYBOARD), - "Aspect_ratio" => Some(EV_KEY::KEY_ASPECT_RATIO), - "Pc" => Some(EV_KEY::KEY_PC), - "Tv" => Some(EV_KEY::KEY_TV), - "Tv2" => Some(EV_KEY::KEY_TV2), - "Vcr" => Some(EV_KEY::KEY_VCR), - "Vcr2" => Some(EV_KEY::KEY_VCR2), - "Sat" => Some(EV_KEY::KEY_SAT), - "Sat2" => Some(EV_KEY::KEY_SAT2), - "Cd" => Some(EV_KEY::KEY_CD), - "Tape" => Some(EV_KEY::KEY_TAPE), - "Radio" => Some(EV_KEY::KEY_RADIO), - "Tuner" => Some(EV_KEY::KEY_TUNER), - "Player" => Some(EV_KEY::KEY_PLAYER), - "Text" => Some(EV_KEY::KEY_TEXT), - "Dvd" => Some(EV_KEY::KEY_DVD), - "Aux" => Some(EV_KEY::KEY_AUX), - "Mp3" => Some(EV_KEY::KEY_MP3), - "Audio" => Some(EV_KEY::KEY_AUDIO), - "Video" => Some(EV_KEY::KEY_VIDEO), - "Directory" => Some(EV_KEY::KEY_DIRECTORY), - "List" => Some(EV_KEY::KEY_LIST), - "Memo" => Some(EV_KEY::KEY_MEMO), - "Calendar" => Some(EV_KEY::KEY_CALENDAR), - "Red" => Some(EV_KEY::KEY_RED), - "Green" => Some(EV_KEY::KEY_GREEN), - "Yellow" => Some(EV_KEY::KEY_YELLOW), - "Blue" => Some(EV_KEY::KEY_BLUE), - "Channelup" => Some(EV_KEY::KEY_CHANNELUP), - "Channeldown" => Some(EV_KEY::KEY_CHANNELDOWN), - "First" => Some(EV_KEY::KEY_FIRST), - "Last" => Some(EV_KEY::KEY_LAST), - "Ab" => Some(EV_KEY::KEY_AB), - "Next" => Some(EV_KEY::KEY_NEXT), - "Restart" => Some(EV_KEY::KEY_RESTART), - "Slow" => Some(EV_KEY::KEY_SLOW), - "Shuffle" => Some(EV_KEY::KEY_SHUFFLE), - "Break" => Some(EV_KEY::KEY_BREAK), - "Previous" => Some(EV_KEY::KEY_PREVIOUS), - "Digits" => Some(EV_KEY::KEY_DIGITS), - "Teen" => Some(EV_KEY::KEY_TEEN), - "Twen" => Some(EV_KEY::KEY_TWEN), - "Videophone" => Some(EV_KEY::KEY_VIDEOPHONE), - "Games" => Some(EV_KEY::KEY_GAMES), - "Zoomin" => Some(EV_KEY::KEY_ZOOMIN), - "Zoomout" => Some(EV_KEY::KEY_ZOOMOUT), - "Zoomreset" => Some(EV_KEY::KEY_ZOOMRESET), - "Wordprocessor" => Some(EV_KEY::KEY_WORDPROCESSOR), - "Editor" => Some(EV_KEY::KEY_EDITOR), - "Spreadsheet" => Some(EV_KEY::KEY_SPREADSHEET), - "Graphicseditor" => Some(EV_KEY::KEY_GRAPHICSEDITOR), - "Presentation" => Some(EV_KEY::KEY_PRESENTATION), - "Database" => Some(EV_KEY::KEY_DATABASE), - "News" => Some(EV_KEY::KEY_NEWS), - "Voicemail" => Some(EV_KEY::KEY_VOICEMAIL), - "Addressbook" => Some(EV_KEY::KEY_ADDRESSBOOK), - "Messenger" => Some(EV_KEY::KEY_MESSENGER), - "Displaytoggle" => Some(EV_KEY::KEY_DISPLAYTOGGLE), - "Spellcheck" => Some(EV_KEY::KEY_SPELLCHECK), - "Logoff" => Some(EV_KEY::KEY_LOGOFF), - "Dollar" => Some(EV_KEY::KEY_DOLLAR), - "Euro" => Some(EV_KEY::KEY_EURO), - "Frameback" => Some(EV_KEY::KEY_FRAMEBACK), - "Frameforward" => Some(EV_KEY::KEY_FRAMEFORWARD), - "Context_menu" => Some(EV_KEY::KEY_CONTEXT_MENU), - "Media_repeat" => Some(EV_KEY::KEY_MEDIA_REPEAT), - "10channelsup" => Some(EV_KEY::KEY_10CHANNELSUP), - "10channelsdown" => Some(EV_KEY::KEY_10CHANNELSDOWN), - "Images" => Some(EV_KEY::KEY_IMAGES), - "Notification_center" => Some(EV_KEY::KEY_NOTIFICATION_CENTER), - "Pickup_phone" => Some(EV_KEY::KEY_PICKUP_PHONE), - "Hangup_phone" => Some(EV_KEY::KEY_HANGUP_PHONE), - "Del_eol" => Some(EV_KEY::KEY_DEL_EOL), - "Del_eos" => Some(EV_KEY::KEY_DEL_EOS), - "Ins_line" => Some(EV_KEY::KEY_INS_LINE), - "Del_line" => Some(EV_KEY::KEY_DEL_LINE), - "Fn" => Some(EV_KEY::KEY_FN), - "Fn_esc" => Some(EV_KEY::KEY_FN_ESC), - "Fn_f1" => Some(EV_KEY::KEY_FN_F1), - "Fn_f2" => Some(EV_KEY::KEY_FN_F2), - "Fn_f3" => Some(EV_KEY::KEY_FN_F3), - "Fn_f4" => Some(EV_KEY::KEY_FN_F4), - "Fn_f5" => Some(EV_KEY::KEY_FN_F5), - "Fn_f6" => Some(EV_KEY::KEY_FN_F6), - "Fn_f7" => Some(EV_KEY::KEY_FN_F7), - "Fn_f8" => Some(EV_KEY::KEY_FN_F8), - "Fn_f9" => Some(EV_KEY::KEY_FN_F9), - "Fn_f10" => Some(EV_KEY::KEY_FN_F10), - "Fn_f11" => Some(EV_KEY::KEY_FN_F11), - "Fn_f12" => Some(EV_KEY::KEY_FN_F12), - "Fn_1" => Some(EV_KEY::KEY_FN_1), - "Fn_2" => Some(EV_KEY::KEY_FN_2), - "Fn_d" => Some(EV_KEY::KEY_FN_D), - "Fn_e" => Some(EV_KEY::KEY_FN_E), - "Fn_f" => Some(EV_KEY::KEY_FN_F), - "Fn_s" => Some(EV_KEY::KEY_FN_S), - "Fn_b" => Some(EV_KEY::KEY_FN_B), - "Fn_right_shift" => Some(EV_KEY::KEY_FN_RIGHT_SHIFT), - "Brl_dot1" => Some(EV_KEY::KEY_BRL_DOT1), - "Brl_dot2" => Some(EV_KEY::KEY_BRL_DOT2), - "Brl_dot3" => Some(EV_KEY::KEY_BRL_DOT3), - "Brl_dot4" => Some(EV_KEY::KEY_BRL_DOT4), - "Brl_dot5" => Some(EV_KEY::KEY_BRL_DOT5), - "Brl_dot6" => Some(EV_KEY::KEY_BRL_DOT6), - "Brl_dot7" => Some(EV_KEY::KEY_BRL_DOT7), - "Brl_dot8" => Some(EV_KEY::KEY_BRL_DOT8), - "Brl_dot9" => Some(EV_KEY::KEY_BRL_DOT9), - "Brl_dot10" => Some(EV_KEY::KEY_BRL_DOT10), - "Numeric_0" => Some(EV_KEY::KEY_NUMERIC_0), - "Numeric_1" => Some(EV_KEY::KEY_NUMERIC_1), - "Numeric_2" => Some(EV_KEY::KEY_NUMERIC_2), - "Numeric_3" => Some(EV_KEY::KEY_NUMERIC_3), - "Numeric_4" => Some(EV_KEY::KEY_NUMERIC_4), - "Numeric_5" => Some(EV_KEY::KEY_NUMERIC_5), - "Numeric_6" => Some(EV_KEY::KEY_NUMERIC_6), - "Numeric_7" => Some(EV_KEY::KEY_NUMERIC_7), - "Numeric_8" => Some(EV_KEY::KEY_NUMERIC_8), - "Numeric_9" => Some(EV_KEY::KEY_NUMERIC_9), - "Numeric_star" => Some(EV_KEY::KEY_NUMERIC_STAR), - "Numeric_pound" => Some(EV_KEY::KEY_NUMERIC_POUND), - "Numeric_a" => Some(EV_KEY::KEY_NUMERIC_A), - "Numeric_b" => Some(EV_KEY::KEY_NUMERIC_B), - "Numeric_c" => Some(EV_KEY::KEY_NUMERIC_C), - "Numeric_d" => Some(EV_KEY::KEY_NUMERIC_D), - "Camera_focus" => Some(EV_KEY::KEY_CAMERA_FOCUS), - "Wps_button" => Some(EV_KEY::KEY_WPS_BUTTON), - "Touchpad_toggle" => Some(EV_KEY::KEY_TOUCHPAD_TOGGLE), - "Touchpad_on" => Some(EV_KEY::KEY_TOUCHPAD_ON), - "Touchpad_off" => Some(EV_KEY::KEY_TOUCHPAD_OFF), - "Camera_zoomin" => Some(EV_KEY::KEY_CAMERA_ZOOMIN), - "Camera_zoomout" => Some(EV_KEY::KEY_CAMERA_ZOOMOUT), - "Camera_up" => Some(EV_KEY::KEY_CAMERA_UP), - "Camera_down" => Some(EV_KEY::KEY_CAMERA_DOWN), - "Camera_left" => Some(EV_KEY::KEY_CAMERA_LEFT), - "Camera_right" => Some(EV_KEY::KEY_CAMERA_RIGHT), - "Attendant_on" => Some(EV_KEY::KEY_ATTENDANT_ON), - "Attendant_off" => Some(EV_KEY::KEY_ATTENDANT_OFF), - "Attendant_toggle" => Some(EV_KEY::KEY_ATTENDANT_TOGGLE), - "Lights_toggle" => Some(EV_KEY::KEY_LIGHTS_TOGGLE), - "Als_toggle" => Some(EV_KEY::KEY_ALS_TOGGLE), - "Rotate_lock_toggle" => Some(EV_KEY::KEY_ROTATE_LOCK_TOGGLE), - "Buttonconfig" => Some(EV_KEY::KEY_BUTTONCONFIG), - "Taskmanager" => Some(EV_KEY::KEY_TASKMANAGER), - "Journal" => Some(EV_KEY::KEY_JOURNAL), - "Controlpanel" => Some(EV_KEY::KEY_CONTROLPANEL), - "Appselect" => Some(EV_KEY::KEY_APPSELECT), - "Screensaver" => Some(EV_KEY::KEY_SCREENSAVER), - "Voicecommand" => Some(EV_KEY::KEY_VOICECOMMAND), - "Assistant" => Some(EV_KEY::KEY_ASSISTANT), - "Kbd_layout_next" => Some(EV_KEY::KEY_KBD_LAYOUT_NEXT), - "Emoji_picker" => Some(EV_KEY::KEY_EMOJI_PICKER), - "Dictate" => Some(EV_KEY::KEY_DICTATE), - "Brightness_min" => Some(EV_KEY::KEY_BRIGHTNESS_MIN), - "Brightness_max" => Some(EV_KEY::KEY_BRIGHTNESS_MAX), - "Kbdinputassist_prev" => Some(EV_KEY::KEY_KBDINPUTASSIST_PREV), - "Kbdinputassist_next" => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXT), - "Kbdinputassist_prevgroup" => Some(EV_KEY::KEY_KBDINPUTASSIST_PREVGROUP), - "Kbdinputassist_nextgroup" => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXTGROUP), - "Kbdinputassist_accept" => Some(EV_KEY::KEY_KBDINPUTASSIST_ACCEPT), - "Kbdinputassist_cancel" => Some(EV_KEY::KEY_KBDINPUTASSIST_CANCEL), - "Right_up" => Some(EV_KEY::KEY_RIGHT_UP), - "Right_down" => Some(EV_KEY::KEY_RIGHT_DOWN), - "Left_up" => Some(EV_KEY::KEY_LEFT_UP), - "Left_down" => Some(EV_KEY::KEY_LEFT_DOWN), - "Root_menu" => Some(EV_KEY::KEY_ROOT_MENU), - "Media_top_menu" => Some(EV_KEY::KEY_MEDIA_TOP_MENU), - "Numeric_11" => Some(EV_KEY::KEY_NUMERIC_11), - "Numeric_12" => Some(EV_KEY::KEY_NUMERIC_12), - "Audio_desc" => Some(EV_KEY::KEY_AUDIO_DESC), - "3d_mode" => Some(EV_KEY::KEY_3D_MODE), - "Next_favorite" => Some(EV_KEY::KEY_NEXT_FAVORITE), - "Stop_record" => Some(EV_KEY::KEY_STOP_RECORD), - "Pause_record" => Some(EV_KEY::KEY_PAUSE_RECORD), - "Vod" => Some(EV_KEY::KEY_VOD), - "Unmute" => Some(EV_KEY::KEY_UNMUTE), - "Fastreverse" => Some(EV_KEY::KEY_FASTREVERSE), - "Slowreverse" => Some(EV_KEY::KEY_SLOWREVERSE), - "Data" => Some(EV_KEY::KEY_DATA), - "Onscreen_keyboard" => Some(EV_KEY::KEY_ONSCREEN_KEYBOARD), - "Privacy_screen_toggle" => Some(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), - "Selective_screenshot" => Some(EV_KEY::KEY_SELECTIVE_SCREENSHOT), - "Macro1" => Some(EV_KEY::KEY_MACRO1), - "Macro2" => Some(EV_KEY::KEY_MACRO2), - "Macro3" => Some(EV_KEY::KEY_MACRO3), - "Macro4" => Some(EV_KEY::KEY_MACRO4), - "Macro5" => Some(EV_KEY::KEY_MACRO5), - "Macro6" => Some(EV_KEY::KEY_MACRO6), - "Macro7" => Some(EV_KEY::KEY_MACRO7), - "Macro8" => Some(EV_KEY::KEY_MACRO8), - "Macro9" => Some(EV_KEY::KEY_MACRO9), - "Macro10" => Some(EV_KEY::KEY_MACRO10), - "Macro11" => Some(EV_KEY::KEY_MACRO11), - "Macro12" => Some(EV_KEY::KEY_MACRO12), - "Macro13" => Some(EV_KEY::KEY_MACRO13), - "Macro14" => Some(EV_KEY::KEY_MACRO14), - "Macro15" => Some(EV_KEY::KEY_MACRO15), - "Macro16" => Some(EV_KEY::KEY_MACRO16), - "Macro17" => Some(EV_KEY::KEY_MACRO17), - "Macro18" => Some(EV_KEY::KEY_MACRO18), - "Macro19" => Some(EV_KEY::KEY_MACRO19), - "Macro20" => Some(EV_KEY::KEY_MACRO20), - "Macro21" => Some(EV_KEY::KEY_MACRO21), - "Macro22" => Some(EV_KEY::KEY_MACRO22), - "Macro23" => Some(EV_KEY::KEY_MACRO23), - "Macro24" => Some(EV_KEY::KEY_MACRO24), - "Macro25" => Some(EV_KEY::KEY_MACRO25), - "Macro26" => Some(EV_KEY::KEY_MACRO26), - "Macro27" => Some(EV_KEY::KEY_MACRO27), - "Macro28" => Some(EV_KEY::KEY_MACRO28), - "Macro29" => Some(EV_KEY::KEY_MACRO29), - "Macro30" => Some(EV_KEY::KEY_MACRO30), - "Macro_record_start" => Some(EV_KEY::KEY_MACRO_RECORD_START), - "Macro_record_stop" => Some(EV_KEY::KEY_MACRO_RECORD_STOP), - "Macro_preset_cycle" => Some(EV_KEY::KEY_MACRO_PRESET_CYCLE), - "Macro_preset1" => Some(EV_KEY::KEY_MACRO_PRESET1), - "Macro_preset2" => Some(EV_KEY::KEY_MACRO_PRESET2), - "Macro_preset3" => Some(EV_KEY::KEY_MACRO_PRESET3), - "Kbd_lcd_menu1" => Some(EV_KEY::KEY_KBD_LCD_MENU1), - "Kbd_lcd_menu2" => Some(EV_KEY::KEY_KBD_LCD_MENU2), - "Kbd_lcd_menu3" => Some(EV_KEY::KEY_KBD_LCD_MENU3), - "Kbd_lcd_menu4" => Some(EV_KEY::KEY_KBD_LCD_MENU4), - "Kbd_lcd_menu5" => Some(EV_KEY::KEY_KBD_LCD_MENU5), - "Max" => Some(EV_KEY::KEY_MAX), - "Btn_0" => Some(EV_KEY::BTN_0), - "Btn_1" => Some(EV_KEY::BTN_1), - "Btn_2" => Some(EV_KEY::BTN_2), - "Btn_3" => Some(EV_KEY::BTN_3), - "Btn_4" => Some(EV_KEY::BTN_4), - "Btn_5" => Some(EV_KEY::BTN_5), - "Btn_6" => Some(EV_KEY::BTN_6), - "Btn_7" => Some(EV_KEY::BTN_7), - "Btn_8" => Some(EV_KEY::BTN_8), - "Btn_9" => Some(EV_KEY::BTN_9), - "Btn_left" => Some(EV_KEY::BTN_LEFT), - "Btn_right" => Some(EV_KEY::BTN_RIGHT), - "Btn_middle" => Some(EV_KEY::BTN_MIDDLE), - "Btn_side" => Some(EV_KEY::BTN_SIDE), - "Btn_extra" => Some(EV_KEY::BTN_EXTRA), - "Btn_forward" => Some(EV_KEY::BTN_FORWARD), - "Btn_back" => Some(EV_KEY::BTN_BACK), - "Btn_task" => Some(EV_KEY::BTN_TASK), - "Btn_trigger" => Some(EV_KEY::BTN_TRIGGER), - "Btn_thumb" => Some(EV_KEY::BTN_THUMB), - "Btn_thumb2" => Some(EV_KEY::BTN_THUMB2), - "Btn_top" => Some(EV_KEY::BTN_TOP), - "Btn_top2" => Some(EV_KEY::BTN_TOP2), - "Btn_pinkie" => Some(EV_KEY::BTN_PINKIE), - "Btn_base" => Some(EV_KEY::BTN_BASE), - "Btn_base2" => Some(EV_KEY::BTN_BASE2), - "Btn_base3" => Some(EV_KEY::BTN_BASE3), - "Btn_base4" => Some(EV_KEY::BTN_BASE4), - "Btn_base5" => Some(EV_KEY::BTN_BASE5), - "Btn_base6" => Some(EV_KEY::BTN_BASE6), - "Btn_dead" => Some(EV_KEY::BTN_DEAD), - "Btn_south" => Some(EV_KEY::BTN_SOUTH), - "Btn_east" => Some(EV_KEY::BTN_EAST), - "Btn_c" => Some(EV_KEY::BTN_C), - "Btn_north" => Some(EV_KEY::BTN_NORTH), - "Btn_west" => Some(EV_KEY::BTN_WEST), - "Btn_z" => Some(EV_KEY::BTN_Z), - "Btn_tl" => Some(EV_KEY::BTN_TL), - "Btn_tr" => Some(EV_KEY::BTN_TR), - "Btn_tl2" => Some(EV_KEY::BTN_TL2), - "Btn_tr2" => Some(EV_KEY::BTN_TR2), - "Btn_select" => Some(EV_KEY::BTN_SELECT), - "Btn_start" => Some(EV_KEY::BTN_START), - "Btn_mode" => Some(EV_KEY::BTN_MODE), - "Btn_thumbl" => Some(EV_KEY::BTN_THUMBL), - "Btn_thumbr" => Some(EV_KEY::BTN_THUMBR), - "Btn_tool_pen" => Some(EV_KEY::BTN_TOOL_PEN), - "Btn_tool_rubber" => Some(EV_KEY::BTN_TOOL_RUBBER), - "Btn_tool_brush" => Some(EV_KEY::BTN_TOOL_BRUSH), - "Btn_tool_pencil" => Some(EV_KEY::BTN_TOOL_PENCIL), - "Btn_tool_airbrush" => Some(EV_KEY::BTN_TOOL_AIRBRUSH), - "Btn_tool_finger" => Some(EV_KEY::BTN_TOOL_FINGER), - "Btn_tool_mouse" => Some(EV_KEY::BTN_TOOL_MOUSE), - "Btn_tool_lens" => Some(EV_KEY::BTN_TOOL_LENS), - "Btn_tool_quinttap" => Some(EV_KEY::BTN_TOOL_QUINTTAP), - "Btn_stylus3" => Some(EV_KEY::BTN_STYLUS3), - "Btn_touch" => Some(EV_KEY::BTN_TOUCH), - "Btn_stylus" => Some(EV_KEY::BTN_STYLUS), - "Btn_stylus2" => Some(EV_KEY::BTN_STYLUS2), - "Btn_tool_doubletap" => Some(EV_KEY::BTN_TOOL_DOUBLETAP), - "Btn_tool_tripletap" => Some(EV_KEY::BTN_TOOL_TRIPLETAP), - "Btn_tool_quadtap" => Some(EV_KEY::BTN_TOOL_QUADTAP), - "Btn_gear_down" => Some(EV_KEY::BTN_GEAR_DOWN), - "Btn_gear_up" => Some(EV_KEY::BTN_GEAR_UP), - "Btn_dpad_up" => Some(EV_KEY::BTN_DPAD_UP), - "Btn_dpad_down" => Some(EV_KEY::BTN_DPAD_DOWN), - "Btn_dpad_left" => Some(EV_KEY::BTN_DPAD_LEFT), - "Btn_dpad_right" => Some(EV_KEY::BTN_DPAD_RIGHT), - "Btn_trigger_happy1" => Some(EV_KEY::BTN_TRIGGER_HAPPY1), - "Btn_trigger_happy2" => Some(EV_KEY::BTN_TRIGGER_HAPPY2), - "Btn_trigger_happy3" => Some(EV_KEY::BTN_TRIGGER_HAPPY3), - "Btn_trigger_happy4" => Some(EV_KEY::BTN_TRIGGER_HAPPY4), - "Btn_trigger_happy5" => Some(EV_KEY::BTN_TRIGGER_HAPPY5), - "Btn_trigger_happy6" => Some(EV_KEY::BTN_TRIGGER_HAPPY6), - "Btn_trigger_happy7" => Some(EV_KEY::BTN_TRIGGER_HAPPY7), - "Btn_trigger_happy8" => Some(EV_KEY::BTN_TRIGGER_HAPPY8), - "Btn_trigger_happy9" => Some(EV_KEY::BTN_TRIGGER_HAPPY9), - "Btn_trigger_happy10" => Some(EV_KEY::BTN_TRIGGER_HAPPY10), - "Btn_trigger_happy11" => Some(EV_KEY::BTN_TRIGGER_HAPPY11), - "Btn_trigger_happy12" => Some(EV_KEY::BTN_TRIGGER_HAPPY12), - "Btn_trigger_happy13" => Some(EV_KEY::BTN_TRIGGER_HAPPY13), - "Btn_trigger_happy14" => Some(EV_KEY::BTN_TRIGGER_HAPPY14), - "Btn_trigger_happy15" => Some(EV_KEY::BTN_TRIGGER_HAPPY15), - "Btn_trigger_happy16" => Some(EV_KEY::BTN_TRIGGER_HAPPY16), - "Btn_trigger_happy17" => Some(EV_KEY::BTN_TRIGGER_HAPPY17), - "Btn_trigger_happy18" => Some(EV_KEY::BTN_TRIGGER_HAPPY18), - "Btn_trigger_happy19" => Some(EV_KEY::BTN_TRIGGER_HAPPY19), - "Btn_trigger_happy20" => Some(EV_KEY::BTN_TRIGGER_HAPPY20), - "Btn_trigger_happy21" => Some(EV_KEY::BTN_TRIGGER_HAPPY21), - "Btn_trigger_happy22" => Some(EV_KEY::BTN_TRIGGER_HAPPY22), - "Btn_trigger_happy23" => Some(EV_KEY::BTN_TRIGGER_HAPPY23), - "Btn_trigger_happy24" => Some(EV_KEY::BTN_TRIGGER_HAPPY24), - "Btn_trigger_happy25" => Some(EV_KEY::BTN_TRIGGER_HAPPY25), - "Btn_trigger_happy26" => Some(EV_KEY::BTN_TRIGGER_HAPPY26), - "Btn_trigger_happy27" => Some(EV_KEY::BTN_TRIGGER_HAPPY27), - "Btn_trigger_happy28" => Some(EV_KEY::BTN_TRIGGER_HAPPY28), - "Btn_trigger_happy29" => Some(EV_KEY::BTN_TRIGGER_HAPPY29), - "Btn_trigger_happy30" => Some(EV_KEY::BTN_TRIGGER_HAPPY30), - "Btn_trigger_happy31" => Some(EV_KEY::BTN_TRIGGER_HAPPY31), - "Btn_trigger_happy32" => Some(EV_KEY::BTN_TRIGGER_HAPPY32), - "Btn_trigger_happy33" => Some(EV_KEY::BTN_TRIGGER_HAPPY33), - "Btn_trigger_happy34" => Some(EV_KEY::BTN_TRIGGER_HAPPY34), - "Btn_trigger_happy35" => Some(EV_KEY::BTN_TRIGGER_HAPPY35), - "Btn_trigger_happy36" => Some(EV_KEY::BTN_TRIGGER_HAPPY36), - "Btn_trigger_happy37" => Some(EV_KEY::BTN_TRIGGER_HAPPY37), - "Btn_trigger_happy38" => Some(EV_KEY::BTN_TRIGGER_HAPPY38), - "Btn_trigger_happy39" => Some(EV_KEY::BTN_TRIGGER_HAPPY39), - "Btn_trigger_happy40" => Some(EV_KEY::BTN_TRIGGER_HAPPY40), + "Reserved" => Some(Key::KEY_RESERVED), + "Escape" => Some(Key::KEY_ESC), + "1" => Some(Key::KEY_1), + "2" => Some(Key::KEY_2), + "3" => Some(Key::KEY_3), + "4" => Some(Key::KEY_4), + "5" => Some(Key::KEY_5), + "6" => Some(Key::KEY_6), + "7" => Some(Key::KEY_7), + "8" => Some(Key::KEY_8), + "9" => Some(Key::KEY_9), + "0" => Some(Key::KEY_0), + "Minus" => Some(Key::KEY_MINUS), + "Equal" => Some(Key::KEY_EQUAL), + "Backspace" => Some(Key::KEY_BACKSPACE), + "Tab" => Some(Key::KEY_TAB), + "q" => Some(Key::KEY_Q), + "w" => Some(Key::KEY_W), + "e" => Some(Key::KEY_E), + "r" => Some(Key::KEY_R), + "t" => Some(Key::KEY_T), + "y" => Some(Key::KEY_Y), + "u" => Some(Key::KEY_U), + "i" => Some(Key::KEY_I), + "o" => Some(Key::KEY_O), + "p" => Some(Key::KEY_P), + "Leftbrace" => Some(Key::KEY_LEFTBRACE), + "Rightbrace" => Some(Key::KEY_RIGHTBRACE), + "Enter" => Some(Key::KEY_ENTER), + "Leftctrl" => Some(Key::KEY_LEFTCTRL), + "a" => Some(Key::KEY_A), + "s" => Some(Key::KEY_S), + "d" => Some(Key::KEY_D), + "f" => Some(Key::KEY_F), + "g" => Some(Key::KEY_G), + "h" => Some(Key::KEY_H), + "j" => Some(Key::KEY_J), + "k" => Some(Key::KEY_K), + "l" => Some(Key::KEY_L), + "Semicolon" => Some(Key::KEY_SEMICOLON), + "Apostrophe" => Some(Key::KEY_APOSTROPHE), + "Grave" => Some(Key::KEY_GRAVE), + "Leftshift" => Some(Key::KEY_LEFTSHIFT), + "Backslash" => Some(Key::KEY_BACKSLASH), + "z" => Some(Key::KEY_Z), + "x" => Some(Key::KEY_X), + "c" => Some(Key::KEY_C), + "v" => Some(Key::KEY_V), + "b" => Some(Key::KEY_B), + "n" => Some(Key::KEY_N), + "m" => Some(Key::KEY_M), + "Comma" => Some(Key::KEY_COMMA), + "Dot" => Some(Key::KEY_DOT), + "Slash" => Some(Key::KEY_SLASH), + "Rightshift" => Some(Key::KEY_RIGHTSHIFT), + "Kpasterisk" => Some(Key::KEY_KPASTERISK), + "Leftalt" => Some(Key::KEY_LEFTALT), + "Space" => Some(Key::KEY_SPACE), + "Capslock" => Some(Key::KEY_CAPSLOCK), + "F1" => Some(Key::KEY_F1), + "F2" => Some(Key::KEY_F2), + "F3" => Some(Key::KEY_F3), + "F4" => Some(Key::KEY_F4), + "F5" => Some(Key::KEY_F5), + "F6" => Some(Key::KEY_F6), + "F7" => Some(Key::KEY_F7), + "F8" => Some(Key::KEY_F8), + "F9" => Some(Key::KEY_F9), + "F10" => Some(Key::KEY_F10), + "Numlock" => Some(Key::KEY_NUMLOCK), + "Scrolllock" => Some(Key::KEY_SCROLLLOCK), + "Kp7" => Some(Key::KEY_KP7), + "Kp8" => Some(Key::KEY_KP8), + "Kp9" => Some(Key::KEY_KP9), + "Kpminus" => Some(Key::KEY_KPMINUS), + "Kp4" => Some(Key::KEY_KP4), + "Kp5" => Some(Key::KEY_KP5), + "Kp6" => Some(Key::KEY_KP6), + "Kpplus" => Some(Key::KEY_KPPLUS), + "Kp1" => Some(Key::KEY_KP1), + "Kp2" => Some(Key::KEY_KP2), + "Kp3" => Some(Key::KEY_KP3), + "Kp0" => Some(Key::KEY_KP0), + "Kpdot" => Some(Key::KEY_KPDOT), + "Zenkakuhankaku" => Some(Key::KEY_ZENKAKUHANKAKU), + "102nd" => Some(Key::KEY_102ND), + "F11" => Some(Key::KEY_F11), + "F12" => Some(Key::KEY_F12), + "Ro" => Some(Key::KEY_RO), + "Katakana" => Some(Key::KEY_KATAKANA), + "Hiragana" => Some(Key::KEY_HIRAGANA), + "Henkan" => Some(Key::KEY_HENKAN), + "Katakanahiragana" => Some(Key::KEY_KATAKANAHIRAGANA), + "Muhenkan" => Some(Key::KEY_MUHENKAN), + "Kpjpcomma" => Some(Key::KEY_KPJPCOMMA), + "Kpenter" => Some(Key::KEY_KPENTER), + "Rightctrl" => Some(Key::KEY_RIGHTCTRL), + "Kpslash" => Some(Key::KEY_KPSLASH), + "Sysrq" => Some(Key::KEY_SYSRQ), + "Rightalt" => Some(Key::KEY_RIGHTALT), + "Linefeed" => Some(Key::KEY_LINEFEED), + "Home" => Some(Key::KEY_HOME), + "Up" => Some(Key::KEY_UP), + "Pageup" => Some(Key::KEY_PAGEUP), + "Left" => Some(Key::KEY_LEFT), + "Right" => Some(Key::KEY_RIGHT), + "End" => Some(Key::KEY_END), + "Down" => Some(Key::KEY_DOWN), + "Pagedown" => Some(Key::KEY_PAGEDOWN), + "Insert" => Some(Key::KEY_INSERT), + "Delete" => Some(Key::KEY_DELETE), + "Macro" => Some(Key::KEY_MACRO), + "Mute" => Some(Key::KEY_MUTE), + "Volumedown" => Some(Key::KEY_VOLUMEDOWN), + "Volumeup" => Some(Key::KEY_VOLUMEUP), + "Power" => Some(Key::KEY_POWER), + "Kpequal" => Some(Key::KEY_KPEQUAL), + "Kpplusminus" => Some(Key::KEY_KPPLUSMINUS), + "Pause" => Some(Key::KEY_PAUSE), + "Scale" => Some(Key::KEY_SCALE), + "Kpcomma" => Some(Key::KEY_KPCOMMA), + "Hangeul" => Some(Key::KEY_HANGEUL), + "Hanja" => Some(Key::KEY_HANJA), + "Yen" => Some(Key::KEY_YEN), + "Leftmeta" => Some(Key::KEY_LEFTMETA), + "Rightmeta" => Some(Key::KEY_RIGHTMETA), + "Compose" => Some(Key::KEY_COMPOSE), + "Stop" => Some(Key::KEY_STOP), + "Again" => Some(Key::KEY_AGAIN), + "Props" => Some(Key::KEY_PROPS), + "Undo" => Some(Key::KEY_UNDO), + "Front" => Some(Key::KEY_FRONT), + "Copy" => Some(Key::KEY_COPY), + "Open" => Some(Key::KEY_OPEN), + "Paste" => Some(Key::KEY_PASTE), + "Find" => Some(Key::KEY_FIND), + "Cut" => Some(Key::KEY_CUT), + "Help" => Some(Key::KEY_HELP), + "Menu" => Some(Key::KEY_MENU), + "Calc" => Some(Key::KEY_CALC), + "Setup" => Some(Key::KEY_SETUP), + "Sleep" => Some(Key::KEY_SLEEP), + "Wakeup" => Some(Key::KEY_WAKEUP), + "File" => Some(Key::KEY_FILE), + "Sendfile" => Some(Key::KEY_SENDFILE), + "Deletefile" => Some(Key::KEY_DELETEFILE), + "Xfer" => Some(Key::KEY_XFER), + "Prog1" => Some(Key::KEY_PROG1), + "Prog2" => Some(Key::KEY_PROG2), + "Www" => Some(Key::KEY_WWW), + "Msdos" => Some(Key::KEY_MSDOS), + "Coffee" => Some(Key::KEY_COFFEE), + "Rotate_display" => Some(Key::KEY_ROTATE_DISPLAY), + "Cyclewindows" => Some(Key::KEY_CYCLEWINDOWS), + "Mail" => Some(Key::KEY_MAIL), + "Bookmarks" => Some(Key::KEY_BOOKMARKS), + "Computer" => Some(Key::KEY_COMPUTER), + "Back" => Some(Key::KEY_BACK), + "Forward" => Some(Key::KEY_FORWARD), + "Closecd" => Some(Key::KEY_CLOSECD), + "Ejectcd" => Some(Key::KEY_EJECTCD), + "Ejectclosecd" => Some(Key::KEY_EJECTCLOSECD), + "Nextsong" => Some(Key::KEY_NEXTSONG), + "Playpause" => Some(Key::KEY_PLAYPAUSE), + "Previoussong" => Some(Key::KEY_PREVIOUSSONG), + "Stopcd" => Some(Key::KEY_STOPCD), + "Record" => Some(Key::KEY_RECORD), + "Rewind" => Some(Key::KEY_REWIND), + "Phone" => Some(Key::KEY_PHONE), + "Iso" => Some(Key::KEY_ISO), + "Config" => Some(Key::KEY_CONFIG), + "Homepage" => Some(Key::KEY_HOMEPAGE), + "Refresh" => Some(Key::KEY_REFRESH), + "Exit" => Some(Key::KEY_EXIT), + "Move" => Some(Key::KEY_MOVE), + "Edit" => Some(Key::KEY_EDIT), + "Scrollup" => Some(Key::KEY_SCROLLUP), + "Scrolldown" => Some(Key::KEY_SCROLLDOWN), + "Kpleftparen" => Some(Key::KEY_KPLEFTPAREN), + "Kprightparen" => Some(Key::KEY_KPRIGHTPAREN), + "New" => Some(Key::KEY_NEW), + "Redo" => Some(Key::KEY_REDO), + "F13" => Some(Key::KEY_F13), + "F14" => Some(Key::KEY_F14), + "F15" => Some(Key::KEY_F15), + "F16" => Some(Key::KEY_F16), + "F17" => Some(Key::KEY_F17), + "F18" => Some(Key::KEY_F18), + "F19" => Some(Key::KEY_F19), + "F20" => Some(Key::KEY_F20), + "F21" => Some(Key::KEY_F21), + "F22" => Some(Key::KEY_F22), + "F23" => Some(Key::KEY_F23), + "F24" => Some(Key::KEY_F24), + "Playcd" => Some(Key::KEY_PLAYCD), + "Pausecd" => Some(Key::KEY_PAUSECD), + "Prog3" => Some(Key::KEY_PROG3), + "Prog4" => Some(Key::KEY_PROG4), + "Dashboard" => Some(Key::KEY_DASHBOARD), + "Suspend" => Some(Key::KEY_SUSPEND), + "Close" => Some(Key::KEY_CLOSE), + "Play" => Some(Key::KEY_PLAY), + "Fastforward" => Some(Key::KEY_FASTFORWARD), + "Bassboost" => Some(Key::KEY_BASSBOOST), + "Print" => Some(Key::KEY_PRINT), + "Hp" => Some(Key::KEY_HP), + "Camera" => Some(Key::KEY_CAMERA), + "Sound" => Some(Key::KEY_SOUND), + "Question" => Some(Key::KEY_QUESTION), + "Email" => Some(Key::KEY_EMAIL), + "Chat" => Some(Key::KEY_CHAT), + "Search" => Some(Key::KEY_SEARCH), + "Connect" => Some(Key::KEY_CONNECT), + "Finance" => Some(Key::KEY_FINANCE), + "Sport" => Some(Key::KEY_SPORT), + "Shop" => Some(Key::KEY_SHOP), + "Alterase" => Some(Key::KEY_ALTERASE), + "Cancel" => Some(Key::KEY_CANCEL), + "Brightnessdown" => Some(Key::KEY_BRIGHTNESSDOWN), + "Brightnessup" => Some(Key::KEY_BRIGHTNESSUP), + "Media" => Some(Key::KEY_MEDIA), + "Switchvideomode" => Some(Key::KEY_SWITCHVIDEOMODE), + "Kbdillumtoggle" => Some(Key::KEY_KBDILLUMTOGGLE), + "Kbdillumdown" => Some(Key::KEY_KBDILLUMDOWN), + "Kbdillumup" => Some(Key::KEY_KBDILLUMUP), + "Send" => Some(Key::KEY_SEND), + "Reply" => Some(Key::KEY_REPLY), + "Forwardmail" => Some(Key::KEY_FORWARDMAIL), + "Save" => Some(Key::KEY_SAVE), + "Documents" => Some(Key::KEY_DOCUMENTS), + "Battery" => Some(Key::KEY_BATTERY), + "Bluetooth" => Some(Key::KEY_BLUETOOTH), + "Wlan" => Some(Key::KEY_WLAN), + "Uwb" => Some(Key::KEY_UWB), + "Unknown" => Some(Key::KEY_UNKNOWN), + "Video_next" => Some(Key::KEY_VIDEO_NEXT), + "Video_prev" => Some(Key::KEY_VIDEO_PREV), + "Brightness_cycle" => Some(Key::KEY_BRIGHTNESS_CYCLE), + "Brightness_auto" => Some(Key::KEY_BRIGHTNESS_AUTO), + "Display_off" => Some(Key::KEY_DISPLAY_OFF), + "Wwan" => Some(Key::KEY_WWAN), + "Rfkill" => Some(Key::KEY_RFKILL), + "Micmute" => Some(Key::KEY_MICMUTE), + "Ok" => Some(Key::KEY_OK), + "Select" => Some(Key::KEY_SELECT), + "Goto" => Some(Key::KEY_GOTO), + "Clear" => Some(Key::KEY_CLEAR), + "Power2" => Some(Key::KEY_POWER2), + "Option" => Some(Key::KEY_OPTION), + "Info" => Some(Key::KEY_INFO), + "Time" => Some(Key::KEY_TIME), + "Vendor" => Some(Key::KEY_VENDOR), + "Archive" => Some(Key::KEY_ARCHIVE), + "Program" => Some(Key::KEY_PROGRAM), + "Channel" => Some(Key::KEY_CHANNEL), + "Favorites" => Some(Key::KEY_FAVORITES), + "Epg" => Some(Key::KEY_EPG), + "Pvr" => Some(Key::KEY_PVR), + "Mhp" => Some(Key::KEY_MHP), + "Language" => Some(Key::KEY_LANGUAGE), + "Title" => Some(Key::KEY_TITLE), + "Subtitle" => Some(Key::KEY_SUBTITLE), + "Angle" => Some(Key::KEY_ANGLE), + "Full_screen" => Some(Key::KEY_FULL_SCREEN), + "Mode" => Some(Key::KEY_MODE), + "Keyboard" => Some(Key::KEY_KEYBOARD), + "Screen" => Some(Key::KEY_SCREEN), + "Pc" => Some(Key::KEY_PC), + "Tv" => Some(Key::KEY_TV), + "Tv2" => Some(Key::KEY_TV2), + "Vcr" => Some(Key::KEY_VCR), + "Vcr2" => Some(Key::KEY_VCR2), + "Sat" => Some(Key::KEY_SAT), + "Sat2" => Some(Key::KEY_SAT2), + "Cd" => Some(Key::KEY_CD), + "Tape" => Some(Key::KEY_TAPE), + "Radio" => Some(Key::KEY_RADIO), + "Tuner" => Some(Key::KEY_TUNER), + "Player" => Some(Key::KEY_PLAYER), + "Text" => Some(Key::KEY_TEXT), + "Dvd" => Some(Key::KEY_DVD), + "Aux" => Some(Key::KEY_AUX), + "Mp3" => Some(Key::KEY_MP3), + "Audio" => Some(Key::KEY_AUDIO), + "Video" => Some(Key::KEY_VIDEO), + "Directory" => Some(Key::KEY_DIRECTORY), + "List" => Some(Key::KEY_LIST), + "Memo" => Some(Key::KEY_MEMO), + "Calendar" => Some(Key::KEY_CALENDAR), + "Red" => Some(Key::KEY_RED), + "Green" => Some(Key::KEY_GREEN), + "Yellow" => Some(Key::KEY_YELLOW), + "Blue" => Some(Key::KEY_BLUE), + "Channelup" => Some(Key::KEY_CHANNELUP), + "Channeldown" => Some(Key::KEY_CHANNELDOWN), + "First" => Some(Key::KEY_FIRST), + "Last" => Some(Key::KEY_LAST), + "Ab" => Some(Key::KEY_AB), + "Next" => Some(Key::KEY_NEXT), + "Restart" => Some(Key::KEY_RESTART), + "Slow" => Some(Key::KEY_SLOW), + "Shuffle" => Some(Key::KEY_SHUFFLE), + "Break" => Some(Key::KEY_BREAK), + "Previous" => Some(Key::KEY_PREVIOUS), + "Digits" => Some(Key::KEY_DIGITS), + "Teen" => Some(Key::KEY_TEEN), + "Twen" => Some(Key::KEY_TWEN), + "Videophone" => Some(Key::KEY_VIDEOPHONE), + "Games" => Some(Key::KEY_GAMES), + "Zoomin" => Some(Key::KEY_ZOOMIN), + "Zoomout" => Some(Key::KEY_ZOOMOUT), + "Zoomreset" => Some(Key::KEY_ZOOMRESET), + "Wordprocessor" => Some(Key::KEY_WORDPROCESSOR), + "Editor" => Some(Key::KEY_EDITOR), + "Spreadsheet" => Some(Key::KEY_SPREADSHEET), + "Graphicseditor" => Some(Key::KEY_GRAPHICSEDITOR), + "Presentation" => Some(Key::KEY_PRESENTATION), + "Database" => Some(Key::KEY_DATABASE), + "News" => Some(Key::KEY_NEWS), + "Voicemail" => Some(Key::KEY_VOICEMAIL), + "Addressbook" => Some(Key::KEY_ADDRESSBOOK), + "Messenger" => Some(Key::KEY_MESSENGER), + "Displaytoggle" => Some(Key::KEY_DISPLAYTOGGLE), + "Spellcheck" => Some(Key::KEY_SPELLCHECK), + "Logoff" => Some(Key::KEY_LOGOFF), + "Dollar" => Some(Key::KEY_DOLLAR), + "Euro" => Some(Key::KEY_EURO), + "Frameback" => Some(Key::KEY_FRAMEBACK), + "Frameforward" => Some(Key::KEY_FRAMEFORWARD), + "Context_menu" => Some(Key::KEY_CONTEXT_MENU), + "Media_repeat" => Some(Key::KEY_MEDIA_REPEAT), + "10channelsup" => Some(Key::KEY_10CHANNELSUP), + "10channelsdown" => Some(Key::KEY_10CHANNELSDOWN), + "Images" => Some(Key::KEY_IMAGES), + "Del_eol" => Some(Key::KEY_DEL_EOL), + "Del_eos" => Some(Key::KEY_DEL_EOS), + "Ins_line" => Some(Key::KEY_INS_LINE), + "Del_line" => Some(Key::KEY_DEL_LINE), + "Fn" => Some(Key::KEY_FN), + "Fn_esc" => Some(Key::KEY_FN_ESC), + "Fn_f1" => Some(Key::KEY_FN_F1), + "Fn_f2" => Some(Key::KEY_FN_F2), + "Fn_f3" => Some(Key::KEY_FN_F3), + "Fn_f4" => Some(Key::KEY_FN_F4), + "Fn_f5" => Some(Key::KEY_FN_F5), + "Fn_f6" => Some(Key::KEY_FN_F6), + "Fn_f7" => Some(Key::KEY_FN_F7), + "Fn_f8" => Some(Key::KEY_FN_F8), + "Fn_f9" => Some(Key::KEY_FN_F9), + "Fn_f10" => Some(Key::KEY_FN_F10), + "Fn_f11" => Some(Key::KEY_FN_F11), + "Fn_f12" => Some(Key::KEY_FN_F12), + "Fn_1" => Some(Key::KEY_FN_1), + "Fn_2" => Some(Key::KEY_FN_2), + "Fn_d" => Some(Key::KEY_FN_D), + "Fn_e" => Some(Key::KEY_FN_E), + "Fn_f" => Some(Key::KEY_FN_F), + "Fn_s" => Some(Key::KEY_FN_S), + "Fn_b" => Some(Key::KEY_FN_B), + "Brl_dot1" => Some(Key::KEY_BRL_DOT1), + "Brl_dot2" => Some(Key::KEY_BRL_DOT2), + "Brl_dot3" => Some(Key::KEY_BRL_DOT3), + "Brl_dot4" => Some(Key::KEY_BRL_DOT4), + "Brl_dot5" => Some(Key::KEY_BRL_DOT5), + "Brl_dot6" => Some(Key::KEY_BRL_DOT6), + "Brl_dot7" => Some(Key::KEY_BRL_DOT7), + "Brl_dot8" => Some(Key::KEY_BRL_DOT8), + "Brl_dot9" => Some(Key::KEY_BRL_DOT9), + "Brl_dot10" => Some(Key::KEY_BRL_DOT10), + "Numeric_0" => Some(Key::KEY_NUMERIC_0), + "Numeric_1" => Some(Key::KEY_NUMERIC_1), + "Numeric_2" => Some(Key::KEY_NUMERIC_2), + "Numeric_3" => Some(Key::KEY_NUMERIC_3), + "Numeric_4" => Some(Key::KEY_NUMERIC_4), + "Numeric_5" => Some(Key::KEY_NUMERIC_5), + "Numeric_6" => Some(Key::KEY_NUMERIC_6), + "Numeric_7" => Some(Key::KEY_NUMERIC_7), + "Numeric_8" => Some(Key::KEY_NUMERIC_8), + "Numeric_9" => Some(Key::KEY_NUMERIC_9), + "Numeric_star" => Some(Key::KEY_NUMERIC_STAR), + "Numeric_pound" => Some(Key::KEY_NUMERIC_POUND), + "Numeric_a" => Some(Key::KEY_NUMERIC_A), + "Numeric_b" => Some(Key::KEY_NUMERIC_B), + "Numeric_c" => Some(Key::KEY_NUMERIC_C), + "Numeric_d" => Some(Key::KEY_NUMERIC_D), + "Camera_focus" => Some(Key::KEY_CAMERA_FOCUS), + "Wps_button" => Some(Key::KEY_WPS_BUTTON), + "Touchpad_toggle" => Some(Key::KEY_TOUCHPAD_TOGGLE), + "Touchpad_on" => Some(Key::KEY_TOUCHPAD_ON), + "Touchpad_off" => Some(Key::KEY_TOUCHPAD_OFF), + "Camera_zoomin" => Some(Key::KEY_CAMERA_ZOOMIN), + "Camera_zoomout" => Some(Key::KEY_CAMERA_ZOOMOUT), + "Camera_up" => Some(Key::KEY_CAMERA_UP), + "Camera_down" => Some(Key::KEY_CAMERA_DOWN), + "Camera_left" => Some(Key::KEY_CAMERA_LEFT), + "Camera_right" => Some(Key::KEY_CAMERA_RIGHT), + "Attendant_on" => Some(Key::KEY_ATTENDANT_ON), + "Attendant_off" => Some(Key::KEY_ATTENDANT_OFF), + "Attendant_toggle" => Some(Key::KEY_ATTENDANT_TOGGLE), + "Lights_toggle" => Some(Key::KEY_LIGHTS_TOGGLE), + "Als_toggle" => Some(Key::KEY_ALS_TOGGLE), + "Buttonconfig" => Some(Key::KEY_BUTTONCONFIG), + "Taskmanager" => Some(Key::KEY_TASKMANAGER), + "Journal" => Some(Key::KEY_JOURNAL), + "Controlpanel" => Some(Key::KEY_CONTROLPANEL), + "Appselect" => Some(Key::KEY_APPSELECT), + "Screensaver" => Some(Key::KEY_SCREENSAVER), + "Voicecommand" => Some(Key::KEY_VOICECOMMAND), + "Assistant" => Some(Key::KEY_ASSISTANT), + "Kbd_layout_next" => Some(Key::KEY_KBD_LAYOUT_NEXT), + "Brightness_min" => Some(Key::KEY_BRIGHTNESS_MIN), + "Brightness_max" => Some(Key::KEY_BRIGHTNESS_MAX), + "Kbdinputassist_prev" => Some(Key::KEY_KBDINPUTASSIST_PREV), + "Kbdinputassist_next" => Some(Key::KEY_KBDINPUTASSIST_NEXT), + "Kbdinputassist_prevgroup" => Some(Key::KEY_KBDINPUTASSIST_PREVGROUP), + "Kbdinputassist_nextgroup" => Some(Key::KEY_KBDINPUTASSIST_NEXTGROUP), + "Kbdinputassist_accept" => Some(Key::KEY_KBDINPUTASSIST_ACCEPT), + "Kbdinputassist_cancel" => Some(Key::KEY_KBDINPUTASSIST_CANCEL), + "Right_up" => Some(Key::KEY_RIGHT_UP), + "Right_down" => Some(Key::KEY_RIGHT_DOWN), + "Left_up" => Some(Key::KEY_LEFT_UP), + "Left_down" => Some(Key::KEY_LEFT_DOWN), + "Root_menu" => Some(Key::KEY_ROOT_MENU), + "Media_top_menu" => Some(Key::KEY_MEDIA_TOP_MENU), + "Numeric_11" => Some(Key::KEY_NUMERIC_11), + "Numeric_12" => Some(Key::KEY_NUMERIC_12), + "Audio_desc" => Some(Key::KEY_AUDIO_DESC), + "3d_mode" => Some(Key::KEY_3D_MODE), + "Next_favorite" => Some(Key::KEY_NEXT_FAVORITE), + "Stop_record" => Some(Key::KEY_STOP_RECORD), + "Pause_record" => Some(Key::KEY_PAUSE_RECORD), + "Vod" => Some(Key::KEY_VOD), + "Unmute" => Some(Key::KEY_UNMUTE), + "Fastreverse" => Some(Key::KEY_FASTREVERSE), + "Slowreverse" => Some(Key::KEY_SLOWREVERSE), + "Data" => Some(Key::KEY_DATA), + "Onscreen_keyboard" => Some(Key::KEY_ONSCREEN_KEYBOARD), + "Privacy_screen_toggle" => Some(Key::KEY_PRIVACY_SCREEN_TOGGLE), + "Selective_screenshot" => Some(Key::KEY_SELECTIVE_SCREENSHOT), + "Btn_0" => Some(Key::BTN_0), + "Btn_1" => Some(Key::BTN_1), + "Btn_2" => Some(Key::BTN_2), + "Btn_3" => Some(Key::BTN_3), + "Btn_4" => Some(Key::BTN_4), + "Btn_5" => Some(Key::BTN_5), + "Btn_6" => Some(Key::BTN_6), + "Btn_7" => Some(Key::BTN_7), + "Btn_8" => Some(Key::BTN_8), + "Btn_9" => Some(Key::BTN_9), + "Btn_left" => Some(Key::BTN_LEFT), + "Btn_right" => Some(Key::BTN_RIGHT), + "Btn_middle" => Some(Key::BTN_MIDDLE), + "Btn_side" => Some(Key::BTN_SIDE), + "Btn_extra" => Some(Key::BTN_EXTRA), + "Btn_forward" => Some(Key::BTN_FORWARD), + "Btn_back" => Some(Key::BTN_BACK), + "Btn_task" => Some(Key::BTN_TASK), + "Btn_trigger" => Some(Key::BTN_TRIGGER), + "Btn_thumb" => Some(Key::BTN_THUMB), + "Btn_thumb2" => Some(Key::BTN_THUMB2), + "Btn_top" => Some(Key::BTN_TOP), + "Btn_top2" => Some(Key::BTN_TOP2), + "Btn_pinkie" => Some(Key::BTN_PINKIE), + "Btn_base" => Some(Key::BTN_BASE), + "Btn_base2" => Some(Key::BTN_BASE2), + "Btn_base3" => Some(Key::BTN_BASE3), + "Btn_base4" => Some(Key::BTN_BASE4), + "Btn_base5" => Some(Key::BTN_BASE5), + "Btn_base6" => Some(Key::BTN_BASE6), + "Btn_dead" => Some(Key::BTN_DEAD), + "Btn_south" => Some(Key::BTN_SOUTH), + "Btn_east" => Some(Key::BTN_EAST), + "Btn_c" => Some(Key::BTN_C), + "Btn_north" => Some(Key::BTN_NORTH), + "Btn_west" => Some(Key::BTN_WEST), + "Btn_z" => Some(Key::BTN_Z), + "Btn_tl" => Some(Key::BTN_TL), + "Btn_tr" => Some(Key::BTN_TR), + "Btn_tl2" => Some(Key::BTN_TL2), + "Btn_tr2" => Some(Key::BTN_TR2), + "Btn_select" => Some(Key::BTN_SELECT), + "Btn_start" => Some(Key::BTN_START), + "Btn_mode" => Some(Key::BTN_MODE), + "Btn_thumbl" => Some(Key::BTN_THUMBL), + "Btn_thumbr" => Some(Key::BTN_THUMBR), + "Btn_tool_pen" => Some(Key::BTN_TOOL_PEN), + "Btn_tool_rubber" => Some(Key::BTN_TOOL_RUBBER), + "Btn_tool_brush" => Some(Key::BTN_TOOL_BRUSH), + "Btn_tool_pencil" => Some(Key::BTN_TOOL_PENCIL), + "Btn_tool_airbrush" => Some(Key::BTN_TOOL_AIRBRUSH), + "Btn_tool_finger" => Some(Key::BTN_TOOL_FINGER), + "Btn_tool_mouse" => Some(Key::BTN_TOOL_MOUSE), + "Btn_tool_lens" => Some(Key::BTN_TOOL_LENS), + "Btn_tool_quinttap" => Some(Key::BTN_TOOL_QUINTTAP), + "Btn_touch" => Some(Key::BTN_TOUCH), + "Btn_stylus" => Some(Key::BTN_STYLUS), + "Btn_stylus2" => Some(Key::BTN_STYLUS2), + "Btn_tool_doubletap" => Some(Key::BTN_TOOL_DOUBLETAP), + "Btn_tool_tripletap" => Some(Key::BTN_TOOL_TRIPLETAP), + "Btn_tool_quadtap" => Some(Key::BTN_TOOL_QUADTAP), + "Btn_gear_down" => Some(Key::BTN_GEAR_DOWN), + "Btn_gear_up" => Some(Key::BTN_GEAR_UP), + "Btn_dpad_up" => Some(Key::BTN_DPAD_UP), + "Btn_dpad_down" => Some(Key::BTN_DPAD_DOWN), + "Btn_dpad_left" => Some(Key::BTN_DPAD_LEFT), + "Btn_dpad_right" => Some(Key::BTN_DPAD_RIGHT), + "Btn_trigger_happy1" => Some(Key::BTN_TRIGGER_HAPPY1), + "Btn_trigger_happy2" => Some(Key::BTN_TRIGGER_HAPPY2), + "Btn_trigger_happy3" => Some(Key::BTN_TRIGGER_HAPPY3), + "Btn_trigger_happy4" => Some(Key::BTN_TRIGGER_HAPPY4), + "Btn_trigger_happy5" => Some(Key::BTN_TRIGGER_HAPPY5), + "Btn_trigger_happy6" => Some(Key::BTN_TRIGGER_HAPPY6), + "Btn_trigger_happy7" => Some(Key::BTN_TRIGGER_HAPPY7), + "Btn_trigger_happy8" => Some(Key::BTN_TRIGGER_HAPPY8), + "Btn_trigger_happy9" => Some(Key::BTN_TRIGGER_HAPPY9), + "Btn_trigger_happy10" => Some(Key::BTN_TRIGGER_HAPPY10), + "Btn_trigger_happy11" => Some(Key::BTN_TRIGGER_HAPPY11), + "Btn_trigger_happy12" => Some(Key::BTN_TRIGGER_HAPPY12), + "Btn_trigger_happy13" => Some(Key::BTN_TRIGGER_HAPPY13), + "Btn_trigger_happy14" => Some(Key::BTN_TRIGGER_HAPPY14), + "Btn_trigger_happy15" => Some(Key::BTN_TRIGGER_HAPPY15), + "Btn_trigger_happy16" => Some(Key::BTN_TRIGGER_HAPPY16), + "Btn_trigger_happy17" => Some(Key::BTN_TRIGGER_HAPPY17), + "Btn_trigger_happy18" => Some(Key::BTN_TRIGGER_HAPPY18), + "Btn_trigger_happy19" => Some(Key::BTN_TRIGGER_HAPPY19), + "Btn_trigger_happy20" => Some(Key::BTN_TRIGGER_HAPPY20), + "Btn_trigger_happy21" => Some(Key::BTN_TRIGGER_HAPPY21), + "Btn_trigger_happy22" => Some(Key::BTN_TRIGGER_HAPPY22), + "Btn_trigger_happy23" => Some(Key::BTN_TRIGGER_HAPPY23), + "Btn_trigger_happy24" => Some(Key::BTN_TRIGGER_HAPPY24), + "Btn_trigger_happy25" => Some(Key::BTN_TRIGGER_HAPPY25), + "Btn_trigger_happy26" => Some(Key::BTN_TRIGGER_HAPPY26), + "Btn_trigger_happy27" => Some(Key::BTN_TRIGGER_HAPPY27), + "Btn_trigger_happy28" => Some(Key::BTN_TRIGGER_HAPPY28), + "Btn_trigger_happy29" => Some(Key::BTN_TRIGGER_HAPPY29), + "Btn_trigger_happy30" => Some(Key::BTN_TRIGGER_HAPPY30), + "Btn_trigger_happy31" => Some(Key::BTN_TRIGGER_HAPPY31), + "Btn_trigger_happy32" => Some(Key::BTN_TRIGGER_HAPPY32), + "Btn_trigger_happy33" => Some(Key::BTN_TRIGGER_HAPPY33), + "Btn_trigger_happy34" => Some(Key::BTN_TRIGGER_HAPPY34), + "Btn_trigger_happy35" => Some(Key::BTN_TRIGGER_HAPPY35), + "Btn_trigger_happy36" => Some(Key::BTN_TRIGGER_HAPPY36), + "Btn_trigger_happy37" => Some(Key::BTN_TRIGGER_HAPPY37), + "Btn_trigger_happy38" => Some(Key::BTN_TRIGGER_HAPPY38), + "Btn_trigger_happy39" => Some(Key::BTN_TRIGGER_HAPPY39), + "Btn_trigger_happy40" => Some(Key::BTN_TRIGGER_HAPPY40), _ => None, } } diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index e91546e..98bfc17 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -8,8 +8,7 @@ use crate::errors::{self, LeftError}; use crate::evdev::{EvDev, Task}; use crate::ipc::Pipe; use crate::keysym_lookup::{self, is_modifier, MOD_MASK}; -use evdev_rs::enums::{EventCode, EV_KEY}; -use evdev_rs::InputEvent; +use evdev::{EventType, InputEvent, InputEventKind, Key}; use xdg::BaseDirectories; #[derive(Clone, Copy, Debug)] @@ -42,9 +41,9 @@ pub struct Worker { keybinds: Vec, base_directory: BaseDirectories, - keys_pressed: Vec, + keys_pressed: Vec, mods_pressed: Vec, - eaten_keys: Vec, + eaten_keys: Vec, eaten_mods: Vec, pub evdev: EvDev, @@ -85,7 +84,7 @@ impl Worker { Some(task) = self.evdev.task_receiver.recv() => { match task { Task::KeyboardEvent((path, event)) => { - self.handle_event(path, &event); + self.handle_event(path, event); } Task::KeyboardAdded(path) => { self.evdev.add_device(path); @@ -110,12 +109,12 @@ impl Worker { errors::exit!(Pipe::new(pipe_file).await) } - fn handle_event(&mut self, path: PathBuf, event: &InputEvent) { - let r#type = KeyEventType::from(event.value); + fn handle_event(&mut self, path: PathBuf, event: InputEvent) { + let r#type = KeyEventType::from(event.value()); let mut eaten = false; match r#type { KeyEventType::Release => { - if let EventCode::EV_KEY(key) = event.event_code { + if let InputEventKind::Key(key) = event.kind() { if is_modifier(&key) { if let Ok(modifier) = key.try_into() { self.mods_pressed.retain(|&m| m != modifier); @@ -135,7 +134,7 @@ impl Worker { } KeyEventType::Press => { let mut new_key = false; - if let EventCode::EV_KEY(key) = event.event_code { + if let InputEventKind::Key(key) = event.kind() { if is_modifier(&key) { match key.try_into() { Ok(modifier) if !self.mods_pressed.contains(&modifier) => { @@ -173,12 +172,31 @@ impl Worker { } } - fn pass_event(&self, path: PathBuf, event: &InputEvent) { - // println!("Passing event: {:?}", event); - match self.evdev.devices.get(&path) { - Some(device) => errors::log!(device.write_event(event)), - None => errors::log!(Err(LeftError::UInputNotFound)), + fn pass_event(&mut self, _path: PathBuf, event: InputEvent) { + match self.evdev.device.emit(&[event]) { + Ok(_) => { + tracing::debug!("Successfully sent event: {:?}", event); + } + Err(err) => { + tracing::warn!("Failed to pass event: {:?}", err); + } } + let code = Key::KEY_K.code(); + let down_event = InputEvent::new(EventType::KEY, code, 1); + self.evdev.device.emit(&[down_event]).unwrap(); + println!("Pressed."); + // sleep(Duration::from_secs(2)); + + // alternativeley we can create a InputEvent, which will be any variant of InputEvent + // depending on the type_ value + let up_event = InputEvent::new(EventType::KEY, code, 0); + self.evdev.device.emit(&[up_event]).unwrap(); + println!("Released."); + // sleep(Duration::from_secs(2)); + // match self.evdev.devices.get(&path) { + // Some(device) => errors::log!(device.write_event(event)), + // None => errors::log!(Err(LeftError::UInputNotFound)), + // } } fn check_for_keybind(&self) -> Option { From 318f0e60aa23fba6bc759319c8d2e694f4b6e23a Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Sun, 10 Mar 2024 13:42:47 +0000 Subject: [PATCH 12/16] small clean up, for some reason virtual device doesn't work on laptop/fedora --- lefthk-core/src/evdev.rs | 15 +++----------- lefthk-core/src/worker/mod.rs | 39 +++++------------------------------ 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index de2d75c..1f58d70 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -8,7 +8,7 @@ use crate::errors::{self, LeftError}; #[derive(Debug)] pub enum Task { - KeyboardEvent((PathBuf, InputEvent)), + KeyboardEvent(InputEvent), KeyboardAdded(PathBuf), KeyboardRemoved(PathBuf), } @@ -43,7 +43,7 @@ impl Default for EvDev { let builder = errors::exit!(VirtualDeviceBuilder::new()); - let mut device = builder + let device = builder .name("LeftHK Virtual Keyboard") .input_id(InputId::new(BusType::BUS_I8042, 1, 1, 1)) .with_keys(&keys) @@ -52,10 +52,6 @@ impl Default for EvDev { .unwrap() .build() .unwrap(); - println!("Device: {:?}", device.get_syspath()); - - let devnode = device.enumerate_dev_nodes_blocking().unwrap().next(); - println!("Devnode: {:?}", devnode); let (task_transmitter, task_receiver) = mpsc::channel(128); @@ -97,19 +93,14 @@ impl EvDev { let transmitter = self.task_transmitter.clone(); let mut stream = errors::r#return!(device.into_event_stream()); - let p = path.clone(); tokio::task::spawn(async move { while !guard.is_closed() { match stream.next_event().await { Ok(event) => { - transmitter - .send(Task::KeyboardEvent((p.clone(), event))) - .await - .unwrap(); + transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); } Err(err) => { tracing::warn!("Evdev device stream failed with {:?}", err); - // poll_fn(|cx| guard.poll_closed(cx)).await; break; } } diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index 98bfc17..e6e330e 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -1,14 +1,12 @@ pub mod context; -use std::path::PathBuf; - use crate::child::Children; use crate::config::{command, Keybind}; use crate::errors::{self, LeftError}; use crate::evdev::{EvDev, Task}; use crate::ipc::Pipe; use crate::keysym_lookup::{self, is_modifier, MOD_MASK}; -use evdev::{EventType, InputEvent, InputEventKind, Key}; +use evdev::{InputEvent, InputEventKind, Key}; use xdg::BaseDirectories; #[derive(Clone, Copy, Debug)] @@ -83,8 +81,8 @@ impl Worker { } Some(task) = self.evdev.task_receiver.recv() => { match task { - Task::KeyboardEvent((path, event)) => { - self.handle_event(path, event); + Task::KeyboardEvent(event) => { + self.handle_event(event); } Task::KeyboardAdded(path) => { self.evdev.add_device(path); @@ -109,7 +107,7 @@ impl Worker { errors::exit!(Pipe::new(pipe_file).await) } - fn handle_event(&mut self, path: PathBuf, event: InputEvent) { + fn handle_event(&mut self, event: InputEvent) { let r#type = KeyEventType::from(event.value()); let mut eaten = false; match r#type { @@ -168,35 +166,8 @@ impl Worker { KeyEventType::Repeat | KeyEventType::Unknown => {} } if !eaten { - self.pass_event(path, event); - } - } - - fn pass_event(&mut self, _path: PathBuf, event: InputEvent) { - match self.evdev.device.emit(&[event]) { - Ok(_) => { - tracing::debug!("Successfully sent event: {:?}", event); - } - Err(err) => { - tracing::warn!("Failed to pass event: {:?}", err); - } + let _ = self.evdev.device.emit(&[event]); } - let code = Key::KEY_K.code(); - let down_event = InputEvent::new(EventType::KEY, code, 1); - self.evdev.device.emit(&[down_event]).unwrap(); - println!("Pressed."); - // sleep(Duration::from_secs(2)); - - // alternativeley we can create a InputEvent, which will be any variant of InputEvent - // depending on the type_ value - let up_event = InputEvent::new(EventType::KEY, code, 0); - self.evdev.device.emit(&[up_event]).unwrap(); - println!("Released."); - // sleep(Duration::from_secs(2)); - // match self.evdev.devices.get(&path) { - // Some(device) => errors::log!(device.write_event(event)), - // None => errors::log!(Err(LeftError::UInputNotFound)), - // } } fn check_for_keybind(&self) -> Option { From 7e2ef37fbc36ac9feeb83244db57d3cdd1fbd80a Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Sun, 10 Mar 2024 20:44:45 +0000 Subject: [PATCH 13/16] Handle more errors, issue seems to be under kde --- lefthk-core/src/errors.rs | 13 +++- lefthk-core/src/evdev.rs | 136 ++++++++++++++++++---------------- lefthk-core/src/worker/mod.rs | 4 +- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/lefthk-core/src/errors.rs b/lefthk-core/src/errors.rs index 3a09f4c..97cb0eb 100644 --- a/lefthk-core/src/errors.rs +++ b/lefthk-core/src/errors.rs @@ -49,6 +49,15 @@ pub enum LeftError { #[error("XDG error: {0}.")] XdgBaseDirError(#[from] xdg::BaseDirectoriesError), + #[error("Failed to open device {0}.")] + DeviceOpenFailed(String), + #[error("Failed to grab device {0}.")] + DeviceGrabFailed(String), + #[error("Failed to ungrab device {0}.")] + DeviceUngrabFailed(String), + + #[error("Path could not be converted to str.")] + PathToStrError, #[error("Given String doesn't match with a command.")] UnmatchingCommand, #[error("No command found for keybind.")] @@ -61,6 +70,6 @@ pub enum LeftError { NoConfigFound, #[error("No value set for execution.")] ValueNotFound, - #[error("UInput device could not be found.")] - UInputNotFound, + #[error("Failed to create virtual device, please check that your permissions are setup. (TODO: link to setup page).")] + VirtualDeviceCreationFailed, } diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index 1f58d70..8c8908f 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use std::{collections::HashMap, ffi::OsStr}; use tokio::sync::{mpsc, oneshot}; -use crate::errors::{self, LeftError}; +use crate::errors::{self, LeftError, Result}; #[derive(Debug)] pub enum Task { @@ -21,37 +21,9 @@ pub struct EvDev { _keyboard_watcher: KeyboardWatcher, } -impl Default for EvDev { - fn default() -> Self { - let keys = AttributeSet::from_iter( - (Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code()).map(Key::new), - ); - let relative_axes = evdev::AttributeSet::from_iter([ - RelativeAxisType::REL_WHEEL, - RelativeAxisType::REL_HWHEEL, - RelativeAxisType::REL_X, - RelativeAxisType::REL_Y, - RelativeAxisType::REL_Z, - RelativeAxisType::REL_RX, - RelativeAxisType::REL_RY, - RelativeAxisType::REL_RZ, - RelativeAxisType::REL_DIAL, - RelativeAxisType::REL_MISC, - RelativeAxisType::REL_WHEEL_HI_RES, - RelativeAxisType::REL_HWHEEL_HI_RES, - ]); - - let builder = errors::exit!(VirtualDeviceBuilder::new()); - - let device = builder - .name("LeftHK Virtual Keyboard") - .input_id(InputId::new(BusType::BUS_I8042, 1, 1, 1)) - .with_keys(&keys) - .unwrap() - .with_relative_axes(&relative_axes) - .unwrap() - .build() - .unwrap(); +impl EvDev { + pub fn new() -> Result { + let device = generate_device().map_err(|_| LeftError::VirtualDeviceCreationFailed)?; let (task_transmitter, task_receiver) = mpsc::channel(128); @@ -70,54 +42,90 @@ impl Default for EvDev { match find_keyboards() { Some(devices) => { for device in devices { - evdev.add_device(device); + errors::log!(evdev.add_device(device)); } } None => tracing::warn!("No devices found on intialization."), } - evdev + Ok(evdev) } -} -impl EvDev { - pub fn add_device(&mut self, path: PathBuf) { - tracing::info!("Adding device with path: {:?}", path); - if let Ok(mut device) = Device::open(path.clone()) { - wait_for_all_keys_unpressed(&device); - errors::r#return!(device.grab()); - errors::r#return!(device.ungrab()); - errors::r#return!(device.grab()); - - let (guard, task_guard) = oneshot::channel(); - let transmitter = self.task_transmitter.clone(); - - let mut stream = errors::r#return!(device.into_event_stream()); - tokio::task::spawn(async move { - while !guard.is_closed() { - match stream.next_event().await { - Ok(event) => { - transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); - } - Err(err) => { - tracing::warn!("Evdev device stream failed with {:?}", err); - break; - } + pub fn add_device(&mut self, path: PathBuf) -> Result<()> { + let p = path.clone(); + let path_str = p.to_str().ok_or(LeftError::PathToStrError)?; + tracing::info!("Adding device with path: {:?}", path_str); + let mut device = Device::open(path.clone()) + .map_err(|_| LeftError::DeviceOpenFailed(path_str.to_owned()))?; + wait_for_keys_to_unpress(&device); + device + .grab() + .map_err(|_| LeftError::DeviceGrabFailed(path_str.to_owned()))?; + device + .ungrab() + .map_err(|_| LeftError::DeviceUngrabFailed(path_str.to_owned()))?; + device + .grab() + .map_err(|_| LeftError::DeviceGrabFailed(path_str.to_owned()))?; + + let (guard, task_guard) = oneshot::channel(); + let transmitter = self.task_transmitter.clone(); + + let mut stream = device.into_event_stream()?; + tokio::task::spawn(async move { + while !guard.is_closed() { + match stream.next_event().await { + Ok(event) => { + transmitter.send(Task::KeyboardEvent(event)).await.unwrap(); + } + Err(err) => { + tracing::warn!("Evdev device stream failed with {:?}", err); + break; } } - tracing::info!("Device loop has closed."); - errors::r#return!(stream.device_mut().ungrab()); - }); + } + tracing::info!("Closing loop for device {:?}.", p); + errors::r#return!(stream.device_mut().ungrab()); + }); - self.task_guards.insert(path, task_guard); - } + self.task_guards.insert(path, task_guard); + Ok(()) } + pub fn remove_device(&mut self, path: PathBuf) { tracing::info!("Removing device with path: {:?}", path); self.task_guards.remove(&path); } } +fn generate_device() -> Result { + let keys = AttributeSet::from_iter( + (Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code()).map(Key::new), + ); + + let relative_axes = evdev::AttributeSet::from_iter([ + RelativeAxisType::REL_WHEEL, + RelativeAxisType::REL_HWHEEL, + RelativeAxisType::REL_X, + RelativeAxisType::REL_Y, + RelativeAxisType::REL_Z, + RelativeAxisType::REL_RX, + RelativeAxisType::REL_RY, + RelativeAxisType::REL_RZ, + RelativeAxisType::REL_DIAL, + RelativeAxisType::REL_MISC, + RelativeAxisType::REL_WHEEL_HI_RES, + RelativeAxisType::REL_HWHEEL_HI_RES, + ]); + + Ok(VirtualDeviceBuilder::new()? + .name("LeftHK Virtual Keyboard") + .input_id(InputId::new(BusType::BUS_USB, 0x1234, 0x5678, 0x111)) + .with_keys(&keys)? + .with_relative_axes(&relative_axes)? + .build()?) +} + fn find_keyboards() -> Option> { let mut devices = vec![]; let mut enumerator = udev::Enumerator::new().ok()?; @@ -142,7 +150,7 @@ fn find_keyboards() -> Option> { Some(devices) } -fn wait_for_all_keys_unpressed(device: &Device) { +fn wait_for_keys_to_unpress(device: &Device) { let mut pending_release = false; loop { match device.get_key_state() { diff --git a/lefthk-core/src/worker/mod.rs b/lefthk-core/src/worker/mod.rs index e6e330e..145020c 100644 --- a/lefthk-core/src/worker/mod.rs +++ b/lefthk-core/src/worker/mod.rs @@ -62,7 +62,7 @@ impl Worker { mods_pressed: Vec::new(), eaten_keys: Vec::new(), eaten_mods: Vec::new(), - evdev: EvDev::default(), + evdev: errors::exit!(EvDev::new()), children: Children::default(), chord_ctx: context::Chord::new(), } @@ -85,7 +85,7 @@ impl Worker { self.handle_event(event); } Task::KeyboardAdded(path) => { - self.evdev.add_device(path); + errors::log!(self.evdev.add_device(path)); } Task::KeyboardRemoved(path) => { self.evdev.remove_device(path); From cc5f09e3129783047bfc9035350c3b96cb899ea6 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Mon, 11 Mar 2024 19:54:14 +0000 Subject: [PATCH 14/16] Fix issue under kde/wayland --- lefthk-core/src/evdev.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index 8c8908f..54f54b1 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -99,8 +99,9 @@ impl EvDev { } fn generate_device() -> Result { + // Can't enable more keys due to wayland/kde let keys = AttributeSet::from_iter( - (Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code()).map(Key::new), + (Key::KEY_RESERVED.code()..Key::KEY_ALS_TOGGLE.code()).map(Key::new), ); let relative_axes = evdev::AttributeSet::from_iter([ From cdf2873163f50f514a8556071a6e52261bb67414 Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Mon, 11 Mar 2024 21:03:52 +0000 Subject: [PATCH 15/16] Add some docs --- lefthk-core/src/evdev.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lefthk-core/src/evdev.rs b/lefthk-core/src/evdev.rs index 54f54b1..1d555ca 100644 --- a/lefthk-core/src/evdev.rs +++ b/lefthk-core/src/evdev.rs @@ -51,6 +51,10 @@ impl EvDev { Ok(evdev) } + /// Start up a task for the device at the given path. + /// # Errors + /// + /// Errors if the device cannot be opened, grabbed, or if the stream cannot be started. pub fn add_device(&mut self, path: PathBuf) -> Result<()> { let p = path.clone(); let path_str = p.to_str().ok_or(LeftError::PathToStrError)?; @@ -92,12 +96,17 @@ impl EvDev { Ok(()) } + /// Remove the device at the given path. pub fn remove_device(&mut self, path: PathBuf) { tracing::info!("Removing device with path: {:?}", path); self.task_guards.remove(&path); } } +/// Generate a virtual device. +/// # Errors +/// +/// Errors if the device cannot be built. fn generate_device() -> Result { // Can't enable more keys due to wayland/kde let keys = AttributeSet::from_iter( @@ -127,6 +136,7 @@ fn generate_device() -> Result { .build()?) } +/// Find the keyboards on the system. fn find_keyboards() -> Option> { let mut devices = vec![]; let mut enumerator = udev::Enumerator::new().ok()?; @@ -151,6 +161,8 @@ fn find_keyboards() -> Option> { Some(devices) } +/// Wait for keys to be unpress. +/// This is necessary because the device is grabbed and we need to wait for the keys to be unpressed before we can grab it. fn wait_for_keys_to_unpress(device: &Device) { let mut pending_release = false; loop { @@ -169,6 +181,7 @@ struct KeyboardWatcher { _task_guard: oneshot::Receiver<()>, } +/// Watch for keyboards being added or removed. impl KeyboardWatcher { pub fn new(task_transmitter: mpsc::Sender) -> Self { let (guard, task_guard) = oneshot::channel(); @@ -194,9 +207,6 @@ impl KeyboardWatcher { for e in socket.iter() { let device = e.device(); - // for property in device.properties() { - // tracing::info!("Property: {:?}, {:?}", property.name(), property.value()); - // } if device .property_value("NAME") .unwrap_or(OsStr::new("")) From d40bbde940f4ec82b8e1e49e5d763825ef22158c Mon Sep 17 00:00:00 2001 From: AethanFoot Date: Mon, 11 Mar 2024 22:49:09 +0000 Subject: [PATCH 16/16] Partially add old names to keysymlookup --- lefthk-core/src/keysym_lookup.rs | 90 ++++++++++++++++---------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/lefthk-core/src/keysym_lookup.rs b/lefthk-core/src/keysym_lookup.rs index 85ef765..8395465 100644 --- a/lefthk-core/src/keysym_lookup.rs +++ b/lefthk-core/src/keysym_lookup.rs @@ -59,12 +59,12 @@ pub fn into_mods(keys: &[String]) -> Vec { pub fn into_mod(key: &str) -> Option { match key { "Shift" => Some(MOD_MASK::MASK_SHIFT), - "Ctrl" => Some(MOD_MASK::MASK_CTRL), - "Alt" => Some(MOD_MASK::MASK_ALT), - "Meta" => Some(MOD_MASK::MASK_META), - "Capslock" => Some(MOD_MASK::MASK_CAPSLOCK), - "Numlock" => Some(MOD_MASK::MASK_NUMLOCK), - "Scrolllock" => Some(MOD_MASK::MASK_SCROLLLOCK), + "Ctrl" | "Control" => Some(MOD_MASK::MASK_CTRL), + "Alt" | "Mod1" => Some(MOD_MASK::MASK_ALT), + "Meta" | "Mod4" => Some(MOD_MASK::MASK_META), + "Capslock" | "Lock" => Some(MOD_MASK::MASK_CAPSLOCK), + "Numlock" | "Mod2" => Some(MOD_MASK::MASK_NUMLOCK), + "Scrolllock" | "Mod3" => Some(MOD_MASK::MASK_SCROLLLOCK), _ => None, } } @@ -85,7 +85,7 @@ pub fn into_keys(keys: &[String]) -> Vec { pub fn into_key(key: &str) -> Option { match key { "Reserved" => Some(Key::KEY_RESERVED), - "Escape" => Some(Key::KEY_ESC), + "ESC" | "Escape" => Some(Key::KEY_ESC), "1" => Some(Key::KEY_1), "2" => Some(Key::KEY_2), "3" => Some(Key::KEY_3), @@ -98,7 +98,7 @@ pub fn into_key(key: &str) -> Option { "0" => Some(Key::KEY_0), "Minus" => Some(Key::KEY_MINUS), "Equal" => Some(Key::KEY_EQUAL), - "Backspace" => Some(Key::KEY_BACKSPACE), + "BackSpace" => Some(Key::KEY_BACKSPACE), "Tab" => Some(Key::KEY_TAB), "q" => Some(Key::KEY_Q), "w" => Some(Key::KEY_W), @@ -112,8 +112,8 @@ pub fn into_key(key: &str) -> Option { "p" => Some(Key::KEY_P), "Leftbrace" => Some(Key::KEY_LEFTBRACE), "Rightbrace" => Some(Key::KEY_RIGHTBRACE), - "Enter" => Some(Key::KEY_ENTER), - "Leftctrl" => Some(Key::KEY_LEFTCTRL), + "Enter" | "Return" => Some(Key::KEY_ENTER), + "Leftctrl" | "Control_L" => Some(Key::KEY_LEFTCTRL), "a" => Some(Key::KEY_A), "s" => Some(Key::KEY_S), "d" => Some(Key::KEY_D), @@ -126,7 +126,7 @@ pub fn into_key(key: &str) -> Option { "Semicolon" => Some(Key::KEY_SEMICOLON), "Apostrophe" => Some(Key::KEY_APOSTROPHE), "Grave" => Some(Key::KEY_GRAVE), - "Leftshift" => Some(Key::KEY_LEFTSHIFT), + "Leftshift" | "Shift_L" => Some(Key::KEY_LEFTSHIFT), "Backslash" => Some(Key::KEY_BACKSLASH), "z" => Some(Key::KEY_Z), "x" => Some(Key::KEY_X), @@ -138,11 +138,11 @@ pub fn into_key(key: &str) -> Option { "Comma" => Some(Key::KEY_COMMA), "Dot" => Some(Key::KEY_DOT), "Slash" => Some(Key::KEY_SLASH), - "Rightshift" => Some(Key::KEY_RIGHTSHIFT), - "Kpasterisk" => Some(Key::KEY_KPASTERISK), - "Leftalt" => Some(Key::KEY_LEFTALT), + "Rightshift" | "Shift_R" => Some(Key::KEY_RIGHTSHIFT), + "KPAsterisk" | "KP_Multiply" => Some(Key::KEY_KPASTERISK), + "Leftalt" | "Alt_L" => Some(Key::KEY_LEFTALT), "Space" => Some(Key::KEY_SPACE), - "Capslock" => Some(Key::KEY_CAPSLOCK), + "Capslock" | "Caps_Lock" => Some(Key::KEY_CAPSLOCK), "F1" => Some(Key::KEY_F1), "F2" => Some(Key::KEY_F2), "F3" => Some(Key::KEY_F3), @@ -153,22 +153,22 @@ pub fn into_key(key: &str) -> Option { "F8" => Some(Key::KEY_F8), "F9" => Some(Key::KEY_F9), "F10" => Some(Key::KEY_F10), - "Numlock" => Some(Key::KEY_NUMLOCK), - "Scrolllock" => Some(Key::KEY_SCROLLLOCK), - "Kp7" => Some(Key::KEY_KP7), - "Kp8" => Some(Key::KEY_KP8), - "Kp9" => Some(Key::KEY_KP9), - "Kpminus" => Some(Key::KEY_KPMINUS), - "Kp4" => Some(Key::KEY_KP4), - "Kp5" => Some(Key::KEY_KP5), - "Kp6" => Some(Key::KEY_KP6), - "Kpplus" => Some(Key::KEY_KPPLUS), - "Kp1" => Some(Key::KEY_KP1), - "Kp2" => Some(Key::KEY_KP2), - "Kp3" => Some(Key::KEY_KP3), - "Kp0" => Some(Key::KEY_KP0), - "Kpdot" => Some(Key::KEY_KPDOT), - "Zenkakuhankaku" => Some(Key::KEY_ZENKAKUHANKAKU), + "Numlock" | "Num_Lock" => Some(Key::KEY_NUMLOCK), + "Scrolllock" | "Scroll_Lock" => Some(Key::KEY_SCROLLLOCK), + "KP7" | "KP_7" => Some(Key::KEY_KP7), + "KP8" | "KP_8" => Some(Key::KEY_KP8), + "KP9" | "KP_9" => Some(Key::KEY_KP9), + "KPMinus" | "KP_Subtract" => Some(Key::KEY_KPMINUS), + "KP4" | "KP_4" => Some(Key::KEY_KP4), + "KP5" | "KP_5" => Some(Key::KEY_KP5), + "KP6" | "KP_6" => Some(Key::KEY_KP6), + "KPPlus" => Some(Key::KEY_KPPLUS), + "KP1" | "KP_1" => Some(Key::KEY_KP1), + "KP2" | "KP_2" => Some(Key::KEY_KP2), + "KP3" | "KP_3" => Some(Key::KEY_KP3), + "KP0" | "KP_0" => Some(Key::KEY_KP0), + "KPDot" | "KP_Decimal" => Some(Key::KEY_KPDOT), + "Zenkakuhankaku" | "Zenkaku_Hankaku" => Some(Key::KEY_ZENKAKUHANKAKU), "102nd" => Some(Key::KEY_102ND), "F11" => Some(Key::KEY_F11), "F12" => Some(Key::KEY_F12), @@ -176,23 +176,23 @@ pub fn into_key(key: &str) -> Option { "Katakana" => Some(Key::KEY_KATAKANA), "Hiragana" => Some(Key::KEY_HIRAGANA), "Henkan" => Some(Key::KEY_HENKAN), - "Katakanahiragana" => Some(Key::KEY_KATAKANAHIRAGANA), + "Katakanahiragana" | "Hiragana_Katakana" => Some(Key::KEY_KATAKANAHIRAGANA), "Muhenkan" => Some(Key::KEY_MUHENKAN), - "Kpjpcomma" => Some(Key::KEY_KPJPCOMMA), - "Kpenter" => Some(Key::KEY_KPENTER), - "Rightctrl" => Some(Key::KEY_RIGHTCTRL), - "Kpslash" => Some(Key::KEY_KPSLASH), - "Sysrq" => Some(Key::KEY_SYSRQ), - "Rightalt" => Some(Key::KEY_RIGHTALT), + "KPJpcomma" => Some(Key::KEY_KPJPCOMMA), + "KPEnter" | "KP_Enter" => Some(Key::KEY_KPENTER), + "Rightctrl" | "Control_R" => Some(Key::KEY_RIGHTCTRL), + "KPSlash" | "KP_Divide" => Some(Key::KEY_KPSLASH), + "Sysrq" | "Sys_Req" => Some(Key::KEY_SYSRQ), + "Rightalt" | "Alt_R" => Some(Key::KEY_RIGHTALT), "Linefeed" => Some(Key::KEY_LINEFEED), "Home" => Some(Key::KEY_HOME), "Up" => Some(Key::KEY_UP), - "Pageup" => Some(Key::KEY_PAGEUP), + "Pageup" | "Page_Up" => Some(Key::KEY_PAGEUP), "Left" => Some(Key::KEY_LEFT), "Right" => Some(Key::KEY_RIGHT), "End" => Some(Key::KEY_END), "Down" => Some(Key::KEY_DOWN), - "Pagedown" => Some(Key::KEY_PAGEDOWN), + "Pagedown" | "Page_Down" => Some(Key::KEY_PAGEDOWN), "Insert" => Some(Key::KEY_INSERT), "Delete" => Some(Key::KEY_DELETE), "Macro" => Some(Key::KEY_MACRO), @@ -200,11 +200,11 @@ pub fn into_key(key: &str) -> Option { "Volumedown" => Some(Key::KEY_VOLUMEDOWN), "Volumeup" => Some(Key::KEY_VOLUMEUP), "Power" => Some(Key::KEY_POWER), - "Kpequal" => Some(Key::KEY_KPEQUAL), - "Kpplusminus" => Some(Key::KEY_KPPLUSMINUS), + "KPEqual" | "KP_Equal" => Some(Key::KEY_KPEQUAL), + "KPPlusminus" => Some(Key::KEY_KPPLUSMINUS), "Pause" => Some(Key::KEY_PAUSE), "Scale" => Some(Key::KEY_SCALE), - "Kpcomma" => Some(Key::KEY_KPCOMMA), + "KPComma" => Some(Key::KEY_KPCOMMA), "Hangeul" => Some(Key::KEY_HANGEUL), "Hanja" => Some(Key::KEY_HANJA), "Yen" => Some(Key::KEY_YEN), @@ -262,8 +262,8 @@ pub fn into_key(key: &str) -> Option { "Edit" => Some(Key::KEY_EDIT), "Scrollup" => Some(Key::KEY_SCROLLUP), "Scrolldown" => Some(Key::KEY_SCROLLDOWN), - "Kpleftparen" => Some(Key::KEY_KPLEFTPAREN), - "Kprightparen" => Some(Key::KEY_KPRIGHTPAREN), + "KPLeftparen" => Some(Key::KEY_KPLEFTPAREN), + "KPRightparen" => Some(Key::KEY_KPRIGHTPAREN), "New" => Some(Key::KEY_NEW), "Redo" => Some(Key::KEY_REDO), "F13" => Some(Key::KEY_F13),