summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWez Furlong <wez@wezfurlong.org>2019-12-30 23:58:58 -0800
committerWez Furlong <wez@wezfurlong.org>2019-12-30 23:58:58 -0800
commit0211476bdb5fd138ecb12e344caeaf16c2cbc84a (patch)
tree9cec2a746491e505793f50a600b273029f1a55a0 /src
parent517f6a033e7f974f31c23ea6c54ad635cf0bdb28 (diff)
implement N:M remapping
Diffstat (limited to 'src')
-rw-r--r--src/main.rs183
1 files changed, 140 insertions, 43 deletions
diff --git a/src/main.rs b/src/main.rs
index 99e1cbe..dc29887 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,8 +14,8 @@ enum Mapping {
tap: Vec<KeyCode>,
},
Remap {
- input: Vec<KeyCode>,
- output: Vec<KeyCode>,
+ input: HashSet<KeyCode>,
+ output: HashSet<KeyCode>,
},
}
@@ -76,6 +76,13 @@ struct InputMapper {
output_keys: HashSet<KeyCode>,
}
+fn enable_key_code(input: &mut Device, key: KeyCode) -> Result<()> {
+ input
+ .enable(&EventCode::EV_KEY(key.clone()))
+ .context(format!("enable key {:?}", key))?;
+ Ok(())
+}
+
impl InputMapper {
pub fn create_mapper<P: AsRef<Path>>(path: P, mappings: Vec<Mapping>) -> Result<Self> {
let path = path.as_ref();
@@ -86,6 +93,26 @@ impl InputMapper {
.context(format!("assigning fd for {} to Device", path.display()))?;
input.set_name(&format!("evremap Virtual input for {}", path.display()));
+
+ // Ensure that any remapped keys are supported by the generated output device
+ for map in &mappings {
+ match map {
+ Mapping::DualRole { tap, hold, .. } => {
+ for t in tap {
+ enable_key_code(&mut input, t.clone())?;
+ }
+ for h in hold {
+ enable_key_code(&mut input, h.clone())?;
+ }
+ }
+ Mapping::Remap { output, .. } => {
+ for o in output {
+ enable_key_code(&mut input, o.clone())?;
+ }
+ }
+ }
+ }
+
let output = UInputDevice::create_from_device(&input)
.context(format!("creating UInputDevice from {}", path.display()))?;
@@ -103,6 +130,76 @@ impl InputMapper {
})
}
+ /// Compute the effective set of keys that are pressed
+ fn compute_keys(&self) -> HashSet<KeyCode> {
+ // Start with the input keys
+ let mut keys: HashSet<KeyCode> = self.input_state.keys().cloned().collect();
+
+ // First phase is to apply any DualRole mappings as they are likely to
+ // be used to produce modifiers when held.
+ for map in &self.mappings {
+ if let Mapping::DualRole { input, hold, .. } = map {
+ if keys.contains(input) {
+ keys.remove(input);
+ for h in hold {
+ keys.insert(h.clone());
+ }
+ }
+ }
+ }
+
+ // Second pass to apply Remap items
+ for map in &self.mappings {
+ if let Mapping::Remap { input, output } = map {
+ if input.is_subset(&keys) {
+ for i in input {
+ keys.remove(i);
+ }
+ for o in output {
+ keys.insert(o.clone());
+ }
+ }
+ }
+ }
+
+ keys
+ }
+
+ fn compute_and_apply_keys(&mut self, time: &TimeVal) -> Result<()> {
+ let desired_keys = self.compute_keys();
+ let to_release: Vec<KeyCode> = self
+ .output_keys
+ .difference(&desired_keys)
+ .cloned()
+ .collect();
+
+ let to_press: Vec<KeyCode> = desired_keys
+ .difference(&self.output_keys)
+ .cloned()
+ .collect();
+
+ if !to_release.is_empty() {
+ self.emit_keys(&to_release, time, KeyEventType::Release)?;
+ }
+ if !to_press.is_empty() {
+ self.emit_keys(&to_press, time, KeyEventType::Press)?;
+ }
+ Ok(())
+ }
+
+ fn lookup_dual_role_mapping(&self, code: KeyCode) -> Option<Mapping> {
+ for map in &self.mappings {
+ if let Mapping::DualRole { input, .. } = map {
+ if *input == code {
+ // A DualRole mapping has the highest precedence
+ // so we've found our match
+ return Some(map.clone());
+ }
+ }
+ }
+ None
+ }
+
fn lookup_mapping(&self, code: KeyCode) -> Option<Mapping> {
let mut candidates = vec![];
@@ -160,46 +257,34 @@ impl InputMapper {
Some(p) => p,
};
- match self.lookup_mapping(code.clone()) {
- Some(Mapping::DualRole { hold, tap, .. }) => {
- // If released quickly enough, becomes a tap press.
- // Regardless: release the hold keys
- self.emit_keys(&hold, &event.time, KeyEventType::Release)?;
-
- if let Some(tapping) = self.tapping.take() {
- if tapping == code
- && timeval_diff(&event.time, &pressed_at)
- <= Duration::from_millis(200)
- {
- self.emit_keys(&tap, &event.time, KeyEventType::Press)?;
- self.emit_keys(&tap, &event.time, KeyEventType::Release)?;
- }
+ self.compute_and_apply_keys(&event.time)?;
+
+ if let Some(Mapping::DualRole { tap, .. }) =
+ self.lookup_dual_role_mapping(code.clone())
+ {
+ // If released quickly enough, becomes a tap press.
+ if let Some(tapping) = self.tapping.take() {
+ if tapping == code
+ && timeval_diff(&event.time, &pressed_at) <= Duration::from_millis(200)
+ {
+ self.emit_keys(&tap, &event.time, KeyEventType::Press)?;
+ self.emit_keys(&tap, &event.time, KeyEventType::Release)?;
}
}
- Some(Mapping::Remap { .. }) => {
- unreachable!();
- }
- None => {
- // Just pass it through
- self.write_event_and_sync(event)?;
- }
}
}
KeyEventType::Press => {
self.input_state.insert(code.clone(), event.time.clone());
match self.lookup_mapping(code.clone()) {
- Some(Mapping::DualRole { hold, .. }) => {
- self.emit_keys(&hold, &event.time, KeyEventType::Press)?;
+ Some(_) => {
+ self.compute_and_apply_keys(&event.time)?;
self.tapping.replace(code);
}
- Some(Mapping::Remap { .. }) => {
- unreachable!();
- }
None => {
// Just pass it through
self.cancel_pending_tap();
- self.write_event_and_sync(event)?;
+ self.compute_and_apply_keys(&event.time)?;
}
}
}
@@ -208,8 +293,9 @@ impl InputMapper {
Some(Mapping::DualRole { hold, .. }) => {
self.emit_keys(&hold, &event.time, KeyEventType::Repeat)?;
}
- Some(Mapping::Remap { .. }) => {
- unreachable!();
+ Some(Mapping::Remap { output, .. }) => {
+ let output: Vec<KeyCode> = output.iter().cloned().collect();
+ self.emit_keys(&output, &event.time, KeyEventType::Repeat)?;
}
None => {
// Just pass it through
@@ -238,19 +324,12 @@ impl InputMapper {
) -> Result<()> {
for k in key {
let event = make_event(k.clone(), time, event_type);
- log::trace!("OUT: {:?}", event);
self.write_event(&event)?;
}
self.generate_sync_event(time)?;
Ok(())
}
- fn emit_key(&mut self, key: KeyCode, time: &TimeVal, event_type: KeyEventType) -> Result<()> {
- let event = make_event(key, time, event_type);
- self.write_event_and_sync(&event)?;
- Ok(())
- }
-
fn write_event_and_sync(&mut self, event: &InputEvent) -> Result<()> {
self.write_event(event)?;
self.generate_sync_event(&event.time)?;
@@ -292,11 +371,29 @@ fn make_event(key: KeyCode, time: &TimeVal, event_type: KeyEventType) -> InputEv
fn main() -> Result<()> {
pretty_env_logger::init();
- let mappings = vec![Mapping::DualRole {
- input: KeyCode::KEY_CAPSLOCK,
- hold: vec![KeyCode::KEY_LEFTCTRL],
- tap: vec![KeyCode::KEY_ESC],
- }];
+ let mappings = vec![
+ Mapping::DualRole {
+ input: KeyCode::KEY_CAPSLOCK,
+ hold: vec![KeyCode::KEY_LEFTCTRL],
+ tap: vec![KeyCode::KEY_ESC],
+ },
+ Mapping::Remap {
+ input: [KeyCode::KEY_F1].into_iter().cloned().collect(),
+ output: [KeyCode::KEY_BACK].into_iter().cloned().collect(),
+ },
+ Mapping::Remap {
+ input: [KeyCode::KEY_F8].into_iter().cloned().collect(),
+ output: [KeyCode::KEY_MUTE].into_iter().cloned().collect(),
+ },
+ Mapping::Remap {
+ input: [KeyCode::KEY_F5].into_iter().cloned().collect(),
+ output: [KeyCode::KEY_BRIGHTNESSDOWN].into_iter().cloned().collect(),
+ },
+ Mapping::Remap {
+ input: [KeyCode::KEY_F6].into_iter().cloned().collect(),
+ output: [KeyCode::KEY_BRIGHTNESSUP].into_iter().cloned().collect(),
+ },
+ ];
log::error!("Short delay: release any keys now!");
std::thread::sleep(Duration::new(2, 0));