Monday, June 29, 2009

The joy of Xlib

Last week I started writing the next part of the XI2 recipes series, this time detailing button/key/motion events etc. Except that while fixing various inconsistencies I ran into a wall put up by Xlib years ago. In short, Xlib only allows for event sizes of up to 96 bytes (on 32 bit). Previously I've gotten around that by storing pointers to extra memory freed by the client (see XIFreeEventData(3)). However, this not only introduces a potentially huge memory leak in "naive" clients but also makes the actual event structure be quite annoying. Having to dereference into three structs to get the button mask is just not particularly great.

So now I have to rethink how to deal with events on the client side (Xlib only, xcb is not affected, server isn't affected either).

Either way, this means two things:

  • the next XI2 tutorial will take another few days

  • there will likely be a fairly big API change in how XI2 clients can deal with events.

Tuesday, June 16, 2009

XI2 Recipes, Part 3

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 Part 1 I covered how to initialise and select for events and in Part 2 how to query for devices and monitor the hierarchy. In this part, I will cover how to get more information about the devices.

Device capabilities for master and slave devices


Once upon a time, mice were simply mice and keyboards were simply keyboards. Nowadays, some mice have 3 buttons, others have 8, some have two scrollwheels, etc. Tablets, touchscreens and touchpads behave like a mouse, but have their own coordinate systems, their own set of buttons etc.

Why is this information important anyway? The valuator (== axis) information is sent in device-dependent coordinates. So for example, a wacom tablet may send coordinates from 0 to 50000 instead of the screen resolution. Likewise, some device may have a particular button that other devices don't have. Since button numbers are sequential, button 8 one one device may have a completely different functionality than button 8 on another device.

In Part 2, I detailed the master/slave device hierarchy and explained that each event from a slave device is routed through the attached master device. In addition to that, whenever a slave device sends an event, the master device changes capabilities to reflect the current slave device. Assuming that two slave devices (one mouse, one graphics tablet) hang off one master device (i.e. cursor), the master device will thus look like either the mouse or the tablet - depending on who sent the last event. This switch happens before the actual event.

The order of things is (if we assume the mouse was the last used device):

  1. Move the mouse

    1. Mouse events are sent to the client

  2. Move the tablet


    1. The master changes capabilities to look like the tablet.

    2. A DeviceChanged event is sent to notify clients about this change.

    3. The move event and all following events originating from the tablet are sent to clients.


  3. Move the mouse


    1. The master changes capabilities to look like the mouse.

    2. A DeviceChanged event is sent to notify clients about this change.

    3. The move event and all following events originating from the mouse are sent to clients.


  4. rinse, wash, repeat



Following the DeviceChanged events, the master device the client will always look like the physical device that is currently sending the events. And since clients should listen to master devices, this means that clients can actually adjust their UI depending on what physical device is in use. For example, a drawing program such as the GIMP could switch automatically between mouse mode and tablet mode. The drawback of this approach though is that depending on when you call XIQueryDevice the information may be different. You can mitigate this by monitoring XIDeviceChangedEvents (see below).

Key and valuator information reflects the currently used slave device. Button information - not quite as simple. A master device always has as many buttons as the slave device with the highest number of buttons. This allows combining multiple devices for button presses, e.g. button 1 on one device and button 3 on another device is a combined 1+3 button state on the master device. The master device will change dynamically to reflect the right number of buttons whenever a slave device is attached or detached. More about that below.

Querying extra device information


So, assuming you just called XIQueryDevice and you got back a set of devices and XIDeviceInfo structs. The device capabilities are in the device classes, much in the same matter as in XI 1.

Right now, three classes are defined: button class, key class, and valuator class. A device may have zero or one button class, zero or one key class and zero or more valuator classes. A typical mouse will have a button class and two valuator classes (one for the x axis and one for the y axis).


static void print_info(XIDeviceInfo *info)
{
int i;
XIAnyClassInfo **classes = info->classes;

printf("\tReporting %d classes:\n", info->num_classes);
for (i = 0; i < info->num_classes; i++) {
switch(classes[i]->type) {
case XIButtonClass:
print_button_class((XIButtonClassInfo*)classes[i]);
break;
case XIKeyClass:
print_key_class((XIKeyClassInfo*)classes[i]);
break;
case XIValuatorClass:
print_valuator_class((XIValuatorClassInfo*)classes[i];
break;
}


All classes share the same two fields, type and sourceid. The sourceid denotes the device this particular class came from. For slave devices, this is the device id. For master devices, this is the device id of the slave that has sent the event through the server.
The current X server implementation always copies all classes from the slave device into the master device, so all sourceids of one device are the same. In the future, we may add partial class copying and you are not guaranteed that the button class is from the same physical device as the valuator class, etc.


Let's look at the button class struct:

typedef struct
{
int type;
int sourceid;
int num_buttons;
Atom *labels;
XIButtonState state;
} XIButtonClassInfo;


The first field is always XIButtonClass, and num_buttons tells us how many buttons the device actually has. This is complemented by the array of button labels. Each atom defines a button label (note that this label might be None, an Atom of zero, if the device cannot label its buttons). So for a standard mouse you should get num_buttons of at least 3 and the labels are (usually) the atoms for "Button Left", "Button Middle", "Button Right". Finally, the state field has a bit set for each button currently down on this device.

You can now write user interfaces that interpret the buttons as what they are. Previously, you had to apply magic to guess which button would do what, now the devices actually tell you what each button does (provided the kernel drivers do). The order of the buttons array is always the physical order, regardless of any button mapping.

It is a bit trickier on the master device. Since the master device buttons are the union of all attached slave devices, the number of buttons is always the highest number of buttons on any slave device. For example, if three devices with 3, 9 and 20 buttons, the master device will have 20 buttons. If you press button 20 on device 3 and button 3 on device 1, the combined state is 20+3 on the master device. There is one downside to this union: if the devices have different button labels, the labels may change between press and release. For example, pressing button 1 on device 1 ("Button Left") will result in the DeviceChangedEvent with "Button Left" as label for 1. If you then press button 1 on device 2 ("Button 0"), no event is sent from the MD (because the button is already down). Now release device 1 (no release event, the button is still down), then release device 2. At the time of the release event, the label for button 1 was "Button 0", even though it was "Button Left" at the time of the press.

Let's look at the key class struct:

typedef struct
{
int type;
int sourceid;
int num_keycodes;
int *keycodes;
} XIKeyClassInfo;


Essentially the same as the button class info, except that it specifies numerical keycodes instead of button labels. This is somewhat different to the core approach that gives you a min_keycode and a max_keycode (which incidentally must always be the same anyway). In XI2, a keyboard with three keys will only give you those three keycodes it actually sends.

Finally, let's look at the valuator struct:

typedef struct
{
int type;
int sourceid;
int number;
Atom label;
double min;
double max;
double value;
int resolution;
int mode;
} XIValuatorClassInfo;


Number is the physical number of the axis (you're not guaranteed to get them in order from the server). The label is - like button labels - an Atom specifying the axis type (e.g. "Rel X"). Min, max and resolution give you the minimum and maximum axis value and the resolution in units/m, mode is either absolute or relative depending on the device.
Value is the current value of the valuator - which of course only makes sense if a valuator is an absolute axis.

That's it. With this information, you can now get all necessary info about a device.

Monitoring device changes


As mentioned above, the XIDeviceChangedEvent notifies you when a device has changed. Its information is mostly the same as what XIQueryDevice provides, with two extra fields: reason and sourceid. The sourceid simply specifies the device that triggered this device. In some cases, this is the slave device, in some cases sourceid and deviceid are the same. The reason field specifies why this event was sent. Right now, there's two reasons: XISlaveSwitch and XIDeviceChange. The former is sent whenever you change between physical devices attached to the same master. The latter is sent whenever a device changes capabilities on its own accord. One example for this is the master device changing the number of available buttons when a slave device is attached or detached.

Monday, June 15, 2009

linux multitouch getting somewhere

About two years ago, I dabbled a bit with multitouch. The low-level parts were a hack, a custom kernel driver that spat out a special protocol, the X driver custom-written for this kernel driver and then a new type of events in the X server. The new events were the really interesting bits, but I had to ditch it because of lack of time.

Now, thanks mainly to Henrik Rydberg, we have a multitouch protocol in 2.6.30. The protocol is based on the event protocol that the evdev driver uses and adds the capabilities to track multiple touch points over time.

And now Stephane Chatty from the Interactive Computing Lab at ENAC sent me this link, showing off a few demos that use this new multi-touch API.

The good thing about this? A normal mouse or keyboard event usually goes hardware -> kernel -> X driver -> X server -> toolkit -> application. For multitouch, we now have the low-level bits in places. The demos above are simply missing the X bits in this equation. Bryan Goldstein has taken on the task to resurrect the blob branch mentioned before, so with a bit of luck we can beat that into shape, rebase it to current master and work on completing it. At which point multitouch is fully integrated with the desktop rather than relying on custom hardware-specific hacks at every point in the stack.

Note: don't hold your breath for that to arrive soon, at the moment I'm looking at an 8 months+ timeframe until a release with these features.

Tuesday, June 9, 2009

XI2 Recipies, Part 2

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.

Update 13.07.09: adjusted to the new cookie event API

A word of warning: XI2 is still in flux and the code documented here may change before the 2.0 release.

In Part 1 I covered how to initialise and select for events. In this part, I will cover how to query and modify the device hierarchy.

What is the device hierarchy?


The device hierarchy is the tree of master and slave devices. A master pointer is represented by a visible cursor, a master keyboard is represented by a keyboard focus. Slave pointers and keyboards are (usually) physical devices attached to one master device.

The distinction may sound odd first but we've been using it for years. A computer has two sets of interfaces: Physical interfaces are what we humans employ to interact with the computer (e.g. mouse, keyboard, touchpad). Virtual interfaces is what applications actually see. Think of it: if you have a laptop with two physical devices (a mouse and a touchpad) you're still only controlling one virtual device (the cursor). So although you have two very different physical interfaces, the application isn't aware of it at all.

This works mostly fine as long as you have only one virtual interface per type but it gets confusing really quickly if you have multiple users on the same screen at the same time. Hence the explicit device hierarchy in XI2.

We call virtual devices master devices, and physical devices slave devices. Note that there are exceptions where a slave device is a emulation of a physical device.

A device may be of one of five device types:

  • Master pointers are devices that represent a cursor on the screen. One master pointer is always available (the "Virtual core pointer"). Master pointers usually send core events, meaning they appear like a normal pointer device to non-XI applications.

  • Master keyboards are devices that represent a keyboard focus. One master keyboard is always available (the "Virtual core keyboard"). Master keyboards usually send core events, meaning they appear like a normal keyboard device to non-XI applications.

  • Slave pointers are pointer devices that are attached to a master pointer. Slave pointers never send core events, they are invisible to non-XI applications and can only interact with a core application if they are attached to a master device (in which case it's actually the master device that interacts)

  • Slave keyboards are keyboard devices that are attached to a master keyboard. Slave keyboards never send core events, they are invisible to non-XI applications and can only interact with a core application if they are attached to a master device (in which case it's actually the master device that interacts)

  • Floating slaves are slave devices that are currently not attached to a master device. They can only be used by XI or XI2 applications and do not have a visible cursor or keyboard focus.



So what does attachment mean? A master device cannot generate events by itself. If a slave device is attached to a master device, then each event that the slave device generates is also passed through the master device. This is how the X server works since 1.4, if you click a mouse button, the server sends a click event from the mouse and from the "virtual core pointer".

A floating device on the other hand does not send events through the master device. They don't control a visible cursor or keyboard focus and any application listening to a floating slave device needs to control focus and cursor manually. One example where floating slaves are useful is the use of graphics tablets in the GIMP (where the area of the tablet is mapped to the canvas).

For most applications, you will only ever care about master devices.

Querying the device hierarchy



At some point, clients may need to know which devices are actually present in the system right now.


int ndevices;
XIDeviceInfo *devices, device;

devices = XIQueryDevice(display, XIAllDevices, &ndevices);

for (i = 0; i < ndevices; i++) {
device = &devices[i];
printf("Device %s (id: %d) is a ", device->name, device->deviceid);

switch(device->use) {
case XIMasterPointer: printf("master pointer\n"); break;
case XIMasterKeyboard: printf("master keyboard\n"); break;
case XISlavePointer: printf("slave pointer\n"); break;
case XISlaveKeyboard: printf("slave keyboard\n"); break;
case XIFloatingSlave: printf("floating slave\n"); break;
}

printf("Device is attached to/paired with %d\n", device->attachement);
}

XIFreeDeviceInfo(devices);



As with event selection, XIAllDevices and XIAllMaster devices are valid as device ID parameter. Alternatively, just supply the device ID of the device you're interested in.

The attachment simply states which device this device is attached to. For master pointers, this is always the paired master keyboard and the other way round. For floating slaves, this value is undefined.

Now we know the layout of the hierarchy, including how many master devices and physical devices are actually present.

XIQueryDevice returns more information such as the capabilities of each device. I'll leave this for another post (mainly because I just found a deficiency in the XI2 protocol that needs to be fixed first. :)


Hierarchy events


Now, knowing the hierarchy is only useful for a short time as another client may change it at any point in time. So your client should listen for hierarchy events. These events are sent to all windows, so it doesn't really matter where you register. The traditional approach is to register on the root window.


XIEventMask evmask;
unsigned char mask[2] = { 0, 0 };

XISetMask(mask, XI_HierarchyChanged);
evmask.deviceid = XIAllDevices;
evmask.mask_len = sizeof(mask);
evmask.mask = mask;

XISelectEvents(dpy, DefaultRootWindow(dpy), &evmask, 1);


Device hierarchy events are a bit special, they can only be selected for XIAllDevices. Trying to set the mask on any other device will result in a BadValue error.

In your event loop, you need something like this:

XEvent ev;
XNextEvent(dpy, &ev);

if (ev.xcookie.type == GenericEvent &&
ev.xcookie.extension == xi_opcode &&
XGetEventData(dpy, &ev.xcookie)) {
switch(ev.xcookie.type) {
case XI_HierarchyChanged:
printf("Hierarchy has changed!\n");
process_hierarchy_event(ev.xcookie.data);
break;
/* process other events */
}
XFreeEventData(dpy, &ev);
}


The XIHierarchyEvent has a couple of interesting fields:

typedef struct {
int type; /* GenericEvent */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
int extension; /* XI extension offset */
int evtype; /* XI_DeviceHierarchyChangedNotify */
Time time;
int flags;
int num_devices;
XIHierarchyInfo *info;
} XIHierarchyEvent;


The first couple of fields are standard. The flags field lists all changes that occurred, a combination of:

XIMasterAdded

A new master device has been created.

XIMasterRemoved

A master device has been deleted.

XISlaveAdded

A new slave device has been added (e.g. plugged in).

XISlaveRemoved

A slave device has been removed (e.g. unplugged).

XISlaveAttached

A slave device has been attached to a master device.

XISlaveDetached

A slave device has been detached (set floating).

XIDeviceEnabled

A device has been enabled (i.e. it may send events now)).

XIDeviceDisabled

A device has been disabled (i.e. it will not send events until enabled again).



The info field contains the details for all devices currently present and those removed with this change. Each info->flags states what happened to this particular device. For example, one could search for the slave device that just got removed by searching for the XISlaveRemoved flag.


if (!(ev.flags & XISlaveRemoved)) {
printf("No slave device removed.\n");
return;
}

for (i = 0; i < ev.num_devices; i++) {
if (info[i].flags & XISlaveRemoved) {
printf("Device %d has been removed.\n", info[i].deviceid);
}
}


With this information, it is possible to keep track of the device hierarchy at all times and adjust the user interface as necessary.


Modifying the device hierarchy



First of all - there is hardly a reason why you should do this. Only configuration tools should ever touch the device hierarchy.

XIChangeHierarchy allows to move devices around at will. It takes a number of XIAnyHierarchyChangeInfo structs and applies them in order.


XIAddMasterInfo add;

add.type = XIAddMaster;
add.name = "My new master";
add.sendCore = True;
add.enable = True;

XIChangeHierarchy(dpy, &add, 1);


After this change is performed, there will be two new master devices: "My new master pointer" and "My new master keyboard" (remember, master devices always come in pairs). They will send core events (i.e. they are usable in non-XI applications) and they will be enabled immediately. If you registered for hierarchy events, you will get and event with XIMasterAdded and XIDeviceEnabled flags.

The master device can be removed again by executing:


XIRemoveMasterInfo remove;
remove.type = XIRemoveMaster;
remove.deviceid = 10; /* assuming this is one of the master devices */
remove.return_mode = XIAttachToMaster;
remove.return_pointer = 2;
remove.return_keyboard = 3;

XIChangeHierarchy(dpy, &remove, 1);


This has the effect of removing device 10 (provided it is a master device) and also its paired master device. Any slave devices currently attached to device 10 or it's paired master device will be reattached to device 2 (for pointers) or device 3 (for keyboards).

Attaching and detaching slave devices is equally simple and shouldn't require a lot of explanation. If you want to change a slave from one master to the other, a single attach command is sufficient, the slave device does not need to be detached first. Note that attaching a slave device will also enable it if it is currently disabled.

Finally, if you submit multiple commands in one go, they are applied in order until all of them are processed or an error occurs. If an error occurs, the client is notified and processing stops, but already processed commands will take effect. Regardless of an error, a hierarchy event is sent to all clients with the new state.


That's it. You now know how to stay aware of the device hierarchy and how to modify it. In the next part, I'll discuss how to get more information about the devices.

Sunday, June 7, 2009

XI2 Recipes, Part 1

XI2 is now merged into master. Over the next couple of days, I will post various recipes on how to deal with the new functionality. The examples here are merely snippets, full example programs to summarize each part are available here.

Update 13.07.09: Adjusted adjusted to the new cookie event API

In this first part, I will cover general things, initialisation and event selection.

Why XI2?


One of the major reasons for XI2 was the merge of MPX into the server. The current X Input Extension version 1.5 is quite limiting and extending it to fully support MPX has been tough. Programming against it is mostly annoying, so a replacement was sought that makes it easy to program against multiple devices and is flexible enough to cover future use-cases.

The XI2 protocol is fairly conservative, adding only a few requests and events but it also leaves room for more. XI2 and it's APIs are somewhat closer to the core protocol paradigms. The big differences to XI from a client's perspective are: Calls take a deviceid as parameter, there is no need to open devices and event types and masks are constant (more of this below).

Right now, the only bindings for XI2 are Xlib bindings. Nonetheless, I encourage you to start playing with XI2 and think how applications may use it. By testing it now, you can help identify problems and missing bits with the current requests before version 2.0 is released. And of course, fixing issues before a release benefits everyone.

MPX


MPX allows the use of multiple cusors and keyboard foci simultaneously. This again leads to pretty funky user interfaces - bi-manual, multi-user, you name it. It also throws a number of assumptions about current GUIs out of the window but I'll get to that some other time.

MPX introduces an explicit master/slave device hierarchy. The easiest way to remember which is which is: a physical device is a slave device, and a cursor or keyboard focus is a master device.

Slave devices are attached to one master and each time a slave generates an event, this event is routed through the master device to the client. Master devices always come in twos (one cursor, one keyboard focus) and these two are paired.

So a common setup may be a laptop with four master devices (i.e. two cursors and two keyboard foci), the touchpad controls and built-in keyboard control the first pair of master devices, and a USB wireless combo controls the second pair of master devices. The standard setup is to have one pair of master devices, and all devices are attached to this pair's master pointer or master keyboard. Which, incidentally (or not :), is exactly the same setup as we've had since server 1.4. It also means that MPX only takes effect if you create a new pair of master devices, otherwise it's invisible.

I will cover more of MPX in a follow-up post. From most clients' perspective, master device are the devices that matter, only configuration tools and some other specialised apps need to worry about slave devices.

XI2 Initialisation



The typical XI2 program starts something like this:

/* Connect to the X server */
Display *dpy = XOpenDisplay(NULL);

/* XInput Extension available? */
int opcode, event, error;
if (!XQueryExtension(dpy, "XInputExtension", &opcode, &event, &error)) {
printf("X Input extension not available.\n");
return -1;
}

/* Which version of XI2? We support 2.0 */
int major = 2, minor = 0;
if (XIQueryVersion(dpy, &major, &minor) == BadRequest) {
printf("XI2 not available. Server supports %d.%d\n", major, minor);
return -1;
}


First the client connects to the X server, then asks whether the extension is available. XQueryExtension not only tells us whether the X Input Extension is supported, but it also returns the opcode of the extension. This opcode is needed for event parsing (all XI2 events use this opcode). This opcode is set when the server started, so you cannot rely on it being constant across server sessions.

Finally, we announce that we support XI 2.0 and the server returns the version it supports. Although XIQueryVersion is a pure XI2 call, it is implemented so that it will not result in a BadRequest error if you run it against a server that doesn't support XI2 (this part is implemented in Xlib, so if you use xcb this behaviour is not the same).

XIQueryVersion not only returns the supported version, the server will also store the version your client supports. As XI2 progresses it becomes important that you use this call as the server may treat the client differently depending on the supported version.

Selecting for events


After initialising the GUI, a client usually needs to select for events. This can be achieved with XISelectEvents.


XIEventMask eventmask;
unsigned char mask[1] = { 0 }; /* the actual mask */

eventmask.deviceid = 2;
eventmask.mask_len = sizeof(mask); /* always in bytes */
eventmask.mask = mask;
/* now set the mask */
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_Motion);
XISetMask(mask, XI_KeyPress);

/* select on the window */
XISelectEvents(display, window, &eventmask, 1);


An XIEventMask defines the mask for one device. A mask is defined as (1 << event type) and the matching bits must be set in the eventmask.mask field. The size of eventmask.mask can be arbitrary as long as it has enough bits for the masks you need to set. The XI_LASTEVENT define specifies the highest event type in the current XI2 protocol version, so you can use this to determine the mask size. In this example, we only need 6 bits, so a 1 byte mask is enough.

XISelectEvents takes multiple event masks, so you can submit many of these (e.g. one for each device) in one go.

As shown above, each XIEventMask takes a deviceid. This is either the numeric ID for a device or the special ID of XIAllDevices or XIAllMasterDevices. If you select an event mask for XIAllDevices, all devices will send the selected events. XIAllMasterDevices does the same, but only for master devices.

Event masks for XIAllDevices and XIAllMasterDevices are in addition to device-specific event masks. For example, if you select for button press on XIAllDevices, button release on XIAllMasterDevices and motion events on device 2, device 2 will report all three types of events to the client (the effective mask is a bitwise OR).

Furthermore, XIAllDevices and XIAllMasterDevices always apply, even if the device has been added after the client has selected for events. So you only need to issue the event mask once, regardless of how many devices are currently connected and how many will be connected in the future.

The check for events is quite simple:

XEvent event;
XNextEvent(display, &event);
if (ev.xcookie.type == GenericEvent &&
ev.xcookie.extension == opcode &&
XGetEventData(dpy, &ev.xcookie))
{
switch(ev.xcookie.evtype)
{
case XI_ButtonPress:
case XI_Motion:
case XI_KeyPress:
do_something(ev.xcookie.data);
break;
}
}
XFreeEventData(dpy, &ev.xcookie);


A more detailed analysis of the data in each event will be described in a later post.
Note that you need to check against the opcode of the X Input Extension as returned by XQueryExtension(3) (as discussed before).

That's it. With this knowledge, you can already go and write simple programs that listen for events from any device. In the next part, I will cover how to list the input devices and listen for changes in the master/slave device hierarchy.

Friday, June 5, 2009

git patches from tarballs

Generating patches with git is easy if you clone upstream. Many users don't run their software from repositories. They work from either distribution packages or tarballs. So a number of times I've heard something along the lines of "Sorry, it's not a git patch because I'm working from the tarball". It is quite easy though to create git patches from your tarballs. Simply run the following command in the extracted directory:


git init && git add --ignore-errors .; git commit -m "`basename $PWD`"


Explanation:
  • "git init" initialises a new git repository.

  • "git add ." adds all existing files to the repo. The --ignore-errors is there so git skips over files that can't be added, I've had that happen in a few tarballs that had their permissions busted, etc. If there are errors, you obviously need to check whether they affect files you want to hack on. If not, ignore the errors. (btw. you want to run git add before compiling everything, having all object files in the git index is painful)

  • "git commit" commits all newly added files with the name of the current directory as commit message.



Now the directory is basically the same as upstream when they released the tarball, without the history. Either way, you can just hack, commit, rebase, etc. and then create a patch with git-format-patch and submit it to upstream. Assuming that upstream hasn't diverged too much from the tarball, chances are the maintainers can just apply the patch as-is.

Disclaimer: I learned this workflow from the Fedora X11 packages where this method is used to apply upstream patches to the tarballs. So the credit goes to ajax (or maybe someone else).

[edits]
"git init-db" replaced with "git init".

Tuesday, June 2, 2009

Button mapping in X

X differs between device buttons and logical buttons. The former are handled in the device/driver, the latter are what applies to clients. By default, these are mapped in a 1:1 fashion, with device button 1 (D1) being mapped to logical button 1 (L1), D2 to L2, D3 to L3, etc.

Logical buttons are what the client sees in the end. The first logical button is L1 and there are some standard behaviours:
L1 .. left click
L2 .. middle click
L3 .. right click
L4 .. vert scroll wheel up
L5 .. vert scroll wheel down
L6 .. horiz scroll wheel left
L7 .. horiz scroll wheel right

These are de-facto standard in virtually all clients, with middle click usually resulting in a paste from the current selection. Additionally, some clients (e.g. firefox) map L8 and L9 to back/forward.
All these standard actions are by convention only, there's no defined standard (that I know of) and a client may choose to interpret L4 as left clicks (hint: this is not a smart thing to do).

The meaning of the logical buttons is always the same but the user can re-map device buttons to logical buttons. For example, a left-handed mouse uses a button mapping of D1:L3 and D3:L1 (a mapping of 3 2 1 4 5 6 7 ...). Now, when a button 3 is pressed, the server maps this button to logical button 1 and the client executes a left-click.

In-driver physical button mapping


There's one more stage of button mapping, and that stage happens in the driver. The driver communicates with the server over a defined API and the buttons passed across this API are treated as the device buttons. The driver may chose to map the physical button to a different device button to accommodate for certain hardware features or deficiencies. This mapping is driver-specific and may not exist for all drivers. Evdev has it mainly to accommodate for broken hardware, and synaptics has it for tap-to-click behaviour.

As an example, an evdev button mapping may ask for physical button 1 (P1) being mapped to device button 3. When P1 is pressed on the device, evdev passes D3 to the server which may then map it to L1 if a right-handed mouse is configured.

This method is more commonly used in synaptics for the tap-to-click behaviour. This is a purely in-driver emulation of a button, so the TapButton1/2/3 settings define what device button will be sent on a one/two/three finger tap. By default, TapButton1 is mapped to D1, but one could set it to D4 and scroll by tapping. This mapping is handy for the right-handed synaptics quirk described later.

The button map chain


Starting with server 1.6, devices have a chain of button mappings. Since both the physical device and the core pointer show up as separate devices (run xinput --list -short), they may have different button mappings. Virtually all clients only see the core pointer's events and any device that controls the core pointer is subject to the core pointer's mappings. The button mapping for physical devices that control the core pointer is thus as follows:


physical to device mapping → device to logical mapping → core pointer device to logical → client.


No contemporary configuration tool should change the core pointer's mapping. These tools should be XI aware and modify the configuration for each device.

In the following examples, '→' is used for a standard X:X mapping, and '»' for a X:Y button mapping. Assume Mouse M that controls the core pointer CP. Both have the standard 1 2 3 mapping with no in-driver mapping. The path is thus:


M(P1) → M(D1) → M(L1) → CP(D1) → CP(L1) → client.


If a 3 2 1 left-handed mapping on the core pointer is set up, the path is:

M(P1) → M(D1) → M(L1) → CP(D1) » CP(L3) → client.

Thus, all devices that send events through the core pointer are now left-handed.

On the other hand, if only a single device needs to be left-handed, the core pointer maintains it 1 2 3 mapping and the mouse M gets a 3 2 1 mapping:

M(P1) → M(D1) » M(L3) → CP(D3) → CP(L3) → client.

This is the setup configuration tools should choose if the user requires a left-handed mouse.

If a the same mapping is applied to both CP and M, then this mapping is neutralised:

M(P1) → M(D1) » M(L3) → CP(D3) » CP(L1) → client.

Although both M and CP have a left-handed setup, M effectively acts as a right-handed mouse. Even worse, an application that listens for XI events (e.g. the gimp) would see M as a left-handed mouse while all other applications would see it as a right-handed one. This is the reason why the core pointer mapping should be left alone.

Now, for fun, let's also assume that M has a hardware problem and physical button 4 is actually the left mouse button. So an in-driver mapping from 4 to 1 is needed.

M(P4) » M(D1) » M(L3) → CP(D3) » CP(L1) → client.


These examples above are the most common scenarios the average user may encounter.

Scrollwheel and buttons


As mentioned above, scrollwheels are mapped to the logical buttons 4,5 and 6,7. This has historical reasons, the core protocol only allows for two axes and by the time scrollwheels came about, the protocol was already set in stone. So now they're mapped in-driver to logical buttons instead and most applications and toolkits interpret it correctly. This is not ideal, as mapping wheel events to button events looses information (such as missing out on pointer acceleration for smoother scrolling).

The right-handed synaptics quirk


One common problem is how to configure a right-handed synaptics pad while leaving tap-to-click behaviour in the default configuration. As mentioned above, tap-to-click is an in-driver mapping. Right-handed behaviour is (usually) a global mapping applied by the desktop environment and may even be applied to the core pointer (especially with older, non-XI aware configuration tools). The easiest solution is to map tapping in-driver to an unused button number and then map this button back to the logical buttons as desired. For example, TapButton1 on synaptics device S could be mapped to 10, and D10 mapped to L1.


tap 1 » S(D10) → S(L10) → CP(D10) » CP(L1) → client


Thus, even if S is set up with a 3 2 1 mapping for the first three buttons, tapping still works in a "right-handed" fashion.

Note that in order to map two device buttons to the same logical button, X Input 1.5 is needed (server 1.6).

The tools


For a device button mapping:
XSetDeviceButtonMapping(3)
xinput --set-button-map 1 2 3 4 5 ...

For the core pointer mapping:
XSetPointerMapping(3)
xmodmap -e "pointer = 1 2 3"

For the synaptics mapping:
synclient TapButton1=1

For an evdev button mapping:
Option "ButtonMapping" "1 2 3 ..."


[edits]
mclasen pointed out that the middle button is "primary paste", not "clipboard paste". Text amended to be neutral regarding the paste type.