I've been accused recently of not posting to my journal enough. If truth be told, it's largely because I don't think I ever do anything, or have anything to say, that would be of wider interest to many people at once. So perhaps I'll start writing some more specific things and see what the reaction is.
I've recently been writing a program that tries to fulfil a rather old nagging thought in my head; that of fairly arbitrary event binding. We have ever-growing numbers of programs that try to bind various events to various actions. Most people use a window manager to bind hotkeys to start new programs. Then there's cron, that binds certain times of the day/week/month/year to commands. I happen to run imwheel to bind the thumb buttons on my mouse to send keyboard events to programs that wouldn't otherwise pay attention to these mouse buttons. Cough firefox YES I AM LOOKING AT YOU.
Individually, these are all moderately nice programs. But taken all together, they start to feel a little underpowered. For example, imwheel can be sensitive to the title/class/role of the window you clicked the mouse button on, but it can only listen for mouse buttons. Window manager or xbindkeys can listen for keypress events, but aren't sensitive to the window it was on. And so on... Then there's the annoyance that almost all programs simply do a fork()/exec() to run some external command - this makes them flexible, if inefficient. And occasionally there's persistence tricks you'd like to do but can't - e.g. easily find a way to have a combined mute/unmute button for sound.
So I started thinking of how I can do this generically; a program that can just listen for "events", and respond with "actions", being sensitive to some sort of "condition". So far, I have an implementation that waits on X11 key/mousebutton presses, ACPI events or raw evdev events. I'm planning joystick and wiimote support too, for things I want. On the action side, so far just command execution, mpd control, and printing of messages to stdout (great for debug testing). These are all implemented in little plugins, so it's very easy to add just about anything.
My current struggle, leading on to the subject of this post, concerns the delivery of faked X11 input events. There's basically two ways to do this, neither seem very satisfactory to me.
The XTEST extension
This rather-oddly named extension was originally designed for testing X servers without real user input. The extension has the ability to inject faked keyboard input and mouse press or motion events programmatically. The upside of this approach is that generally it works with little effort. The downside being it doesn't have any context - keyboard events are just generated as if the user typed them; they can't be directed to some particular window or other, for example. Furthermore, should I wish to send e.g. a Ctrl-A to a program, I'd have to fake pressing the Ctrl key, then A, then releasing them both.
This X11 call can be used to deliver any event to any window. It's more complex to work out how to use than XTEST; for example it needs an entire valid event being sent. This event would need to include the absolute and window-relative position of the mouse cursor, and other details. It can be sent to a specific window (likely the one under the mouse at the time; though this might be "tricky" if this action was triggered by some non-X11 event). It can also be given a specific set of state bits, so no tricky hackery of Ctrl keys just to send those. The downside though is it's much more complex to send, and not quite guaranteed to work, since the delivered event will be marked as being faked by XSendEvent rather than natively generated by the server from user input. There may be some programs that would ignore it in this case.
So, those are my options. Neither quite seems right, for the reasons outlined. I'm not sure which of these methods I ought to use; or maybe if in fact I should allow a choice of either in config - each action could specify which one to use. But is that just my "any difficult decisions should just be deferred to the user by config" kicking in? Is this a good response though?