From de0cc97475b0d953d2e13d05b5758b06ea863302 Mon Sep 17 00:00:00 2001 From: cyber-sushi Date: Sat, 13 Apr 2024 05:39:22 +0200 Subject: [PATCH] Added shell command invocation through bindings --- examples/config-keyboard.toml | 7 + examples/config-mouse.toml | 6 +- examples/config-playstation.toml | 6 +- examples/config-ps2.toml | 6 +- examples/config-stadia.toml | 6 +- examples/config-switch.toml | 6 +- examples/config-xbox.toml | 6 +- src/config.rs | 255 ++++++++++++++++++++----------- src/event_reader.rs | 28 +++- 9 files changed, 222 insertions(+), 104 deletions(-) diff --git a/examples/config-keyboard.toml b/examples/config-keyboard.toml index f9ca4f3..3a3d45a 100644 --- a/examples/config-keyboard.toml +++ b/examples/config-keyboard.toml @@ -13,5 +13,12 @@ KEY_LEFTSHIFT.KEY_UP = ["KEY_LEFTSHIFT", "KEY_PAGEUP"] KEY_LEFTSHIFT.KEY_DOWN = ["KEY_LEFTSHIFT", "KEY_PAGEDOWN"] KEY_LEFTSHIFT-KEY_LEFTMETA-KEY_LEFTALT.BTN_RIGHT = ["KEY_LEFTCTRL", "KEY_C"] +[commands] +#Examples of Modifier + Key => run a shell command +KEY_LEFTCTRL.KEY_N = ["nautilus"] +KEY_LEFTMETA.KEY_F = ["firefox", "discord"] +KEY_LEFTALT.KEY_SPACE = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] +KEY_LEFTCTRL-KEY_LEFTALT-KEY_LEFTSHIFT.KEY_O = ["notify-send 'OwO'"] + [settings] GRAB_DEVICE = "true" #gain exclusivity on the device diff --git a/examples/config-mouse.toml b/examples/config-mouse.toml index ad7e682..ea6708b 100644 --- a/examples/config-mouse.toml +++ b/examples/config-mouse.toml @@ -15,8 +15,12 @@ KEY_LEFTCTRL.BTN_RIGHT = ["KEY_SYSRQ"] KEY_LEFTCTRL-KEY_LEFTSHIFT.BTN_LEFT = ["KEY_LEFTSHIFT", "KEY_DELETE"] #Examples of Modifier(s) + Axis event => Key(s) KEY_LEFTCTRL-KEY_LEFTSHIFT.SCROLL_WHEEL_UP = ["KEY_HOME"] -KEY_LEFTCTRL-KEY_LEFTSHIFT.SCROLL_WHEEL_DOWN = ["KEY_END"] KEY_LEFTCTRL-KEY_LEFTSHIFT-KEY_LEFTALT.SCROLL_WHEEL_DOWN = ["KEY_LEFTALT", "KEY_F4"] +[commands] +#Examples of Modifier + Key => run a shell command +KEY_LEFTCTRL-KEY_LEFTSHIFT.SCROLL_WHEEL_DOWN = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] +BTN_MIDDLE = ["notify-send 'OwO'"] + [settings] GRAB_DEVICE = "true" #gain exclusivity on the device diff --git a/examples/config-playstation.toml b/examples/config-playstation.toml index 9cfd0fd..90caca3 100644 --- a/examples/config-playstation.toml +++ b/examples/config-playstation.toml @@ -27,8 +27,10 @@ BTN_DPAD_DOWN = ["KEY_DOWN"] #directional pad down BTN_DPAD_LEFT = ["KEY_LEFT"] #directional pad left RSTICK_UP = ["KEY_UP"] #right analog stick up RSTICK_DOWN = ["KEY_DOWN"] #right analog stick down -RSTICK_LEFT = ["KEY_LEFT"] #right analog stick left -RSTICK_RIGHT = ["KEY_RIGHT"] #right analog stick right + +[commands] +RSTICK_LEFT = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] #right analog stick left +RSTICK_RIGHT = ["firefox", "discord"] #right analog stick right [settings] LSTICK_SENSITIVITY = "6" #sensitivity when scrolling or moving cursor, lower value is higher sensitivity, minimum 1 diff --git a/examples/config-ps2.toml b/examples/config-ps2.toml index 44356cd..ae38f20 100644 --- a/examples/config-ps2.toml +++ b/examples/config-ps2.toml @@ -26,8 +26,10 @@ BTN_DPAD_DOWN = ["KEY_DOWN"] #directional pad down BTN_DPAD_LEFT = ["KEY_LEFT"] #directional pad left RSTICK_UP = ["KEY_UP"] #right analog stick up RSTICK_DOWN = ["KEY_DOWN"] #right analog stick down -RSTICK_LEFT = ["KEY_LEFT"] #right analog stick left -RSTICK_RIGHT = ["KEY_RIGHT"] #right analog stick right + +[commands] +RSTICK_LEFT = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] #right analog stick left +RSTICK_RIGHT = ["firefox", "discord"] #right analog stick right [settings] LSTICK_SENSITIVITY = "6" #sensitivity when scrolling or moving cursor, lower value is higher sensitivity, minimum 1 diff --git a/examples/config-stadia.toml b/examples/config-stadia.toml index fcb2a53..b953f20 100644 --- a/examples/config-stadia.toml +++ b/examples/config-stadia.toml @@ -29,8 +29,10 @@ BTN_DPAD_DOWN = ["KEY_DOWN"] #directional pad down BTN_DPAD_LEFT = ["KEY_LEFT"] #directional pad left RSTICK_UP = ["KEY_UP"] #right analog stick up RSTICK_DOWN = ["KEY_DOWN"] #right analog stick down -RSTICK_LEFT = ["KEY_LEFT"] #right analog stick left -RSTICK_RIGHT = ["KEY_RIGHT"] #right analog stick right + +[commands] +RSTICK_LEFT = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] #right analog stick left +RSTICK_RIGHT = ["firefox", "discord"] #right analog stick right [settings] LSTICK_SENSITIVITY = "6" #sensitivity when scrolling or moving cursor, lower value is higher sensitivity, minimum 1 diff --git a/examples/config-switch.toml b/examples/config-switch.toml index 32df786..1682ff9 100644 --- a/examples/config-switch.toml +++ b/examples/config-switch.toml @@ -28,8 +28,10 @@ BTN_DPAD_DOWN = ["KEY_DOWN"] #directional pad down BTN_DPAD_LEFT = ["KEY_LEFT"] #directional pad left RSTICK_UP = ["KEY_UP"] #right analog stick up RSTICK_DOWN = ["KEY_DOWN"] #right analog stick down -RSTICK_LEFT = ["KEY_LEFT"] #right analog stick left -RSTICK_RIGHT = ["KEY_RIGHT"] #right analog stick right + +[commands] +RSTICK_LEFT = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] #right analog stick left +RSTICK_RIGHT = ["firefox", "discord"] #right analog stick right [settings] LSTICK_SENSITIVITY = "6" #sensitivity when scrolling or moving cursor, lower value is higher sensitivity, minimum 1 diff --git a/examples/config-xbox.toml b/examples/config-xbox.toml index 77f3bc6..eb25654 100644 --- a/examples/config-xbox.toml +++ b/examples/config-xbox.toml @@ -27,8 +27,10 @@ BTN_DPAD_DOWN = ["KEY_DOWN"] #directional pad down BTN_DPAD_LEFT = ["KEY_LEFT"] #directional pad left RSTICK_UP = ["KEY_UP"] #right analog stick up RSTICK_DOWN = ["KEY_DOWN"] #right analog stick down -RSTICK_LEFT = ["KEY_LEFT"] #right analog stick left -RSTICK_RIGHT = ["KEY_RIGHT"] #right analog stick right + +[commands] +RSTICK_LEFT = ["foot sh -c 'pacman -Q | wc -l && sleep 1 && neofetch' && sleep 5"] #right analog stick left +RSTICK_RIGHT = ["firefox", "discord"] #right analog stick right [settings] LSTICK_SENSITIVITY = "6" #sensitivity when scrolling or moving cursor, lower value is higher sensitivity, minimum 1 diff --git a/src/config.rs b/src/config.rs index 831f297..5f2ce96 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,118 +8,202 @@ use serde; pub struct Bindings { pub keys: HashMap>, pub axis: HashMap>, + pub keys_sh: HashMap>, + pub axis_sh: HashMap>, } #[derive(Default, Debug, Clone)] pub struct Combinations { pub keys: HashMap>>, pub axis: HashMap>>, + pub keys_sh: HashMap>>, + pub axis_sh: HashMap>>, } #[derive(Default, Debug, Clone)] pub struct Modifiers { pub keys: HashMap, HashMap>>, pub axis: HashMap, HashMap>>, + pub keys_sh: HashMap, HashMap>>, + pub axis_sh: HashMap, HashMap>>, } + #[derive(serde::Deserialize, Debug, Clone)] -pub struct Config { - #[serde(skip)] - pub name: String, +pub struct RawConfig { #[serde(default)] pub remap: HashMap>, - #[serde(skip)] + #[serde(default)] + pub commands: HashMap>, + pub settings: HashMap, +} + +impl RawConfig { + fn new_from_file(file: &str) -> Self { + println!("Parsing config file:\n{:?}\n", file); + let file_content: String = std::fs::read_to_string(file).unwrap(); + let raw_config: RawConfig = toml::from_str(&file_content) + .expect("Couldn't parse config file."); + let remap = raw_config.remap; + let commands = raw_config.commands; + let settings = raw_config.settings; + Self { + remap, + commands, + settings, + } + } +} + +#[derive(Debug, Clone)] +pub struct Config { + pub name: String, pub bindings: Bindings, - #[serde(skip)] - pub combinations: Combinations, - #[serde(skip)] pub modifiers: Modifiers, pub settings: HashMap, } impl Config { pub fn new_from_file(file: &str, file_name: String) -> Self { - println!("Parsing config file:\n{:?}\n", file); - let file_content: String = std::fs::read_to_string(file).unwrap(); - let config: Config = toml::from_str(&file_content) - .expect("Couldn't parse config file."); - let settings: HashMap = config.settings; - let remap: HashMap> = config.remap; - let mut bindings: Bindings = Default::default(); - let mut combinations: Combinations = Default::default(); + let raw_config = RawConfig::new_from_file(file); + let (bindings, combinations, settings) = parse_raw_config(raw_config); + let bindings: Bindings = merge_axis_bindings(bindings); + let modifiers: Modifiers = parse_modifiers(combinations); - let abs = [ - "DPAD_UP", - "DPAD_DOWN", - "DPAD_LEFT", - "DPAD_RIGHT", - "LSTICK_UP", - "LSTICK_DOWN", - "LSTICK_LEFT", - "LSTICK_RIGHT", - "RSTICK_UP", - "RSTICK_DOWN", - "RSTICK_LEFT", - "RSTICK_RIGHT", - "SCROLLWHEEL_UP", - "SCROLLWHEEL_DOWN", - "BTN_TL2", - "BTN_TR2", - ]; - for (input, output) in remap.clone().into_iter() { - if input.contains(".") { - let (mods, key) = input.split_once(".").unwrap(); - if abs.contains(&key) { - if !combinations.axis.contains_key(&mods.to_string()) { - combinations.axis.insert(mods.to_string(), HashMap::from([(key.to_string(), output)])); - } else { - combinations.axis.get_mut(mods).unwrap().insert(key.to_string(), output); - } + Self { + name: file_name, + bindings, + modifiers, + settings, + } + } +} + +fn parse_raw_config(raw_config: RawConfig) -> (Bindings, Combinations, HashMap) { + let remap: HashMap> = raw_config.remap; + let commands: HashMap> = raw_config.commands; + let settings: HashMap = raw_config.settings; + let mut bindings: Bindings = Default::default(); + let mut combinations: Combinations = Default::default(); + + let abs = [ + "DPAD_UP", + "DPAD_DOWN", + "DPAD_LEFT", + "DPAD_RIGHT", + "LSTICK_UP", + "LSTICK_DOWN", + "LSTICK_LEFT", + "LSTICK_RIGHT", + "RSTICK_UP", + "RSTICK_DOWN", + "RSTICK_LEFT", + "RSTICK_RIGHT", + "SCROLLWHEEL_UP", + "SCROLLWHEEL_DOWN", + "BTN_TL2", + "BTN_TR2", + ]; + + for (input, output) in remap.clone().into_iter() { + if input.contains(".") { + let (mods, key) = input.split_once(".").unwrap(); + if abs.contains(&key) { + if !combinations.axis.contains_key(&mods.to_string()) { + combinations.axis.insert(mods.to_string(), HashMap::from([(key.to_string(), output)])); } else { - if !combinations.keys.contains_key(&mods.to_string()) { - combinations.keys.insert(mods.to_string(), HashMap::from([(Key::from_str(key).expect("Invalid KEY value."), output)])); - } else { - combinations.keys.get_mut(mods).unwrap().insert(Key::from_str(key).expect("Invalid KEY value."), output); - } + combinations.axis.get_mut(mods).unwrap().insert(key.to_string(), output); } } else { - if abs.contains(&input.as_str()) { - bindings.axis.insert(input, output); + if !combinations.keys.contains_key(&mods.to_string()) { + combinations.keys.insert(mods.to_string(), HashMap::from([(Key::from_str(key).expect("Invalid KEY value."), output)])); } else { - bindings.keys.insert(Key::from_str(input.as_str()).expect("Invalid KEY value."), output); + combinations.keys.get_mut(mods).unwrap().insert(Key::from_str(key).expect("Invalid KEY value."), output); } } - } - - let empty_modmap = BTreeMap::from ([ - (Key::KEY_LEFTSHIFT, 0), - (Key::KEY_LEFTCTRL, 0), - (Key::KEY_LEFTALT, 0), - (Key::KEY_RIGHTSHIFT, 0), - (Key::KEY_RIGHTCTRL, 0), - (Key::KEY_RIGHTALT, 0), - (Key::KEY_LEFTMETA, 0) - ]); - let mut modifiers: Modifiers = Default::default(); - - for (mods, key) in combinations.keys.iter() { - let mods_vector = mods.split("+").map(str::to_string).collect::>(); - let mut modmap = empty_modmap.clone(); - for modifier in mods_vector { - modmap.insert(Key::from_str(&modifier).unwrap(), 1); + } else { + if abs.contains(&input.as_str()) { + bindings.axis.insert(input, output); + } else { + bindings.keys.insert(Key::from_str(input.as_str()).expect("Invalid KEY value."), output); } - modifiers.keys.insert(modmap, key.clone()); - } - - for (mods, key) in combinations.axis.iter() { - let mods_vector = mods.split("+").map(str::to_string).collect::>(); - let mut modmap = empty_modmap.clone(); - for modifier in mods_vector { - modmap.insert(Key::from_str(&modifier).unwrap(), 1); - } - modifiers.axis.insert(modmap, key.clone()); } + } + for (input, output) in commands.clone().into_iter() { + if input.contains(".") { + let (mods, key) = input.split_once(".").unwrap(); + if abs.contains(&key) { + if !combinations.axis_sh.contains_key(&mods.to_string()) { + combinations.axis_sh.insert(mods.to_string(), HashMap::from([(key.to_string(), output)])); + } else { + combinations.axis_sh.get_mut(mods).unwrap().insert(key.to_string(), output); + } + } else { + if !combinations.keys_sh.contains_key(&mods.to_string()) { + combinations.keys_sh.insert(mods.to_string(), HashMap::from([(Key::from_str(key).expect("Invalid KEY value."), output)])); + } else { + combinations.keys_sh.get_mut(mods).unwrap().insert(Key::from_str(key).expect("Invalid KEY value."), output); + } + } + } else { + if abs.contains(&input.as_str()) { + bindings.axis_sh.insert(input, output); + } else { + bindings.keys_sh.insert(Key::from_str(input.as_str()).expect("Invalid KEY value."), output); + } + } + } + (bindings, combinations, settings) +} + +fn parse_modifiers(combinations: Combinations) -> Modifiers { + let empty_modmap = BTreeMap::from ([ + (Key::KEY_LEFTSHIFT, 0), + (Key::KEY_LEFTCTRL, 0), + (Key::KEY_LEFTALT, 0), + (Key::KEY_RIGHTSHIFT, 0), + (Key::KEY_RIGHTCTRL, 0), + (Key::KEY_RIGHTALT, 0), + (Key::KEY_LEFTMETA, 0) + ]); + let mut modifiers: Modifiers = Default::default(); + for (mods, key) in combinations.keys.iter() { + let mods_vector = mods.split("-").map(str::to_string).collect::>(); + let mut modmap = empty_modmap.clone(); + for modifier in mods_vector { + modmap.insert(Key::from_str(&modifier).unwrap(), 1); + } + modifiers.keys.insert(modmap, key.clone()); + } + for (mods, key) in combinations.axis.iter() { + let mods_vector = mods.split("-").map(str::to_string).collect::>(); + let mut modmap = empty_modmap.clone(); + for modifier in mods_vector { + modmap.insert(Key::from_str(&modifier).unwrap(), 1); + } + modifiers.axis.insert(modmap, key.clone()); + } + for (mods, key) in combinations.keys_sh.iter() { + let mods_vector = mods.split("-").map(str::to_string).collect::>(); + let mut modmap = empty_modmap.clone(); + for modifier in mods_vector { + modmap.insert(Key::from_str(&modifier).unwrap(), 1); + } + modifiers.keys_sh.insert(modmap, key.clone()); + } + for (mods, key) in combinations.axis_sh.iter() { + let mods_vector = mods.split("-").map(str::to_string).collect::>(); + let mut modmap = empty_modmap.clone(); + for modifier in mods_vector { + modmap.insert(Key::from_str(&modifier).unwrap(), 1); + } + modifiers.axis_sh.insert(modmap, key.clone()); + } + modifiers +} + +fn merge_axis_bindings(mut bindings: Bindings) -> Bindings { let mut pad_x: Vec = bindings.axis.get("BTN_DPAD_LEFT") .unwrap_or(&Vec::new()).clone(); pad_x.extend(bindings.axis.get("BTN_DPAD_RIGHT") @@ -128,8 +212,8 @@ impl Config { .unwrap_or(&Vec::new()).clone(); pad_y.extend(bindings.axis.get("BTN_DPAD_DOWN") .unwrap_or(&Vec::new())); - bindings.axis.insert("NONE_X".to_string(), pad_x); - bindings.axis.insert("NONE_Y".to_string(), pad_y); + bindings.axis.insert("BTN_DPAD_X".to_string(), pad_x); + bindings.axis.insert("BTN_DPAD_y".to_string(), pad_y); let mut lstick_x: Vec = bindings.axis.get("LSTICK_LEFT") .unwrap_or(&Vec::new()).clone(); @@ -152,14 +236,5 @@ impl Config { .unwrap_or(&Vec::new())); bindings.axis.insert("RSTICK_X".to_string(), rstick_x); bindings.axis.insert("RSTICK_Y".to_string(), rstick_y); - - Self { - name: file_name, - remap, - bindings, - combinations, - modifiers, - settings, - } - } + bindings } diff --git a/src/event_reader.rs b/src/event_reader.rs index f185bda..38afebd 100644 --- a/src/event_reader.rs +++ b/src/event_reader.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, BTreeMap}, sync::Arc, option::Option}; +use std::{collections::{HashMap, BTreeMap}, sync::Arc, option::Option, process::Command}; use tokio::sync::Mutex; use tokio_stream::StreamExt; use evdev::{EventStream, Key, RelativeAxisType, AbsoluteAxisType, EventType, InputEvent}; @@ -257,9 +257,16 @@ impl EventReader { self.emit_event_without_modifiers(event_list, &modifiers, event.value()).await; return } + } else if let Some(command_hashmap) = path.modifiers.keys_sh.get(&modifiers) { + if let Some(command_list) = command_hashmap.get(&Key(event.code())) { + spawn_subprocess(command_list).await; + return + } } if let Some(event_list) = path.bindings.keys.get(&Key(event.code())) { self.emit_event(event_list, event.value()).await; + } else if let Some(command_list) = path.bindings.keys_sh.get(&Key(event.code())) { + spawn_subprocess(command_list).await; } else { self.emit_default_event(event).await; } @@ -276,13 +283,19 @@ impl EventReader { } return } + } else if let Some(command_hashmap) = path.modifiers.axis_sh.get(&modifiers) { + if let Some(command_list) = command_hashmap.get(event_string) { + spawn_subprocess(command_list).await; + return + } } if let Some(event_list) = path.bindings.axis.get(event_string) { - println!("{:?}", event_list); self.emit_event(event_list, event.value()).await; if send_zero { self.emit_event_without_modifiers(event_list, &modifiers, 0).await; } + } else if let Some(command_list) = path.bindings.axis_sh.get(event_string) { + spawn_subprocess(command_list).await; } else { self.emit_default_event(event).await; } @@ -339,7 +352,7 @@ impl EventReader { virt_dev.keys.emit(&[virtual_event]).unwrap(); } } - + async fn get_axis_value(&self, event: &InputEvent, deadzone: &i32) -> i32 { let distance_from_center: i32 = match self.settings.axis_16_bit { false => (event.value() as i32 - 128) * 200, @@ -438,3 +451,12 @@ impl EventReader { } } +async fn spawn_subprocess(command_list: &Vec) { + for command in command_list { + Command::new("sh") + .arg("-c") + .arg(command) + .spawn() + .expect("Failed to run command."); + } +}