Attempt at input event documentation

Peter Hutterer mailinglists at who-t.net
Wed Aug 9 00:38:32 PDT 2006


Hey,

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.

Cheers,
   Peter



===== X Server Input Events =====

This is a puny attempt to explain how the X Server generates and  
processes
input events. This document was created as part of the development  
for MPX,
the Multi-Pointer X Server [[http://wearables.unisa.edu.au/mpx]].
This document does not replace a good look at the source code. It  
just helps
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  
generation, where
input is gathered from the connected devices and transformed into  
abstract input
events (''struct xEvent'') and the processing of the events, where  
the events
are adopted to match the client's requirements. In the processing  
stage, more
abstract events (such as enter and leave events) are generated.

In between those two stages, there is the event queue. Events are put  
on the
event queue after the creation stage and taken off again at the start  
of the
processing stage.

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  
stuff.
   * ''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  
in this
method the server mostly waits in ''WaitForSomething()'' for any client
events.
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.  
Depending on
where you have the source tree the document is in
xserver/hw/xfree86/doc/DESIGN.sgml or if you have the xserver-xorg  
package
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  
input
events are handled.

==== Event creation ====
When a device emits events, a SIGIO is fired and ''xf86SIGIO()'' is  
called
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  
the
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  
function
pointer in the ''InputInfoPtr'') into ''MouseDoPostEvent()''.

For a motion event, we call now ''xf86PostMotionEvent()''. For button  
events
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  
differently here.
XI events get the type, time, detail, device ID  and the valuators  
set. Core
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()''  
is that
only non-core events are put on the event queue.
Core events are processed but only if a ''drag'' flag is on. How is  
the drag
flag determined? It is > 0 if no button is pressed. It is > 0 if a  
button is
pressed and the device is set to send drag events. It is only 0, if  
buttons
are down and the device is set to not emit drag events.

If the drag flag is set, ''miPointerAbsoluteCursor()'' is called with  
the new
position. ''miPointerAbsoluteCursor()'' confines the coordinates to  
the screen
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  
cursor
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  
of two
''struct XEvents'', one with the event and one with the device  
valuators) and
core events.

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  
back to
the X Server which constructs a ''struct xEvent'' and puts it onto  
the event
queue.

==== Event processing ====
The event processing stage is the stage where the events are taken  
off the
event queue, individually processed and then sent to the client.  
Also, more
abstract input events (enter and leave notifies) are generated  
synthetically
here.

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  
beginning to
end. Depending on the type of event, either ''ProcessKeyboardEvent()'',
''ProcessPointerEvent()'' or (in the case of XI devices)
''ProcessOtherEvent()''.

''ProcessPointerEvent()'' calls ''CoreProcessPointerEvent()''. And we  
get to the
interesting bits.
For all events that are not ''MotionNotify'', the rootX and rootY  
values are
set to the values of the sprite. For button presses, device grabs are  
checked
and if there is one, nothing else happens. For ''MotionNotify''  
events, we
call ''CheckMotion()''. After ''CheckMotion()'' finishes, the  
delivery enters
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  
sprite's
position and then sets the event's coordinates to the new sprite  
positions.
Finally, we compare the window the updated sprite is over with the  
previous
one and call ''DoEnterLeaveEvent()'' if necessary. If the window has  
changed,
we also issue a call to ''PostNewCursor()'' which basically changes  
to the
updated cursor shape.

