Thursday, July 30, 2009

XI2 ready for the final mile

As announced on the xorg-devel list, I think XI2 is ready for branching to 1.7.

This means that I consider the protocol stable enough and I will focus only on bugfixing. In a few days time I'll cut a 901 release (release candidate 1) for inputproto and libXi.

Any testing is appreciated, feel free to file bugs for anything that's broken.

Thursday, July 23, 2009

XI2 Recipes, Part 6

This post is part of a mini-series of various recipes on how to deal with the new functionality in XI2. The examples here are merely snippets, full example programs to summarize each part are available here.

In the first five parts, I covered how to get and manipulate the device hierarchy, how to select for events, how to get extended device information, the common event types and active and passive grabs. In this part, I will focus on the client pointer.

The ClientPointer principle


The ClientPointer (CP) principle is only partly interesting for normal applications since no XI2 client should ever need it. The exception is the window manager. About one quarter of XI2 protocol requests and replies are ambiguous in the presence of multiple master devices. The best example is XQueryPointer(3). If there are two or more master pointers, XQueryPointer has a <50% chance of returning the right data.

The ClientPointer is a dedicated master pointer assigned to each application, either implicitly or explicitly. This pointer is then used for any ambiguous requests the application may send. This adds predictability as the data returned is always from the same device. Given the above example, XQueryPointer requests from one client will always return the same pointer's coordinates. Thinking of xeyes this means that the eyes will follow the same cursor. For any requests or replies that require keyboard data, the master keyboard paired with the CP is used.

The CP is implicitly assigned whenever an application sends an ambiguous request. Then the server picks the first master pointer and assigns it to the client. This of course happens only when the client doesn't have an assigned CP yet.

Alternatively, the CP can be explicitly assigned. The XISetClientPointer(3) and XIGetClientPointer(3) calls are to set and query the current ClientPointer for a client.


Status XISetClientPointer(
Display* dpy,
Window win,
int deviceid
);

Bool XIGetClientPointer(
Display* dpy,
Window win,
int* deviceid
);


Both calls take a window and a deviceid. If the window is a valid window, the client owning this window will have the CP set to the given device. The window parameter may also be just a pure client ID. Finally, the window parameter may be None, in which case the requesting client's CP is set to the given device. This is not useful beyond debugging, if the client understands enough XI2 to set the CP it should be able to handle multiple devices properly.

Getting the CP takes the same parameters but it returns the deviceid and it returns True if the CP has been set for the target client, regardless of whether it was set implicitly or explicitly. If no CP is set yet, XIGetClientPointer returns False.

Event delivery, XI2 and and grabs


The CP setting does not affect event delivery in any way. Regardless of which master pointer is the ClientPointer, any device can still interact with the client. This also means that the CP has no effect whatsoever on XI2 or XI1 requests since they are not ambiguous.

Grabs are a different matter. Since the activation of a grab is ambiguous in the core protocol (XGrabPointer - well, which pointer?) a grab will by default activate on the CP. This can be a bit iffy since an application that just grabs the pointer may not grab the one currently within the window boundaries. So the grabbing code has two exceptions. One, if a device is already grabbed by the client, a grab request will act on the already-grabbed device instead of the CP. Two, if a passive grab activates it will activate on the device triggering the grab, not on the CP.

In practice, this means that if a client has a passive grab on a button and any device presses this button, the passive grab activates on this device. If the client then requests and active grab (which toolkits such as GTK do), the active grab is set on the already-grabbed device.
The result: in most cases the grab happens on the "correct" device for the current situation.

How to use the ClientPointer


As said above, the only application that should really need to know about the CP is the window manager who manages core applications as well as XI apps. The most straightforward manner to managing an application is to set the CP whenever a pointer clicks into a client window. This ensures that if the applications requests some ambiguous data, a pointer that is interacting with the application is used.

I have used this method in a custom window manager written for a user study several moons ago and it works well enough. Of course, you are free to contemplate situations where such a simple approach is not sufficient.

