Saturday, February 12, 2005

You want recording ? ( X11 Event recording)

I had the task of creating/updating an X11 application with the ability to "always" have control (or at least knowledge) of the keyboard and mouse no matter what. There were a couple of options. Either grab the mouse and keyboard but with this you have the disadvantage of needing to send events to the target applications by hand. Or you can release the keys and do a grab on a specific key. This might work but you loose the ability to process some keys, or you can watch the events from the outside (using the XRecord extension for example). So I/we choose to go with the third option.

Another part of the equation was that we needed to watch the events only in the cases in which we were forced to release the keyboard and mouse. So the ability to programmatically turn on/off the recording was needed.

I looked on the net for a XRecord API documentation and the only useful one was XRecord library description document. This document is a PDF describing how the API is structured, what the functions and the structures are and how to use them. Everything is fine once you read it but if you read it like me then you are somewhat in trouble because you might miss a proposition that is key in how this API works.
(The document in available here: http://www.x.org/X11R6.8.1/docs/Xext/recordlib.pdf)

How it works:
- first you make a X connection ( using xlib's XOpenDisplay for example ).
- query for the extension with XRecordQueryVersion API function call.
- after that you create a recording context.
- once you have that you need to enable it in order to get events.
- once enabled you start receiving events and when you are tired of them you can disable it.
- free the context
- close the display and that's it.

Now the catch is that you need to use 2 connections: one for data and one to control it but this is not the complete story. This is mentioned in the document but in only 2 lines somewhere at the 1.3 part of the document. And if you read it quick (like me for example) you might miss it and then you start wondering why all the Xlib calls that you try to execute on the connection used to enable the context seem to block mysteriously ? This is somewhat a mistake from my part because I should have read the damn document more closely but this is life....

There are other quirks that you need to remember if you plan to use this: you need to call the XRecordCreateContext on the connection that you will use to enable it (the data connectoin) because it will fail at runtime if not with an Xlib error BadParameter or the like (this is not in the document). Also if you enable the context on one connection (the data connectio usually) you will need to disable it on the other connection (the control) because your calls into Xlib on the data connection will fail. And in the end you need to keep in mind that recorded events are actually xEvents and they seem kind of raw. For example the ButtonPress and ButtonRelease have the correct state for the button which was Pressed/Released but no position information which means you have to keep that state yourself but using the MotionNotify event types and reading the mouse position from them. This is not very good because it places a lot of overhead. If I for example don't need the mouse move events and only need the mouse clicks and clicked position i should be able to get that info from the actual ButtonDown event and not from tracking the mouse.

Mihai