使用 python xlib 全局捕获、忽略和发送 keyevents,识别假输入
问题描述
我想在普通键盘上实现键和弦,我想我使用 python xlib.为此,程序必须全局吞下所有关键事件,然后才允许它们通过.
i want to implement key chording on a normal keyboard and i thought i use python xlib. for this to work the program has to globally swallow all keyevents and only later allow them to go through.
我当前的测试只是抓住了1"键.如果按下此键,它会调用一个处理程序,该处理程序通过 xtest.fake_input 将x"发送到焦点窗口.因为我只抓住1"键,所以应该没有问题,对吧?但不知何故,处理程序再次被调用,因为x"被按下.事实上,当我输入1"时,程序正在监听 all 键.这可能与调用有关
my current test just grabs the "1" key. if this key is pressed it calls a handler which sends "x" to the focused window via xtest.fake_input. because im only grabbing the "1"-key, there shouldn't be a problem, right? but somehow the handler gets called again, because "x" was pressed. in fact, the moment i type "1" the program is listening to all keys. this could have something to do with calling
display.allow_events(X.ReplayKeyboard, X.CurrentTime)
在处理一个事件之后,但如果我不这样做,一切都会冻结.
after handling an event, but if i don't do this, everything freezes.
对于最终程序,收听行为的变化并不真正相关,但我必须能够将假事件与用户事件区分开来.要做到这一点,我只是快进 display.next_event(),但这并不理想,因为用户可能在那个确切的时刻打字,而那些击键会丢失.
for the final program the change in the listening behaviour is not really relevant, but i have to be able to distinguish the fake events from the user events. to do this i'm just fast forwarding display.next_event(), but this isnt ideal, because the user could be typing at that exact moment and than those keystrokes would be lost.
我尝试在通过发送和清空事件队列期间释放 keygrab
i tried releasing the keygrab during sending and emptying the eventqueue via
display.flush()
display.sync()
但这并没有任何作用.
那么,知道如何识别或忽略虚假输入事件以及为什么我会突然听到所有按键(和释放)吗?
so, any idea how to recognize or ignore fake input events and why i'm suddenly listening to all keypresses (and releases)?
xlib 非常令人沮丧.
xlib is very frustrating.
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()
解决方案
我发现了问题.我几乎可以肯定 xtest.fake_input 做了一些奇怪的事情,因为当我手动发送按键和释放时(使用一些代码我 找到),它可以工作
i found the problem. im almost certain that xtest.fake_input does something weird, because when i send keypresses and -releases manually (with some code i found), it works
这是一个例子,它只吞下按键时的1"键,并在按键释放时将x"发送到焦点窗口:
here is an example, that swallows only the "1"-key on keypress and sends "x" on keyrelease to the focused window:
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()
相关文章