I'm trying to implement a Python script in Linux to capture the key press a
send the key press a
again (so it sends a
twice).
Through some code at https://github.com/PeterHo/Linalfred/blob/master/src/globalhotkey.py I arrived at the following.
The key is captured fine, but sending the key press event does nothing. What am I missing here?
I also looked at the question globally capture, ignore and send keyevents with python xlib, recognize fake input. The posted solution doesn't even appear to receive the events (after porting the print
to Python 3).
import time
from Xlib import X, protocol
from Xlib.display import Display
from Xlib.ext import record
display = None
root = None
def handler(reply):
data = reply.data
while len(data):
event, data = protocol.rq.EventField(None).parse_binary_value(data, display.display, None, None)
if event.type == X.KeyPress:
keycode = event.detail
print(keycode)
if keycode == 38:
window = Display().get_input_focus().focus
event = protocol.event.KeyPress(
time=int(time.time()),
root=root,
window=window,
same_screen=0, child=X.NONE,
root_x=0, root_y=0, event_x=0, event_y=0,
state=0,
detail=keycode
)
window.send_event(event, propagate=True)
event = protocol.event.KeyRelease(
time=int(time.time()),
root=root,
window=window,
same_screen=0, child=X.NONE,
root_x=0, root_y=0, event_x=0, event_y=0,
state=0,
detail=keycode
)
window.send_event(event, propagate=True)
def main():
global display, root
display = Display()
root = display.screen().root
ctx = display.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': (X.KeyReleaseMask, X.ButtonReleaseMask),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}]
)
display.record_enable_context(ctx, handler)
display.record_free_context(ctx)
while True:
# Infinite wait, doesn't do anything as no events are grabbed.
event = root.display.next_event()
if __name__ == '__main__':
main()
I googled quite a bit more today, and came across Sending key presses to specific windows in X, which worked for me.
The key idea is to use display
instead of window
to send the events, and to sync
the display. Without syncing the display, the key press won't be registered at all.
disp.send_event(window, event, propagate=True)
disp.sync()
Applied that to our script above, we have the following working version:
import time
from Xlib import X, protocol
from Xlib.display import Display
from Xlib.ext import record
display = None
root = None
def handler(reply):
data = reply.data
while len(data):
event, data = protocol.rq.EventField(None).parse_binary_value(data, display.display, None, None)
if event.type == X.KeyPress:
keycode = event.detail
print(keycode)
if keycode == 38:
disp = Display()
window = disp.get_input_focus().focus
root = disp.screen().root
event = protocol.event.KeyPress(
time=0,
root=root, window=window, same_screen=0, child=X.NONE,
root_x=0, root_y=0, event_x=0, event_y=0,
state=0, detail=keycode
)
disp.send_event(window, event, propagate=True)
disp.sync()
event = protocol.event.KeyRelease(
time=0,
root=root, window=window, same_screen=0, child=X.NONE,
root_x=0, root_y=0, event_x=0, event_y=0,
state=0, detail=keycode
)
disp.send_event(window, event, propagate=True)
disp.sync()
def main():
global display, root
display = Display()
root = display.screen().root
ctx = display.record_create_context(
0,
[record.AllClients],
[{
'core_requests': (0, 0),
'core_replies': (0, 0),
'ext_requests': (0, 0, 0, 0),
'ext_replies': (0, 0, 0, 0),
'delivered_events': (0, 0),
'device_events': (X.KeyReleaseMask, X.ButtonReleaseMask),
'errors': (0, 0),
'client_started': False,
'client_died': False,
}]
)
display.record_enable_context(ctx, handler)
if __name__ == '__main__':
main()