全局捕获、忽略和发送Python Xlib的按键事件,识别虚假输入。

8
我想在普通键盘上实现按键编码,并考虑使用Python xlib。为了使其正常工作,程序必须全局捕获所有按键事件,并稍后才允许它们通过。
我的当前测试仅抓取“1”键。如果按下此键,则调用处理程序,通过xtest.fake_input将“x”发送到焦点窗口。因为只有“1”键被抓取,所以不应该有问题,对吗? 但是,由于按下了“x”,处理程序再次被调用。实际上,从我键入“1”的那一刻起,程序就侦听所有键。 这可能与调用有关。
display.allow_events(X.ReplayKeyboard, X.CurrentTime)

在处理完事件后,如果我不这样做,一切都会冻结。

对于最终程序,监听行为的变化并不是很重要,但我必须能够区分伪造事件和用户事件。为了做到这一点,我只是快进display.next_event(),但这并不理想,因为用户可能正在输入,那么那些按键就会丢失。

我试过在发送期间释放keygrab并通过以下方式清空eventqueue

display.flush()
display.sync()

但那并没有做任何事情。

那么,有什么办法可以识别或忽略虚假的输入事件,为什么我突然听到了所有按键(和释放)?

Xlib非常令人沮丧。

from Xlib.display import Display
import Xlib
from Xlib import X
import Xlib.XK
import sys
import signal 

display = None
root = None

def handle_event(aEvent):
    print "handle!"
    send_key("x")

def send_key(emulated_key):

    global display,root
    print "send key"
    # ungrabbing doesnt help
    root.ungrab_key(10,X.AnyModifier) 
    window = display.get_input_focus()._data["focus"]
    # Generate the correct keycode
    keysym = Xlib.XK.string_to_keysym(emulated_key)
    keycode = display.keysym_to_keycode(keysym)
    # Send a fake keypress via xtestaaa
    Xlib.ext.xtest.fake_input(window, Xlib.X.KeyPress, keycode)
    Xlib.ext.xtest.fake_input(window, Xlib.X.KeyRelease, keycode)
    display.flush()
    display.sync()
    # fast forward those two events,this seems a bit hacky, 
    # what if theres another keyevent coming in at that exact time?
    while display.pending_events():
        display.next_event()
    #
    root.grab_key(10, X.AnyModifier, True,X.GrabModeSync, X.GrabModeSync)

def main():
    # current display
    global display,root
    display = Display()
    root = display.screen().root

    # we tell the X server we want to catch keyPress event
    root.change_attributes(event_mask = X.KeyPressMask)
    # just grab the "1"-key for now
    root.grab_key(10, X.AnyModifier, True,X.GrabModeSync, X.GrabModeSync)
    # while experimenting everything could freeze, so exit after 10 seconds
    signal.signal(signal.SIGALRM, lambda a,b:sys.exit(1))
    signal.alarm(10)
    while 1:
        event = display.next_event()
        print "event"
        #if i dont call this, the whole thing freezes
        display.allow_events(X.ReplayKeyboard, X.CurrentTime)
        handle_event(event)

if __name__ == '__main__':
    main()
1个回答

6
我找到了问题。我几乎可以确定xtest.fake_input做了一些奇怪的事情,因为当我手动发送按键和释放(使用我找到的一些代码)时,它可以正常工作。
这里有一个例子,它只在按下"1"键时吞噬该键,并在释放时向焦点窗口发送"x":
from Xlib.display import Display
import Xlib
from Xlib import X
import Xlib.XK
import sys
import signal 
import time
display = None
root = None

def handle_event(event):
    print "handle!"
    if (event.type == X.KeyRelease):
        send_key("x")

# from http://shallowsky.com/software/crikey/pykey-0.1 
def send_key(emulated_key):
    shift_mask = 0 # or Xlib.X.ShiftMask
    window = display.get_input_focus()._data["focus"]
    keysym = Xlib.XK.string_to_keysym(emulated_key)
    keycode = display.keysym_to_keycode(keysym)
    event = Xlib.protocol.event.KeyPress(
        time = int(time.time()),
        root = root,
        window = window,
        same_screen = 0, child = Xlib.X.NONE,
        root_x = 0, root_y = 0, event_x = 0, event_y = 0,
        state = shift_mask,
        detail = keycode
        )
    window.send_event(event, propagate = True)
    event = Xlib.protocol.event.KeyRelease(
        time = int(time.time()),
        root = display.screen().root,
        window = window,
        same_screen = 0, child = Xlib.X.NONE,
        root_x = 0, root_y = 0, event_x = 0, event_y = 0,
        state = shift_mask,
        detail = keycode
        )
    window.send_event(event, propagate = True)

def main():
    # current display
    global display,root
    display = Display()
    root = display.screen().root

    # we tell the X server we want to catch keyPress event
    root.change_attributes(event_mask = X.KeyPressMask|X.KeyReleaseMask)
    # just grab the "1"-key for now
    root.grab_key(10, 0, True,X.GrabModeSync, X.GrabModeSync)

    signal.signal(signal.SIGALRM, lambda a,b:sys.exit(1))
    signal.alarm(10)
    while 1:
        event = display.next_event()
        print "event"
        handle_event(event)
        display.allow_events(X.AsyncKeyboard, X.CurrentTime)            

if __name__ == '__main__':
    main()

这似乎在一段时间后或在按下1和其他带有键重复的键后默默崩溃(无法确定哪个)。另一个用户也报告了这个问题,但他的评论被删除了,因为它是作为答案发布的。 - ninjagecko
@ninjagecko 我也遇到了同样的问题。我也没找到是哪个键组合触发了这种情况。但是我遇到了20分钟和1分半后突然出现问题的情况。看起来这可能是随机发生的,或者与键盘/鼠标的使用有关。 - Munchhausen
1
time=int(time.time()) 替换为 time = X.CurrentTime,问题得到了解决。 - Gilles Castel
这个键盘监听代码对我很有效:http://www.larsen-b.com/Article/184.html - thomasa88

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接