summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/remapper.rs56
1 files changed, 54 insertions, 2 deletions
diff --git a/src/remapper.rs b/src/remapper.rs
index b81f29b..4292c53 100644
--- a/src/remapper.rs
+++ b/src/remapper.rs
@@ -1,6 +1,7 @@
use crate::mapping::*;
use anyhow::*;
use evdev_rs::{Device, GrabMode, InputEvent, ReadFlag, TimeVal, UInputDevice};
+use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::path::Path;
use std::time::Duration;
@@ -172,23 +173,37 @@ impl InputMapper {
keys
}
+ /// Compute the difference between our desired set of keys
+ /// and the set of keys that are currently pressed in the
+ /// output device.
+ /// Release any keys that should not be pressed, and then
+ /// press any keys that should be pressed.
+ ///
+ /// When releasing, release modifiers last so that mappings
+ /// that produce eg: CTRL-C don't emit a random C character
+ /// when released.
+ ///
+ /// Similarly, when pressing, emit modifiers first so that
+ /// we don't emit C and then CTRL for such a mapping.
fn compute_and_apply_keys(&mut self, time: &TimeVal) -> Result<()> {
let desired_keys = self.compute_keys();
- let to_release: Vec<KeyCode> = self
+ let mut to_release: Vec<KeyCode> = self
.output_keys
.difference(&desired_keys)
.cloned()
.collect();
- let to_press: Vec<KeyCode> = desired_keys
+ let mut to_press: Vec<KeyCode> = desired_keys
.difference(&self.output_keys)
.cloned()
.collect();
if !to_release.is_empty() {
+ to_release.sort_by(modifiers_last);
self.emit_keys(&to_release, time, KeyEventType::Release)?;
}
if !to_press.is_empty() {
+ to_press.sort_by(modifiers_first);
self.emit_keys(&to_press, time, KeyEventType::Press)?;
}
Ok(())
@@ -374,3 +389,40 @@ impl InputMapper {
fn make_event(key: KeyCode, time: &TimeVal, event_type: KeyEventType) -> InputEvent {
InputEvent::new(time, &EventCode::EV_KEY(key), event_type.value())
}
+
+fn is_modifier(key: &KeyCode) -> bool {
+ match key {
+ KeyCode::KEY_FN
+ | KeyCode::KEY_LEFTALT
+ | KeyCode::KEY_RIGHTALT
+ | KeyCode::KEY_LEFTMETA
+ | KeyCode::KEY_RIGHTMETA
+ | KeyCode::KEY_LEFTCTRL
+ | KeyCode::KEY_RIGHTCTRL
+ | KeyCode::KEY_LEFTSHIFT
+ | KeyCode::KEY_RIGHTSHIFT => true,
+ _ => false,
+ }
+}
+
+/// Orders modifier keys ahead of non-modifier keys.
+/// Unfortunately the underlying type doesn't allow direct
+/// comparison, but that's ok for our purposes.
+fn modifiers_first(a: &KeyCode, b: &KeyCode) -> Ordering {
+ if is_modifier(a) {
+ if is_modifier(b) {
+ Ordering::Equal
+ } else {
+ Ordering::Less
+ }
+ } else if is_modifier(b) {
+ Ordering::Greater
+ } else {
+ // Neither are modifiers
+ Ordering::Equal
+ }
+}
+
+fn modifiers_last(a: &KeyCode, b: &KeyCode) -> Ordering {
+ modifiers_first(a, b).reverse()
+}