Add hotkey command support

This commit is contained in:
Greg Dallavalle 2021-12-29 23:08:12 -06:00
parent 7df4333ea1
commit 225f8019bd
6 changed files with 96 additions and 9 deletions

10
example/hotkey_test.yml Normal file
View File

@ -0,0 +1,10 @@
hotkeys:
- name: hotkey_test
keys:
- KEY_GRAVE
application:
not: [Google-chrome, konsole]
command:
- "/bin/sh"
- "-c"
- "date > /tmp/hotkey_test"

32
src/command.rs Normal file
View File

@ -0,0 +1,32 @@
use std::process::{Command, Stdio};
use std::thread;
use log::{debug, error};
pub fn run_command(command: Vec<String>) {
// To avoid defunct processes, spawn a thread to wait on the process.
thread::spawn(move || {
debug!("Running command: {:?}", command);
match Command::new(&command[0])
.args(&command[1..])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
{
Err(e) => {
error!("Error running command: {:?}", e);
}
Ok(mut child) => {
debug!("Process spawned: {:?}, pid {}", command, child.id());
match child.wait() {
Ok(status) => {
debug!("Process exited: pid: {}, status: {:?}", child.id(), status);
}
Err(e) => {
error!("Error from process: {:?}", e);
}
}
}
}
});
}

13
src/config/hotkey.rs Normal file
View File

@ -0,0 +1,13 @@
use crate::config::application::Application;
use crate::config::key_press::KeyPress;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Hotkey {
#[serde(default = "String::new")]
pub name: String,
pub keys: Vec<KeyPress>,
pub command: Vec<String>,
pub application: Option<Application>,
}

View File

@ -3,6 +3,7 @@ pub mod application;
pub mod key;
pub mod key_action;
pub mod key_press;
mod hotkey;
mod keymap;
mod modmap;
@ -13,6 +14,7 @@ extern crate serde_yaml;
use keymap::Keymap;
use modmap::Modmap;
use hotkey::Hotkey;
use serde::Deserialize;
use std::error::Error;
use std::fs;
@ -24,6 +26,8 @@ pub struct Config {
pub modmap: Vec<Modmap>,
#[serde(default = "Vec::new")]
pub keymap: Vec<Keymap>,
#[serde(default = "Vec::new")]
pub hotkeys: Vec<Hotkey>,
}
pub fn load_config(filename: &str) -> Result<Config, Box<dyn Error>> {

View File

@ -1,4 +1,5 @@
use crate::client::{build_client, WMClient};
use crate::command::run_command;
use crate::config::action::Action;
use crate::config::application::Application;
use crate::config::key_action::KeyAction;
@ -57,9 +58,13 @@ impl EventHandler {
// Apply keymap
for (key, value) in key_values.into_iter() {
if MODIFIER_KEYS.contains(&key.code()) {
let key_press = self.key_to_key_press(&key);
// Run hotkey commands
if self.handle_hotkey(&key_press, value, &config) {
return Ok(());
} else if MODIFIER_KEYS.contains(&key.code()) {
self.update_modifier(key.code(), value);
} else if let Some(actions) = self.find_keymap(config, &key, value) {
} else if let Some(actions) = self.find_keymap(config, &key_press, value) {
for action in &actions {
self.dispatch_action(action)?;
}
@ -70,6 +75,25 @@ impl EventHandler {
Ok(())
}
fn handle_hotkey(&mut self, key_press: &KeyPress, value: i32, config: &Config) -> bool {
let mut hotkey_used = false;
'outer_hotkey: for hotkey in &config.hotkeys {
for hotkey_key_press in &hotkey.keys {
if hotkey_key_press == key_press && is_pressed(value) {
if let Some(app) = &hotkey.application {
if !self.match_application(app) {
continue 'outer_hotkey;
}
}
run_command(hotkey.command.clone());
hotkey_used = true;
break 'outer_hotkey;
}
}
}
return hotkey_used;
}
pub fn send_event(&mut self, event: InputEvent) -> std::io::Result<()> {
if event.event_type() == EventType::KEY {
debug!("{}: {:?}", event.value(), Key::new(event.code()))
@ -150,18 +174,21 @@ impl EventHandler {
None
}
fn find_keymap(&mut self, config: &Config, key: &Key, value: i32) -> Option<Vec<Action>> {
if !is_pressed(value) {
return None;
}
let key_press = KeyPress {
fn key_to_key_press(&mut self, key: &Key) -> KeyPress {
KeyPress {
key: key.clone(),
shift: self.shift.left || self.shift.right,
control: self.control.left || self.control.right,
alt: self.alt.left || self.alt.right,
windows: self.windows.left || self.windows.right,
};
}
}
fn find_keymap(&mut self, config: &Config, key_press: &KeyPress, value: i32) -> Option<Vec<Action>> {
if !is_pressed(value) {
return None;
}
if let Some(override_remap) = &self.override_remap {
let override_remap = override_remap.clone();
self.override_remap = None;

View File

@ -15,6 +15,7 @@ use std::process::exit;
extern crate getopts;
mod client;
mod command;
mod config;
mod device;
mod event_handler;