Let us see what ''DoEnterLeaveEvent()'' does. If the old window is a  
parent of
the new window, we issue a ''LeaveNotify'' to the old window, then  
recursively
send ''EnterNotify'' events to the ancestors of the target window  
(this is
done in ''EnterNotifies()'') and then finally a ''EnterNotify'' to  
our new
window. If the old window is a child of the new window, we do the  
same but
with the leave and enter notifies swapped around. Finally, if the  
window are
not related, we send a ''LeaveNotify'' to the old window and then  
recursively
to its parents (using ''LeaveNotifies()''), then recursively send
''EnterNotify'' events (using ''EnterNotifies()'' again) to the new  
window's
parents and finally a ''EnterNotify'' to the new window.
Remember that there are multiple types of ''EnterNotify'' and  
''LeaveNotify''
events. The ones sent to the parents are all of type  
''NotifyVirtual'' (or
''NotifyNonlinearVirtual'' if the windows are unrelated). The ones  
sent to the
old and the new window are of types ''NotifyAncestor'' or  
''NotifyInferior''
for related windows and ''NotifyNonlinear'' for unrelated windows.  
All enter
and leave events are constructed in ''EnterLeaveEvent()''. A  
''xEvent'' is
created, filled with values and then sent to the window using
''DeliverEventsToWindow()''. Again, rootX and rootY is taken from the  
sprite
coordinates.

So now that we have finishedthe enter/leave events we concentrate on  
what the
final event processing consists of.

''DeliverDeviceEvents()'' has two code paths: one for extension  
events, one
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  
window
specific values to the event's window (the child, eventX and eventY  
values).
If the delivery failed to a given window, the parent is tried until  
we run out
of parent windows. ''DeliverEventToWindow()'' calls ''TryClientEvents 
()'' to
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  
to the
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  
takes the
events off the queue, fills them with the right variables, generate  
enter and
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  
much
everything is called via function pointers. If the cursor is fully  
rendered in
hardware the functions just go directly into the driver and the card  
writes
the cursor directly into the output stream. If it is done in sofware,  
the
cursor has to be back-buffered. Every time it moves we restore the  
previous
image, save the window at the target position, then render the cursor  
into
the stream.

We start with everything at the end of ''ProcessInputEvents()'' and  
the call
to ''miPointerUpdate()''. Here we grab the current coordinates of the  
pointer
(remember, they were set when we called ''miPointerMove()'' in the event
generation stage) and call the ''MoveCursor'' function in the  
''spriteFuncs''
of the ''miPointerScreenRec'' struct. Of course, if the cursor has  
changed
screen or the shape has changed, this needs to be taken care of too. The
''MoveCursor'' function is set to ''miSpriteMoveCursor()'' which just  
calls
''miSpriteSetCursor()''. This function first checks whether the  
cursor has
changed at all and then the new positions of the cursor. If the  
cursor is
within the saved region, the saved region is changed and the cursor  
is moved.
If it is not, the cursor is removed with ''miSpriteRemoveCursor()''  
and then
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  
cursor again
(''miDCPutUpCursor()'').
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  
when all
input events have been processed.
Interestingly, pretty much all other function that handle sprite  
rendering
(everything with ''miSprite...'') basically remove the cursor from  
the screen
if necessary (i.e. when the window is moved).
The one exception is the block handler function (called when there's  
nothing
else to do and the server would block while waiting for input).
''miSpriteBlockHandler()'' checks if the cursor was previously  
removed but
should be visible and renders it to the screen again if necessary.


==== Functions and where to find them ====

| Dispatch()                 | xserver/dix/ 
dispatch.c                       |
| WaitForSomething()         | xserver/os/ 
WaitFor.c                         |
| SIGIO()                    | xserver/hw/xfree86/os-support/shared/ 
sigio.c |
| xf86SigioReadInput()       | xserver/hw/xfree86/ 
xf86Events.c              |

| MouseReadInput()           | driver/xf86-input-mouse/src/ 
mouse.c          |
| InputInfoPtr               | xserver/hw/xfree86/common/ 
xf86Xinput.h       |
| MousePostEvent()           | driver/xf86-input-mouse/src/ 
mouse.c          |
| MouseDoPostEvent()         | driver/xf86-input-mouse/src/ 
mouse.c          |
| xf86PostMotionEvent()      | xserver/hw/xfree86/common/ 
xf86Xinput.c       |
| xf86PostButtonEvent()      | xserver/hw/xfree86/common/ 
xf86Xinput.c       |

