Attempt at input event documentation
mailinglists at who-t.net
Wed Aug 9 00:38:32 PDT 2006
In my attempt to write MPX I had to dig through a lot of functions
and try to figure out how a (mouse) event is created and sent to the
client. I wrote up a summary of how the X server processes input events.
If the information is useful (and correct), I am happy with putting
it up on the wiki or anywhere else where it is of use to other
developers. At the moment it uses wiki syntax (DokuWiki), I can
change that to whatever meets the requirements.
I'd appreciate any feedback, especially if I misunderstood something
and the information is wrong.
===== X Server Input Events =====
This is a puny attempt to explain how the X Server generates and
input events. This document was created as part of the development
the Multi-Pointer X Server [[http://wearables.unisa.edu.au/mpx]].
This document does not replace a good look at the source code. It
understanding what happens and what order functions are called. And
it gives a
general overview on how events are born and sent to the client.
I do not give any warranty that the information here is complete and/or
accurate. The information here concentrates on pointer events but
some is the
same for keyboard events.
==== Overview ====
Generally, input events live through two stages. One is the
input is gathered from the connected devices and transformed into
events (''struct xEvent'') and the processing of the events, where
are adopted to match the client's requirements. In the processing
abstract events (such as enter and leave events) are generated.
In between those two stages, there is the event queue. Events are put
event queue after the creation stage and taken off again at the start
There are only a few directories that are interesting for all that:
* ''xserver/dix'' ... device independent X. The events.c file is
handling most of the events.
* ''xserver/mi'' ... machine independent X. Mouse cursor rendering
* ''xserver/hw/xfree86/common'' ... some additional stuff,
especially for X Input.
* ''xserver/Xi'' ... X Input Extension protocol stuff.
The method the server spends the most time in is ''Dispatch()'', and
method the server mostly waits in ''WaitForSomething()'' for any client
Lots and lots of functions are called using function pointers.
Finding them can be very frustrating. See the last section on how
to set up ctags to jump around in the source and find functions easier.
== The DESIGN document ==
There is a document that describes the design of the X server.
where you have the source tree the document is in
xserver/hw/xfree86/doc/DESIGN.sgml or if you have the xserver-xorg
installed you should have it in /usr/share/doc/xserver-xorg/DESIGN.gz.
It's worth a read but I did not find a lot of information about how
events are handled.
==== Event creation ====
When a device emits events, a SIGIO is fired and ''xf86SIGIO()'' is
which in turn calls the ''xf86SigioReadInput()'' for the given
socket. The latter in turn calls the read input function for the pointer
provided. For the mouse, this function is ''MouseReadInput()''.
''MouseReadInput'' is one of the functions in the ''InputInfoPtr'' of
mouse driver. It is set when the input driver is initialised and
''MousePreInit()'' is called (see section 5.6 in the DESIGN doc).
''MouseReadInput()'' does all the processing for the different mouse
protocols and then posts the event via ''MousePostEvent()'' (again a
pointer in the ''InputInfoPtr'') into ''MouseDoPostEvent()''.
For a motion event, we call now ''xf86PostMotionEvent()''. For button
it is ''xf86PostButtonEvent()''.
Here an actual ''struct xEvent'' is assembled and put
onto the event queue. Not all of the events values are set already and
X Input Extension (XI) devices and core devices are handled
XI events get the type, time, detail, device ID and the valuators
devices only have time, type and detail set, the device ID field and the
valuators are omitted.
One very interesting thing that happens in ''xf86PostMotionEvent()''
only non-core events are put on the event queue.
Core events are processed but only if a ''drag'' flag is on. How is
flag determined? It is > 0 if no button is pressed. It is > 0 if a
pressed and the device is set to send drag events. It is only 0, if
are down and the device is set to not emit drag events.
If the drag flag is set, ''miPointerAbsoluteCursor()'' is called with
position. ''miPointerAbsoluteCursor()'' confines the coordinates to
range (after changing screen if necessary) and calls ''miPointerMove
Another ''xEvent'' is created here and filled with the values type
(''MotionNotify''), rootX, rootY and time. It is then put onto the event
queue. There is also one global ''miPointerRec'' that represents the
and it is updated to the new coordinates (and screen) here. We need that
later. If the screen needs to be updated, the cursor sprite is
rendered but we
ignore that for now.
So we can have two types on the event queue: XI events (they consist
''struct XEvents'', one with the event and one with the device
To sum it up in short: each time a interrupt happens on one of the
sockets to an input event, the mouse driver reads the data, hands it
the X Server which constructs a ''struct xEvent'' and puts it onto
==== Event processing ====
The event processing stage is the stage where the events are taken
event queue, individually processed and then sent to the client.
abstract input events (enter and leave notifies) are generated
All input are processed in ''ProcessInputEvents()'' which calls
''xf86eqProcessInputEvents()'' (it also does additional stuff while the
server starts up but we ignore that).
''xf86eqProcessInputEvents()'' runs through the event queue from
end. Depending on the type of event, either ''ProcessKeyboardEvent()'',
''ProcessPointerEvent()'' or (in the case of XI devices)
''ProcessPointerEvent()'' calls ''CoreProcessPointerEvent()''. And we
get to the
For all events that are not ''MotionNotify'', the rootX and rootY
set to the values of the sprite. For button presses, device grabs are
and if there is one, nothing else happens. For ''MotionNotify''
call ''CheckMotion()''. After ''CheckMotion()'' finishes, the
its final stages with a call to ''DeliverDeviceEvents()'' or
''DeliverGrabbedEvents()'' (if the mouse has a grab). If the grab
needs to be
deactivated, we finish with a call to ''DeactivatePointerGrab()''.
So what does ''CheckMotion()'' do? This function updates the cursor
position and then sets the event's coordinates to the new sprite
Finally, we compare the window the updated sprite is over with the
one and call ''DoEnterLeaveEvent()'' if necessary. If the window has
we also issue a call to ''PostNewCursor()'' which basically changes
updated cursor shape.
Let us see what ''DoEnterLeaveEvent()'' does. If the old window is a
the new window, we issue a ''LeaveNotify'' to the old window, then
send ''EnterNotify'' events to the ancestors of the target window
done in ''EnterNotifies()'') and then finally a ''EnterNotify'' to
window. If the old window is a child of the new window, we do the
with the leave and enter notifies swapped around. Finally, if the
not related, we send a ''LeaveNotify'' to the old window and then
to its parents (using ''LeaveNotifies()''), then recursively send
''EnterNotify'' events (using ''EnterNotifies()'' again) to the new
parents and finally a ''EnterNotify'' to the new window.
Remember that there are multiple types of ''EnterNotify'' and
events. The ones sent to the parents are all of type
''NotifyNonlinearVirtual'' if the windows are unrelated). The ones
sent to the
old and the new window are of types ''NotifyAncestor'' or
for related windows and ''NotifyNonlinear'' for unrelated windows.
and leave events are constructed in ''EnterLeaveEvent()''. A
created, filled with values and then sent to the window using
''DeliverEventsToWindow()''. Again, rootX and rootY is taken from the
So now that we have finishedthe enter/leave events we concentrate on
final event processing consists of.
''DeliverDeviceEvents()'' has two code paths: one for extension
for core events but they are fairly similar. The event is adopted to
the window in
''FixUpEventFromWindow()'' and then delivered to the window with
''DeliverEventsToWindow()''. ''FixUpEventFromWindow()'' adopts the
specific values to the event's window (the child, eventX and eventY
If the delivery failed to a given window, the parent is tried until
we run out
of parent windows. ''DeliverEventToWindow()'' calls ''TryClientEvents
write the events to the client. If the event is a button press event,
''DeliverEventToWindow()'' also activates the pointer grab.
Now we have completed event processing, all the events were written
client and we jump back to the last lines of ''ProcessInputEvents
()''. What is
left now is cursor rendering.
Again, a short summary of the event processing stage: the server
events off the queue, fills them with the right variables, generate
leave notifies if necessary and writes them to the client.
==== Cursor rendering ====
Cursor rendering is a bit complicated to understand and hard to
debug. It is a
layered architecture to do as much in hardware as possible and pretty
everything is called via function pointers. If the cursor is fully
hardware the functions just go directly into the driver and the card
the cursor directly into the output stream. If it is done in sofware,
cursor has to be back-buffered. Every time it moves we restore the
image, save the window at the target position, then render the cursor
We start with everything at the end of ''ProcessInputEvents()'' and
to ''miPointerUpdate()''. Here we grab the current coordinates of the
(remember, they were set when we called ''miPointerMove()'' in the event
generation stage) and call the ''MoveCursor'' function in the
of the ''miPointerScreenRec'' struct. Of course, if the cursor has
screen or the shape has changed, this needs to be taken care of too. The
''MoveCursor'' function is set to ''miSpriteMoveCursor()'' which just
''miSpriteSetCursor()''. This function first checks whether the
changed at all and then the new positions of the cursor. If the
within the saved region, the saved region is changed and the cursor
If it is not, the cursor is removed with ''miSpriteRemoveCursor()''
restored at the new position with ''miSpriteRestoreCursor()''.
''miSpriteRemoveCursor()'' is fairly simple, it just calls the restore
function ''miDCRestoreUnderCursor()'', which then calls the next layer
(damage) to copy the saved area into the window at a given position.
''miSpriteRestoreCursor()'' saves the area under the cursor
(''miDCSaveUnderCursor()'') into the buffer and then puts up the
If, as mentioned before, the new position is insided the saved
buffer, a call
to ''miDCChangeSave()'' updates the saved region and a call to
''miDCMoveCursor()'' will move the cursor. This moving doesn't cause any
flickering, the remove and restore procedure may flicker.
As easy as this sounds, there is more to cursor rendering. Quite a
fair bit of
work is done outside this explicit rendering calls that are issued
input events have been processed.
Interestingly, pretty much all other function that handle sprite
(everything with ''miSprite...'') basically remove the cursor from
if necessary (i.e. when the window is moved).
The one exception is the block handler function (called when there's
else to do and the server would block while waiting for input).
''miSpriteBlockHandler()'' checks if the cursor was previously
should be visible and renders it to the screen again if necessary.
==== Functions and where to find them ====
| Dispatch() | xserver/dix/
| WaitForSomething() | xserver/os/
| SIGIO() | xserver/hw/xfree86/os-support/shared/
| xf86SigioReadInput() | xserver/hw/xfree86/
| MouseReadInput() | driver/xf86-input-mouse/src/
| InputInfoPtr | xserver/hw/xfree86/common/
| MousePostEvent() | driver/xf86-input-mouse/src/
| MouseDoPostEvent() | driver/xf86-input-mouse/src/
| xf86PostMotionEvent() | xserver/hw/xfree86/common/
| xf86PostButtonEvent() | xserver/hw/xfree86/common/
| struct xEvent | proto/X11/
| miPointerAbsoluteCursor() | xserver/mi/
| miPointerMove() | xserver/mi/
| struct miPointerRec | xserver/mi/
| ProcessInputEvents() | xserver/hw/xfree86/common/
| xf86eqProcessInputEvents() | xserver//hw/xfree86/common/
| ProcessKeyboardEvent() | xserver/xkb/
| ProcessPointerEvent() | xserver/xkb/
| ProcessOtherEvent() | xserver/Xi/
| DeliverDeviceEvents() | xserver/dix/
| DeliverGrabbedEvents() | xserver/dix/
| DeactivatePointerGrab() | xserver/dix/
| CheckMotion() | xserver/mi/
| DoEnterLeaveEvents() | xserver/dix/
| PostNewCursor() | xserver/dix/
| EnterNotifies() | xserver/dix/
| LeaveNotifies() | xserver/dix/
| EnterNotifies() | xserver/dix/
| EnterLeaveEvent() | xserver/dix/
| FixUpEventFromWindow() | xserver/dix/
| DeliverEventToWindow() | xserver/dix/
| miPointerUpdate() | xserver/mi/
| struct miPointerScreenRec | xserver/mi/
| miSpriteMoveCursor() | xserver/mi/
| miSpriteSetCursor() | xserver/mi/
| miSpriteRemoveCursor() | xserver/mi/
| miSpriteRestoreCursor() | xserver/mi/
| miDCRestoreUnderCursor() | xserver/mi/
| miDCSaveUnderCursor() | xserver/mi/
| miDCPutUpCursor() | xserver/mi/
| miDCChangeSave() | xserver/mi/
| miDCMoveCursor() | xserver/mi/
==== Using ctags to find functions ====
Finding functions in X is hard. One way to search for the actual
a data type is to grep the source directory and then open the file.
take forever, especially when you don't quite know where to look for.
However, vim's support for ctags makes it easier. It is possible to
tags file for the whole system and then just use it from within vim.
in vim you only have to go to the occurence of the data type, press
it will open the matching definition. With CTRL+T you jump back to the
I created my tags file somewhere in my .vim directory.
$> mkdir .vim/tags/
$> cd .vim/tags/
$> ctags -R /usr/include/* /path/to/X/source/code
ctags will create a file "tags" in the current directory ($HOME/.vim/
this case). This way I got pretty much all defintions I need at the
Now you need to tell vim to include this file. Add the following line
On your next startup of vim, everything will be available with CTRL
+]. If you
use tags heavily, you will find CTRL+G helpful. It shows the name of
in the current buffer.
A recommendation is to write a little script to update your ctags and
as a cron job every night. Your computer will not be very responsive
recursively searching for ctags in a multi-GB directory.
== Warning ==
This can be a hazardous setup as the ctags are absolute. If you are
two different source trees (i.e. two releases of the same software),
CTRL+ ] will jump to the functions as defined in ctags. So you might be
editing the wrong source tree.
Multi-Pointer X Server
More information about the xorg