Attempt at working with optional keys

main
Tait Hoyem 2 years ago
parent 85779876dd
commit f40385ae65

369
Cargo.lock generated

@ -2,18 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "Simple-Wayland-HotKey-Daemon"
version = "1.0.0"
dependencies = [
"clap",
"env_logger",
"evdev",
"log",
"nix",
"sysinfo",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
@ -58,6 +46,12 @@ dependencies = [
"wyz",
]
[[package]]
name = "bytes"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
[[package]]
name = "cc"
version = "1.0.72"
@ -72,19 +66,28 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.0.7"
version = "3.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12e8611f9ae4e068fa3e56931fded356ff745e70987ff76924a6e0ab1c8ef2e3"
checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b"
dependencies = [
"atty",
"bitflags",
"clap_lex",
"indexmap",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@ -143,9 +146,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.8.4"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
@ -156,13 +159,16 @@ dependencies = [
[[package]]
name = "evdev"
version = "0.11.4"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21eef104bd659ef808f1f84bed9a924e1aebcdd066845b377cd3b52cc497bb9f"
checksum = "c8afc805e5e0c306722a9517cd479a92692319e81b50354b31db150ecd89a6b4"
dependencies = [
"bitvec",
"futures-core",
"libc",
"nix",
"thiserror",
"tokio",
]
[[package]]
@ -171,6 +177,12 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures-core"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115"
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -202,6 +214,21 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -210,9 +237,18 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.112"
version = "0.2.131"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
[[package]]
name = "lock_api"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
@ -238,6 +274,18 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "nix"
version = "0.23.1"
@ -281,8 +329,52 @@ name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"memchr",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
@ -316,6 +408,15 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.5.4"
@ -333,23 +434,122 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
[[package]]
name = "serde_json"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "signal-hook-tokio"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e"
dependencies = [
"futures-core",
"libc",
"signal-hook",
"tokio",
]
[[package]]
name = "smallvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "sohkd"
version = "1.2.1"
dependencies = [
"clap",
"env_logger",
"evdev",
"itertools",
"log",
"nix",
"serde_json",
"signal-hook",
"signal-hook-tokio",
"sysinfo",
"tokio",
"tokio-stream",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sysinfo"
version = "0.23.0"
version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e757000a4bed2b1be9be65a3f418b9696adf30bb419214c73997422de73a591"
checksum = "3977ec2e0520829be45c8a2df70db2bf364714d8a748316a10c3c35d4d2b01c9"
dependencies = [
"cfg-if",
"core-foundation-sys",
@ -377,9 +577,83 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.14.2"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-stream"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "unicode-ident"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
@ -412,6 +686,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "wyz"
version = "0.5.0"

@ -1,7 +1,8 @@
[package]
name = "Simple-Odilia-HotKey-Daemon"
version = "1.0.1"
description = "Swhkd clone for Odilia"
edition = "2021"
name = "sohkd"
version = "1.2.1"
authors = [
"Shinyzenith <aakashsensharma@gmail.com>\n",
"Angelo Fallaria <ba.fallaria@gmail.com>\n",
@ -10,23 +11,16 @@ authors = [
]
[dependencies]
clap = "3.0.4"
env_logger = "0.8.4"
#rdev = { version = "^0.5.1", features = ["unstable_grab"] }
rdev = { git = "https://github.com/TTWNO/rdev", features = ["unstable_grab"] }
log = "0.4.0"
clap = "3.1.6"
env_logger = "0.9.0"
evdev = { version = "^0.11.6", features = ["tokio"] }
itertools = "0.10.3"
log = "0.4.14"
nix = "0.23.1"
sysinfo = "0.23.0"
once_cell = "^1.9.0"
[[bin]]
name = "sohkd"
path = "src/daemon.rs"
[[bin]]
name = "sohks"
path = "src/server.rs"
[[bin]]
name = "sohkctl"
path = "src/ctrl.rs"
signal-hook = "0.3.13"
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }
sysinfo = "0.23.5"
tokio = { version = "1.17.0", features = ["full"] }
tokio-stream = "0.1.8"
#odilia-common = { path = "../common" }
serde_json = "1.0"

File diff suppressed because it is too large Load Diff

@ -0,0 +1,540 @@
use crate::config::Value;
use serde_json;
use clap::{arg, Command};
use evdev::{AttributeSet, Device, InputEventKind, Key};
use nix::{
sys::stat::{umask, Mode},
unistd::{Group, Uid},
};
use signal_hook::consts::signal::*;
use signal_hook_tokio::Signals;
use std::{
collections::{HashMap, HashSet},
env,
error::Error,
fs,
fs::Permissions,
io::prelude::*,
os::unix::{fs::PermissionsExt, net::UnixStream},
path::{Path, PathBuf},
process::{exit, id},
};
use sysinfo::{ProcessExt, System, SystemExt};
use tokio::select;
use tokio::time::Duration;
use tokio::time::{sleep, Instant};
use tokio_stream::{StreamExt, StreamMap};
mod config;
mod perms;
mod uinput;
#[cfg(test)]
mod tests;
struct KeyboardState {
state_modifiers: HashSet<config::Modifier>,
state_keysyms: AttributeSet<evdev::Key>,
}
impl KeyboardState {
fn new() -> KeyboardState {
KeyboardState { state_modifiers: HashSet::new(), state_keysyms: AttributeSet::new() }
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let args = set_command_line_args().get_matches();
env::set_var("RUST_LOG", "sohkd=warn");
if args.is_present("debug") {
env::set_var("RUST_LOG", "sohkd=trace");
}
env_logger::init();
log::trace!("Logger initialized.");
let invoking_uid = match env::var("PKEXEC_UID") {
Ok(uid) => {
let uid = uid.parse::<u32>().unwrap();
log::trace!("Invoking UID: {}", uid);
uid
}
Err(_) => {
log::error!("Failed to launch sohkd!!!");
log::error!("Make sure to launch the binary with pkexec.");
exit(1);
}
};
setup_sohkd(invoking_uid);
let load_config = || {
// Drop privileges to the invoking user.
perms::drop_privileges(invoking_uid);
let config_file_path: PathBuf = if args.is_present("config") {
Path::new(args.value_of("config").unwrap()).to_path_buf()
} else {
fetch_xdg_config_path()
};
log::debug!("Using config file path: {:#?}", config_file_path);
let modes = match config::load(&config_file_path) {
Err(e) => {
log::error!("Config Error: {}", e);
exit(1)
}
Ok(out) => out,
};
modes
};
let mut modes = load_config();
let mut mode_stack: Vec<usize> = vec![0];
macro_rules! send_command {
($hotkey: expr, $socket_path: expr) => {
log::info!("Hotkey pressed: {:#?}", $hotkey);
let command = $hotkey.command;
let mut commands_to_send = String::new();
if command.contains('@') {
let commands = command.split("&&").map(|s| s.trim()).collect::<Vec<_>>();
for cmd in commands {
match cmd.split(' ').next().unwrap() {
config::MODE_ENTER_STATEMENT => {
let enter_mode = cmd.split(' ').nth(1).unwrap();
for (i, mode) in modes.iter().enumerate() {
if mode.name == enter_mode {
mode_stack.push(i);
break;
}
}
log::info!(
"Entering mode: {}",
modes[mode_stack[mode_stack.len() - 1]].name
);
}
config::MODE_ESCAPE_STATEMENT => {
mode_stack.pop();
}
config::ODILIA_SEND_STATEMENT => {
log::debug!("Odilia event statement matched");
}
_ => commands_to_send.push_str(format!("{cmd} &&").as_str()),
}
}
} else {
commands_to_send = command;
}
if commands_to_send.ends_with("&& ") {
commands_to_send = commands_to_send.strip_suffix("&& ").unwrap().to_string();
}
if let Err(e) = socket_write(&commands_to_send, $socket_path.to_path_buf()) {
log::error!("Failed to send command to swhks through IPC.");
log::error!("Please make sure that swhks is running.");
log::error!("Err: {:#?}", e)
}
};
}
// Escalate back to the root user after reading the config file.
perms::raise_privileges();
let keyboard_devices: Vec<Device> = {
if let Some(arg_devices) = args.values_of("device") {
// for device in arg_devices {
// let device_path = Path::new(device);
// if let Ok(device_to_use) = Device::open(device_path) {
// log::info!("Using device: {}", device_to_use.name().unwrap_or(device));
// keyboard_devices.push(device_to_use);
// }
// }
let arg_devices = arg_devices.collect::<Vec<&str>>();
evdev::enumerate()
.filter(|(_, device)| arg_devices.contains(&device.name().unwrap_or("")))
.map(|(_, device)| device)
.collect()
} else {
log::trace!("Attempting to find all keyboard file descriptors.");
evdev::enumerate().filter(check_device_is_keyboard).map(|(_, device)| device).collect()
}
};
if keyboard_devices.is_empty() {
log::error!("No valid keyboard device was detected!");
exit(1);
}
log::debug!("{} Keyboard device(s) detected.", keyboard_devices.len());
let mut uinput_device = match uinput::create_uinput_device() {
Ok(dev) => dev,
Err(e) => {
log::error!("Err: {:#?}", e);
exit(1);
}
};
let modifiers_map: HashMap<Key, config::Modifier> = HashMap::from([
(Key::KEY_LEFTMETA, config::Modifier::Super),
(Key::KEY_RIGHTMETA, config::Modifier::Super),
(Key::KEY_LEFTALT, config::Modifier::Alt),
(Key::KEY_RIGHTALT, config::Modifier::Alt),
(Key::KEY_LEFTCTRL, config::Modifier::Control),
(Key::KEY_RIGHTCTRL, config::Modifier::Control),
(Key::KEY_LEFTSHIFT, config::Modifier::Shift),
(Key::KEY_RIGHTSHIFT, config::Modifier::Shift),
(Key::KEY_CAPSLOCK, config::Modifier::CapsLock),
]);
let repeat_cooldown_duration: u64 = if args.is_present("cooldown") {
args.value_of("cooldown").unwrap().parse::<u64>().unwrap()
} else {
250
};
let mut signals = Signals::new(&[
SIGUSR1, SIGUSR2, SIGHUP, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGINT, SIGPIPE, SIGQUIT,
SIGSYS, SIGTERM, SIGTRAP, SIGTSTP, SIGVTALRM, SIGXCPU, SIGXFSZ,
])?;
let mut execution_is_paused = false;
let mut last_hotkey: Option<config::Hotkey> = None;
let mut pending_release: bool = false;
let mut keyboard_states: Vec<KeyboardState> = Vec::new();
let mut keyboard_stream_map = StreamMap::new();
for (i, mut device) in keyboard_devices.into_iter().enumerate() {
let _ = device.grab();
keyboard_stream_map.insert(i, device.into_event_stream()?);
keyboard_states.push(KeyboardState::new());
}
// The initial sleep duration is never read because last_hotkey is initialized to None
let hotkey_repeat_timer = sleep(Duration::from_millis(0));
tokio::pin!(hotkey_repeat_timer);
// The socket we're sending the commands to.
let socket_file_path = fetch_xdg_runtime_socket_path();
loop {
select! {
_ = &mut hotkey_repeat_timer, if &last_hotkey.is_some() => {
let hotkey = last_hotkey.clone().unwrap();
if hotkey.keybinding.on_release {
continue;
}
send_command!(hotkey.clone(), &socket_file_path);
hotkey_repeat_timer.as_mut().reset(Instant::now() + Duration::from_millis(repeat_cooldown_duration));
}
Some(signal) = signals.next() => {
match signal {
SIGUSR1 => {
execution_is_paused = true;
for (_, mut device) in evdev::enumerate().filter(check_device_is_keyboard) {
let _ = device.ungrab();
}
}
SIGUSR2 => {
execution_is_paused = false;
for (_, mut device) in evdev::enumerate().filter(check_device_is_keyboard) {
let _ = device.grab();
}
}
SIGHUP => {
modes = load_config();
mode_stack = vec![0];
}
SIGINT => {
for (_, mut device) in evdev::enumerate().filter(check_device_is_keyboard) {
let _ = device.ungrab();
}
log::warn!("Received SIGINT signal, exiting...");
exit(1);
}
_ => {
for (_, mut device) in evdev::enumerate().filter(check_device_is_keyboard) {
let _ = device.ungrab();
}
log::warn!("Received signal: {:#?}", signal);
log::warn!("Exiting...");
exit(1);
}
}
}
Some((i, Ok(command))) = keyboard_stream_map.next() => {
let keyboard_state = &mut keyboard_states[i];
let key = match command.kind() {
InputEventKind::Key(keycode) => keycode,
_ => continue
};
match command.value() {
// Key press
1 => {
if let Some(modifier) = modifiers_map.get(&key) {
keyboard_state.state_modifiers.insert(*modifier);
} else {
keyboard_state.state_keysyms.insert(key);
}
}
// Key release
0 => {
if last_hotkey.is_some() && pending_release {
pending_release = false;
send_command!(last_hotkey.clone().unwrap(), &socket_file_path);
last_hotkey = None;
}
if let Some(modifier) = modifiers_map.get(&key) {
if let Some(hotkey) = &last_hotkey {
if hotkey.modifiers().contains(modifier) {
last_hotkey = None;
}
}
keyboard_state.state_modifiers.remove(modifier);
} else if keyboard_state.state_keysyms.contains(key) {
if let Some(hotkey) = &last_hotkey {
if hotkey.keysym().is_some() && key == hotkey.keysym().unwrap() {
last_hotkey = None;
}
}
keyboard_state.state_keysyms.remove(key);
}
}
_ => {}
}
let possible_hotkeys: Vec<&config::Hotkey> = modes[mode_stack[mode_stack.len() - 1]].hotkeys.iter()
.filter(|hotkey| hotkey.modifiers().len() == keyboard_state.state_modifiers.len())
.collect();
let command_in_hotkeys = modes[mode_stack[mode_stack.len() - 1]].hotkeys.iter().any(|hotkey| {
((hotkey.keysym().is_some() &&
hotkey.keysym().unwrap().code() == command.code()) ||
hotkey.keysym().is_none() && keyboard_state.state_keysyms.iter().count() == 0) &&
(!keyboard_state.state_modifiers.is_empty() && hotkey.modifiers().contains(&config::Modifier::Any) || keyboard_state.state_modifiers
.iter()
.all(|x| hotkey.modifiers().contains(x)) &&
keyboard_state.state_modifiers.len() == hotkey.modifiers().len())
&& !hotkey.is_send()
});
// Don't emit command to virtual device if it's from a valid hotkey
// TODO: this will make sure that individual capslock keys send without any other modifiers or keys pressed will ALWAYS be consumed. This should be an option.
if !command_in_hotkeys && !(keyboard_state.state_keysyms.iter().count() == 0 && keyboard_state.state_modifiers.len() == 1 && keyboard_state.state_modifiers.iter().all(|&m| m == config::Modifier::CapsLock)) {
uinput_device.emit(&[command]).unwrap();
}
if execution_is_paused || possible_hotkeys.is_empty() || last_hotkey.is_some() {
continue;
}
log::debug!("state_modifiers: {:#?}", keyboard_state.state_modifiers);
log::debug!("state_keysyms: {:#?}", keyboard_state.state_keysyms);
log::debug!("hotkey: {:#?}", possible_hotkeys);
for hotkey in possible_hotkeys {
// this should check if state_modifiers and hotkey.modifiers have the same elements
if (!keyboard_state.state_modifiers.is_empty() && hotkey.modifiers().contains(&config::Modifier::Any) || keyboard_state.state_modifiers.iter().all(|x| hotkey.modifiers().contains(x))
&& keyboard_state.state_modifiers.len() == hotkey.modifiers().len())
&& ((hotkey.keysym().is_some()
&& keyboard_state.state_keysyms.contains(hotkey.keysym().unwrap()))
|| (hotkey.keysym().is_none()
&& keyboard_state.state_keysyms.iter().count() == 0 /* no keys are pressed that are not modiiers */))
{
last_hotkey = Some(hotkey.clone());
if pending_release { break; }
if hotkey.is_on_release() {
pending_release = true;
break;
}
send_command!(hotkey.clone(), &socket_file_path);
hotkey_repeat_timer.as_mut().reset(Instant::now() + Duration::from_millis(repeat_cooldown_duration));
break;
}
}
}
}
}
}
fn socket_write(command: &str, socket_path: PathBuf) -> Result<(), Box<dyn Error>> {
let mut stream = UnixStream::connect(socket_path)?;
stream.write_all(command.as_bytes())?;
Ok(())
}
pub fn check_input_group() -> Result<(), Box<dyn Error>> {
if !Uid::current().is_root() {
let groups = nix::unistd::getgroups();
for (_, groups) in groups.iter().enumerate() {
for group in groups {
let group = Group::from_gid(*group);
if group.unwrap().unwrap().name == "input" {
log::error!("Note: INVOKING USER IS IN INPUT GROUP!!!!");
log::error!("THIS IS A HUGE SECURITY RISK!!!!");
}
}
}
log::error!("Consider using `pkexec sohkd ...`");
exit(1);
} else {
log::warn!("Running sohkd as root!");
Ok(())
}
}
pub fn check_device_is_keyboard(tup: &(PathBuf, Device)) -> bool {
let device = &tup.1;
if device.supported_keys().map_or(false, |keys| keys.contains(Key::KEY_ENTER)) {
if device.name() == Some("sohkd virtual output") {
return false;
}
log::debug!("Keyboard: {}", device.name().unwrap(),);
true
} else {
log::trace!("Other: {}", device.name().unwrap(),);
false
}
}
pub fn set_command_line_args() -> Command<'static> {
let app = Command::new("sohkd")
.version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS"))
.about("Simple Wayland HotKey Daemon")
.arg(
arg!(-c --config <CONFIG_FILE_PATH>)
.required(false)
.takes_value(true)
.help("Set a custom config file path."),
)
.arg(
arg!(-C --cooldown <COOLDOWN_IN_MS>)
.required(false)
.takes_value(true)
.help("Set a custom repeat cooldown duration. Default is 250ms."),
)
.arg(arg!(-d - -debug).required(false).help("Enable debug mode."))
.arg(
arg!(-D --device <DEVICE_NAME>)
.required(false)
.takes_value(true)
.multiple_occurrences(true)
.help(
"Specific keyboard devices to use. Seperate multiple devices with semicolon.",
),
);
app
}
pub fn fetch_xdg_config_path() -> PathBuf {
let config_file_path: PathBuf = match env::var("XDG_CONFIG_HOME") {
Ok(val) => {
log::debug!("XDG_CONFIG_HOME exists: {:#?}", val);
Path::new(&val).join("sohkd/sohkdrc")
}
Err(_) => {
log::error!("XDG_CONFIG_HOME has not been set.");
Path::new("/etc/sohkd/sohkdrc").to_path_buf()
}
};
config_file_path
}
pub fn fetch_xdg_runtime_socket_path() -> PathBuf {
match env::var("XDG_RUNTIME_DIR") {
Ok(val) => {
log::debug!("XDG_RUNTIME_DIR exists: {:#?}", val);
Path::new(&val).join("sohkd.sock")
}
Err(_) => {
log::error!("XDG_RUNTIME_DIR has not been set.");
Path::new(&format!("/run/user/{}/sohkd.sock", env::var("PKEXEC_UID").unwrap()))
.to_path_buf()
}
}
}
pub fn setup_sohkd(invoking_uid: u32) {
// Set a sane process umask.
log::trace!("Setting process umask.");
umask(Mode::S_IWGRP | Mode::S_IWOTH);
// Get the runtime path and create it if needed.
let runtime_path: String = match env::var("XDG_RUNTIME_DIR") {
Ok(runtime_path) => {
log::debug!("XDG_RUNTIME_DIR exists: {:#?}", runtime_path);
Path::new(&runtime_path).join("sohkd").to_str().unwrap().to_owned()
}
Err(_) => {
log::error!("XDG_RUNTIME_DIR has not been set.");
String::from("/run/sohkd/")
}
};
if !Path::new(&runtime_path).exists() {
match fs::create_dir_all(Path::new(&runtime_path)) {
Ok(_) => {
log::debug!("Created runtime directory.");
match fs::set_permissions(Path::new(&runtime_path), Permissions::from_mode(0o600)) {
Ok(_) => log::debug!("Set runtime directory to readonly."),
Err(e) => log::error!("Failed to set runtime directory to readonly: {}", e),
}
}
Err(e) => log::error!("Failed to create runtime directory: {}", e),
}
}
// Get the PID file path for instance tracking.
let pidfile: String = format!("{}sohkd_{}.pid", runtime_path, invoking_uid);
if Path::new(&pidfile).exists() {
log::trace!("Reading {} file and checking for running instances.", pidfile);
let sohkd_pid = match fs::read_to_string(&pidfile) {
Ok(sohkd_pid) => sohkd_pid,
Err(e) => {
log::error!("Unable to read {} to check all running instances", e);
exit(1);
}
};
log::debug!("Previous PID: {}", sohkd_pid);
// Check if sohkd is already running!
let mut sys = System::new_all();
sys.refresh_all();
for (pid, process) in sys.processes() {
if pid.to_string() == sohkd_pid && process.exe() == env::current_exe().unwrap() {
log::error!("Swhkd is already running!");
log::error!("pid of existing sohkd process: {}", pid.to_string());
log::error!("To close the existing sohkd process, run `sudo killall sohkd`");
exit(1);
}
}
}
// Write to the pid file.
match fs::write(&pidfile, id().to_string()) {
Ok(_) => {}
Err(e) => {
log::error!("Unable to write to {}: {}", pidfile, e);
exit(1);
}
}
// Check if the user is in input group.
if check_input_group().is_err() {
exit(1);
}
}

@ -0,0 +1,52 @@
use nix::unistd::{Gid, Uid, User};
use std::process::exit;
pub fn drop_privileges(user_uid: u32) {
let user_uid = Uid::from_raw(user_uid);
let user = User::from_uid(user_uid).unwrap().unwrap();
set_initgroups(&user, user_uid.as_raw());
set_egid(user_uid.as_raw());
set_euid(user_uid.as_raw());
}
pub fn raise_privileges() {
let root_user = User::from_uid(Uid::from_raw(0)).unwrap().unwrap();
set_egid(0);
set_euid(0);
set_initgroups(&root_user, 0);
}
fn set_initgroups(user: &nix::unistd::User, gid: u32) {
let gid = Gid::from_raw(gid);
match nix::unistd::initgroups(&user.gecos, gid) {
Ok(_) => log::debug!("Setting initgroups..."),
Err(e) => {
log::error!("Failed to set init groups: {:#?}", e);
exit(1);
}
}
}
fn set_egid(gid: u32) {
let gid = Gid::from_raw(gid);
match nix::unistd::setegid(gid) {
Ok(_) => log::debug!("Setting EGID..."),
Err(e) => {
log::error!("Failed to set EGID: {:#?}", e);
exit(1);
}
}
}
fn set_euid(uid: u32) {
let uid = Uid::from_raw(uid);
match nix::unistd::seteuid(uid) {
Ok(_) => log::debug!("Setting EUID..."),
Err(e) => {
log::error!("Failed to set EUID: {:#?}", e);
exit(1);
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,569 @@
use evdev::{
uinput::{VirtualDevice, VirtualDeviceBuilder},
AttributeSet, Key,
};
pub fn create_uinput_device() -> Result<VirtualDevice, Box<dyn std::error::Error>> {
let mut keys = AttributeSet::<Key>::new();
for key in get_all_keys() {
keys.insert(key);
}
let device = VirtualDeviceBuilder::new()?
.name("sohkd virtual output")
.with_keys(&keys)?
.build()
.unwrap();
Ok(device)
}
pub fn get_all_keys() -> Vec<evdev::Key> {
return vec![
evdev::Key::KEY_RESERVED,
evdev::Key::KEY_ESC,
evdev::Key::KEY_1,
evdev::Key::KEY_2,
evdev::Key::KEY_3,
evdev::Key::KEY_4,
evdev::Key::KEY_5,
evdev::Key::KEY_6,
evdev::Key::KEY_7,
evdev::Key::KEY_8,
evdev::Key::KEY_9,
evdev::Key::KEY_0,
evdev::Key::KEY_MINUS,
evdev::Key::KEY_EQUAL,
evdev::Key::KEY_BACKSPACE,
evdev::Key::KEY_TAB,
evdev::Key::KEY_Q,
evdev::Key::KEY_W,
evdev::Key::KEY_E,
evdev::Key::KEY_R,
evdev::Key::KEY_T,
evdev::Key::KEY_Y,
evdev::Key::KEY_U,
evdev::Key::KEY_I,
evdev::Key::KEY_O,
evdev::Key::KEY_P,
evdev::Key::KEY_LEFTBRACE,
evdev::Key::KEY_RIGHTBRACE,
evdev::Key::KEY_ENTER,
evdev::Key::KEY_LEFTCTRL,
evdev::Key::KEY_A,
evdev::Key::KEY_S,
evdev::Key::KEY_D,
evdev::Key::KEY_F,
evdev::Key::KEY_G,
evdev::Key::KEY_H,
evdev::Key::KEY_J,
evdev::Key::KEY_K,
evdev::Key::KEY_L,
evdev::Key::KEY_SEMICOLON,
evdev::Key::KEY_APOSTROPHE,
evdev::Key::KEY_GRAVE,
evdev::Key::KEY_LEFTSHIFT,
evdev::Key::KEY_BACKSLASH,
evdev::Key::KEY_Z,
evdev::Key::KEY_X,
evdev::Key::KEY_C,
evdev::Key::KEY_V,
evdev::Key::KEY_B,
evdev::Key::KEY_N,
evdev::Key::KEY_M,
evdev::Key::KEY_COMMA,
evdev::Key::KEY_DOT,
evdev::Key::KEY_SLASH,
evdev::Key::KEY_RIGHTSHIFT,
evdev::Key::KEY_KPASTERISK,
evdev::Key::KEY_LEFTALT,
evdev::Key::KEY_SPACE,
evdev::Key::KEY_CAPSLOCK,
evdev::Key::KEY_F1,
evdev::Key::KEY_F2,
evdev::Key::KEY_F3,
evdev::Key::KEY_F4,
evdev::Key::KEY_F5,
evdev::Key::KEY_F6,
evdev::Key::KEY_F7,
evdev::Key::KEY_F8,
evdev::Key::KEY_F9,
evdev::Key::KEY_F10,
evdev::Key::KEY_NUMLOCK,
evdev::Key::KEY_SCROLLLOCK,
evdev::Key::KEY_KP7,
evdev::Key::KEY_KP8,
evdev::Key::KEY_KP9,
evdev::Key::KEY_KPMINUS,
evdev::Key::KEY_KP4,
evdev::Key::KEY_KP5,
evdev::Key::KEY_KP6,
evdev::Key::KEY_KPPLUS,
evdev::Key::KEY_KP1,
evdev::Key::KEY_KP2,
evdev::Key::KEY_KP3,
evdev::Key::KEY_KP0,
evdev::Key::KEY_KPDOT,
evdev::Key::KEY_ZENKAKUHANKAKU,
evdev::Key::KEY_102ND,
evdev::Key::KEY_F11,
evdev::Key::KEY_F12,
evdev::Key::KEY_RO,
evdev::Key::KEY_KATAKANA,
evdev::Key::KEY_HIRAGANA,
evdev::Key::KEY_HENKAN,
evdev::Key::KEY_KATAKANAHIRAGANA,
evdev::Key::KEY_MUHENKAN,
evdev::Key::KEY_KPJPCOMMA,
evdev::Key::KEY_KPENTER,
evdev::Key::KEY_RIGHTCTRL,
evdev::Key::KEY_KPSLASH,
evdev::Key::KEY_SYSRQ,
evdev::Key::KEY_RIGHTALT,
evdev::Key::KEY_LINEFEED,
evdev::Key::KEY_HOME,
evdev::Key::KEY_UP,
evdev::Key::KEY_PAGEUP,
evdev::Key::KEY_LEFT,
evdev::Key::KEY_RIGHT,
evdev::Key::KEY_END,
evdev::Key::KEY_DOWN,
evdev::Key::KEY_PAGEDOWN,
evdev::Key::KEY_INSERT,
evdev::Key::KEY_DELETE,
evdev::Key::KEY_MACRO,
evdev::Key::KEY_MUTE,
evdev::Key::KEY_VOLUMEDOWN,
evdev::Key::KEY_VOLUMEUP,
evdev::Key::KEY_POWER,
evdev::Key::KEY_KPEQUAL,
evdev::Key::KEY_KPPLUSMINUS,
evdev::Key::KEY_PAUSE,
evdev::Key::KEY_SCALE,
evdev::Key::KEY_KPCOMMA,
evdev::Key::KEY_HANGEUL,
evdev::Key::KEY_HANJA,
evdev::Key::KEY_YEN,
evdev::Key::KEY_LEFTMETA,
evdev::Key::KEY_RIGHTMETA,
evdev::Key::KEY_COMPOSE,
evdev::Key::KEY_STOP,
evdev::Key::KEY_AGAIN,
evdev::Key::KEY_PROPS,
evdev::Key::KEY_UNDO,
evdev::Key::KEY_FRONT,
evdev::Key::KEY_COPY,
evdev::Key::KEY_OPEN,
evdev::Key::KEY_PASTE,
evdev::Key::KEY_FIND,
evdev::Key::KEY_CUT,
evdev::Key::KEY_HELP,
evdev::Key::KEY_MENU,
evdev::Key::KEY_CALC,
evdev::Key::KEY_SETUP,
evdev::Key::KEY_SLEEP,
evdev::Key::KEY_WAKEUP,
evdev::Key::KEY_FILE,
evdev::Key::KEY_SENDFILE,
evdev::Key::KEY_DELETEFILE,
evdev::Key::KEY_XFER,
evdev::Key::KEY_PROG1,
evdev::Key::KEY_PROG2,
evdev::Key::KEY_WWW,
evdev::Key::KEY_MSDOS,
evdev::Key::KEY_COFFEE,
evdev::Key::KEY_DIRECTION,
evdev::Key::KEY_ROTATE_DISPLAY,
evdev::Key::KEY_CYCLEWINDOWS,
evdev::Key::KEY_MAIL,
evdev::Key::KEY_BOOKMARKS,
evdev::Key::KEY_COMPUTER,
evdev::Key::KEY_BACK,
evdev::Key::KEY_FORWARD,
evdev::Key::KEY_CLOSECD,
evdev::Key::KEY_EJECTCD,
evdev::Key::KEY_EJECTCLOSECD,
evdev::Key::KEY_NEXTSONG,
evdev::Key::KEY_PLAYPAUSE,
evdev::Key::KEY_PREVIOUSSONG,
evdev::Key::KEY_STOPCD,
evdev::Key::KEY_RECORD,
evdev::Key::KEY_REWIND,
evdev::Key::KEY_PHONE,
evdev::Key::KEY_ISO,
evdev::Key::KEY_CONFIG,
evdev::Key::KEY_HOMEPAGE,
evdev::Key::KEY_REFRESH,
evdev::Key::KEY_EXIT,
evdev::Key::KEY_MOVE,
evdev::Key::KEY_EDIT,
evdev::Key::KEY_SCROLLUP,
evdev::Key::KEY_SCROLLDOWN,
evdev::Key::KEY_KPLEFTPAREN,
evdev::Key::KEY_KPRIGHTPAREN,
evdev::Key::KEY_NEW,
evdev::Key::KEY_REDO,
evdev::Key::KEY_F13,
evdev::Key::KEY_F14,
evdev::Key::KEY_F15,
evdev::Key::KEY_F16,
evdev::Key::KEY_F17,
evdev::Key::KEY_F18,
evdev::Key::KEY_F19,
evdev::Key::KEY_F20,
evdev::Key::KEY_F21,
evdev::Key::KEY_F22,
evdev::Key::KEY_F23,
evdev::Key::KEY_F24,
evdev::Key::KEY_PLAYCD,
evdev::Key::KEY_PAUSECD,
evdev::Key::KEY_PROG3,
evdev::Key::KEY_PROG4,
evdev::Key::KEY_DASHBOARD,
evdev::Key::KEY_SUSPEND,
evdev::Key::KEY_CLOSE,
evdev::Key::KEY_PLAY,
evdev::Key::KEY_FASTFORWARD,
evdev::Key::KEY_BASSBOOST,
evdev::Key::KEY_PRINT,
evdev::Key::KEY_HP,
evdev::Key::KEY_CAMERA,
evdev::Key::KEY_SOUND,
evdev::Key::KEY_QUESTION,
evdev::Key::KEY_EMAIL,
evdev::Key::KEY_CHAT,
evdev::Key::KEY_SEARCH,
evdev::Key::KEY_CONNECT,
evdev::Key::KEY_FINANCE,
evdev::Key::KEY_SPORT,
evdev::Key::KEY_SHOP,
evdev::Key::KEY_ALTERASE,
evdev::Key::KEY_CANCEL,
evdev::Key::KEY_BRIGHTNESSDOWN,
evdev::Key::KEY_BRIGHTNESSUP,
evdev::Key::KEY_MEDIA,
evdev::Key::KEY_SWITCHVIDEOMODE,
evdev::Key::KEY_KBDILLUMTOGGLE,
evdev::Key::KEY_KBDILLUMDOWN,
evdev::Key::KEY_KBDILLUMUP,
evdev::Key::KEY_SEND,
evdev::Key::KEY_REPLY,
evdev::Key::KEY_FORWARDMAIL,
evdev::Key::KEY_SAVE,
evdev::Key::KEY_DOCUMENTS,
evdev::Key::KEY_BATTERY,
evdev::Key::KEY_BLUETOOTH,
evdev::Key::KEY_WLAN,
evdev::Key::KEY_UWB,
evdev::Key::KEY_UNKNOWN,
evdev::Key::KEY_VIDEO_NEXT,
evdev::Key::KEY_VIDEO_PREV,
evdev::Key::KEY_BRIGHTNESS_CYCLE,
evdev::Key::KEY_BRIGHTNESS_AUTO,
evdev::Key::KEY_DISPLAY_OFF,
evdev::Key::KEY_WWAN,
evdev::Key::KEY_RFKILL,
evdev::Key::KEY_MICMUTE,
evdev::Key::BTN_0,
evdev::Key::BTN_1,
evdev::Key::BTN_2,
evdev::Key::BTN_3,
evdev::Key::BTN_4,
evdev::Key::BTN_5,
evdev::Key::BTN_6,
evdev::Key::BTN_7,
evdev::Key::BTN_8,
evdev::Key::BTN_9,
evdev::Key::BTN_LEFT,
evdev::Key::BTN_RIGHT,
evdev::Key::BTN_MIDDLE,
evdev::Key::BTN_SIDE,
evdev::Key::BTN_EXTRA,
evdev::Key::BTN_FORWARD,
evdev::Key::BTN_BACK,
evdev::Key::BTN_TASK,
evdev::Key::BTN_TRIGGER,
evdev::Key::BTN_THUMB,
evdev::Key::BTN_THUMB2,
evdev::Key::BTN_TOP,
evdev::Key::BTN_TOP2,
evdev::Key::BTN_PINKIE,
evdev::Key::BTN_BASE,
evdev::Key::BTN_BASE2,
evdev::Key::BTN_BASE3,
evdev::Key::BTN_BASE4,
evdev::Key::BTN_BASE5,
evdev::Key::BTN_BASE6,
evdev::Key::BTN_DEAD,
evdev::Key::BTN_SOUTH,
evdev::Key::BTN_EAST,
evdev::Key::BTN_C,
evdev::Key::BTN_NORTH,
evdev::Key::BTN_WEST,
evdev::Key::BTN_Z,
evdev::Key::BTN_TL,
evdev::Key::BTN_TR,
evdev::Key::BTN_TL2,
evdev::Key::BTN_TR2,
evdev::Key::BTN_SELECT,
evdev::Key::BTN_START,
evdev::Key::BTN_MODE,
evdev::Key::BTN_THUMBL,
evdev::Key::BTN_THUMBR,
evdev::Key::BTN_TOOL_PEN,
evdev::Key::BTN_TOOL_RUBBER,
evdev::Key::BTN_TOOL_BRUSH,
evdev::Key::BTN_TOOL_PENCIL,
evdev::Key::BTN_TOOL_AIRBRUSH,
evdev::Key::BTN_TOOL_FINGER,
evdev::Key::BTN_TOOL_MOUSE,
evdev::Key::BTN_TOOL_LENS,
evdev::Key::BTN_TOOL_QUINTTAP,
evdev::Key::BTN_TOUCH,
evdev::Key::BTN_STYLUS,
evdev::Key::BTN_STYLUS2,
evdev::Key::BTN_TOOL_DOUBLETAP,
evdev::Key::BTN_TOOL_TRIPLETAP,
evdev::Key::BTN_TOOL_QUADTAP,
evdev::Key::BTN_GEAR_DOWN,
evdev::Key::BTN_GEAR_UP,
evdev::Key::KEY_OK,
evdev::Key::KEY_SELECT,
evdev::Key::KEY_GOTO,
evdev::Key::KEY_CLEAR,
evdev::Key::KEY_POWER2,
evdev::Key::KEY_OPTION,
evdev::Key::KEY_INFO,
evdev::Key::KEY_TIME,
evdev::Key::KEY_VENDOR,
evdev::Key::KEY_ARCHIVE,
evdev::Key::KEY_PROGRAM,
evdev::Key::KEY_CHANNEL,
evdev::Key::KEY_FAVORITES,
evdev::Key::KEY_EPG,
evdev::Key::KEY_PVR,
evdev::Key::KEY_MHP,
evdev::Key::KEY_LANGUAGE,
evdev::Key::KEY_TITLE,
evdev::Key::KEY_SUBTITLE,
evdev::Key::KEY_ANGLE,
evdev::Key::KEY_ZOOM,
evdev::Key::KEY_FULL_SCREEN,
evdev::Key::KEY_MODE,
evdev::Key::KEY_KEYBOARD,
evdev::Key::KEY_SCREEN,
evdev::Key::KEY_PC,
evdev::Key::KEY_TV,
evdev::Key::KEY_TV2,
evdev::Key::KEY_VCR,
evdev::Key::KEY_VCR2,
evdev::Key::KEY_SAT,
evdev::Key::KEY_SAT2,
evdev::Key::KEY_CD,
evdev::Key::KEY_TAPE,
evdev::Key::KEY_RADIO,
evdev::Key::KEY_TUNER,
evdev::Key::KEY_PLAYER,
evdev::Key::KEY_TEXT,
evdev::Key::KEY_DVD,
evdev::Key::KEY_AUX,
evdev::Key::KEY_MP3,
evdev::Key::KEY_AUDIO,
evdev::Key::KEY_VIDEO,
evdev::Key::KEY_DIRECTORY,
evdev::Key::KEY_LIST,
evdev::Key::KEY_MEMO,
evdev::Key::KEY_CALENDAR,
evdev::Key::KEY_RED,
evdev::Key::KEY_GREEN,
evdev::Key::KEY_YELLOW,
evdev::Key::KEY_BLUE,
evdev::Key::KEY_CHANNELUP,
evdev::Key::KEY_CHANNELDOWN,
evdev::Key::KEY_FIRST,
evdev::Key::KEY_LAST,
evdev::Key::KEY_AB,
evdev::Key::KEY_NEXT,
evdev::Key::KEY_RESTART,
evdev::Key::KEY_SLOW,
evdev::Key::KEY_SHUFFLE,
evdev::Key::KEY_BREAK,
evdev::Key::KEY_PREVIOUS,
evdev::Key::KEY_DIGITS,
evdev::Key::KEY_TEEN,
evdev::Key::KEY_TWEN,
evdev::Key::KEY_VIDEOPHONE,
evdev::Key::KEY_GAMES,
evdev::Key::KEY_ZOOMIN,
evdev::Key::KEY_ZOOMOUT,
evdev::Key::KEY_ZOOMRESET,
evdev::Key::KEY_WORDPROCESSOR,
evdev::Key::KEY_EDITOR,
evdev::Key::KEY_SPREADSHEET,
evdev::Key::KEY_GRAPHICSEDITOR,
evdev::Key::KEY_PRESENTATION,
evdev::Key::KEY_DATABASE,
evdev::Key::KEY_NEWS,
evdev::Key::KEY_VOICEMAIL,
evdev::Key::KEY_ADDRESSBOOK,
evdev::Key::KEY_MESSENGER,
evdev::Key::KEY_DISPLAYTOGGLE,
evdev::Key::KEY_SPELLCHECK,
evdev::Key::KEY_LOGOFF,
evdev::Key::KEY_DOLLAR,
evdev::Key::KEY_EURO,
evdev::Key::KEY_FRAMEBACK,
evdev::Key::KEY_FRAMEFORWARD,
evdev::Key::KEY_CONTEXT_MENU,
evdev::Key::KEY_MEDIA_REPEAT,
evdev::Key::KEY_10CHANNELSUP,
evdev::Key::KEY_10CHANNELSDOWN,
evdev::Key::KEY_IMAGES,
evdev::Key::KEY_DEL_EOL,
evdev::Key::KEY_DEL_EOS,
evdev::Key::KEY_INS_LINE,
evdev::Key::KEY_DEL_LINE,
evdev::Key::KEY_FN,
evdev::Key::KEY_FN_ESC,
evdev::Key::KEY_FN_F1,
evdev::Key::KEY_FN_F2,
evdev::Key::KEY_FN_F3,
evdev::Key::KEY_FN_F4,
evdev::Key::KEY_FN_F5,
evdev::Key::KEY_FN_F6,
evdev::Key::KEY_FN_F7,
evdev::Key::KEY_FN_F8,
evdev::Key::KEY_FN_F9,
evdev::Key::KEY_FN_F10,
evdev::Key::KEY_FN_F11,
evdev::Key::KEY_FN_F12,
evdev::Key::KEY_FN_1,
evdev::Key::KEY_FN_2,
evdev::Key::KEY_FN_D,
evdev::Key::KEY_FN_E,
evdev::Key::KEY_FN_F,
evdev::Key::KEY_FN_S,
evdev::Key::KEY_FN_B,
evdev::Key::KEY_BRL_DOT1,
evdev::Key::KEY_BRL_DOT2,
evdev::Key::KEY_BRL_DOT3,
evdev::Key::KEY_BRL_DOT4,
evdev::Key::KEY_BRL_DOT5,
evdev::Key::KEY_BRL_DOT6,
evdev::Key::KEY_BRL_DOT7,
evdev::Key::KEY_BRL_DOT8,
evdev::Key::KEY_BRL_DOT9,
evdev::Key::KEY_BRL_DOT10,
evdev::Key::KEY_NUMERIC_0,
evdev::Key::KEY_NUMERIC_1,
evdev::Key::KEY_NUMERIC_2,
evdev::Key::KEY_NUMERIC_3,
evdev::Key::KEY_NUMERIC_4,
evdev::Key::KEY_NUMERIC_5,
evdev::Key::KEY_NUMERIC_6,
evdev::Key::KEY_NUMERIC_7,
evdev::Key::KEY_NUMERIC_8,
evdev::Key::KEY_NUMERIC_9,
evdev::Key::KEY_NUMERIC_STAR,
evdev::Key::KEY_NUMERIC_POUND,
evdev::Key::KEY_NUMERIC_A,
evdev::Key::KEY_NUMERIC_B,
evdev::Key::KEY_NUMERIC_C,
evdev::Key::KEY_NUMERIC_D,
evdev::Key::KEY_CAMERA_FOCUS,
evdev::Key::KEY_WPS_BUTTON,
evdev::Key::KEY_TOUCHPAD_TOGGLE,
evdev::Key::KEY_TOUCHPAD_ON,
evdev::Key::KEY_TOUCHPAD_OFF,
evdev::Key::KEY_CAMERA_ZOOMIN,
evdev::Key::KEY_CAMERA_ZOOMOUT,
evdev::Key::KEY_CAMERA_UP,
evdev::Key::KEY_CAMERA_DOWN,
evdev::Key::KEY_CAMERA_LEFT,
evdev::Key::KEY_CAMERA_RIGHT,
evdev::Key::KEY_ATTENDANT_ON,
evdev::Key::KEY_ATTENDANT_OFF,
evdev::Key::KEY_ATTENDANT_TOGGLE,
evdev::Key::KEY_LIGHTS_TOGGLE,
evdev::Key::BTN_DPAD_UP,
evdev::Key::BTN_DPAD_DOWN,
evdev::Key::BTN_DPAD_LEFT,
evdev::Key::BTN_DPAD_RIGHT,
evdev::Key::KEY_ALS_TOGGLE,
evdev::Key::KEY_BUTTONCONFIG,
evdev::Key::KEY_TASKMANAGER,
evdev::Key::KEY_JOURNAL,
evdev::Key::KEY_CONTROLPANEL,
evdev::Key::KEY_APPSELECT,
evdev::Key::KEY_SCREENSAVER,
evdev::Key::KEY_VOICECOMMAND,
evdev::Key::KEY_ASSISTANT,
evdev::Key::KEY_KBD_LAYOUT_NEXT,
evdev::Key::KEY_BRIGHTNESS_MIN,
evdev::Key::KEY_BRIGHTNESS_MAX,
evdev::Key::KEY_KBDINPUTASSIST_PREV,
evdev::Key::KEY_KBDINPUTASSIST_NEXT,
evdev::Key::KEY_KBDINPUTASSIST_PREVGROUP,
evdev::Key::KEY_KBDINPUTASSIST_NEXTGROUP,
evdev::Key::KEY_KBDINPUTASSIST_ACCEPT,
evdev::Key::KEY_KBDINPUTASSIST_CANCEL,
evdev::Key::KEY_RIGHT_UP,
evdev::Key::KEY_RIGHT_DOWN,
evdev::Key::KEY_LEFT_UP,
evdev::Key::KEY_LEFT_DOWN,
evdev::Key::KEY_ROOT_MENU,
evdev::Key::KEY_MEDIA_TOP_MENU,
evdev::Key::KEY_NUMERIC_11,
evdev::Key::KEY_NUMERIC_12,
evdev::Key::KEY_AUDIO_DESC,
evdev::Key::KEY_3D_MODE,
evdev::Key::KEY_NEXT_FAVORITE,
evdev::Key::KEY_STOP_RECORD,
evdev::Key::KEY_PAUSE_RECORD,
evdev::Key::KEY_VOD,
evdev::Key::KEY_UNMUTE,
evdev::Key::KEY_FASTREVERSE,
evdev::Key::KEY_SLOWREVERSE,
evdev::Key::KEY_DATA,
evdev::Key::KEY_ONSCREEN_KEYBOARD,
evdev::Key::KEY_PRIVACY_SCREEN_TOGGLE,
evdev::Key::KEY_SELECTIVE_SCREENSHOT,
evdev::Key::BTN_TRIGGER_HAPPY1,
evdev::Key::BTN_TRIGGER_HAPPY2,
evdev::Key::BTN_TRIGGER_HAPPY3,
evdev::Key::BTN_TRIGGER_HAPPY4,
evdev::Key::BTN_TRIGGER_HAPPY5,
evdev::Key::BTN_TRIGGER_HAPPY6,
evdev::Key::BTN_TRIGGER_HAPPY7,
evdev::Key::BTN_TRIGGER_HAPPY8,
evdev::Key::BTN_TRIGGER_HAPPY9,
evdev::Key::BTN_TRIGGER_HAPPY10,
evdev::Key::BTN_TRIGGER_HAPPY11,
evdev::Key::BTN_TRIGGER_HAPPY12,
evdev::Key::BTN_TRIGGER_HAPPY13,
evdev::Key::BTN_TRIGGER_HAPPY14,
evdev::Key::BTN_TRIGGER_HAPPY15,
evdev::Key::BTN_TRIGGER_HAPPY16,
evdev::Key::BTN_TRIGGER_HAPPY17,
evdev::Key::BTN_TRIGGER_HAPPY18,
evdev::Key::BTN_TRIGGER_HAPPY19,
evdev::Key::BTN_TRIGGER_HAPPY20,
evdev::Key::BTN_TRIGGER_HAPPY21,
evdev::Key::BTN_TRIGGER_HAPPY22,
evdev::Key::BTN_TRIGGER_HAPPY23,
evdev::Key::BTN_TRIGGER_HAPPY24,
evdev::Key::BTN_TRIGGER_HAPPY25,
evdev::Key::BTN_TRIGGER_HAPPY26,
evdev::Key::BTN_TRIGGER_HAPPY27,
evdev::Key::BTN_TRIGGER_HAPPY28,
evdev::Key::BTN_TRIGGER_HAPPY29,
evdev::Key::BTN_TRIGGER_HAPPY30,
evdev::Key::BTN_TRIGGER_HAPPY31,
evdev::Key::BTN_TRIGGER_HAPPY32,
evdev::Key::BTN_TRIGGER_HAPPY33,
evdev::Key::BTN_TRIGGER_HAPPY34,
evdev::Key::BTN_TRIGGER_HAPPY35,
evdev::Key::BTN_TRIGGER_HAPPY36,
evdev::Key::BTN_TRIGGER_HAPPY37,
evdev::Key::BTN_TRIGGER_HAPPY38,
evdev::Key::BTN_TRIGGER_HAPPY39,
evdev::Key::BTN_TRIGGER_HAPPY40,
];
}
Loading…
Cancel
Save