Add modal keybindings

main
Tait Hoyem 2 years ago
parent dd8573c379
commit 78179cc836

@ -1,11 +1,12 @@
[package] [package]
name = "Simple-Wayland-HotKey-Daemon" name = "Simple-Odilia-HotKey-Daemon"
version = "1.0.0" version = "1.0.1"
edition = "2021" edition = "2021"
authors = [ authors = [
"Shinyzenith <aakashsensharma@gmail.com>\n", "Shinyzenith <aakashsensharma@gmail.com>\n",
"Angelo Fallaria <ba.fallaria@gmail.com>\n", "Angelo Fallaria <ba.fallaria@gmail.com>\n",
"EdenQwQ <lsahlm1eden@gmail.com>\n" "EdenQwQ <lsahlm1eden@gmail.com>\n",
"Tait Hoyem <tait@tait.tech>\n"
] ]
[dependencies] [dependencies]
@ -15,6 +16,7 @@ evdev = "0.11.3"
log = "0.4.0" log = "0.4.0"
nix = "0.23.1" nix = "0.23.1"
sysinfo = "0.23.0" sysinfo = "0.23.0"
once_cell = "^1.9.0"
[[bin]] [[bin]]
name = "swhkd" name = "swhkd"

@ -52,6 +52,7 @@ impl fmt::Display for Error {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Hotkey { pub struct Hotkey {
pub mode: String,
pub keysym: evdev::Key, pub keysym: evdev::Key,
pub modifiers: Vec<Modifier>, pub modifiers: Vec<Modifier>,
pub command: String, pub command: String,
@ -76,8 +77,8 @@ pub enum Modifier {
} }
impl Hotkey { impl Hotkey {
pub fn new(keysym: evdev::Key, modifiers: Vec<Modifier>, command: String) -> Self { pub fn new(mode: String, keysym: evdev::Key, modifiers: Vec<Modifier>, command: String) -> Self {
Hotkey { keysym, modifiers, command } Hotkey { mode, keysym, modifiers, command }
} }
} }
@ -281,9 +282,9 @@ fn parse_contents(contents: String) -> Result<Vec<Hotkey>, Error> {
'hotkey_parse: for (key, command) in extracted_keys.iter().zip(extracted_commands.iter()) { 'hotkey_parse: for (key, command) in extracted_keys.iter().zip(extracted_commands.iter()) {
println!("{} {}", key, command); println!("{} {}", key, command);
let (keysym, modifiers) = let (mode, keysym, modifiers) =
parse_keybind(key, line_number + 1, &key_to_evdev_key, &mod_to_mod_enum)?; parse_keybind(key, line_number + 1, &key_to_evdev_key, &mod_to_mod_enum)?;
let hotkey = Hotkey { keysym, modifiers, command: command.to_string() }; let hotkey = Hotkey { mode, keysym, modifiers, command: command.to_string() };
// Ignore duplicate hotkeys // Ignore duplicate hotkeys
for i in hotkeys.iter() { for i in hotkeys.iter() {
@ -306,7 +307,7 @@ fn parse_keybind(
line_nr: u32, line_nr: u32,
key_to_evdev_key: &HashMap<&str, evdev::Key>, key_to_evdev_key: &HashMap<&str, evdev::Key>,
mod_to_mod_enum: &HashMap<&str, Modifier>, mod_to_mod_enum: &HashMap<&str, Modifier>,
) -> Result<(evdev::Key, Vec<Modifier>), Error> { ) -> Result<(String, evdev::Key, Vec<Modifier>), Error> {
let line = line.split('#').next().unwrap(); let line = line.split('#').next().unwrap();
let tokens: Vec<String> = let tokens: Vec<String> =
line.split('+').map(|s| s.trim().to_lowercase()).filter(|s| s != "_").collect(); line.split('+').map(|s| s.trim().to_lowercase()).filter(|s| s != "_").collect();
@ -314,6 +315,9 @@ fn parse_keybind(
// Check if each token is valid // Check if each token is valid
for token in &tokens { for token in &tokens {
if token.starts_with("[") && token.ends_with("]") {
continue;
}
if key_to_evdev_key.contains_key(token.as_str()) { if key_to_evdev_key.contains_key(token.as_str()) {
// Can't have a key that's like a modifier // Can't have a key that's like a modifier
if token != last_token { if token != last_token {
@ -331,13 +335,22 @@ fn parse_keybind(
// Translate keypress into evdev key // Translate keypress into evdev key
let keysym = key_to_evdev_key.get(last_token).unwrap(); let keysym = key_to_evdev_key.get(last_token).unwrap();
let mode = tokens
.iter()
.filter(|s| s.starts_with("[") && s.ends_with("]"))
.map(|s| s.replace(&['[', ']'][..], ""))
.collect::<Vec<String>>()
.get(0)
.unwrap_or(&"".to_string())
.to_string();
let mod_index = if mode.is_empty() { 0 } else { 1 };
let modifiers: Vec<Modifier> = tokens[0..(tokens.len() - 1)] let modifiers: Vec<Modifier> = tokens[mod_index..(tokens.len() - 1)]
.iter() .iter()
.map(|token| *mod_to_mod_enum.get(token.as_str()).unwrap()) .map(|token| *mod_to_mod_enum.get(token.as_str()).unwrap())
.collect(); .collect();
Ok((*keysym, modifiers)) Ok((mode, *keysym, modifiers))
} }
fn extract_curly_brace(line: &str) -> Vec<String> { fn extract_curly_brace(line: &str) -> Vec<String> {
@ -561,7 +574,7 @@ r
eval_config_test( eval_config_test(
contents, contents,
vec![Hotkey::new(evdev::Key::KEY_R, vec![], String::from("alacritty"))], vec![Hotkey::new("".to_string(), evdev::Key::KEY_R, vec![], String::from("alacritty"))],
) )
} }
@ -578,9 +591,9 @@ t
/bin/firefox /bin/firefox
"; ";
let hotkey_1 = Hotkey::new(evdev::Key::KEY_R, vec![], String::from("alacritty")); let hotkey_1 = Hotkey::new("".to_string(), evdev::Key::KEY_R, vec![], String::from("alacritty"));
let hotkey_2 = Hotkey::new(evdev::Key::KEY_W, vec![], String::from("kitty")); let hotkey_2 = Hotkey::new("".to_string(), evdev::Key::KEY_W, vec![], String::from("kitty"));
let hotkey_3 = Hotkey::new(evdev::Key::KEY_T, vec![], String::from("/bin/firefox")); let hotkey_3 = Hotkey::new("".to_string(), evdev::Key::KEY_T, vec![], String::from("/bin/firefox"));
eval_config_test(contents, vec![hotkey_1, hotkey_2, hotkey_3]) eval_config_test(contents, vec![hotkey_1, hotkey_2, hotkey_3])
} }
@ -599,8 +612,8 @@ w
"; ";
let expected_keybinds = vec![ let expected_keybinds = vec![
Hotkey::new(evdev::Key::KEY_R, vec![], String::from("alacritty")), Hotkey::new("".to_string(), evdev::Key::KEY_R, vec![], String::from("alacritty")),
Hotkey::new(evdev::Key::KEY_W, vec![], String::from("kitty")), Hotkey::new("".to_string(), evdev::Key::KEY_W, vec![], String::from("kitty")),
]; ];
eval_config_test(contents, expected_keybinds) eval_config_test(contents, expected_keybinds)
@ -614,7 +627,7 @@ super + 5
"; ";
let expected_keybinds = let expected_keybinds =
vec![Hotkey::new(evdev::Key::KEY_5, vec![Modifier::Super], String::from("alacritty"))]; vec![Hotkey::new("".to_string(), evdev::Key::KEY_5, vec![Modifier::Super], String::from("alacritty"))];
eval_config_test(contents, expected_keybinds) eval_config_test(contents, expected_keybinds)
} }
@ -679,21 +692,25 @@ super + z
let expected_hotkeys = vec![ let expected_hotkeys = vec![
Hotkey::new( Hotkey::new(
"".to_string(),
evdev::Key::KEY_K, evdev::Key::KEY_K,
vec![Modifier::Shift], vec![Modifier::Shift],
"notify-send 'Hello world!'".to_string(), "notify-send 'Hello world!'".to_string(),
), ),
Hotkey::new( Hotkey::new(
"".to_string(),
evdev::Key::KEY_5, evdev::Key::KEY_5,
vec![Modifier::Control], vec![Modifier::Control],
"notify-send 'Hello world!'".to_string(), "notify-send 'Hello world!'".to_string(),
), ),
Hotkey::new( Hotkey::new(
"".to_string(),
evdev::Key::KEY_2, evdev::Key::KEY_2,
vec![Modifier::Alt], vec![Modifier::Alt],
"notify-send 'Hello world!'".to_string(), "notify-send 'Hello world!'".to_string(),
), ),
Hotkey::new( Hotkey::new(
"".to_string(),
evdev::Key::KEY_Z, evdev::Key::KEY_Z,
vec![Modifier::Super], vec![Modifier::Super],
"notify-send 'Hello world!'".to_string(), "notify-send 'Hello world!'".to_string(),

@ -1,11 +1,15 @@
use clap::{arg, App}; use clap::{arg, App};
use evdev::{AttributeSet, Device, Key}; use evdev::{AttributeSet, Device, Key};
use nix::unistd::{Group, Uid}; use nix::unistd::{Group, Uid};
use once_cell::sync::OnceCell;
use std::{ use std::{
thread,
sync::Mutex,
collections::HashMap, collections::HashMap,
env, fs, env, fs,
io::prelude::*, io::prelude::*,
os::unix::net::UnixStream, os::unix::net::UnixStream,
os::unix::net::UnixListener,
path::Path, path::Path,
process::{exit, id}, process::{exit, id},
thread::sleep, thread::sleep,
@ -22,7 +26,39 @@ pub struct LastHotkey {
ran_at: SystemTime, ran_at: SystemTime,
} }
pub fn main() { static MODE_SOCK: &str = "/tmp/odilia-mode.sock";
static STATE_MODE: OnceCell<Mutex<String>> = OnceCell::new();
fn listen_for_mode_change() -> std::io::Result<()> {
log::trace!("Mode change listener started!");
let listener = UnixListener::bind(MODE_SOCK)?;
match listener.accept() {
Ok((mut socket, _addr)) => {
let mut response = String::new();
socket.read_to_string(&mut response)?;
log::trace!("reponse from odilia-mode.sock: '{}'", response);
let mode_m = STATE_MODE.get();
match mode_m {
Some(mm) => {
let mut mode = mm.lock();
match mode {
Ok(mut m) => {
*m = response;
println!("Mode updated to '{}'", m);
log::trace!("Mode updated to '{}'", m);
},
Err(e) => log::error!("Could not lock mode."),
}
},
_ => log::error!("Could not get static STATE_MODE."),
}
},
Err(e) => log::error!("accept function failed: {:?}", e),
}
Ok(())
}
pub fn key_listener() {
let args = set_flags().get_matches(); let args = set_flags().get_matches();
env::set_var("RUST_LOG", "swhkd=warn"); env::set_var("RUST_LOG", "swhkd=warn");
@ -30,12 +66,16 @@ pub fn main() {
env::set_var("RUST_LOG", "swhkd=trace"); env::set_var("RUST_LOG", "swhkd=trace");
} }
env_logger::init();
log::trace!("Logger initialized."); log::trace!("Logger initialized.");
match STATE_MODE.set(Mutex::new(String::new())) {
Err(e) => log::error!("could not initialize STATE_MODE"),
_ => log::trace!("STATE_MODE initialized"),
}
let pidfile: String = String::from("/tmp/swhkd.pid"); let pidfile: String = String::from("/tmp/swhkd.pid");
if Path::new(&pidfile).exists() { if Path::new(&pidfile).exists() {
log::trace!("Reading {} file and checking for running instances.", pidfile); log::trace!("Reading {} file and checking for running instances.", pidfile);
let swhkd_pid = match fs::read_to_string(&pidfile) { let swhkd_pid = match fs::read_to_string(&pidfile) {
Ok(swhkd_pid) => swhkd_pid, Ok(swhkd_pid) => swhkd_pid,
Err(e) => { Err(e) => {
@ -131,6 +171,7 @@ pub fn main() {
let mut last_hotkey = LastHotkey { let mut last_hotkey = LastHotkey {
// just a dummy last_hotkey so I don't need to mess with Option<T>. TODO: Change this to Option<T> // just a dummy last_hotkey so I don't need to mess with Option<T>. TODO: Change this to Option<T>
hotkey: config::Hotkey::new( hotkey: config::Hotkey::new(
"".to_string(),
evdev::Key::KEY_A, evdev::Key::KEY_A,
default_test_modifier, default_test_modifier,
String::from("notify-send \"it works\""), String::from("notify-send \"it works\""),
@ -158,6 +199,21 @@ pub fn main() {
let mut state_modifiers: Vec<config::Modifier> = Vec::new(); let mut state_modifiers: Vec<config::Modifier> = Vec::new();
let mut state_keysyms: Vec<evdev::Key> = Vec::new(); let mut state_keysyms: Vec<evdev::Key> = Vec::new();
let state_mode: String = match STATE_MODE.get() {
Some(mm) => {
match mm.lock() {
Ok(m) => m.to_string(),
Err(e) => {
log::error!("Could not lock state_mode within main event loop.");
"".to_string()
},
}
}
None => {
log::error!("Could not get STATE_MODE in main event loop.");
"".to_string()
}
};
for key in state.iter() { for key in state.iter() {
if let Some(modifier) = modifiers_map.get(&key) { if let Some(modifier) = modifiers_map.get(&key) {
state_modifiers.push(*modifier); state_modifiers.push(*modifier);
@ -167,12 +223,14 @@ pub fn main() {
} }
log::debug!("state_modifiers: {:#?}", state_modifiers); log::debug!("state_modifiers: {:#?}", state_modifiers);
log::debug!("state_keysyms: {:#?}", state_keysyms); log::debug!("state_keysyms: {:#?}", state_keysyms);
log::debug!("state_mode: {:#?}", state_mode);
log::debug!("hotkey: {:#?}", possible_hotkeys); log::debug!("hotkey: {:#?}", possible_hotkeys);
for hotkey in &possible_hotkeys { for hotkey in &possible_hotkeys {
// this should check if state_modifiers and hotkey.modifiers have the same elements // this should check if state_modifiers and hotkey.modifiers have the same elements
if state_modifiers.iter().all(|x| hotkey.modifiers.contains(x)) if state_modifiers.iter().all(|x| hotkey.modifiers.contains(x))
&& state_modifiers.len() == hotkey.modifiers.len() && state_modifiers.len() == hotkey.modifiers.len()
&& state_keysyms.contains(&hotkey.keysym) && state_keysyms.contains(&hotkey.keysym)
&& state_mode == hotkey.mode
{ {
if last_hotkey.hotkey == hotkey.clone() { if last_hotkey.hotkey == hotkey.clone() {
let time_since_ran_at = let time_since_ran_at =
@ -214,6 +272,24 @@ pub fn main() {
sleep(Duration::from_millis(10)); // without this, swhkd will start to chew through your cpu. sleep(Duration::from_millis(10)); // without this, swhkd will start to chew through your cpu.
} }
} }
pub fn main() {
env_logger::init();
let key_handler = thread::spawn(|| {
key_listener();
});
let mode_updater = thread::spawn(|| {
let res = listen_for_mode_change();
match res {
Ok(listener) => log::trace!("Finished mode changer!"),
Err(e) => log::error!("Error setting up mode socket: {}", e),
}
});
log::trace!("Threads set up");
mode_updater.join().unwrap();
key_handler.join().unwrap();
}
pub fn permission_check() { pub fn permission_check() {
if !Uid::current().is_root() { if !Uid::current().is_root() {
let groups = nix::unistd::getgroups(); let groups = nix::unistd::getgroups();
@ -227,7 +303,6 @@ pub fn permission_check() {
} }
} }
log::error!("Consider using `pkexec swhkd ...`"); log::error!("Consider using `pkexec swhkd ...`");
exit(1);
} else { } else {
log::warn!("Running swhkd as root!"); log::warn!("Running swhkd as root!");
} }

Loading…
Cancel
Save