I am currently angry over: The difficulty of faking X key events

This issue made me angry on Sunday the 23 of March, 2003. Other irritations may be found here . The most recent irritation may be found here

Update

It turns out that XFree 4.0.2 and above have an undocumented feature. Any Unicode character can be turned into a keysym by ORing it with 0x01000000, and that can then be thrown into the keymap in order to generate a useful symbol. This facility is available if X_HAVE_UTF8_STRING is defined in the X headers. The SPI_generatekeyevent() function now has a partially functional implementation for UTF8 to event translation.

Original content

I work on a project called Dasher. It's an input application that requires very little mobility, and it's potentially faster than an on-screen keyboard. So, it would be nice to be able to use it to input text into arbitrary applications, rather than having to use cut and paste to do so. Windows has a horribly messy way of doing this, which involves wedging yourself into an application's input stream and then constructing a key event (in earlier versions, you need to vkkeyscan() the key that you want and then send it - in 2000 and higher, there's a nice function that'll just take a UTF8 character and take responsibility for it all), but it's not that difficult to implement.

X, on the other hand?

Firstly, sending the event sounds easier. You've got XSendEvent(), and you can construct a key event without too much trouble. And without too much trouble you can get a window id for your target application. Sadly, this is where things start going downhill. A key event sent with XSendEvent is tagged as "fake", and applications can choose to ignore it (security reasons - otherwise an untrusted remote application could write a keylogger in one of your xterms and build and run it without your knowledge. Especially if it did it when the window was moved to the background). Since xterm and emacs do this, it's a pain. Thankfully, there's XTestFakeKeyEvent(), which uses the XTest extension (intended for testing the X server in an automated fashion) to fake a key event. This is basically identical to the user actually pressing a key, and so applications aren't informed of the change.

Sadly, the fake key event ends up being sent to whichever application happens to have input focus at the time. You can't XSetInputFocus() to another application because the window manager will set it back again. So that's irritating. The easiest way around this is to refuse to take focus in the application window, and then just ask the user to focus the window they want to enter text in. For added goodness, grab the pointer when Dasher is running, since the user may fling the mouse about all over the place and you don't want them to end up focussing another window by accident.

So far so good. But XTestFakeKeyEvent() takes a keysym as an argument representing which key should be pressed. Which ought to be easy enough. But isn't.

There is no mechanism for getting from a character to the keysym that would, in most circumstances, generate it.

Oh, sure, there's a nice function called XStringToKeysym(). It takes the name of a keysym as an argument, rather than an arbitrary string. Argh.

(It's not as if it's an intrinsically hard problem - there's not inherently a one to one mapping between characters and keysyms, because applications are going to render stuff as they want, and there's pathological cases like the rendering of ')

There is, thankfully, understanding of this problem. There's a nice generatekeyevent() function in the at-spi library that's documented as being able to take a UTF8 string as input. But that bit's not implemented yet. It's actually very hard to implement, as generating an event that doesn't correspond to a key on the keyboard is fairly nightmarish and is best done by actually implementing a new X input method.

In summary, I would appear to lose.