Added shell command invocation through bindings

This commit is contained in:
cyber-sushi 2024-04-13 05:39:22 +02:00
parent 3846a358c1
commit de0cc97475
9 changed files with 222 additions and 104 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -8,118 +8,202 @@ use serde;
pub struct Bindings {
pub keys: HashMap<Key, Vec<Key>>,
pub axis: HashMap<String, Vec<Key>>,
pub keys_sh: HashMap<Key, Vec<String>>,
pub axis_sh: HashMap<String, Vec<String>>,
}
#[derive(Default, Debug, Clone)]
pub struct Combinations {
pub keys: HashMap<String, HashMap<Key, Vec<Key>>>,
pub axis: HashMap<String, HashMap<String, Vec<Key>>>,
pub keys_sh: HashMap<String, HashMap<Key, Vec<String>>>,
pub axis_sh: HashMap<String, HashMap<String, Vec<String>>>,
}
#[derive(Default, Debug, Clone)]
pub struct Modifiers {
pub keys: HashMap<BTreeMap<Key, i32>, HashMap<Key, Vec<Key>>>,
pub axis: HashMap<BTreeMap<Key, i32>, HashMap<String, Vec<Key>>>,
pub keys_sh: HashMap<BTreeMap<Key, i32>, HashMap<Key, Vec<String>>>,
pub axis_sh: HashMap<BTreeMap<Key, i32>, HashMap<String, Vec<String>>>,
}
#[derive(serde::Deserialize, Debug, Clone)]
pub struct Config {
#[serde(skip)]
pub name: String,
pub struct RawConfig {
#[serde(default)]
pub remap: HashMap<String, Vec<Key>>,
#[serde(skip)]
#[serde(default)]
pub commands: HashMap<String, Vec<String>>,
pub settings: HashMap<String, String>,
}
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<String, String>,
}
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<String, String> = config.settings;
let remap: HashMap<String, Vec<Key>> = 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<String, String>) {
let remap: HashMap<String, Vec<Key>> = raw_config.remap;
let commands: HashMap<String, Vec<String>> = raw_config.commands;
let settings: HashMap<String, String> = 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::<Vec<String>>();
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::<Vec<String>>();
let mut modmap = empty_modmap.clone();
for modifier in mods_vector {
modmap.insert(Key::from_str(&modifier).unwrap(), 1);
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);
}
modifiers.axis.insert(modmap, key.clone());
}
}
(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::<Vec<String>>();
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::<Vec<String>>();
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::<Vec<String>>();
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::<Vec<String>>();
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<Key> = 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<Key> = 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
}

View file

@ -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;
}
@ -438,3 +451,12 @@ impl EventReader {
}
}
async fn spawn_subprocess(command_list: &Vec<String>) {
for command in command_list {
Command::new("sh")
.arg("-c")
.arg(command)
.spawn()
.expect("Failed to run command.");
}
}