wez / evremap

A keyboard input remapper for Linux/Wayland systems, written by @wez
MIT License
378 stars 30 forks source link

Combos also press the individual keys #13

Closed cgoates closed 3 months ago

cgoates commented 2 years ago

Hello, your code here looks right up the alley of what I'm looking for, but I've noticed some unexpected behavior as I'm trying it out, wondering if I'm just doing something wrong. Here's my config (minus the device name):

[[remap]]
input=["KEY_A", "KEY_S"]
output = ["KEY_N"]

Every time I hit the combo, I get not only an n, but also an s or an a. I was expecting/hoping to just get the n. Is this an expected behavior? If so, I might take the time to dig in and see if I can resolve that at least for my own use, and I'm happy to submit a PR for it if you would like.

wez commented 2 years ago

I wouldn't expect it to work like that, but it wouldn't surprise me if it has been doing that all along, as my use cases always involve the main key and a modifier key, and it is typically harmless if the modifier continues to be reported.

I don't have a ton of bandwidth to investigate this, so I would value your effort to figure out what's going on and perhaps resolve it!

and3rson commented 1 year ago

~I'm experiencing a similar issue~ While doing further research, it turned out that my issue is unrelated. Keeping this for historic purposes.

[[dual_role]]
input = "KEY_CAPSLOCK"
hold = ["KEY_RIGHTMETA"]
tap = ["KEY_ESC"]

[[remap]]
input = ["KEY_LEFTMETA", "KEY_I"]
output = ["KEY_UP"]
[[remap]]
input = ["KEY_LEFTMETA", "KEY_J"]
output = ["KEY_LEFT"]
[[remap]]
input = ["KEY_LEFTMETA", "KEY_K"]
output = ["KEY_DOWN"]
[[remap]]
input = ["KEY_LEFTMETA", "KEY_L"]
output = ["KEY_RIGHT"]

When performing the following sequence:

...the following inputs are generated by evremap:

and3rson commented 1 year ago

Seems like the issue might be due to evremap releasing the modifier (META) once J is pressed. I've added some logs to debug this, here's the output of my previous key sequence:

 2023-07-02T01:13:12.439 ERROR evremap::remapper > Press [KEY_RIGHTMETA]
 2023-07-02T01:13:12.520 ERROR evremap::remapper > Press [KEY_LEFT]
 2023-07-02T01:13:12.520 ERROR evremap::remapper > Release [KEY_RIGHTMETA]
 2023-07-02T01:13:12.571 ERROR evremap::remapper > Press [KEY_L]
 2023-07-02T01:13:12.614 ERROR evremap::remapper > Press [KEY_RIGHT]
 2023-07-02T01:13:12.614 ERROR evremap::remapper > Release [KEY_LEFT, KEY_L]
 2023-07-02T01:13:12.657 ERROR evremap::remapper > Press [KEY_RIGHTMETA]
 2023-07-02T01:13:12.657 ERROR evremap::remapper > Release [KEY_RIGHT]
 2023-07-02T01:13:12.723 ERROR evremap::remapper > Release [KEY_RIGHTMETA]

Here's the logs of me pressing META+J alone:

 2023-07-02T01:14:21.745 ERROR evremap > Short delay: release any keys now!
 2023-07-02T01:14:24.121 ERROR evremap::remapper > Going into read loop
 2023-07-02T01:14:24.863 ERROR evremap::remapper > Press [KEY_RIGHTMETA]
 2023-07-02T01:14:25.092 ERROR evremap::remapper > Press [KEY_LEFT]
 2023-07-02T01:14:25.092 ERROR evremap::remapper > Release [KEY_RIGHTMETA] <---- fake release
 2023-07-02T01:14:25.252 ERROR evremap::remapper > Press [KEY_RIGHTMETA]   <---- followed by a press again 
 2023-07-02T01:14:25.252 ERROR evremap::remapper > Release [KEY_LEFT]
 2023-07-02T01:14:25.580 ERROR evremap::remapper > Release [KEY_RIGHTMETA]

This also happens when I use a single-role modifier, e. g. CTRL or LEFTMETA.

EDIT: When I press META+J, desired_keys becomes empty. I think this is what causes evremap to release the modifier. Inside compute_keys, keys still contain META & J during first phase, however they are removed during second phase. This seems like a tricky situation, since:

EDIT 2: I was able to resolve my issue with the following patch:

diff --git a/src/remapper.rs b/src/remapper.rs
index 0e90697..2965184 100644
--- a/src/remapper.rs
+++ b/src/remapper.rs
@@ -164,13 +164,17 @@ impl InputMapper {
                 if input.is_subset(&keys_minus_remapped) {
                     for i in input {
                         keys.remove(i);
-                        keys_minus_remapped.remove(i);
+                        if !is_modifier(i) {
+                            keys_minus_remapped.remove(i);
+                        }
                     }
                     for o in output {
                         keys.insert(o.clone());
                         // Outputs that apply are not visible as
                         // inputs for later remap rules
-                        keys_minus_remapped.remove(o);
+                        if !is_modifier(o) {
+                            keys_minus_remapped.remove(o);
+                        }
                     }
                 }
             }

Not sure if there are any side effects to this, but this made my life much easier! The idea here is to allow modifier keys to be reused for multiple remaps, e. g. when keys that are pressed trigger multiple remaps with shared key, e. g. META+J+K -> META+J and META+K. Let me know what you think.

mikezila commented 3 months ago

I'm still getting this behavior when trying to turn a trio of keys into arrow keys on a 60% keyboard.

[[remap]]
input = ["KEY_RIGHTALT"]
output = ["KEY_LEFT"]

[[remap]]
input = ["KEY_RIGHTCTRL"]
output = ["KEY_RIGHT"]

[[remap]]
input = ["KEY_COMPOSE", "KEY_RIGHTALT"]
output = ["KEY_UP"]

[[remap]]
input = ["KEY_COMPOSE", "KEY_RIGHTCTRL"]
output = ["KEY_DOWN"]

[[remap]]
input = ["KEY_COMPOSE"]
output = []

My goal is to use right alt and control as left and right, and also as up and down if I'm holding the compose key. It does work, but when doing the combinations I still get left and right inputs instead of just the up and down. I've tried it with the rules in various orders and the behavior is always the same.