Problem
I use xdotool keydown Control
and xdotool keyup Control
from my application to emulate Ctrl presses. When layout is set to us
, everything works, but when layout changes to something else (fr
or ru
), applications stop seeing the ctrl events.
Question
Why is that happening? What can I do to make ctrl key manipulation work uniformly across layouts?
Some info
Command that I use to setup layouts:
setxkbmap -layout us,fr -option -option "grp:lctrl_lshift_toggle,ctrl:nocaps"
Output from xev with us
layout:
KeyPress event, serial 25, synthetic NO, window 0x4a00001,
root 0x5c, subw 0x0, time 11278564, (317,709), root:(1279,736),
state 0x10, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False
KeyRelease event, serial 28, synthetic NO, window 0x4a00001,
root 0x5c, subw 0x0, time 11278676, (317,709), root:(1279,736),
state 0x14, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False
Output from xev with fr
layout:
KeyPress event, serial 109, synthetic NO, window 0x4a00001,
root 0x5c, subw 0x0, time 11343218, (312,520), root:(1274,547),
state 0x2010, keycode 8 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 109, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 116, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
KeyRelease event, serial 123, synthetic NO, window 0x4a00001,
root 0x5c, subw 0x0, time 11343460, (312,520), root:(1274,547),
state 0x2010, keycode 8 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XFilterEvent returns: False
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
MappingNotify event, serial 123, synthetic NO, window 0x0,
request MappingKeyboard, first_keycode 8, count 1
Verbose output from setxkbmap:
Setting verbose level to 10
locale is C
Applied rules from evdev:
rules: evdev
model: pc105
layout: us,fr
options: grp:lctrl_lshift_toggle,ctrl:nocaps
Trying to build keymap using the following components:
keycodes: evdev+aliases(qwerty)
types: complete
compat: complete
symbols: pc+us+fr:2+inet(evdev)+group(lctrl_lshift_toggle)+ctrl(nocaps)
geometry: pc(pc105)
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+fr:2+inet(evdev)+group(lctrl_lshift_toggle)+ctrl(nocaps)" };
xkb_geometry { include "pc(pc105)" };
};
xmodmap output for control:
$ xmodmap -pme | grep -i control
control Control_L (0x25), Control_L (0x42), Control_R (0x69)
$ xmodmap -pke | grep -i control
keycode 37 = Control_L Control_L Control_L Control_L
keycode 66 = Control_L Control_L Control_L Control_L
keycode 105 = Control_R NoSymbol Control_R
As you can see from your xev
output, when in the us mode the Control_L
key down is equivalent to keycode 37 with the state moving from 0x10 to
0x14, whereas in the fr mode you have keycode 8 and state 0x2010 not
changing, and several MappingNotify events.
The state is a bitmap of which modifiers are currently applied, eg shift, control, alt and so on. They can be shown with
xmodmap -pme
which for example for me (on a completely different keyboard setup) is currently
shift Shift_L (0x32), Shift_R (0x3e)
lock
control Control_L (0x25), Control_L (0x42), Control_R (0x69)
mod1 Meta_R (0x86)
mod2 Num_Lock (0x4d)
mod3
mod4
mod5 ISO_Level3_Shift (0x5c), Mode_switch (0xcb)
These list the 8 bit positions in state, with:
Your state 0x2010 includes a value 0x2000 beyond these 8 bits, which is how the 2 keymaps in one is implemented, and shows when you are in fr mode.
If we look at how xdotool
works, when you are in the 2nd key mapping as shown by state 0x2000, it looks up Control_L to find the keycode, then looks up the keycode in the xmodmap -pke
columns for the current state, and doesnt find Control_L. So it takes a spare keycode 8, and temporarily changes the mapping so keycode 8 = keysym Control_L, and then sends that key event. Unfortunately, this keycode is not in the modifier mapping for the control bit.
So perhaps what will work is if you change the mapping you have for keycode 37 so that all the columns have Control_L. I dont know how many columns you have, but do xmodmap -pke | grep 'keycode 37'
and count them, then change them all eg:
xmodmap -e 'keycode 37 = Control_L Control_L Control_L Control_L'
As mentioned in the comments, xdotool key
can take keycode decimal numbers instead of keysym arguments. This isn't mentioned in the man pages.