Wednesday, July 22, 2009

XI2 recipes, Part 5

This post is part of a mini-series of various recipes on how to deal with the new functionality in XI2. The examples here are merely snippets, full example programs to summarize each part are available here.

In the first four parts, I covered how to get and manipulate the device hierarchy, how to select for events, how to get extended device information and the common event types. In this part, I will focus on grabs.

What are grabs?


Grabs ensure event delivery to a particular client and to this client only. A common application of grabs are drop-down and popup menus. If such a menu is displayed, the pointer is grabbed to ensure that the next click is delivered to the client displaying the popup. The client can then either perform an action or undisplay the popup.

Two different types of grabs exist: active grabs ("grab here and now") and passive grabs ("grab whenever a button/key is pressed"). Both types of grabs may be synchronous or asynchronous. Asynchronous grabs essentially do just the above - deliver all future events to the grabbing client only (in relation to the grab window). Synchronous grabs stop event reporting after an event reported to the grabbing client and it is up to the client what happens next (see below). I recommend reading the XGrabPointer and XAllowEvents man pages for more detail, XI2 grabs work essentially in the same manner.

Active grabs


Devices may be actively grabbed with the XIGrabDevice() call. This call is quite similar to the core protocol's XGrabPointer, so I encourage you to read the matching man page for more detail than provides here.


XIGrabDevice(Display* dpy, int deviceid, Window grab_window, Time time,
Cursor cursor, int grab_mode, int paired_device_mode,
Bool owner_events, XIEventMask *mask)

The device may be a master or slave device that is currently not grabbed, the window must be viewable and time must be CurrentTime or later for the grab to succeed. If the device is a master pointer device, the cursor specified in cursor is displayed for the duration of the grab. Otherwise, the cursor argument is ignored.
Grab mode is either synchronous or asynchronous and applies to the device. If the device is a master device, the paired_device_mode applies to the paired master device. Otherwise, this argument is ignored.
The mask itself is a simple event mask, specifying the events to be delivered to the grabbing client. Depending on owner_events, events are reported only if selected by the mask (if owner_events is false) or are reported if selected by the client on the window (if owner_events is true).

The matching call to release an active grab is XIUngrabDevice(). If a synchronous grab is issued on a device, the client must use XIAllowEvents() to control further event delivery - just as with synchronous core grabs.

Noteworthy about XIGrabDevice is that the deviceid must specify a valid device. The fake deviceids of XIAllDevices or XIAllMasterDevices will result in a BadDevice error.

Passive grabs on buttons and keys


Devices may be passively grabbed on button presses and key presses, similar to the core protocol passive grabs.
Let's have a look at button presses. Passive button grabs ensure that the next time the specified button is pressed, a grab is activated on the device and the client receives the event until the button is released again. This happens automatically provided the matching modifiers are down.
The API to set a button grab is similar to the one from the core protocol with one notable exception. Button grabs can be registered for multiple modifier combinations in one go:


int XIGrabButton( Display *display, int deviceid,
int button, Window grab_window,
Cursor cursor, int grab_mode,
int paired_device_mode, Bool owner_events,
XIEventMask *mask, int num_modifiers,
XIGrabModifiers *modifiers_inout);


The first couple of parameters are identical to the active grab calls. The num_modifiers parameter specifies the length of the modifiers_inout array. The modifiers_inout itself specifies the modifiers states when this grab should be activated. So instead of one request per button+keycode combination, a client can submit all modifier combinations in one go. If any of these combinations fails, the server returns it in the modifiers_inout array.


typedef struct
{
int modifiers;
int status;
} XIGrabModifiers;


The client sets modifiers to the modifiers that should activate the grab, status is ignored by the server. The server returns the number of modifier combinations that could not be set and their status value. The modifiers themselves are always the effective modifiers. No difference between latched, locked, etc. is made.

In pseudo-code, setting a button grab looks something like this.


int nmodifiers = 2;
XIGrabModifiers modifiers[nmodifiers];

