5

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:

  1. press A
  2. press B
  3. release A
  4. release B

will yield only 1 keyrelease for A

  1. press A
  2. press B
  3. release B
  4. release 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()
4

1 回答 1

3

即使这个问题已有 7 年的历史,对于任何遇到此问题的人来说都是一个解决方案:

这是 Xorg 中的一个“错误”(这显然是有意的),它会导致键盘抓取在按键释放时停止,并且仅在按下另一个按钮时重新开始。因此,介于两者之间的任何事件(-> 第二个按键释放事件)都将丢失。见https://bugs.freedesktop.org/show_bug.cgi?id=99280

提出的解决方案是有一个计数器来指示仍有多少键被按下,如果仍有未完成的按下,则手动抓取键盘。此外,在最后一次发布事件之后需要取消抓取键盘。

实现可能如下所示:

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)

请注意发布分支中的检查keys_pressed != 0:这是因为在松开键盘后,捕获了一个额外的发布事件(可能有一种方法可以防止这种情况,但这对我的用例来说并不重要......)

于 2020-08-18T12:50:25.823 回答