i want to catch keydown and keyup events with python xlib, but keyup events disappear when some keys are pressed simultaneously.
if 2 or more keyes are released simultaneously then there will be 2 or more keypress events, but only 1 keyrelease event.
for this to happen the keys don't even have to be released simultaneously, for example if you enter this sequence fast:
will yield only 1 keyrelease for A
will yield 2 keyreleases
from Xlib import X,XK
from Xlib.display import Display
import signal,sys
root = None
display = None
def grab_keyname(n):
global root
keysym = XK.string_to_keysym(n)
keycode = display.keysym_to_keycode(keysym)
root.grab_key(keycode, X.AnyModifier, False,X.GrabModeSync, X.GrabModeAsync)
def main():
# current display
global display,root
display = Display()
root = display.screen().root
root.change_attributes(event_mask = X.KeyPressMask|X.KeyReleaseMask)
grab_keyname("j")
grab_keyname("k")
grab_keyname("l")
signal.signal(signal.SIGALRM, lambda a,b:sys.exit(1))
signal.alarm(4)
while True:
event = display.next_event()
print event.type
main()
Even though this question is 7 years old, a solution for anyone stumbling upon this:
This is a 'bug' in Xorg (which is apparently intentional) which causes keyboard grab to stop upon a key release, and only to start again on another button press. Therefore, any events in between (-> the second key release event) are lost. See https://bugs.freedesktop.org/show_bug.cgi?id=99280
The solution proposed there is to have a counter which indicates how many of the keys are still pressed, and manually grab the keyboard if there are still pending presses. In addition, the keyboard needs to be ungrabbed after the last release event.
The implementation might look like the following:
keys_pressed = 0
# ...
event = display.next_event()
if event.type == X.KeyPress:
keys_pressed += 1
elif event.type == X.KeyRelease:
if keys_pressed != 0:
keys_pressed -= 1
if keys_pressed == 0:
display.flush()
display.ungrab_keyboard(X.CurrentTime)
else:
root.grab_keyboard(False, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
Note the check for keys_pressed != 0
in the release branch: this is because after ungrabbing the keyboard, one additional release event is caught (there is probably a way to prevent this, but it does not matter for my use case...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With