modifers[0].modifiers = ShiftMask;
modifiers[1].modifiers = ShiftMask | Mod1Mask;

if ((nmodifiers = XIGrabButton(..., nmodifiers, modifiers)) {
for (i = 0; i <nmodifiers; i++)
printf("Modifiers %x failed with %d\n", modifiers[i].modifiers, modifiers[i].status);



The API for grabbing keycodes is essentially the same as for button grabs.


int XIGrabKeycode( Display *display, int deviceid,
int keycode, Window grab_window,
int grab_mode, int paired_device_mode,
Bool owner_events, XIEventMask *mask,
int num_modifiers, XIGrabModifiers *modifiers_inout);


Both button and keycode grabs can be removed with XIUngrabButton and XIUngrabKeycode.

The fake deviceids of XIAllDevices and XIAllMasterDevices are permitted for passive button and key grabs.

Passive grabs on enter/focus events



Grabbing on enter and focus events is a new feature of XI2. Similar in principle to button presses except that the device is grabbed when the pointer or the keyboard focus is set to the specified window.


int XIGrabEnter( Display *display, int deviceid,
Window grab_window, Cursor cursor,
int grab_mode, int paired_device_mode,
Bool owner_events, XIEventMask *mask,
int num_modifiers,
XIGrabModifiers *modifiers_inout);

int XIGrabFocusIn ( ...)


The parameters are the same for both (XIGrabFocusIn doesn't take a cursor parameter) and they are the same as for button/keysym grabs anyway. When an enter and focus grabs activates, the grabbing client receives an additional XI_Enter of XI_FocusIn event with detail XINotifyPassiveGrab (provided it is set on the window or in the grab mask). To avoid inconsistencies with the core protocol, this event is sent after the enter event is sent to underlying window.

As an example, consider the following pseudo code:


Window win = create_window();

int nmodifiers = 1;
XIGrabModifiers modifiers[nmodifiers];
modifiers[0].modifiers = 0;

if ((nmodifiers = XIGrabEnter(display, 2, win, ..., num_modifiers, modifiers))
handle_errors();

while(1)
{
XEvent ev;
XNextEvent(dpy, &ev);
if (XGetEventData(dpy, &ev.xcookie) &&
ev.xcookie.extension == xi_opcode &&
ev.xcookie.evtype == XI_Enter)
{
XIEnterEvent *enter = ev.xcookie.data;

if (enter->detail == XINotifyPassiveGrab)
printf("we have grabbed the device\n");
}
XFreeEventData(dpy, &ev);
}


Provided that nmodifiers is 0 after the call to XIGrabEnter, each time the pointer moves into the window win the device will be grabbed (provided no modifiers are logically down). Once the pointer leaves the window again, the device is automatically ungrabbed.

Passive enter/focus grabs can be removed with the XIUngrabEnter and XIUngrabFocusIn calls.

The fake deviceids of XIAllDevices and XIAllMasterDevices are permitted for passive button and key grabs.

Wednesday, July 15, 2009

XI2 recipes, Part 4

This post is part of a mini-series of various recipes on how to deal with the new functionality in XI2. The examples here are merely snippets, full example programs to summarize each part are available here.

In the first three parts, I covered how to get and manipulate the device hierarchy, how to select for events and how to get extended device information. In this part, I will cover the common input events and the data they include.

All XI2 events are cookie events and must be retrieved with XGetEventData.


XIDeviceEvent


The XIDeviceEvent is the default event for button and key press and releases and motion events.
The event types that produce an XIDeviceEvent are XI_Motion, XI_ButtonPress, XI_ButtonRelease, XI_KeyPress and XI_KeyRelease. Note that proximity events do not exist in XI2. A device that supports proximity should instead provide another axis and send valuator events on this axis.

The event itself is (reasonably) close to the core events, so most fields may be familiar in one way or another. It is a GenericEvent, so the type is always GenericEvent (35), extension is the X Input extension's opcode and evtype is the actual type of the event: XI_KeyPress, XI_KeyRelease, XI_ButtonPress, XI_ButtonRelease, XI_Motion. XI2 does not have proximity events like XI1 has, on the basis that if a device supports proximity, it should just provide an axis that reports proximity data (even if that axis has a range of 0-1).

Each event provides the device the event came from and the actual source the event originated from. So for applications that listen to master device events, deviceid is the id of the master device, and sourceid is the id of the physical device that just got moved/clicked/typed.


static void print_deviceevent(XIDeviceEvent* event)
{
printf(" device: %d (%d)\n", event->deviceid, event->sourceid);
printf(" detail: %d\n", event->detail);
if (event->flags & XIKeyRepeat)
printf(" event is a key repeat.\n");

...
}


For button events, detail is the button number (after mapping applies of course). For key events, detail is the keycode. XI2 supports 32-bit keycodes, btw. For motion events, detail is 0. The flags field is a combination of various flags that apply for this event. Right now, the only defined flag is XIKeyRepeat for XI_KeyPress events. If this flag is set, the event is the result of an in-server key repeat instead of a physical key press (waiting for daniels to send me the patch for the server).

Each event includes root-absolute and window-relative coordinates with subpixel precision. For example, if you have your mouse slowed down by constant deceleration, you'll see the pointer's X coordinate move from 100.0 to 100.25, 100.5, 100.75, 101, etc. The same happens with devices that have their own coordinate range (except that that bit is missing in the server right now.).

XIDeviceEvents contain the button, modifier and valuator state in four different structs:

XIButtonState buttons;
XIValuatorState valuators;
XIModifierState mods;
XIGroupState group;


The buttons include the button state for each button on this device. Since we don't have any restrictions on the number of buttons in the protocol, the mask looks like this:


typedef struct {
int mask_len;
unsigned char *mask;
} XIButtonState;


The mask_len specifies the length of the actual mask in bytes. The bit for button N is defined as (1 << N), if it is set then the button is currently logically down. Quite similar is the valuator state, except that the mask specifies which valuators are provided in the values array.


typedef struct {
int mask_len;
unsigned char *mask;
double *values;
} XIValuatorState;


Again, mask_len is in bytes and for each bit set in mask, one double represents the current value of this valuator in this event. These coordinates are always in the device-specific coordinate system (screen coordinates for relative devices). To give you an example, if mask_len is 1 and bits 0 and 5 are set in mask, then values is an array size 2, with the values for axis 0 and axis 5.

Finally, the event contains the state of the modifier keys and the current XKB group info.

typedef struct
{
int base;
int latched;
int locked;
int effective;
} XIModifierState;

typedef XIModifierState XIGroupState;


The base modifiers are the ones currently pressed, latched the ones pressed until a key is pressed that's configured to unlatch it (e.g. some shift-capslock interactions have this behaviour) and finally locked modifiers are the ones permanently active until unlocked (default capslock behaviour in the US layout). The effective modifiers are a bitwise OR of the three above - which is essentially equivalent to the modifiers state supplied in the core protocol events.
The group state is a bit more complicated, since the effective group it is the arithmetic sum of all 3 after the group overflow handling is taken into account. The meaning of base, latched and locked is essentially the same otherwise.

Enter/leave and focus events


One of the biggest deficiencies of XI1 is the lack of enter/leave events for extended devices. XI2 provides both and they are essentially the same as the device events above with three extra fields: mode, focus and same_screen. Both enter/leave events and focus events are the same as core events squashed in an XI2 format. I recommend reading the core protocol spec for these events, it's much more verbose (and eloquent) than this blog.

Unsurprisingly, enter/leave events are generated separately for each device. While the core protocol has a quite funky model to ensure that applications aren't confused when multiple pointers exit or leave a window, the XI2 events are sent for each pointer, regardless of how many devices are currently in the window.

Focus events are simply typedefs of the enter events, so there's nothing exciting there.

Property events


Property events have stayed the same, except that they use the XGenericEvent (and cookie) format now. Property events contain the property that changed, the deviceid and a field detailing what actually changed on this property (one of XIPropertyDeleted, XIPropertyCreated, XIPropertyModified).

Raw events


Raw events are something new. Normal input events are heavily processed by the server (clipped, accelerated, mapped to absolute, etc.). Raw events are essentially a container to forward the data the server works with (e.g. the data passed up by the driver) and thus do not contain state other than the new information. The three interesting fields are detail, valuators and raw_values.


typedef struct {
...
int detail;
XIValuatorState valuators;
double *raw_values;
} XIRawEvent;


That the detail for button events is the unmapped button number or the key code. Possible evtypes for raw events are XI_RawMotion, XI_RawKeyPress, XI_RawKeyRelease, XI_RawButtonPress and XI_RawButtonRelease.

Valuator information works in the same manner as in XIDeviceEvents and contains the transformed (i.e. accelerated) valuators as used in the server. The raw_values array provides the untransformed values as they were passed up from the driver. This is useful for applications that need to provide their own acceleration code (e.g. games).
For example, the following bit shows the acceleration applied on each axis:


void print_rawmotion(XIRawEvent *event)
{
int i;
double *raw_valuator = event->raw_values,
*valuator = event->valuators.values;

for (i = 0; i < event->valuators.mask_len * 8; i++) {
if (XIMaskIsSet(event->valuators.mask, i)) {
printf("Acceleration on valuator %d: %f\n",
i, *valuator - *raw_valuator);
valuator++;
raw_valuator++;
}
}
}



Since raw events do not have target windows they are delivered exclusively to all root windows. Thus, a client that registers for raw events on a standard client window will receive a BadValue from XISelectEvents(). Like normal events however, if a client has a grab on the device, then the event is delivered only to the grabbing client.

Sunday, July 12, 2009

XI2 and xlib cookies

As posted, Xlib now supports cookies. This affects XI2 but luckily not as much as I thought.

All XI2 events are cookie events


Retrieving an XI2 event needs to look like this:


XNextEvent(display, &event);
if (XGetEventData(display, &event))
{
XGenericEventCookie *cookie = &event.xcookie;
if (cookie->extension == xi_opcode &&
cookie->evtype == XI_Motion)
handle_motion_event((XIDeviceEvent*)cookie->data);
XFreeEventData(display, &event);
}

The data pointer is always the extension-specific event, depending on evtype. More on that in the next XI2 recipes post. Failure to call XFreeEventData() will result in a memory leak.

XIFreeEventData is gone


Previous examples featured XIFreeEventData, this call is now replaced by XFreeEventData in Xlib. They both do the same thing, so you can just run sed over your source files.

Less pointers in events


The removal of the size restrictions means a few pointers in events have changed into structs - nothing a search/replace can't fix. One example for this is the button state which used to be a pointer - now it's part of the XIDeviceEvent struct.

No XIEvent padding


This isn't related to cookies but it's quite important: XIEvents may not be used for passing into XNextEvent. The previous struct included padding to force the same size as an XEvent. Except not on 64 bit, so it was broken anyway. The padding is removed, so you must not pass XIEvents into XNextEvent() and similar functions (unless you are a big fan of scrambled memory).

Xlib Cookie Events

Xlib in git has two new functions:

  • XGetEventData(3)

  • XFreeEventData(3)


Both are designed to deal with events delivered by servers supporting the X Generic Event extension (XGE). These events are longer than 32 bytes on the wire and can become rather large when they are unpacked into Xlib's event structures. Since Xlib has an internal maximum size for XEvents we cannot easily deal with XGE events without introducing memory leaks.

XGE event cookies


A new datatype has been introduced - the XGenericEventCookie. This datatype is essentially a wrapper to fit into Xlib but provide access to the actual event data.

It's simple enough and overlaps with XGenericEvents and of course XEvents:


typedef struct
{
int type; /* of event. Always GenericEvent */
unsigned long serial; /* # of last request processed */
Bool send_event; /* true if from SendEvent request */
Display *display; /* Display the event was read from */
int extension; /* major opcode of extension that caused the event */
int evtype; /* actual event type. */
unsigned int cookie; /* unique event cookie */
void *data; /* actual event data */
} XGenericEventCookie;


The two interesting fields are the cookie and the data pointer. The cookie is simply a unique number assigned to each event as it is received. It serves to identify the event when data needs to be retrieved from the Xlib internal event storage. The data pointer is a pointer to the actual event data - its type is of whatever type the extension has specified for this event type (e.g. XIDeviceEvent for XI2 motion events).

Fetching cookie data



Retrieving an event through XNextEvent or similar retrieves a cookie event instead - with a data pointer NULL. The extra data can then be received by passing the cookie event into XGetEventData. XGetEventData returns True if the cookie has been fetched successfully or False for invalid cookies (including already claimed cookies) or events that aren't cookie events.

Here is an example code snippet:

XEvent ev;
XNextEvent(dpy, &ev);
if (XGetEventData(dpy, &ev))
{
XGenericEventCookie *cookie = &ev.xcookie;
if (cookie->extension == <my extension> &&
cookie->evtype == <my event type>)
process_my_extension_event(cookie->data);
} else
printf("Not a cookie event. process as usual.\n");

XFreeEventData(dpy, &ev);


Once data has been obtained by the client, it becomes the client's responsibility to free this data with XFreeEventData. Failure to do so will leak memory. Unclaimed cookies are freed automatically by the library, so if you never call XGetEventData, memory doesn't leak.

XGetEventData and XFreeEventData are safe to be called with non-cookie events.

One cookie - one claim


The important thing about the cookies:

  • Each cookie can only ever be claimed once for each event.

  • XGetEventData and XFreeEventData are symmetrical



Each cookie returned by the library can be claimed exactly once, even if it represents the same actual event. In the following snippet, both XGetEventData calls return the cookie data, even though they are the same event.


XPending(dpy, &ev);
XGetEventData(dpy, &ev);
/* process */
XFreeEventData(dpy, &ev);

XNextEvent(dpy, &ev);
XGetEventData(dpy, &ev);
/* process */
XFreeEventData(dpy, &ev);


The symmetry between XGetEventData and XFreeEventData must be maintained even if the cookie is put back into the event queue:

XNextEvent(dpy, &ev);
XGetEventData(dpy, &ev);
XPutBackEvent(dpy, &ev);
XFreeEventData(dpy, &ev);

XNextEvent(dpy, &ev);
XGetEventData(dpy, &ev);
XFreeEventData(dpy, &ev);


Multi-threaded X clients


Multi-threaded clients must ensure XGetEventData is called before the next call to XNextEvent, XCheckTypedEvent, XCheckWindowEvent, XCheckTypedWindowEvent, XMaskEvent or XPending.

Saturday, July 4, 2009

(Not) blaming HAL

I still see bugreports that blame HAL for various things including my mouse buttons don't work", "the pointer jumps", and various other things. In none of these cases HAL is at fault. From the X server's point-of-view, HAL is merely a replacement for the xorg.conf.

The simple tasks HAL does for us in the X server is:

  1. List all input devices (the equivalent to the InputDevice sections in the xorg.conf).

  2. Nominate the appropriate driver for each input device (the equivalent to the Driver "..." line in each InputDevice section).

  3. Provide user-configured extra options such as the keyboard layout (the equivalent to the Option "Foo" "bar" lines).

Note that 2 and 3 are a result of your local configuration files and not some random guesses.

So whenever it's unclear if a problem is in fact caused by HAL ask yourself: if you had a xorg.conf, could this problem be fixed by editing it? If not, then you need to report the bug against the input driver or the X server. Here's Fedora's rough checklist for reporting input bugs.

That HAL is being replaced by libudev is a completely different issue as well.

[edit]
libudev, not DeviceKit.