Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping multiple keys to an action #111

Closed
alefbragin opened this issue Jan 17, 2022 · 18 comments
Closed

Mapping multiple keys to an action #111

alefbragin opened this issue Jan 17, 2022 · 18 comments

Comments

@alefbragin
Copy link

alefbragin commented Jan 17, 2022

I have Microsoft Ergonomic Keyboard. It's a good keyboard, but contains two strange and useless keys between right alt and right control: "microsoft office" and "smile".

Panel3-Microsoft-Ergonomic-Keyboard-J-Cen-Feat
(photo from microsoft.com).

When the "microsoft office" key is pressed it produces four keys: control, shift, alt and meta.

> sudo systemctl stop keyd && sudo keyd -m
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftcontrol down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftshift down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftalt down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftmeta down

When the "smile" key is pressed it produces five keys, like "microsoft office" but with additional space key.

> sudo systemctl stop keyd && sudo keyd -m
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftcontrol down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftshift down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftalt down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftmeta down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   space down

I just want to bind "microsoft office" to meta, with something like this:

[main]
leftcontrol-leftshift-leftalt-leftmeta = layer(meta)

Can I achive this in the current version? E.g. with some layer configuration. Is there a more direct way to do this planned in future releases?

@rvaiya
Copy link
Owner

rvaiya commented Jan 17, 2022

Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftcontrol down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftshift down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftalt down
Microsoft Microsoft Ergonomic Keyboard  045e:082c   leftmeta down

This strikes me as pathological behaviour :P.

With some ingenuity (read: atrocious kludge) you might be able to tame it

E.G

[control]

leftshift = layer(control_shift)

[control_shift:C-S]

leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A]

leftmeta = macro(Microsoft space is space evil)

note that this will break if you want to assign leftcontrol to something other
than control. It is impossible to avoid this since both sequences begin with
leftcontrol down which introduces inherent ambiguity.

If you are lucky it may also be possible to change the emitted keycode using
the official remapping tool, but it is more likely that this is done in software
rather than in the keyboard firmware.

@alefbragin
Copy link
Author

alefbragin commented Jan 18, 2022

This strikes me as pathological behaviour :P.

me too :)

If you are lucky it may also be possible to change the emitted keycode using
the official remapping tool, but it is more likely that this is done in software
rather than in the keyboard firmware.

That would be great, but i don't think this keyboard has a programmable firmware.


Thanks for the suggested approach! I try it as this:

[control]
leftshift = layer(control_shift)

[control_shift]
leftalt = layer(control_shift_alt)

[control_shift_alt]
leftmeta = layer(meta)

... but the leftcontrol event inserted to any strike of the key:

> sudo keyd -m
keyd virtual keyboard	0fac:0ade	leftcontrol down
keyd virtual keyboard	0fac:0ade	leftmeta down

I slightly modified the configuration:

[main]
leftcontrol = layer(mycontrol)

[mycontrol]
leftshift = layer(control_shift)

[control_shift]
leftalt = layer(control_shift_alt)

[control_shift_alt]
leftmeta = layer(meta)

... but I lost the left control as expected (e.g. LCtrl-o doesn't produce any events).

Also I try leverage layouts actions, but it didn't lead to any meaningful results.

@rvaiya
Copy link
Owner

rvaiya commented Jan 18, 2022

Thanks for the suggested approach! I try it as this:

[control]
leftshift = layer(control_shift)

[control_shift]
leftalt = layer(control_shift_alt)

[control_shift_alt]
leftmeta = layer(meta)

This should be

[control]
leftshift = layer(control_shift)

[control_shift:C-S]
leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A]
leftmeta = layer(meta)

The modifiers at the end (e.g :C-S) are important if you want to preserve normal modifier behaviour (see the man page).

@rvaiya
Copy link
Owner

rvaiya commented Jan 18, 2022

... but the leftcontrol event inserted to any strike of the key:

sudo keyd -m
keyd virtual keyboard 0fac:0ade leftcontrol down
keyd virtual keyboard 0fac:0ade leftmeta down

I should clarify that the config I provided will still depress leftcontrol when it receives leftcontrol down (this is inevitable), but it will compensate for this when it activates the final bindings. For example

[control]

leftshift = layer(control_shift)

[control_shift:C-S]

leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A]

leftmeta = macro(Hello space world)

Should produce something like the following:

leftcontrol down
leftshift down
leftalt down
leftcontrol up
leftshift up
leftalt up
H down
H up
e down
e up
....

Note that won't work if you assign leftmeta to a modifier layer like meta since it will combine with the other active modifiers by design. If you want to avoid this you will have to define a custom layer which explicitly maps meta keys.

E.G

[fakemeta]