| struct xEvent              | proto/X11/ 
Xproto.h                           |
| miPointerAbsoluteCursor()  | xserver/mi/ 
mipointer.c                       |
| miPointerMove()            | xserver/mi/ 
mipointer.c                       |
| struct miPointerRec        | xserver/mi/ 
mipointrst.h                      |

| ProcessInputEvents()       | xserver/hw/xfree86/common/ 
xf86Events.c       |
| xf86eqProcessInputEvents() | xserver//hw/xfree86/common/ 
xf86Xinput.c      |

| ProcessKeyboardEvent()     | xserver/xkb/ 
xkbPrKeyEv.c                     |
| ProcessPointerEvent()      | xserver/xkb/ 
xkbAccess.c                      |
| ProcessOtherEvent()        | xserver/Xi/ 
exevents.c                        |
| DeliverDeviceEvents()      | xserver/dix/ 
events.c                         |
| DeliverGrabbedEvents()     | xserver/dix/ 
events.c                         |
| DeactivatePointerGrab()    | xserver/dix/ 
events.c                         |

| CheckMotion()              | xserver/mi/ 
mipointer.c                       |
| DoEnterLeaveEvents()       | xserver/dix/ 
events.c                         |
| PostNewCursor()            | xserver/dix/ 
events.c                         |
| EnterNotifies()            | xserver/dix/ 
events.c                         |
| LeaveNotifies()            | xserver/dix/ 
events.c                         |
| EnterNotifies()            | xserver/dix/ 
events.c                         |
| EnterLeaveEvent()          | xserver/dix/ 
events.c                         |

| FixUpEventFromWindow()     | xserver/dix/ 
events.c                         |
| DeliverEventToWindow()     | xserver/dix/ 
events.c                         |

| miPointerUpdate()          | xserver/mi/ 
mipointer.c                       |
| struct miPointerScreenRec  | xserver/mi/ 
mipointrst.h                      |
| miSpriteMoveCursor()       | xserver/mi/ 
misprite.c                        |
| miSpriteSetCursor()        | xserver/mi/ 
misprite.c                        |
| miSpriteRemoveCursor()     | xserver/mi/ 
misprite.c                        |
| miSpriteRestoreCursor()    | xserver/mi/ 
misprite.c                        |

| miDCRestoreUnderCursor()   | xserver/mi/ 
midispcur.c                       |
| miDCSaveUnderCursor()      | xserver/mi/ 
midispcur.c                       |
| miDCPutUpCursor()          | xserver/mi/ 
midispcur.c                       |
| miDCChangeSave()           | xserver/mi/ 
midispcur.c                       |
| miDCMoveCursor()           | xserver/mi/ 
midispcur.c                       |


==== Using ctags to find functions ====

Finding functions in X is hard. One way to search for the actual  
definition of
a data type is to grep the source directory and then open the file.  
This can
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  
create a
tags file for the whole system and then just use it from within vim.  
That way,
in vim you only have to go to the occurence of the data type, press  
CTRL+] and
it will open the matching definition. With CTRL+T you jump back to the
original file.

I created my tags file somewhere in my .vim directory.

<code>
$> mkdir .vim/tags/
$> cd .vim/tags/
$> ctags -R /usr/include/* /path/to/X/source/code
</code>
ctags will create a file "tags" in the current directory ($HOME/.vim/ 
tags in
this case). This way I got pretty much all defintions I need at the  
moment.

Now you need to tell vim to include this file. Add the following line  
to your
$HOME/.vimrc.
<code>
set tags=./tags,tags,/home/username/.vim/tags/tags
</code>

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  
the file
in the current buffer.

A recommendation is to write a little script to update your ctags and  
run it
as a cron job every night. Your computer will not be very responsive  
while
recursively searching for ctags in a multi-GB directory.

== Warning ==
This can be a hazardous setup as the ctags are absolute. If you are  
working on
two different source trees (i.e. two releases of the same software),  
using
CTRL+ ] will jump to the functions as defined in ctags. So you might be
editing the wrong source tree.



--
Multi-Pointer X Server
http://wearables.unisa.edu.au/mpx





More information about the xorg mailing list