alols / xcape

Linux utility to configure modifier keys to act as other keys when pressed and released on their own.
GNU General Public License v3.0
2.1k stars 117 forks source link

Fixed bug where the keyboard layout was changed when generating a key event #113

Open janekfleper opened 5 years ago

janekfleper commented 5 years ago

When an event is generated by xcape, the group/layout of the keyboard was always set to 0 (the default group, state 0x0??? in xev -event keyboard).

Example showing the bug

$ ./xcape -d -e "Control_L=#65" Starting with the keyboard layout 0x2000 (group 1) I press the "j" key, then tap the Control_L key to trigger xcape and then press the "j" key again.

before, group = 1 Intercepted key event 2, key code 44 after, group = 1 jbefore, group = 1 // pressing "j" Intercepted key event 3, key code 44 after, group = 1 before, group = 1 Intercepted key event 2, key code 66 Key pressed! after, group = 1 before, group = 1 // before the generated event the group is still correct Intercepted key event 3, key code 66 Key released! Generating space! after, group = 0 // wrong group, corresponds to keyboard state 0x0000 (DVORAK) before, group = 0 Ignoring generated event. before, group = 0 Ignoring generated event. before, group = 0 // generated space at the beginning of this line Intercepted key event 2, key code 44 after, group = 0 hbefore, group = 0 // pressing "j" will now give an "h" (DVORAK) Intercepted key event 3, key code 44 after, group = 0

"before" corresponds to the group at the beginning of intercept() and "after" corresponds to the group at the end of intercept() just before the "exit" mark.

Example showing a change of the keyboard layout

$ ./xcape -d -e "Control_L=#65" My keyboard configuration file: /etc/X11/xorg.conf.d/00-keyboard.conf

Section "InputClass"
        Identifier "system-keyboard"
        MatchIsKeyboard "on"
        Option "XkbLayout" "us,us,de"
        Option "XkbModel" "pc105"
        Option "XkbVariant" "dvp,,"
        Option "XkbOptions" "grp:alt_shift_toggle,ctrl:nocaps"
EndSection

Press Alt_L and Shift_L to change from keyboard layout us (QWERTY) to de (QWERTZ).

before, group = 1 Intercepted key event 2, key code 47 after, group = 1 ;before, group = 1 // pressing semicolon key Intercepted key event 3, key code 47 after, group = 1 before, group = 1 Intercepted key event 2, key code 64 // alt key after, group = 1 before, group = 2 // group already changed to 2 (0x4000, de QWERTZ) Intercepted key event 2, key code 50 // shift key after, group = 2 before, group = 2 Intercepted key event 3, key code 50 after, group = 2 before, group = 2 Intercepted key event 3, key code 64 after, group = 2 before, group = 2 Intercepted key event 2, key code 47 after, group = 2 öbefore, group = 2 // press semicolon key, now an "ö" (de QWERTZ) Intercepted key event 3, key code 47 after, group = 2

Solution

In the first case the group change appears at the end of intercept(), whereas in the second case the group change occurs before intercept(). Saving the group from the previous key event therefore allows to separate the two cases and handle them correctly. If user_data->previous_group matches current_group at the beginning of intercept(), the group will be set to user_data->intended_group. If user_data->previous_group does not match current_group, both user_data->intended_group and user_data->previous_group will be set to current_group to confirm the layout change.