a = M-a
b = M-b
c = M-c
....

[control]

leftshift = layer(control_shift)

[control_shift:C-S]

leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A]

leftmeta = layer(fakemeta)

This also means it won't combine properly with other modifers, but there are inevitably going to be some tradeoffs.

@alefbragin
Copy link
Author

alefbragin commented Jan 19, 2022

Probably I was too abstract in the issue title: Mapping multiple keys to an action. Indeed I want to map multiple keys (leftcontrol, leftshift, leftalt, leftmeta) to the one key -- to the meta key. I.e. I want to ignore leftcontrol, leftshift, leftalt when it was pressed simultaneously with leftmeta. My motivation is to abiltiy to hold with the right hand the meta key (which is missing on my keyboard in favor to the strange office keys), when left hand pressed `, tab etc (GNOME hotkeys for windows switching).

The closest config which I can to invent is:

[main]
leftcontrol = layer(mycontrol)

[mycontrol]
leftshift = layer(control_shift)

[control_shift]
leftalt = layer(control_shift_alt)

[control_shift_alt]
leftmeta = layer(meta)

... but at the cost of left control lack.

I have also tried [fakemeta] approach with ` = M-` and tab = M-tab, but it doesn't work as smoothly as the config above.

I have no idea is this possible at all or possible with keyd, or belongs to keyd philosophy. If it is not possible at all or doesn't belong to keyd philosophy, then it make sense to close this issue.

@rvaiya
Copy link
Owner

rvaiya commented Jan 19, 2022

The closest config which I can to invent is:

[main] 

leftcontrol = layer(mycontrol)

[mycontrol] 

leftshift = layer(control_shift)

[control_shift] 

leftalt = layer(control_shift_alt)

[control_shift_alt] 

leftmeta = layer(meta) 

This config will completely break leftcontrol. It shouldn't be necessary to this. The config I provided should preserve leftcontrol and allow you to use the key which generates multiple keycodes as meta (with some caveats).

E.G

[fakemeta] 

a = M-a
b = M-b

# etc...

[control] 

leftshift = layer(control_shift)

[control_shift:C-S] 

leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A] 

leftmeta = layer(fakemeta) 

I have also tried [fakemeta] approach with = M- and tab = M-tab, but it doesn't work as smoothly as the config above.

What specific problem are you encountering?

I have no idea is this possible at all or possible with keyd

The example config I provided should mostly work, though I can't test it.

@alefbragin
Copy link
Author

What specific problem are you encountering?

When I pressed "fakemeta" with the tab key, GNOME windows switching overlay blinked for a moment and with some chance switch the window. I thrown away that config and kept experimenting. Sorry, I can't reproduce that config now.

The example config I provided should mostly work ...

The config:

[fakemeta] 
a = M-a
b = M-b

[control] 
leftshift = layer(control_shift)

[control_shift:C-S] 
leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A] 
leftmeta = layer(fakemeta) 

... produces:

keyd virtual keyboard	0fac:0ade	leftcontrol down
keyd virtual keyboard	0fac:0ade	leftshift down
keyd virtual keyboard	0fac:0ade	leftalt down
keyd virtual keyboard	0fac:0ade	leftmeta down
keyd virtual keyboard	0fac:0ade	a down
keyd virtual keyboard	0fac:0ade	a up
keyd virtual keyboard	0fac:0ade	leftcontrol up
keyd virtual keyboard	0fac:0ade	leftshift up
keyd virtual keyboard	0fac:0ade	leftalt up
keyd virtual keyboard	0fac:0ade	leftmeta up

... when I press the "office key". This behavior is not differ from standard behavour without any config:

Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftcontrol down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftshift down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftalt down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftmeta down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	a down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	a up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftcontrol up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftshift up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftalt up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftmeta up

Moreover any hitting (without any modifiers) to the "A" (or "B") key produces:

keyd virtual keyboard	0fac:0ade	leftmeta down
keyd virtual keyboard	0fac:0ade	a down
keyd virtual keyboard	0fac:0ade	a up
keyd virtual keyboard	0fac:0ade	leftmeta up

..., though I can't test it.

I think it can be tested on any standard keyboard by pressing leftcontrol, leftshift, leftalt, leftmeta simultaneously. It is what the Microsoft Ergonomic Keyboard actually does when "office" key is pressed.

@rvaiya
Copy link
Owner

rvaiya commented Jan 20, 2022

... produces:...

You didn't specify what key combination you are using to produce this. I assume it is the microsoft office key + a. You should be seeing something like:

Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftcontrol down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftshift down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftalt down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftmeta down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftcontrol up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftshift up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftalt up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	a down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	a up
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftcontrol down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftshift down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftalt down
Microsoft Microsoft Ergonomic Keyboard	045e:082c	leftmeta up

Did you check the log output for errors? (journalctl -fu keyd).

... when I press the "office key". This behavior is not differ from standard behavour without any config:

Yes, something appears to be wrong with your config. I just tested this and it worked as expected.

@rvaiya
Copy link
Owner

rvaiya commented Jan 20, 2022

I just tried copying and pasting the config I suggested, it seems some additional spaces were added at the end of the lines which may be causing your problem (you can confirm this in your system journal). I've also pushed a patch to automatically ignore empty space at the end of lines in the latest version.

@alefbragin
Copy link
Author

I just tried copying and pasting the config I suggested, it seems some additional spaces were added at the end of the lines which may be causing your problem (you can confirm this in your system journal). I've also pushed a patch to automatically ignore empty space at the end of lines in the latest version.

Yes, it is my case) Thank you!

I have tryed this config:

[fakemeta]
` = M-`
tab = M-tab
space = M-space

[control]
leftshift = layer(control_shift)

[control_shift:C-S]
leftalt = layer(control_shift_alt)

[control_shift_alt:C-S-A]
leftmeta = layer(fakemeta)

It is very close to what I want to achieve, but it is still doesn't cover all use cases. As I said earlier, I was hoping to use office key as meta for windows switching. It does the trick for "one hit" case (e.g. just hit meta+tab for app switching). But there is a behavior for windows (or app) switching, when you hold the meta key and tap `( or tab) sequentially. This does not work as expected for obvious reasons: leftcontrol, leftshift, leftalt become down after `( or tab) is released. Actual log is very similar to what you expected. When I hold office, then tap tab, then tap tab again, then release office key, the log is:

keyd virtual keyboard	0fac:0ade	leftcontrol down
keyd virtual keyboard	0fac:0ade	leftshift down
keyd virtual keyboard	0fac:0ade	leftalt down
keyd virtual keyboard	0fac:0ade	leftcontrol up
keyd virtual keyboard	0fac:0ade	leftshift up
keyd virtual keyboard	0fac:0ade	leftmeta down
keyd virtual keyboard	0fac:0ade	leftalt up
keyd virtual keyboard	0fac:0ade	tab down
keyd virtual keyboard	0fac:0ade	tab up
keyd virtual keyboard	0fac:0ade	leftcontrol down
keyd virtual keyboard	0fac:0ade	leftshift down
keyd virtual keyboard	0fac:0ade	leftmeta up
keyd virtual keyboard	0fac:0ade	leftalt down
keyd virtual keyboard	0fac:0ade	leftcontrol up
keyd virtual keyboard	0fac:0ade	leftshift up
keyd virtual keyboard	0fac:0ade	leftmeta down
keyd virtual keyboard	0fac:0ade	leftalt up
keyd virtual keyboard	0fac:0ade	tab down
keyd virtual keyboard	0fac:0ade	tab up
keyd virtual keyboard	0fac:0ade	leftcontrol down
keyd virtual keyboard	0fac:0ade	leftshift down
keyd virtual keyboard	0fac:0ade	leftmeta up
keyd virtual keyboard	0fac:0ade	leftalt down
keyd virtual keyboard	0fac:0ade	leftcontrol up
keyd virtual keyboard	0fac:0ade	leftshift up
keyd virtual keyboard	0fac:0ade	leftalt up

I believe that the ideal behavior is:

keyd virtual keyboard	0fac:0ade	leftcontrol down
keyd virtual keyboard	0fac:0ade	leftshift down
keyd virtual keyboard	0fac:0ade	leftalt down
keyd virtual keyboard	0fac:0ade	leftcontrol up
keyd virtual keyboard	0fac:0ade	leftshift up
keyd virtual keyboard	0fac:0ade	leftalt up
keyd virtual keyboard	0fac:0ade	leftmeta down
keyd virtual keyboard	0fac:0ade	tab down
keyd virtual keyboard	0fac:0ade	tab up
keyd virtual keyboard	0fac:0ade	tab down
keyd virtual keyboard	0fac:0ade	tab up
keyd virtual keyboard	0fac:0ade	leftmeta up

Is it possible to get close to this behavior?

Currently I'm happy with an alternative key mapping that doesn't invlove the "office" key at all.
But this case might be helpful for users of the Microsoft Egronomic Keyboard who want to achieve a more classic mapping.
And if you willing improve the config for this case, I can test it on my system.

rvaiya added a commit that referenced this issue Jan 22, 2022
@rvaiya
Copy link
Owner

rvaiya commented Jan 22, 2022

I have tryed this config:
...
It does the trick for "one hit" case (e.g. just hit meta+tab for app switching).

Yes, this is one of the caveats I alluded to. Another problem is that you won't be able to combine it with other modifier keys (e.g S-M-a).

I believe that the ideal behavior is:

....

Is it possible to get close to this behavior?

You can achieve something close with swap, but it will have a side effect on control combinations. Specifically once you press another modifier you won't be able to deactivate it until you release the first key in the activation path.

This may or may not be a problem for you. It is only an issue if you are in the habit of holding control + another modifier and then releasing the other modifier without releasing control before you strike another key.

E.G

[control]
leftshift = swap(control_shift)

[control_shift:C-S]
leftalt = swap(control_shift_alt)

[control_shift_alt:C-S-A]
leftmeta = swap(meta)

This has the additional advantage of working in combination with other modifier keys.

Note that for the this to work you will need the latest version. I have just released a patch which allows for swap actions to be nested.

And if you willing improve the config for this case, I can test it on my system.

You are running up against the limit of sequence based detection logic. The only way to mitigate this problem is to add an additional timeout to disambiguate the real control key from the synthetic event (since they are otherwise indisguishable).

In this case you would need something which activates a layer only if another key is struck within a certain time interval. This is currently the opposite of how overload works (since you almost never want this behaviour). I am considering adding a more generic timeout mechanism to keyd but I haven't settled on a design yet and I'm not sure if I will end up including one.

#81 is tangentially related.

@rvaiya
Copy link
Owner

rvaiya commented Jan 31, 2022

Did you ever get this working? The last config should have come quite close.

@andrey-shigantsov
Copy link

I think I found a bug not related to the keyboard. (Tell me if you need to create a new task)

If you press a modifier key and then immediately press the same modifier on the other side, then releasing one of them turns off the mode.

$ sudo KEYD_DEBUG=1 keyd
Debug mode enabled.
Starting keyd v2.2.4-beta (cc84f3db9d77c724a7b2e56d04696927f5c71dfd).
Parsing /etc/keyd/ms-ergo-lxm-00001.conf
...
Awaiting keyboard neutrality.
Keyboard neutrality achieved
No config found for Corsair CORSAIR M55 RGB PRO Gaming Mouse (1b1c:1b70)
Managing Microsoft Microsoft Ergonomic Keyboard (045e:082c) (/etc/keyd/ms-ergo-lxm-00001.conf)
Activating layer S // leftshift down
Active mods: S
Activating layer S // rightshift down
Deactivating layer S // leftshift up
Active mods: 
Deactivating layer S // rightshift up

@rvaiya
Copy link
Owner

rvaiya commented Feb 1, 2022

@andrey-shigantsov This is expected behaviour and has been addressed in #85. Feel free to post there if it is actually causing you grief :P.

@alefbragin
Copy link
Author

alefbragin commented Feb 3, 2022

Did you ever get this working? The last config should have come quite close.

Yes! It's works as I expect in my GNOME environment: "office" key is felt as meta key.

My full config is:

[ids]
*

[main]
capslock = esc
esc = capslock
rightalt = layer(alt)

[control]
leftshift = swap(control_shift)

[control_shift:C-S]
leftalt = swap(control_shift_alt)

[control_shift_alt:C-S-A]
leftmeta = swap(meta)

I feel fully comfortable with it in GNOME.

@rvaiya
Copy link
Owner

rvaiya commented Feb 3, 2022

Thanks for closing the loop :). I am going to close this for now.

@rvaiya rvaiya closed this as completed Feb 3, 2022
@rvaiya
Copy link
Owner

rvaiya commented Mar 30, 2022

The new timeout functionality might be a better way to solve this.

Something like this should do the trick:

leftcontrol = timeout(noop, 1, layer(control))
leftshift = timeout(noop, 1, layer(shift))
leftalt = timeout(layer(office_key), 1, layer(alt))

[office_key]

# Paydirt, map this to whatever you want
leftmeta = macro(buy_m$ft)

You may have to increase the timeout values, depending on how fast the keyboard emits successive events, but the end result should be fairly reliable since the threshold will probably be well below what you can achieve manually.

@rvaiya rvaiya reopened this Mar 30, 2022
@andrey-shigantsov
Copy link

Thanks! It works like a charm!

# Microsoft Ergonomic Keyboard (LXM-00001)
[ids]
045e:082c

[main]

[control]
leftshift = timeout(swap(for_fakemeta_C_S), 1, layer(shift))

[for_fakemeta_C_S:C-S]
leftalt = timeout(swap(for_fakemeta_C_S_A), 1, layer(alt))

[for_fakemeta_C_S_A:C-S-A]
leftmeta = swap(fakemeta)

[fakemeta:M]

PS I will test more)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants