Closed fhill2 closed 2 years ago
There is no easy way to do this, but by getting creative with maps and toggles, it is possible to mostly emulate this behaviour:
evsieve --input /dev/input/by-id/keyboard grab domain=in persist=reopen \
--map key:esc key:grave \
`# The following list are keys that will not turn the capslock into a ctrl key,` \
`# so e.g. ctrl+capslock is turned into ctrl+esc instead of ctrl+ctrl.` \
--map key:leftctrl @out \
--map key:rightctrl @out \
--map key:leftshift @out \
--map key:rightshift @out \
--map key:leftalt @out \
--map key:rightalt @out \
--map key:leftmeta @out \
\
`# The main machinery: on capslock press, activate a map that turns all key events` \
`# into a leftctrl + that event. If no leftctrl event was generated that way, then` \
`# a capslock release is turned into an esc press+release.` \
--toggle key:capslock "" @caps-down @ctrl-down mode=passive \
--toggle @in "" @caps-down @caps-down mode=consistent \
--hook key:capslock:1 toggle=:2 \
--hook key:capslock:0 toggle=:1 \
--map key:capslock:0@caps-down key:esc:1@out key:esc:0@out \
--block key:capslock \
--map key@caps-down key:leftctrl "" \
--hook key:leftctrl@caps-down toggle=:3 \
\
`# The --map is necessary to make the --merge properly merge real leftctrl` \
`# events with virtual leftctrl events.` \
--map "" @out \
--merge \
--output
One problem left with the above script is that after pressing capslock+some key (e.g. Caps+A) and thereby generating Ctrl+A, the Ctrl key stays down until the "A" key is released, even if the capslock key is released earlier.
There is no easy way to do this, but by getting creative with maps and toggles, it is possible to mostly emulate this behaviour:
evsieve --input /dev/input/by-id/keyboard grab domain=in persist=reopen \ --map key:esc key:grave \ `# The following list are keys that will not turn the capslock into a ctrl key,` \ `# so e.g. ctrl+capslock is turned into ctrl+esc instead of ctrl+ctrl.` \ --map key:leftctrl @out \ --map key:rightctrl @out \ --map key:leftshift @out \ --map key:rightshift @out \ --map key:leftalt @out \ --map key:rightalt @out \ --map key:leftmeta @out \ \ `# The main machinery: on capslock press, activate a map that turns all key events` \ `# into a leftctrl + that event. If no leftctrl event was generated that way, then` \ `# a capslock release is turned into an esc press+release.` \ --toggle key:capslock "" @caps-down @ctrl-down mode=passive \ --toggle @in "" @caps-down @caps-down mode=consistent \ --hook key:capslock:1 toggle=:2 \ --hook key:capslock:0 toggle=:1 \ --map key:capslock:0@caps-down key:esc:1@out key:esc:0@out \ --block key:capslock \ --map key@caps-down key:leftctrl "" \ --hook key:leftctrl@caps-down toggle=:3 \ \ `# The --map is necessary to make the --merge properly merge real leftctrl` \ `# events with virtual leftctrl events.` \ --map "" @out \ --merge \ --output
One problem left with the above script is that after pressing capslock+some key (e.g. Caps+A) and thereby generating Ctrl+A, the Ctrl key stays down until the "A" key is released, even if the capslock key is released earlier.
Thank you, amazing! I tested and it's working exactly like caps2esc (apart from the problem you described)
This also works for Ctrl+LeftMouseClick
too.
caps2esc
had problems with supporting Caps+LeftMouseClick
--> Ctrl+LeftMouseClick
, and had to mux the mouse & input to get this desired behaviour.
I appreciate this, having all my desired key remaps and persistent virtual devices for qemu in 1 tool really does simplify things.
After testing again I've realized this doesn't support Ctrl+LeftMouseClick
and Ctrl+RightMouseClick
How would you approach this? Been trying and can't quite figure it out. Here's what I've got so far:
evsieve --input /dev/input/by-id/keyboard grab domain=in persist=reopen \ --input /dev/input/by-id/mouse grab domain=in persist=reopen \ --map key:esc key:grave \ `# The following list are keys that will not turn the capslock into a ctrl key,` \ `# so e.g. ctrl+capslock is turned into ctrl+esc instead of ctrl+ctrl.` \ --map key:leftctrl @out \ --map key:rightctrl @out \ --map key:leftshift @out \ --map key:rightshift @out \ --map key:leftalt @out \ --map key:rightalt @out \ --map key:leftmeta @out \ \ `# The main machinery: on capslock press, activate a map that turns all key events` \ `# into a leftctrl + that event. If no leftctrl event was generated that way, then` \ `# a capslock release is turned into an esc press+release.` \ --toggle key:capslock "" @caps-down @ctrl-down mode=passive \ --toggle @in "" @caps-down @caps-down mode=consistent \ --hook key:capslock:1 toggle=:2 \ --hook key:capslock:0 toggle=:1 \ --map key:capslock:0@caps-down key:esc:1@out key:esc:0@out \ --map key@caps-down key:leftctrl "" \ --block key:capslock \ --hook key:leftctrl@caps-down toggle=:3 \ \ `# The --map is necessary to make the --merge properly merge real leftctrl` \ `# events with virtual leftctrl events.` \ --map "" @out \ --merge \ --output
Modifications:
-block key:capslock
moved 1 line down
--input dev/input/by-id/mouse grab domain=in persist=reopen
below keyboard --input
Undo moving the --block
line and then add a --map btn@caps-down key:leftctrl ""
line after --map key@caps-down key:leftctrl ""
:
evsieve --input /dev/input/by-id/keyboard grab domain=in persist=reopen \
--input /dev/input/by-id/mouse grab domain=in persist=reopen \
--map key:esc key:grave \
`# The following list are keys that will not turn the capslock into a ctrl key,` \
`# so e.g. ctrl+capslock is turned into ctrl+esc instead of ctrl+ctrl.` \
--map key:leftctrl @out \
--map key:rightctrl @out \
--map key:leftshift @out \
--map key:rightshift @out \
--map key:leftalt @out \
--map key:rightalt @out \
--map key:leftmeta @out \
\
`# The main machinery: on capslock press, activate a map that turns all key events` \
`# into a leftctrl + that event. If no leftctrl event was generated that way, then` \
`# a capslock release is turned into an esc press+release.` \
--toggle key:capslock "" @caps-down @ctrl-down mode=passive \
--toggle @in "" @caps-down @caps-down mode=consistent \
--hook key:capslock:1 toggle=:2 \
--hook key:capslock:0 toggle=:1 \
--map key:capslock:0@caps-down key:esc:1@out key:esc:0@out \
--block key:capslock \
--map key@caps-down key:leftctrl "" \
--map btn@caps-down key:leftctrl "" \
--hook key:leftctrl@caps-down toggle=:3 \
\
`# The --map is necessary to make the --merge properly merge real leftctrl` \
`# events with virtual leftctrl events.` \
--map "" @out \
--merge \
--output
The issue is that mouse buttons are called btn:left
, btn:right
or something. Because they do not start with key:
, the map key@caps-down
does not apply to them. You need a second map to catch the btn
-type events as well.
I agree that this is highly confusing because both key presses and mouse clicks are of type EV_KEY according to the evdev protocol, but the alternative may be even worse. When stabilising type-level maps I had to either choose "--map key
does not catch all EV_KEY events" or "--map key
applies to btn:something
despite its name not starting with key
"; both behaviours have the potential to surprise an unsuspecting user.
In the end, I decided that the first option had the least insane behaviour and as such --map key
does not catch btn:something
. Whether this was the right decision I still do not know.
... the longer I think about it, the crazier the decision do make --map key
not match btn:*
is starting to look, taking into account that the documentation pretty consistently mentions that key:something
means "an event with type EV_KEY and ...".
In fact, it looks like I haven't even documented that --map key
does not match btn:*
other than the generic sentence "Any part that is not specified will be interpreted to mean "any" for source events." about the key format [which may or may not imply that key
means key:*:*@*
and therefore not btn:*:*@*
.]
Taking that into account, it could be argued that the documentation stating that "key:" is interpreted as "with type EV_KEY..." means that either the current implementation or the current documentation is broken. I wonder what I should do about this...
Testing the example you provided above:
--print
before --output
shows:
Event: type:code = key:leftctrl value = 1 (down) domain = out
Event: type:code = btn:left value = 1 (down) domain = out
yet while evsieve is running:
caps+leftclick
, links on github, youtube etc don't open in a new tab like default.
using physical left-ctrl
right-ctrl
manually then mouse clicking opens these links in a new tab.
Does this work for you in a web browser?
It might have something to do with the virtual key being delayed until the next key, although I moved --block
down 1 line and the issue is still there.
On my system, using this browser based key tester:
caps-ctrl
is registered after the mouse click.
using physical left-ctrl
right-ctrl
, then manually clicking, those keys are registered before the click.
Also tested passed through to a windows vm, same behaviour exists.
Yes using key
, and it actually mean key
and btn
makes less sense imo.
When reading "Any part that is not specified will be interpreted to mean "any" for source events" in the documentation,
to me this implies key
means key:*:*@*
and therefore not btn:*:*@*
ie I was not expecting --map key
to match btn
events. I simply missed this line due to personal error.
I wouldn't say this is highly confusing.
The documentation is very well written and easy to follow along, there is a lot you can do with evsieve, a lot to cover, and therefore a slight learning curve.
For the amount of configuration possible, the implementation is very clean imo, and easy to understand through --print
, trial and error.
The domains, event filters, sequential processing being used directly from the shell is powerful.
I think I've found what's causing the issues with ctrl+leftclick
For comparison, here is a xmodmap+xcape script that provides similar caps2esc behaviour:
After running this, Ctrl+Click
within browsers, links open in a new tab etc, everything working.
xmodmap -e "clear Lock"
# Set real Caps Lock key to present as (left) control
xmodmap -e "keycode 66 = Control_R"
# Set real Escape key to present as Caps Lock
xmodmap -e "keycode 9 = F14"
# Make a fake key to hold the Escape keysym, so xcape can use it
xmodmap -e "keycode 255 = Escape"
# Make Caps_Lock and Control_L work as one would expect
xmodmap -e "add Control = Control_R"
xcape -e '#66=Escape'
Here are both outputs of xev for (1) xmodmap+xcape (2) evsieve example above
(1) xmodmap+xcape
KeyPress event, serial 33, synthetic NO, window 0x2000001,
root 0x721, subw 0x0, time 2144143, (1030,1158), root:(4870,1158),
state 0x0, keycode 66 (keysym 0xffe4, Control_R), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False
ButtonPress event, serial 33, synthetic NO, window 0x2000001,
root 0x721, subw 0x0, time 2144814, (1030,1158), root:(4870,1158),
state 0x4, button 1, same_screen YES
ButtonRelease event, serial 33, synthetic NO, window 0x2000001,
root 0x721, subw 0x0, time 2144934, (1030,1158), root:(4870,1158),
state 0x104, button 1, same_screen YES
KeyRelease event, serial 33, synthetic NO, window 0x2000001,
root 0x721, subw 0x0, time 2145552, (1030,1158), root:(4870,1158),
state 0x4, keycode 66 (keysym 0xffe4, Control_R), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False
(2) evsieve example above
KeyPress event, serial 33, synthetic NO, window 0x1c00001,
root 0x721, subw 0x0, time 2050890, (2112,1074), root:(5952,1074),
state 0x0, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False
ButtonPress event, serial 33, synthetic NO, window 0x1c00001,
root 0x721, subw 0x0, time 2050890, (2112,1074), root:(5952,1074),
state 0x4, button 1, same_screen YES
KeyRelease event, serial 33, synthetic NO, window 0x1c00001,
root 0x721, subw 0x0, time 2051003, (2112,1074), root:(5952,1074),
state 0x104, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False
ButtonRelease event, serial 33, synthetic NO, window 0x1c00001,
root 0x721, subw 0x0, time 2051003, (2112,1074), root:(5952,1074),
state 0x100, button 1, same_screen YES
So, the solution could be changing the release order from CtrlUp BtnUp
to BtnUp CtrlUp
.
will give it a try.
Issue confirmed. Strangely enough, the issue still happens for me on Firefox even if I were to make the CtrlUp
happen after the BtnUp
using the following less-than-elegant solution:
evsieve --input /dev/input/by-id/keyboard grab domain=in persist=reopen \
--input /dev/input/by-id/mouse grab domain=in persist=reopen \
--map key:esc key:grave \
`# The following list are keys that will not turn the capslock into a ctrl key,` \
`# so e.g. ctrl+capslock is turned into ctrl+esc instead of ctrl+ctrl.` \
--map key:leftctrl @out \
--map key:rightctrl @out \
--map key:leftshift @out \
--map key:rightshift @out \
--map key:leftalt @out \
--map key:rightalt @out \
--map key:leftmeta @out \
\
`# The main machinery: on capslock press, activate a map that turns all key events` \
`# into a leftctrl + that event. If no leftctrl event was generated that way, then` \
`# a capslock release is turned into an esc press+release.` \
--toggle key:capslock "" @caps-down @ctrl-down mode=passive \
--toggle @in "" @caps-down @caps-down mode=consistent \
--hook key:capslock:1 toggle=:2 \
--hook key:capslock:0 toggle=:1 \
--map key:capslock:0@caps-down key:esc:1@out key:esc:0@out \
--block key:capslock \
--map key@caps-down key:leftctrl "" \
--map btn:left:1@caps-down key:leftctrl "" \
--map btn:left:0@caps-down "" key:leftctrl \
--map btn:right:1@caps-down key:leftctrl "" \
--map btn:right:0@caps-down "" key:leftctrl \
--map btn:middle:1@caps-down key:leftctrl "" \
--map btn:middle:0@caps-down "" key:leftctrl \
--hook key:leftctrl@caps-down toggle=:3 \
\
`# The --map is necessary to make the --merge properly merge real leftctrl` \
`# events with virtual leftctrl events.` \
--map "" @out \
--merge \
--output
(Feature TODO: consider adding some way to match btn::1
without having to spell out every possible key code.)
I think the issue is that the CtrlUp and the BtnUp are getting sent in the same synchronisation report. There is no way around this on the stable version of evsieve; I'll work on a solution.
Okay, on my system example above is still showing original behaviour in xev:
Ctrl-Down
Btn-Down
Ctrl-Up
Btn-Up
FYI, I thought I'd test caps2esc and view the xev results.
Using Interception Tools + caps2esc:
/etc/interception/udevmon.yaml
- CMD: mux -c caps2esc
- JOB: mux -i caps2esc | caps2esc -m 1 | uinput -d /dev/input/by-id/keyboard -d /dev/input/by-id/mouse
- JOB: intercept -g $DEVNODE | mux -o caps2esc
DEVICE:
LINK: /dev/input/by-id/keyboard
- JOB: intercept -g $DEVNODE | mux -o caps2esc
DEVICE:
LINK: /dev/input/by-id/mouse
This shows Ctrl-Down
Btn-Down
Btn-Up
Ctrl-Up
in xev, same as xmodmap+xcape.
And clicking links in chrome with Caps-Click
open in their new tab.
I added a rudimentary implementation* of a --delay
argument to the main branch (current commit: 97b7c46). It can delay events for a specified amount of seconds. For example, the following script delays the CtrlUp event by a millisecond to make sure it happens after the BtnUp event. It solves the Ctrl+Click problem for me.
evsieve --input /dev/input/by-id/keyboard grab domain=in persist=reopen \
--input /dev/input/by-id/mouse grab domain=in persist=reopen \
--map key:esc key:grave \
`# The following list are keys that will not turn the capslock into a ctrl key,` \
`# so e.g. ctrl+capslock is turned into ctrl+esc instead of ctrl+ctrl.` \
--map key:leftctrl @out \
--map key:rightctrl @out \
--map key:leftshift @out \
--map key:rightshift @out \
--map key:leftalt @out \
--map key:rightalt @out \
--map key:leftmeta @out \
\
`# The main machinery: on capslock press, activate a map that turns all key events` \
`# into a leftctrl + that event. If no leftctrl event was generated that way, then` \
`# a capslock release is turned into an esc press+release.` \
--toggle key:capslock "" @caps-down @ctrl-down mode=passive \
--toggle @in "" @caps-down @caps-down mode=consistent \
--hook key:capslock:1 toggle=:2 \
--hook key:capslock:0 toggle=:1 \
--map key:capslock:0@caps-down key:esc:1@out key:esc:0@out \
--block key:capslock \
--map key@caps-down key:leftctrl "" \
--map btn@caps-down key:leftctrl "" \
--hook key:leftctrl@caps-down toggle=:3 \
--delay key:leftctrl:0@caps-down period=0.001 \
`# The --map is necessary to make the --merge properly merge real leftctrl` \
`# events with virtual leftctrl events.` \
--map "" @out \
--merge \
--output
(*Rudimentary implementation: not properly tested; has at least one known bug that won't matter for your use case; still subject to redesign; etc.)
This solution could possibly be made better by adding an option like period=syn
to make --delay
wait until some input device synchronises instead of waiting a fixed amount of time, to increase the responsivity by a millisecond or something, but that is still subject to some design questions.
Great, just tested.
I can confirm it makes Caps-Click
behaviour work correctly for the above script.
Thank you for implementing this feature.
Thank you for this awesome software, been using it for setting keybinds for all my VM inputs at once.
Is caps2esc behaviour possible using esieve, or is it a job for python evdev lib?
in caps2esc:
CtrlDown
CtrlUp
-->Esc
CtrlDown
d
CtrlUp
-->Ctrl-d
Esc
-->grave