mirror of
https://github.com/k0kubun/xremap
synced 2024-11-02 03:40:25 +00:00
commit
67cff94344
@ -143,6 +143,9 @@ keymap:
|
||||
MOD2-KEY_YYY: MOD3-KEY_ZZZ
|
||||
# key press (MOD1-KEY_XXX) -> sequence (MOD2-KEY_YYY, MOD3-KEY_ZZZ)
|
||||
MOD1-KEY_XXX: [MOD2-KEY_YYY, MOD3-KEY_ZZZ]
|
||||
# execute a command
|
||||
MOD1-KEY_XXX:
|
||||
launch: ["bash", "-c", "echo hello > /tmp/test"]
|
||||
application: # Optional
|
||||
not: [Application, ...]
|
||||
# or
|
||||
|
@ -12,6 +12,8 @@ pub enum Action {
|
||||
KeyPress(KeyPress),
|
||||
#[serde(deserialize_with = "deserialize_remap")]
|
||||
Remap(HashMap<KeyPress, Vec<Action>>),
|
||||
#[serde(deserialize_with = "deserialize_launch")]
|
||||
Launch(Vec<String>),
|
||||
}
|
||||
|
||||
fn deserialize_remap<'de, D>(deserializer: D) -> Result<HashMap<KeyPress, Vec<Action>>, D::Error>
|
||||
@ -27,6 +29,19 @@ where
|
||||
Err(de::Error::custom("not a map with a single \"remap\" key"))
|
||||
}
|
||||
|
||||
fn deserialize_launch<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let mut action = HashMap::<String, Vec<String>>::deserialize(deserializer)?;
|
||||
if let Some(launch) = action.remove("launch") {
|
||||
if action.is_empty() {
|
||||
return Ok(launch);
|
||||
}
|
||||
}
|
||||
Err(de::Error::custom("not a map with a single \"launch\" key"))
|
||||
}
|
||||
|
||||
// Used only for deserializing Vec<Action>
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
|
@ -101,6 +101,19 @@ fn test_keymap_remap() {
|
||||
"})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keymap_launch() {
|
||||
assert_parse(indoc! {r#"
|
||||
keymap:
|
||||
- remap:
|
||||
KEY_GRAVE:
|
||||
launch:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- "date > /tmp/hotkey_test"
|
||||
"#})
|
||||
}
|
||||
|
||||
fn assert_parse(yaml: &str) {
|
||||
let result: Result<Config, Error> = serde_yaml::from_str(&yaml);
|
||||
if let Err(e) = result {
|
||||
|
@ -7,35 +7,40 @@ use crate::Config;
|
||||
use evdev::uinput::VirtualDevice;
|
||||
use evdev::{EventType, InputEvent, Key};
|
||||
use lazy_static::lazy_static;
|
||||
use log::debug;
|
||||
use log::{debug, error};
|
||||
use nix::sys::signal;
|
||||
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::Instant;
|
||||
|
||||
pub struct EventHandler {
|
||||
device: VirtualDevice,
|
||||
wm_client: WMClient,
|
||||
multi_purpose_keys: HashMap<Key, MultiPurposeKeyState>,
|
||||
override_remap: Option<HashMap<KeyPress, Vec<Action>>>,
|
||||
application_cache: Option<String>,
|
||||
shift: PressState,
|
||||
control: PressState,
|
||||
alt: PressState,
|
||||
windows: PressState,
|
||||
wm_client: WMClient,
|
||||
application_cache: Option<String>,
|
||||
multi_purpose_keys: HashMap<Key, MultiPurposeKeyState>,
|
||||
override_remap: Option<HashMap<KeyPress, Vec<Action>>>,
|
||||
sigaction_set: bool,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
pub fn new(device: VirtualDevice) -> EventHandler {
|
||||
EventHandler {
|
||||
device,
|
||||
wm_client: build_client(),
|
||||
multi_purpose_keys: HashMap::new(),
|
||||
override_remap: None,
|
||||
application_cache: None,
|
||||
shift: PressState::new(false),
|
||||
control: PressState::new(false),
|
||||
alt: PressState::new(false),
|
||||
windows: PressState::new(false),
|
||||
wm_client: build_client(),
|
||||
application_cache: None,
|
||||
multi_purpose_keys: HashMap::new(),
|
||||
override_remap: None,
|
||||
sigaction_set: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,10 +213,34 @@ impl EventHandler {
|
||||
}
|
||||
self.override_remap = Some(override_remap)
|
||||
}
|
||||
Action::Launch(command) => self.run_command(command.clone()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_command(&mut self, command: Vec<String>) {
|
||||
if !self.sigaction_set {
|
||||
// Avoid defunct processes
|
||||
let sig_action = SigAction::new(SigHandler::SigDfl, SaFlags::SA_NOCLDWAIT, SigSet::empty());
|
||||
unsafe {
|
||||
sigaction(signal::SIGCHLD, &sig_action).expect("Failed to register SIGCHLD handler");
|
||||
}
|
||||
self.sigaction_set = true;
|
||||
}
|
||||
|
||||
debug!("Running command: {:?}", command);
|
||||
match Command::new(&command[0])
|
||||
.args(&command[1..])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
{
|
||||
Ok(child) => debug!("Process spawned: {:?}, pid {}", command, child.id()),
|
||||
Err(e) => error!("Error running command: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_modifier(&mut self, modifier: Modifier, desired: &PressState) -> Result<PressState, Box<dyn Error>> {
|
||||
let mut current = match modifier {
|
||||
Modifier::Shift => &self.shift,
|
||||
|
Loading…
Reference in New Issue
Block a user