Let's say I have two keyboard layouts configured in Xorg.
If I use the second layout, pynput still reports key presses using the first layout.
This is because pynput doesn't use the layout group index (bits 13 and 14 of event.state):
Normally, the Xkb-aware server reports keyboard state in the state member of events such as a KeyPress event and ButtonPress event, encoded as follows:
bits meaning
15 0
13-14 Group index
8-12 Pointer Buttons
0-7 Modifiers
https://www.x.org/releases/X11R7.7/doc/libX11/XKB/xkblib.html
I'm not sure about the proper way to compute the layout index, but I managed to get something that seems to work using this diff:
diff --git a/lib/pynput/keyboard/_xorg.py b/lib/pynput/keyboard/_xorg.py
index 7011a5a..a4d99a1 100644
--- a/lib/pynput/keyboard/_xorg.py
+++ b/lib/pynput/keyboard/_xorg.py
@@ -635,7 +635,21 @@ class Listener(ListenerMixin, _base.Listener):
:raises IndexError: if the key code is invalid
"""
keycode = event.detail
- index = shift_to_index(display, event.state)
+
+ from pynput._util.xorg import _find_mask
+ shift_mask = Xlib.X.ShiftMask
+ alt_gr_mask = _find_mask(display, 'ISO_Level3_Shift') or _find_mask(display, 'Mode_switch')
+ shift = 1 if event.state & shift_mask else 0
+ alt_gr = 2 if event.state & alt_gr_mask else 0
+ group = 4 * ((event.state >> 13) & 0x03)
+ index = group | alt_gr | shift
+ syms = len(display.get_keyboard_mapping(keycode, 1)[0])
+ # Swap slots 2-3 and 4-5
+ if syms > 4:
+ if index in (2, 3):
+ index += 2
+ elif index in (4, 5):
+ index -= 2
# First try special keys...
keysym = self._keycode_to_keysym(display, keycode, index)
The swapping might look silly but this how the layouts are organized, somehow.
For instance let's say I have 3 layouts configured in this specific order:
- US (
q maps to q, Q, ä, Ä)
- BEPO (
q maps to b, B, |, ¦)
- FR (
q maps to a, A, æ, Æ)
Then display._keymap_codes[keycode] for q contains:
0: 'q',
1: 'Q',
2: 'b',
3: 'B',
4: 'ä',
5: 'Ä',
6: '|',
7: '¦',
8: 'a',
9: 'A',
10: 'æ',
11: 'Æ',
12: 'q',
13: 'Q',
14: '\x00',
15: '\x00',
16: '\x00',
17: '\x00',
18: '\x00',
19: '\x00',
Also note that I had to use 'ISO_Level3_Shift' instead of 'Mode_switch' in order to get the correct alt_gr_mask.
Let's say I have two keyboard layouts configured in Xorg.
If I use the second layout, pynput still reports key presses using the first layout.
This is because pynput doesn't use the layout group index (bits 13 and 14 of
event.state):https://www.x.org/releases/X11R7.7/doc/libX11/XKB/xkblib.html
I'm not sure about the proper way to compute the layout index, but I managed to get something that seems to work using this diff:
The swapping might look silly but this how the layouts are organized, somehow.
For instance let's say I have 3 layouts configured in this specific order:
qmaps toq,Q,ä,Ä)qmaps tob,B,|,¦)qmaps toa,A,æ,Æ)Then
display._keymap_codes[keycode]forqcontains:Also note that I had to use
'ISO_Level3_Shift'instead of'Mode_switch'in order to get the correctalt_gr_mask.