parent
85779876dd
commit
f40385ae65
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…
Reference in new issue