Merge pull request #147 from k0kubun/reimagine-modifiers

Replace modifier: true with virtual_modifiers
This commit is contained in:
Takashi Kokubun 2022-08-14 13:05:07 -07:00 committed by GitHub
commit b2ff35872c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 25 deletions

View File

@ -21,6 +21,7 @@
* Automatically remap newly connected devices by starting xremap with `--watch`.
* Support [Emacs-like key remapping](example/emacs.yml), including the mark mode.
* Trigger commands on key press/release events.
* Use a non-modifier key as a virtual modifier key.
## Installation
@ -155,9 +156,6 @@ modmap:
remap: # Required
# Replace a key with another
KEY_XXX: KEY_YYY # Required
# Make it a logical modifier key
KEY_XXX:
modifier: true # Required
# Dispatch different keys depending on whether you hold it or press it alone
KEY_XXX:
held: KEY_YYY # Required
@ -231,7 +229,7 @@ You can use multiple prefixes like `C-M-Shift-a`.
You may also suffix them with `_L` or `_R` (case-insensitive) so that
remapping is triggered only on a left or right modifier, e.g. `Ctrl_L-a`.
If you set `modifier: true` in `modmap`, you can use it in the `MOD1-` part too.
If you use `virtual_modifiers` explained below, you can use it in the `MOD1-` part too.
### application
@ -278,6 +276,21 @@ swaymsg -t get_tree
Locate `app_id` in the output.
### virtual\_modifiers
You can declare keys that should act like a modifier.
```
virtual_modifiers:
- CapsLock
keymap:
- remap:
CapsLock-i: Up
CapsLock-j: Left
CapsLock-k: Down
CapsLock-l: Right
```
## License
`xremap` is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

View File

@ -12,16 +12,10 @@ use super::action::{Action, Actions};
pub enum KeyAction {
#[serde(deserialize_with = "deserialize_key")]
Key(Key),
ModifierKey(ModifierKey),
MultiPurposeKey(MultiPurposeKey),
PressReleaseKey(PressReleaseKey),
}
#[derive(Clone, Debug, Deserialize)]
pub struct ModifierKey {
pub modifier: bool,
}
#[serde_as]
#[derive(Clone, Debug, Deserialize)]
pub struct MultiPurposeKey {

View File

@ -16,10 +16,13 @@ use evdev::Key;
use keymap::Keymap;
use modmap::Modmap;
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use std::{collections::HashMap, error, fs, path::Path, time::SystemTime};
use self::keymap::{build_keymap_table, KeymapEntry};
use self::{
key::parse_key,
keymap::{build_keymap_table, KeymapEntry},
};
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
@ -31,6 +34,8 @@ pub struct Config {
pub keymap: Vec<Keymap>,
#[serde(default = "default_mode")]
pub default_mode: String,
#[serde(deserialize_with = "deserialize_virtual_modifiers", default = "Vec::new")]
pub virtual_modifiers: Vec<Key>,
// Internals
#[serde(skip)]
@ -69,3 +74,15 @@ pub fn config_watcher(watch: bool, file: &Path) -> anyhow::Result<Option<Inotify
fn default_mode() -> String {
"default".to_string()
}
fn deserialize_virtual_modifiers<'de, D>(deserializer: D) -> Result<Vec<Key>, D::Error>
where
D: Deserializer<'de>,
{
let key_strs = Vec::<String>::deserialize(deserializer)?;
let mut keys: Vec<Key> = vec![];
for key_str in key_strs {
keys.push(parse_key(&key_str).map_err(serde::de::Error::custom)?);
}
return Ok(keys);
}

View File

@ -66,6 +66,14 @@ fn test_modmap_multi_purpose_key() {
"})
}
#[test]
fn test_virtual_modifiers() {
assert_parse(indoc! {"
virtual_modifiers:
- CapsLock
"})
}
#[test]
fn test_modmap_press_release_key() {
assert_parse(indoc! {r#"

View File

@ -1,7 +1,7 @@
use crate::client::{build_client, WMClient};
use crate::config::action::Action;
use crate::config::application::Application;
use crate::config::key_action::{KeyAction, ModifierKey, MultiPurposeKey, PressReleaseKey};
use crate::config::key_action::{KeyAction, MultiPurposeKey, PressReleaseKey};
use crate::config::key_press::{KeyPress, Modifier};
use crate::config::keymap::{build_override_table, OverrideEntry};
use crate::config::remap::Remap;
@ -85,7 +85,10 @@ impl EventHandler {
// Apply keymap
for (key, value) in key_values.into_iter() {
if MODIFIER_KEYS.contains(&key.code()) {
if config.virtual_modifiers.contains(&key) {
self.update_modifier(key, value);
continue;
} else if MODIFIER_KEYS.contains(&key.code()) {
self.update_modifier(key, value);
} else if is_pressed(value) {
if self.escape_next_key {
@ -162,17 +165,6 @@ impl EventHandler {
) -> Result<Vec<(Key, i32)>, Box<dyn Error>> {
let keys = match key_action {
KeyAction::Key(modmap_key) => vec![(modmap_key, value)],
KeyAction::ModifierKey(ModifierKey { modifier }) => {
if modifier {
if value == PRESS {
self.modifiers.insert(key);
} else if value == RELEASE {
self.modifiers.remove(&key);
}
}
// Process this key only in xremap
vec![]
}
KeyAction::MultiPurposeKey(MultiPurposeKey {
held,
alone,