This article is originally titled Abandoning treatment for X11, for my xkeymacs project.
I think I’ve done all that’s possible to achieve with X11 API in this project, but there still remain a bunch of issues. This document is about what they are and why they may not be resolved as an X11 application.
Before we start with the issues, we need clarify the situation first.
There are three ways to grab key events in X11: XGrabKeyboard
, XGrabKey
with GrabModeSync
and XGrabKey
with GrabModeAsync
. In the meantime, there is only one way to send a key event in X11: XTestFakeKeyEvent
, while XSendEvents
marks events sent as synthesized and a bunch of applications (such as xterm
) ignore events with this flag for some security reasons, according to various sources.
If you want to replay the key event you just grabbed, use XAllowEvents
with ReplayKeyboard
. This requires the key event to come from XGrabKey
or a previous XAllowEvents
with SyncKeyboard
, but not from a XGrabKeyboard
.
You cannot use XGrabKeyboard
along with XTestFakeKeyEvent
because the former sets an active grab and it will receive the key event faked by yourself. If you want to XUngrabKeyboard
and wait for XTestFakeKeyEvent
to accomplish, sadly there seems to be no way. XFlush
flushes your own request to X server, while XSync
flushes and make X server process all requests and generate events, but not handling them, according to this thread.
XGrabKey
is called a passive grab. It is activated and then behaves like XGrabKeyboard
when the registered key and modifier combination is found, and actually ungrabbed after the KeyRelease
event of that combination (see this mail). So if you send a key in KeyPress
event, you will intercept the key you just sent. To workaround this issue, you can do XUngrabKeyboard
before you send the key. This results in garbage KeyRelease
events sent to the focused window, however it is normally ignored. Another workaround would be sending the key in KeyRelease
event, which works, but is not the desired behavior.
Note that if you replay a key event with XAllowEvents
in KeyPress
, It will also result in garbage KeyRelease
events, because according to the documentation the replayed event will ignore any key grabs set by any client.
So here is the first issue.
Grabbed keys sent by
XTestFakeKeyEvent
cannot be replayedWhen testing the application, I was surprised to find that when I sent keys that are also grabbed by myself and deliberately do the replay in
KeyPress
, I still receiveKeyRelease
events for them. That is to say theXAllowEvent
replay failed. I guess this is becauseXTestFakeKeyEvent
is part of theXTest
extension and is aimed for automating testing, so the internal implementation did not take this situation into consideration. So there may be no workaround for this.An example for this issue can be when I grabbed
Control+X
for C-x mode and I sendControl+X
inside the handling ofControl+W
When sending some key combination, one have to know the status of the modifier keys and adjust them, send the key, and then restore them. This is expected to happen in a very short period of time so the user won’t notice. To accomplish this, XQueryKeymap
and multiple XTestFakeKeyEvent
is used.
And the second issue.
Sending keys containing
F1
~`F8causes user to be switched to
tty`When sending keys containing
F3
orF4
, sometimes I got switched totty
by an unexpectedControl+Alt+F3/4
combination. This happens for most of the time when I sendControl+F4
for(Control+X) k
. You can see that there is nothing aboutAlt
, but still I was almost always switched totty4
, howeverxev
reported the expected key sequence for this situation. The underlying logic is unknown, it may have something to do withtty
switching hot key implementation.
And some other issues.
Auto repeat cannot work if we intercept the
KeyPress
eventX server generates continuous paired
KeyPress
andKeyRelease
event for clients if a key is physically held for a long time. However if we intercept the firstKeyPress
event, and send another key event, then the auto repeat just disappears. There doesn’t seem to be a solution for this issue.Efficiency
There is a perceptible (annoying but bearable) pause when using the above stated approach to implement keyboard macros. Seems no way to resolve either.
Note that using
GrabModeAsync
helps to improve the performance a little bit, but it results in various problems that I haven’t investigated yet.
So the conclusion is that, while X11 provided some API for keyboard event operations, it is vague in documentation and working mechanism, and clearly not designed, nor suitable for translating key sequences into others. This may be the reason why almost no such project like this is known.
For an alternative API, some people in a mailing list suggested Atspi, which I haven’t tried out myself.
UPDATE:
Later I found out that this can be achieved with evdev
and uinput
.
For a python implementation, see my pykeymacs.