Monday, December 10, 2012

What's new in XI 2.3 - Pointer Barrier events and barrier releases

Update 7 March 2013: XI 2.3 is released, amend accordingly

The main feature making up XI 2.3 is Pointer Barrier events. This feature was initiated by Chris Halse-Rogers and then taken up by Jasper St. Pierre and me, until it became part of XI 2.3.

The usual conditions apply: a client must announce XI2.3 support with the XIQueryVersion() request to utilise any of the below.

Pointer barriers

Pointer Barriers are an XFixes v5.0 additions to restrict pointer movement. They were first available in server 1.11 (released Aug 2011). A pointer barrier is created by a client along a specific horizontal or vertical line of pixels. Relative input devices such as mice or touchpads will be constrained by this barrier, preventing the cursor from moving across the barrier.

For example, GNOME3 creates a vertical barrier on the top-left screen edge. In a multi-monitor screen, flicking the pointer up to the Activities menu will constrain the pointer there even if there is a screen to the left of the menu. This makes the menu much easier to hit.

The client can choose for the barrier to be transparent in specific directions. GNOME3's pointer barrier is permissive in the direction of positive X (i.e. left-to-right). Thus, moving from the left screen to the right is unrestricted, even though the pointer is constrained when moving in the other direction.

A simple client that creates a vertical pointer barrier looks like this:

Display *dpy = XOpenDisplay(NULL);
int fixes_opcode, fixes_event_base, fixes_error_base;
PointerBarrier barrier;

if (!XQueryExtension(dpy, "XFIXES",
                     &fixes_opcode,
                     &fixes_event_base,
                     &fixes_error_base))
    return EXIT_FAILURE;

/* vertical barrier from 20/20 to 20/100 */
barrier = XFixesCreatePointerBarrier(dpy, DefaultRootWindow(dpy),
                                     20, 20,
                                     20, 100,
                                     0, /* block in all directions */
                                     0, NULL); /* no per-device barriers */

mainloop();

The above code will set up a barrier for all devices, blocking in all directions.

Pointer barrier events

As of XI 2.3, pointer barrier events are sent to clients when pointer movement is constrained by a barrier, provided that client created pointer barriers, the clients have the matching event mask set. Two new event types were added, XI_BarrierHit and XI_BarrierLeave. Both events are only sent to clients owning a barrier and are only sent to the window used to create the barrier (the root window in the example above).

unsigned char m[XIMaskLen(XI_BarrierLeave)] = {0};
XIEventMask mask;
mask.deviceid = XIAllMasterDevices;
mask.mask_len = XIMaskLen(XI_BarrierLeave);
mask.mask = m;

XISetMask(mask.mask, XI_BarrierHit);
XISetMask(mask.mask, XI_BarrierLeave);
XISelectEvents(dpy, DefaultRootWindow(dpy), &mask, 1);
XSync(dpy, False);

while (1) {
    XEvent ev;
    XNextEvent(dpy, &ev);
    if (ev.type != GenericEvent || ev.xcookie.extension != xi2_opcode)
       continue;

    XGetEventData(dpy, &ev.xcookie);
    XIBarrierEvent *b = ev.xcookie.data;
    if (b->evtype == XI_BarrierHit)
       printf("Pointer hit the barrier\n");
    else if (b->evtype == XI_BarrierLeave)
       printf("Pointer left the barrier\n");
    
    XFreeEventData(dpy, &ev.xcookie);
}

An XI_BarrierHit event is first sent when the pointer movement is first constrained by a barrier. It includes some information such as the device, the barrier and the window. It also includes coordinate data.

XI_BarrierHit events are sent for each movement of the pointer against this barrier, or along it, until the pointer finally moves away from the barrier again. "Moving away" means a move to different position that is not blocked by the barrier.

  • if the barrier is vertical, the pointer moves to a different X axis value, or
  • if the barrier is horizontal, the pointer moves to a different Y axis value, or
  • the pointer moves past the barrier's end point
Once the pointer does move away, a XI_BarrierLeave event is sent. A pointer that moves against a barrier, pushes against it for 3 more events and then pulls back will thus generate 4 XI_BarrierHit events and one XI_BarrierLeave event.

Who gets events? Always the client that created the barrier, and only if the window has the event mask set. If the client has a grab on the device with the grab_window being the barrier window, the barrier events follow the usual grab event mask behaviour:

  • if the grab event mask has XI_BarrierHit set, the event is delivered
  • if the grab event mask does not have XI_BarrierHit set but the window mask does and owner_events is True, the event is delivered
  • if owner_events is False and the grab mask does not have XI_BarrierHit set, no event is sent
The above applies to XI_BarrierLeave events as well.

If the client's grab has a grab_window different to the barrier window, or the device is grabbed by another client, event delivery is as usual. In all cases, if the device is grabbed, the XIBarrierDeviceIsGrabbed flag is set. Clients should use this flag to determine what to do. For example, the barrier that is used to trigger the GNOME overlay should probably not trigger if another client has a grab as it may interfere with drag-and-drop.

Coordinates and time in pointer barrier events

Barrier events contain two sets of x/y coordinates. First, the root coordinates which represent the position of the pointer after being confined by barrier (and screen extents, where applicable). This coordinate is the same you would get from a subsequent XIQueryPointer request.

The second set are the delta coordinates (dx/dy), in screen coordinates, from the last pointer position, had the barrier not constrained it. So you can calculate how much the pointer would have moved and thus derive speed. The dtime field in the event helps you to calculate speed, it provides the time in milliseconds since the last pointer event. The deltas are calculated after taking pointer acceleration into account.

    XIBarrierEvent *b = ev.xcookie.data;
    double dx, dy;
    double speed;
    unsigned int millis;

    dx = b->dx;
    dy = b->dy;
    millis = b->dtime;

    speed = sqrt(dx * dx + dy * dy) / millis * 1000;

    printf("Barrier was hit at %.2f/%.2f at %.2f pixels/sec\n",
           b->root_x, b->root_y,  speed);

Releasing a pointer from barrier constraints

By default, a pointer barrier blocks all movement of relative input devices across a barrier. However, a client can opt to temporarily release the pointer from the barrier constraints with the XIBarrierReleasePointer request.

To do so, the client needs the event ID of the barrier event. Since a pointer may bump against the same barrier multiple times before the client reacts (X is asynchronous, after all), the event ID serves to identify a set of movements against or along the pointer barrier.

An event ID is assigned to the first XI_BarrierHit event, and then it remains the same until the XI_BarrierLeave event. This event is the last event with the current event ID, any future barrier events will have a new event ID. This approach may be familiar to you from dealing with touch events, that use a similar approach (touch IDs start at TouchBegin and are tracked through to the TouchEnd).

To release a pointer and let it pass through the barrier, call XIBarrierReleasePointer().

    XIBarrierEvent *b = ev.xcookie.data;
    ...
    if (speed > 200) {
        printf("Movement exceeds speed limit, allowing pointer to go through\n")
        XIBarrierReleasePointer(dpy, b->deviceid, b->barrier, b->eventid);
        XFlush(dpy);
    }
If, when the request arrives at the server, the pointer is still trapped by the barrier, the barrier is now transparent and the pointer can move through it with the next movement. If the pointer moves away from the barrier after releasing it and later moves against this barrier again, it will be constrained once more (albeit with a different eventid). Likewise, if the pointer has already moved away and against the barrier again before the client reacted, the release request has no effect.

If the release does succeed and the pointer moves through the barrier, the client gets a XI_BarrierLeave event with the XIBarrierPointerReleased flag.

The pointer barrier hit-box

As mentioned above, a XI_BarrierLeave event is sent when the pointer moves away from the barrier. This would usually require a 1 pixel movement (let's ignore subpixel-movement, our hand's aren't that precise). However, during testing we found that 1 px pointer-movement can still happen even when a user tries hart to move along the barrier, or even when pushing against the barrier. Thus, we implemented a hit-box of 2 pixels. Thus, a 1 px movement along the barrier still counts as hitting the barrier, and the pointer is not treated as having left the barrier until it leaves the hit-box.

Testing the current state

The current code is available in the barriers branch of the following repositories:

git://people.freedesktop.org/~whot/inputproto.git 
git://people.freedesktop.org/~whot/libXi.git 
git://people.freedesktop.org/~whot/xserver.git 
An example program to test the feature is available here

Tuesday, December 4, 2012

Two magic words - "I think"

We live in a world of absolutes. Water is wet, taxes are annoying, death is certain, it's always beer o'clock somewhere and the world is going to end somewhen later this year (which makes this post doubly futile, but what the hell). Hard to dispute any of those.

We also live in a world of non-absolutes. You may be freezing when someone right next to you is hot. You may be enjoying a movie when the person you dragged into the theatre is wishing for stronger teeth to gnaw through that wrist. Saying "it's cold" or "this movie is awesome" is going to be met with disagreement

What does that have to do with software development? We tend to be so convinced of ourselves that we take our personal opinion or experience as absolutes. It's acceptable to say "it's broken" when the application crashes once you start typing. But to say "it's broken" because it doesn't work as you expected is bad.

Such a statement kills the climate of any conversation. If you tell me that something "is broken", "is completely borked", "is idiotic", or any of those you're achieving one thing: You're putting me on the defence, and quite offensively so. After all, how could I have possibly written, approved, applied something that's so clearly wrong? That makes me uncomfortable. So I'll likely attack back because all I need to do now is find one example that proves you wrong. And that's usually quite easy. But at that point it's unlikely a decent conversation will ensue.

Worse, if you keep doing that at some point I'll stop listening because someone who makes false statements all the time should be in politics or advertising, but not involved with FOSS.

So now let's try the same again with those magic words: "I think this is completely broken". Well, now you've changed it from an absolute statement to an expression of your opinion. So my task is now to convince you to reconsider your opinion. I'm not on the defence, and the result is that in the worst case we'll agree to disagree - without that awkward absolute statement in the room.

A few examples:

  • "Nobody needs this" vs. "I don't think anybody needs this"
  • "The application must not do this" vs "I think the application must not do this"
  • "Wearing underpants is wrong" vs "I think wearing underpants is wrong"
  • "Bananas must be peeled from the bottom" vs "I think bananas must be peeled from the bottom"
Did you notice any differences? Any of the "I think" can be quite simply countered with "I don't think so" (especially the third one, please) and all you'd have is a discussion of two people with differing viewpoints. Even re-reading these examples my reaction to the first is automatically "WTF?", but the reaction to the "I think" alternatives is a conversational "nah, mate".

So next time you're not happy about something: just prefix your criticism with "I think". You may be surprised what difference it makes to the conversation.

Oh, two other magic words: "for me". Compare "This workflow is completely broken" vs "This workflow is completely broken for me". Amazing what difference those two words make...

Thursday, November 29, 2012

X.Org integration test suite - part 2, the registry

Back in August, I wrote about the X.Org Integration Test suite, in short XIT. Since then I've been quite busy adding tests to it, expanding it and fixing the odd bug. Jasper St. Pierre has been writing tests against it for the new pointer barrier features, making that feature probably the first to get full, repeatable testing before merging.

Aside from general cleanup so that tests are even easier to write, one of the features I've pushed today is a bug registry. One of the issues with the test suite (especially one that is not integrated with the repository directly) is that tests don't just fail or succeed, they can also be known to fail, or known to succeed.

For example, a test may succeed on git master, but fail on the 1.13 branch - either because the fix has not yet been backported, or because the fix will not be backported anyway.

Keeping track of those failures is quite a task, especially when you have multiple server versions to worry about.

This is what the bug-registry is supposed to address. It's in its early stages, but has already been quite helpful. At it's very base is the commandline client xit-bug-registry, and the matching data file (xml) that keeps track of the various test cases.

This is a simple introduction on how to use it.

To get started, run the tests, then create a new registry from the test output

$> sudo ./tests/server/server --gtest_output="xml:server-results.xml"
$> xit-bug-registry create server-results.xml > registry.xml
Information about the tests can now be queried:
$> xit-bug-registry -f registry.xml list

TestSuite                     TestCase                               Success
---------                     --------                               -------
BarrierConflictingDirections  InvalidConflictingDirectionsBarrier/0     True
BarrierConflictingDirections  InvalidConflictingDirectionsBarrier/1     True
EventQueueTest                mieqOverflow                              True
MiscServerTest                DoubleSegfault                            True
PointerGrabTest               GrabDisabledDevices                       True
PointerGrabTest               ImplicitGrabRawEvents                     False
ScreenSaverTest               ScreenSaverActivateDeactivate             False
...
The above shows a bunch of tests that are expected to succeed, and two that are expected to fail. Ideally, these tests will be annotated, so that looking up info will look like this:
$> xit-bug-registry -f registry.xml info XTest DisabledDevicesProperty
XTest DisabledDevicesProperty:  Expected result: Success
Known bugs: 
0: https://bugs.freedesktop.org/show_bug.cgi?id=56380
Known fixes: 
0: aad65415bff12c6860c19beac42e4165e598a40f
To get there, some manual interaction is required:
$> xit-bug-registry -f registry.xml edit \
 XTest DisabledDevicesProperty \
 add-bug http://bugs.freedesktop.org/56380
$> xit-bug-registry -f registry.xml edit \
 XTest DisabledDevicesProperty \
add-commit aad65415bff12c6860c19beac42e4165e598a40f

There are other bits one can add, but bugs and commits are likely the interesting bits. Right now, that's all it does, but in the future I hope to expand the script to query the bug database for the bug status, and query repositories to check if the fix is on the branch yet.

That's all nice, but the real important bit is to verify that after fixing a bug, one hasn't broken anything.

$> sudo ./tests/server/server --gtest_output="xml:server-results.xml"
$> xit-bug-registry -f registry.xml verify server-results.xml
Code TestSuite                    TestCase                                Result  Expected
---- ---------                    --------                                ------  --------
++   BarrierConflictingDirections InvalidConflictingDirectionsBarrier/0   true      true
++   BarrierConflictingDirections InvalidConflictingDirectionsBarrier/1   true      true
++   BarrierConflictingDirections InvalidConflictingDirectionsBarrier/2   true      true
++   BarrierConflictingDirections InvalidConflictingDirectionsBarrier/3   true      true
++   BarrierConflictingDirections InvalidConflictingDirectionsBarrier/4   true      true
++   BarrierConflictingDirections InvalidConflictingDirectionsBarrier/5   true      true
--   PointerGrabTest              ImplicitGrabRawEvents                  false     false
XX   ScreenSaverTest              ScreenSaverActivateDeactivate           true     false
++   TouchDeviceChangeTest        DeviceChangedEventPointerToTouchSwitch  true      true
++   TouchDeviceChangeTest        DeviceChangedEventTouchToPointerSwitch  true      true
XX   TouchDeviceChangeTest        NoCursorJumpsOnTouchToPointerSwitch     true     false
++   TouchDeviceTest              DisableDeviceEndTouches                 true      true
--   TouchEventHistoryTest        EventHistoryReplay/0                   false     false
--   TouchEventHistoryTest        EventHistoryReplay/1                   false     false
--   TouchEventHistoryTest        EventHistoryReplay/10                  false     false
...

This simply shows us which tests had which result. Cases with code ++ succeeded when they were expected to, -- failed when they were expected to. XX is what you need to look for, it indicates that the test outcome differs to the status in the registry. In this case, two tests now succeed when before they didn't - usually a good outcome.

Once a fix is merged, you can update the status of the bug:

$> xit-bug-registry -f registry.xml edit \
 XTest DisabledDevicesProperty \
 set-status success
If this test fails in the future, you'll now be able to spot it.

The information is all in the registry file and though I admit the interface is still clumsy, it's quite simple to keep a set of registries (e.g. upstream, Fedora, RHEL, etc.) and thus a known set of test outcomes for each.

Finally, as test cases are being added, it's important to update the registry. This is as simple as:

$> xit-bug-registry create results-with-new-testcases.xml > new-registry.xml
$> xit-bug-registry merge registry.xml new-registry.xml
This command will merge any new test cases into our original registry, but won't update the status on any existing ones.

Friday, October 19, 2012

Lenovo x220t Tablet and the broken Wacom 0xE6 tablet

I noticed that my tablet didn't work anymore. In fact, the tablet itself didn't even show up in /proc/bus/input/devices. At first I suspected a kernel bug and rebooted into a variety of different Fedora kernels, but to no avail.

Looking at various logs, I discovered:
:: whot@yabbi:~> dmesg | grep 56a
[    2.291841] usb 2-1.5: New USB device found, idVendor=056a, idProduct=0094
Wacom's vendor ID is 056a, but the tablet in the x220 is a 0xE6, not a 0x94 (the latter of which doesn't exist). With that info, googling for a solution was a bit easier. As suggested in the thread on linuxwacom-devel a power off did indeed fix the issue for me.
:: whot@yabbi:~> dmesg | grep 56a
[    2.291841] usb 2-1.5: New USB device found, idVendor=056a, idProduct=00e6
It turns out this is an issue with kernel 3.5.3 and 3.5.4, but it is fixed in 3.5.5. kernel bug. As Ping states:
The root cause is somewhere in the kernel tablet is set to a wrong mode. Once it is in that mode, a reboot won't reset it to normal. We have to power it off and wait a few seconds for all bits to reset. That's why I tell people to even unplug the power cable since bits may stay as long as they get power.

update 20/10/12: list kernel versions affected, add Ping's quote

The X.Org joystick driver (xf86-input-joystick)

At XDC2012, Sascha Hlusiak, maintainer of the xf86-input-joystick module, gave a talk (slides) outlining the joystick driver's current state and future directions. There were quite a few tidbits of information in there that I want to repeat here, a slightly more public place.

The most important bit about the xf86-input-joystick module is what it is not: it is not a joystick driver. If you want to use your joystick in a game under X, you don't need that driver. The xf86-input-joystick module is designed to control the pointer with a joystick device. Sascha gave the example of using an XBox controller to control a Linux-driven media-center. The controller can serve as a cheap remote control (especially if you already have the controller but you'd need to buy a remote ;)

So why is the joystick module not a joystick driver? The evdev driver handles most input devices with arbitrary axes and games can use those devices through evdev. But this is a game-specific application, and the game likely knows what features of the joystick it wants.

For pointer control, it's not quite as obvious. Your average joystick has a couple of axes, four or more buttons and possibly even force feedback. The joystick module's configuration must determine which axis controls the pointer motion (and must be mapped from absolute to relative) and which button generates button (or key) events. Because no two joysticks are the same, there cannot be a sane default for the joystick driver configuration. The default install is thus almost certainly useless and may even interfere with your normal X install.

For example, a joystick sending absolute input events that does not have the mapping configured correctly will force the mouse pointer onto a specific position.That may make it impossible to move the pointer with the mouse.

For this reason, any distribution that ships the joystick module should add Option "Floating" "on" into the default configuration. This way the joystick is detected and enabled in X, but it won't actually control the cursor. This means the joystick may look like it's not working initially to the users who want it, but it's not breaking anything for those that install the module unintentionally. The joystick module should not be part of a default install, it's use-case is too narrow.

Once you're happy using the joystick module for it's intended purpose, there are vast configuration options to suit all tastes:

  • Option "MapButton1" "button=3" maps the first joystick button into a right-click (X logical button 3)
  • Option "MapButton2" "axis=+5x" maps the second joystick button to a x movement by 5 units
  • Option "MapButton3" "keycode=50,40" maps to Shift +d, though you may want to read about keycodes vs keysyms first
  • Option  "MapAxis2"      "mode=relative    keylow=111  keyhigh=116" will map the second axis to key events up/down
I recommend reading the joystick(4) man page for further examples on configuration options. Note, the linked man page isn't the latest one, but it's at least rendered in html, not plain roff.

Friday, September 28, 2012

Never use a "Questions?" slide in your presentation

Most presenters do this. Give a presentation, finishing with a "questions?" slide. Except, that's a really bad idea.

You give a talk at a conference? You have 20, maybe 45 minutes to get your topic into the mind of the audience? Then don't let your audience stare at "Questions?" for 10, 20 minutes (however long the question time takes). Even if there are no questions, it usually takes a few minutes for applause, saying thanks and until you leave the podium. And the whole time your audience is staring at a meaningless word.

So, skip the useless "Questions?" slide. You can invite your audience to ask questions without it too. The whole point of being a speaker is that you're allowed to also say things that are not on the slides.

Finish with a slide that reminds your audience what you just talked about. A summary of the talk, the one point you really wanted to drive home. Maybe even a joke, if you have one that gets the point across.

And your contact details. It gives those without questions enough time to write it down.

Tuesday, August 28, 2012

X.Org integration test suite

One of the problems we face in X is that testing can be incredibly painful. Somebugs only happen with a specific server state, a specific device and a specific application. Sometimes we don't spot regressions for several releases, and in some cases that means we can't easily go back and fix it either - users may have gotten used to the new behaviour already.

A few months ago, Chase Douglas announced the xorg-gtest framework. I started extending xorg-gtest and writing tests against it. Olivier Fourdan soon joined in with his own tests. The result of that are the xorg integration tests (XIT) found here:

http://cgit.freedesktop.org/~whot/xorg-integration-tests/

This post is an outline of what the XIT do and what we could potentially make it do. We currently have over 50 tests in the XIT repository. Most of the tests are still quite simple, but I've already added some that test for a specific bug and that certainly helped testing. In one case, a failing test reminded me that I forgot to send a pull request :)

googletest and xorg-gtest

xorg-gtest is based on the googletest C++ framework. It provides a bunch of classes with virtual functions that are called set up or tear down a test. For the tests, it provides a set of assertion macros. xorg-gtest provides a set of classes on top of googletest that make starting an X server simpler. The XServer class provided hooks up config and log files, waits for the server to accept connections and terminates the server gracefully. It also includes a class for starting evemu devices and re-playing input event sequences. There are a few additional helper classes in the XIT, for example one that makes writing configurations easier. They may move to xorg-gtest at some point in the future.

Example tests

Your average test now essentially consists of three steps: write out a specific config, start the server and then query that server for information or test it for behaviour.

A straightforward test is the acecad driver test below.

TEST(AcecadInputDriver, WithOptionDevice)
{
    XOrgConfig config;
    xorg::testing::XServer server;

    config.AddInputSection("acecad", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"Device\" \"/dev/input/event0\"\n");
    config.AddDefaultScreenWithDriver();
    StartServer("acecad-type-stylus", server, config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());

    int ndevices;
    XIDeviceInfo *info;
    info = XIQueryDevice(dpy, XIAllDevices, &ndevices);
    XIFreeDeviceInfo(info);

    ASSERT_EQ(ndevices, 7);

    config.RemoveConfig();
    server.Terminate(3000);
    server.RemoveLogFile();
}

A slightly more involved test that makes use of the googletest class hierarchy would be the void driver test below. It makes use of a InputDriverTest class that starts the server for us and tears it down afterwards. So all we need is a couple of lines for the class definition and then the test itself.

class VoidInputDriverTest : public InputDriverTest {
public:
    /**
     * Initialize an xorg.conf with a single CorePointer void device.
     */
    virtual void SetUp() {
        InputDriverTest::SetUp("void");
    }
};

TEST_F(VoidInputDriverTest, VoidDriver)
{
    ASSERT_EQ(FindInputDeviceByName(Display(), "--device--"), 1);
}

These two are just examples as used in the integration test suite. I recommend reading the googletest documentation for a bit more info on test fixtures and assertions. xorg-gtest also is well-documented though I recommend building the doxygen sources.

Emulating input devices

xorg-gtest provides a Device class that, based on evemu recordings, can simulate input devices. So with the current implementation, this as simple as:

  SetDevice("keyboards/AT-Translated-Set-2-Keyboard.desc");
  dev->Play("keyboards/qwerty.events");
  ...
  dev->PlayOne(EV_KEY, KEY_Q, 1, true); // press
  dev->PlayOne(EV_KEY, KEY_Q, 0, true); // release

If you now check for the keysym received, you can easily write a few simple XKB layout tests.

Fighting the build system

As convenient as tests are to write, googletest is not the easiest thing to deal with. The piles of macros subclass your classes and the C++ ODR essentially prevents googletest from being built into a library. Autotools on the other hand make it difficult to compile an external project into your own file. That is the main reason for one big collection of tests instead of a per-driver test suite. Copying Makefile.am's around is painful enough, but at least manageable when only one project does it.

The current need for three different repositories (googletest, xorg-gtest and XIT) doesn't make things easier. I'm somewhat tempted to merge XIT into xorg-gtest, be it as git submodule or some other means.

Having said that, since starting to write the tests I'm also really tempted to merge the drivers into the server, it would make things a lot easier

ifdefs maze

To make the tests useful, they should be able to run on most systems. Right now, I've got some simple defines to test for RHEL6 and also XI 2.2. But I don't think this will scale too well. Every system may have different defaults, and short of having platform-specific tests (which may only be different in one single line) I don't know what the good answer is. I do want it answered soon, before it gets out of hand.

Scalability

Running all tests takes a while. In the current test design, every single test starts a new server and thus it takes a couple of seconds for even the quickest test to succeed. This works with 50 test cases, but it won't work with 500 and even less with 5000. So we need some solution for that. Not re-starting the server is the obvious one and there will be tests that won't require that.

Output testing

Most of the test runs I do either on my laptop or on a virtual machine. For input tests, this is easy since we only need to set up the dummy driver and the rest can be emulated. For output tests, not so much so a good approach of only running tests applicable for the hardware would be useful. I haven't thought too much about this yet.

Testing XIT

Right now, it sits in my personal home directory but I hope that after XDC this year, we can decide what the future of this test suite can be and where to host it proper.

In the meantime, grab it from here:

http://cgit.freedesktop.org/~whot/xorg-integration-tests/

Monday, August 20, 2012

screen configuration for automatic session names

screen is an immensely useful program, but once you start running multiple sessions at the same time, it gets tricky to find the right one again.  For example, which one was offlineimap again?


 :: whot@salty:~> screen -ls
There are screens on:
    5238.pts-3.salty    (Detached)
    5229.pts-3.salty    (Detached)

The -S argument allows to specify a session name but I couldn't find a configuration to set this automatically. So I've added this little script:


$> cat $HOME/scripts/screen
#!/bin/bash
/usr/bin/screen $([[ ($1 != -*) && ($# > 0) ]] && echo "-S $1") "$@"


Explanation: if called with 1 or more parameters and the first parameter does not start with a dash, replace a call in the form of screen command ... with screen -S command command ...

This results in screen sessions having useful names, allowing for more effective reattaching. And the obligatory, ahem, screenshots: 
:: whot@yabbi:~> screen foo
[detached from 17755.foo]
:: whot@yabbi:~> screen bar
[detached from 17766.bar]
:: whot@yabbi:~> screen -ls
There are screens on:
    17766.bar    (Detached)
    17755.foo    (Detached)
2 Sockets in /var/run/screen/S-whot.

Wednesday, July 11, 2012

Daily tmp directory

My $HOME/tmp directory got a bit messy, especially with building test rpms or testing tarballs. My solution was to just change the directory to automatically change daily. Scripts below:
:: whot@yabbi:~> cat scripts/tmp-today 
#!/bin/bash
date=`date +%Y-%m-%d-%a`

tmpdir=$HOME/tmp/$date
tmplink=$HOME/tmp/today

if [ -e "$tmpdir" ]; then
    exit 0
fi

mkdir $tmpdir
ln -sf $tmpdir $tmplink
And the crontab entries to run this script:
0 2 * * * /home/whot/scripts/tmp-today
@reboot /home/whot/scripts/tmp-today
So run the thing at 2.00 am and on every reboot in case the box was shut off overnight. I had it on midnight first, but I think 2 am is better. If I'm still up at 2 and working, then mentally I'm still in on the day before and I don't want files to end up in different directories just because midnight clocked over. And because the laptop may be suspended overnight, we run the script on resume as well:
:: whot@yabbi:~> cat /etc/pm/sleep.d/00-tmp-dir 
#!/bin/bash

case "$1" in
 thaw|resume)
  su -c - whot /home/whot/scripts/tmp-today
  ;;
 *)
  ;;
esac
This obviously works for other directories as well, e.g. your daily download directory.

Friday, July 6, 2012

elographics touchscreen setup

This is a tutorial of sorts on how to set up an elographics serial touchscreen. I don't actually have such a device but the question pops up every couple of months. Together with Tom Munro Glass and Tias Guns, we got one working properly.

Edit: 18/09/2014 - add systemd description

A bit of history first: not too long ago, the X server was talking to serial devices directly and thus had several drivers for specific input devices. The elographics X drivers is one of them. Unfortunately, due to lack of hardware we, the upstream maintainers, can only perform cursory testing of that driver. The Linux kernel however supports a large amount of serial devices too, so for this tutorial I'll explain how to use the linux kernel to talk to the device. I'll show how to set up udev rules to use inputattach and then the X.Org evdev driver, thus making the device utilise a well-tested driver that has some client-stack support not found for the X.Org elographics driver [1].

Kernel setup

USB devices are automagically detected by the kernel and it will load the right drivers. Serial devices on the other hand require some manual configuration. Elographics devices have serial kernel drivers and inputattach can be used to hook serial devices up with those kernel drivers. First we need to load the kernel module, then call inputattach with the right options for our device:
root@localhost:~> modprobe elo
root@localhost:~> inputattach -elo /dev/ttyS0 --daemon
The path to the device may need to be changed on your box. Do look up the inputattach man page, depending on your device you may need a different flag.

Once inputattach is running, the device should be visible in /proc/bus/input/devices. You can run evtest against it and see the event stream.

udev setup pre systemd

Since we don't want to run inputattach manually each time, we'll now set up udev to run this command for us. According to Tom, the elographics device does not have a pnpid, so he configured it with a fixed device path on ttyS4:
$> cat /lib/udev/rules.d/99-elographics.rules
ACTION=="add|change", SUBSYSTEM=="tty|pnp", KERNEL=="ttyS4", \
        RUN+="/sbin/modprobe elo", \
        RUN+="/sbin/inputattach -elo /dev/%k --daemon"
If a device has a pnpid, the rule can be more flexible in what ttyS* the device needs to appear on. For example, a Wacom serial tablet may have the id WACf004 so we can use the match rule below.
$> cat /lib/udev/rules.d/99-wacom-w8001.rules
ACTION=="add|change", SUBSYSTEM=="tty|pnp", KERNEL=="ttyS[0-9]*", \
        ATTRS{id}=="WACf*", \ 
        RUN+="/sbin/modprobe wacom_w8001", \
        RUN+="/sbin/inputattach -w8001 /dev/%k --daemon"
Once this rule is in place, inputattach will be started for us at boot time (of course the device needs to be connected at boot time too). The next step is to configure X to see the device.

udev setup with systemd

Newer udev (for some value of new, we're talking about Fedora 18 or so) won't let RUN start long-running processes, such processes get killed off. If you are on system with systemd you're best off setting up a systemd service file for inputattach (see also this post). In that case, your udev rule becomes
SUBSYSTEM=="tty|pnp", KERNEL=="ttyS4",  \
      RUN+="/sbin/modprobe elo", \
      TAG+="systemd", ENV{SYSTEMD_WANTS}+="elo-inputattach@%k.service"
So we tag the device for systemd and tell it to start a servirce. The %k gets replaced with the ttySx device. The matching systemd unit file looks like this:
$> cat /etc/systemd/system/elo-inputattach@.service
[Unit]
Description=inputattach for elo serial devices

[Service]
Type=simple
ExecStart=/usr/bin/inputattach -elo /dev/%I
Once both of these are in place, a reboot will initialise inputattach, and the device is available as kernel device.

X.Org configuration

X requires the udev properties ID_INPUT and one of ID_INPUT_MOUSE, ID_INPUT_TABLET, etc. to to recognise an input device. Each of those triggers the respective MatchIsPointer, MatchIsTablet, etc. directive in the xorg.conf.d snippets. On modern systems, the udev properties should be assigned automatically [2]. If that doesn't happen on your box, you can modify the above udev rules to add it manually:
$> cat /lib/udev/rules.d/99-elographics.rules
ACTION=="add|change", SUBSYSTEM=="tty|pnp", KERNEL=="ttyS4", \
        RUN+="/sbin/modprobe elo", \
        RUN+="/sbin/inputattach -elo /dev/%k --daemon", \
        ENV{ID_INPUT}="1", ENV{ID_INPUT_TOUCHSCREEN}="1"
Once this is done, the X server will pick up the device, and the default configuration [3] will assign the evdev driver to it. Restart X, and you're done.

If you require additional options, set up an xorg.conf.d snippet. There you can add any options found in evdev(4):
$> cat /etc/X11/xorg.conf.d/99-elographics.conf
Section "InputClass"
  Identifier "elographics config"
  MatchProduct "Elo Serial TouchScreen"
  Option "EmulateThirdButton" "on"
EndSection
This snippet will match any device with the product name containing "Elo Serial Touchscreen" and enable the right button emulation feature in the evdev driver. If you are unsure about the name of your device, check /proc/bus/input/devices once inputattach is running.

X.Org configuration on Red Hat Enterprise Linux (RHEL) 6.x

This section applies to derivatives as well, e.g. CentOS 6. On RHEL 6.x, we use HAL as a configuration backend. So you will need an fdi snippet to match the device up with the driver. The snippet below is the one Tom ended up using, and it also shows the Calibration option being merged.
$> cat /etc/hal/fdi/policy/10-elographics.fdi
<?xml version="1.0" encoding="ISO-8859-1"?>
<deviceinfo version="0.2">
  <device>
    <match key="info.category" contains="input">
      <match key="info.product" contains="Elo Serial TouchScreen">
        <merge key="input.x11_driver" type="string">evdev</merge>
        <!-- next line is equivalent to an xorg.conf.d statement of
                Option "Calibration "40 4000 40 4000" -->
        <merge key="input.x11_options.calibration" type="string">40 4000
40 4000</merge>
      </match>
    </match>
  </device>
</deviceinfo>
Note the "match key" statement. Apparently, this particular device does not set "input.touchscreen", so we match on just "input" instead. Not too big a deal, since we then also match on the product name.

Once this file is in place, restart haldaemon and check with lshal that the right options are assigned. Then restart X and the device should work.

Good luck!

Many thanks to Tias and Tom for proof-reading this article multiple times.

[1] for example, evdev supports dynamic recalibration with the xinput_calibrator tool
[2] check the output of udevadm info --export-db
[3] your distribution should ship /usr/share/X11/xorg.conf.d/10-evdev.conf which assigns evdev to MatchIsTablet.

[edit 22 Feb 13: fix typo in MatchProduct name]

Thursday, June 21, 2012

XKB SlowKeys

XKB has an accessibility feature called SlowKeys. Once enabled, a key must be pressed longer than a certain timeout (default: 300ms) to register as key event. Why? From XkbGetSlowKeysDelay(3):
Some users may accidentally bump keys while moving a hand or typing stick toward the key they want. Usually, the keys that are accidentally bumped are just hit for a very short period of time. The SlowKeys control helps filter these accidental bumps by telling the server to wait a specified period, called the SlowKeys acceptance delay, before delivering key events. If the key is released before this period elapses, no key events are generated.
This is a useful feature for those that need it, but quite disturbing when it gets enabled without the user noticing. And that can happen and it then appears as if the keyboard drivers are broken (after all, a restart of the X server reverts it). So here's a what you have to do to avoid this issue:

The server has a built-in key sequence that if either shift key is held for 8 seconds, SlowKeys are enabled. Likewise, another 8s press of either shift key disables them again. This may accidentally happen when playing games or scrolling through long documents.

The server keeps two bitflags around. One for "accessibility features enabled" and one for "slow keys enabled". If the former is set, the key sequence above will activate slow keys (and thus toggle the second bit).

In GNOME3, you can find these options in the Universal Access panel of your System Settings:



If "Turn on accessibility features from the keyboard" is checked, holding shift will enable/disable SlowKeys. If the SlowKeys toggle is on, SlowKeys are always on.

The equivalent gsettings command (to uncheck the checkbox) is:
gsettings set org.gnome.desktop.a11y.keyboard enable false
 I recommend that if you do not need this feature, uncheck the checkbox to avoid nasty surprises. I've also added a patch to the X server to put a warning in the log if SlowKeys is enabled via the shift key. This should make future triaging a bit easier. If you see the message
(II) XKB SlowKeys are now enabled. Hold shift to disable.
in your log, you're likely better off disabling the checkbox. This patch will be in server 1.13 and server 1.12.3.
 
GNOME3 should also warn about SlowKeys being enabled but due to Bug 668213 it stopped doing so in January. Affected is GNOME 3.2+ but we're working on a fix to this.

_XkbErrCode2 macro explanation

While debugging some keyboard layout issues, I got this error:
X Error of failed request:  BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  145 (XKEYBOARD)
  Minor opcode of failed request:  9 (XkbSetMap)
  Value in failed request:  0x163f0005
  Serial number of failed request:  116
  Current serial number in output stream:  122
X Errors are supposed to be useful to the client, usually denoting some error code (e.g. BadValue) and, if possible, the value that triggered it. After reading the code, I found the _XkbErrCode2 family of macros that expand to fill in the error code.
#define _XkbErrCode2(a,b) \
   ((XID)((((unsigned int)(a))<<24)|((b)&0xffffff)))
#define _XkbErrCode3(a,b,c) \
   _XkbErrCode2(a,(((unsigned int)(b))<<16)|(c))
#define _XkbErrCode4(a,b,c,d) \
   _XkbErrCode3(a,b,((((unsigned int)(c))<<8)|(d)))
Easy enough to decompose on the client though there's no indication of which decomposed representation is the correct one.
xkb/xkb.c:1742 _XkbErrCode3(0x16, i + req->firstKeySym, wire->width);
xkb/xkb.c:1749 _XkbErrCode4(0x16, i + req->firstKeySym, wire->nSyms, w);
That's when I started wondering what the 0x16 could mean - could it possibly be _XkbErrBadValue (from xkbfile.h) Bit generic, but possible. But why isn't it using the define? Looking at other _XkbErrCode calls, some were somewhat close but others were miles out. XkbBell returning _XkbErrMissingVMods? That didn't make sense.

And that's when it struck me: they didn't have any meaning. 0x16 is literally 0x16, the 22nd error code statement in this function. And, in this case also the 23rd since it is used twice. Of course, if one ever de-duplicate some of the XKB code the numbering is out. And I couldn't find a place where this is documented, so who knows how much damage we did to that approach without noticing.

Anyway, if you ever have to google for _XkbErrCode2, I'm hoping this post now comes up and explains the despair you're about to encounter.

Tuesday, June 12, 2012

XI 2.1 protocol design issues

When XI 2.1 smooth scrolling was released, we were glad to finally have a way for fluid scrolling events. Unfortunately, two design issues have since been discovered that make it harder for applications than necessary. Mea culpa, I apologise, these are things that we should have caught in the review process.

Scrolling may overflow the axis range

Scrolling events are now posted through axes marked as scrolling axes. This means they're limited to the 32.32 fixed point value range that valuators can take. That range matters because despite scrolling being relative, coordinates in XIDeviceEvents must always be absolute values. Scrolling down is more common than scrolling up, so the axis will slowly accumulate until it reaches the maximum range. We have to reset them to 0 once we reach INT_MAX to avoid overflow so at some point you may see a scrolling event that jumps from something that's close to 232 to 0. You can at this point assume that this was not a valid scroll event. This is a rather theoretical case, overflowing this range requires a lot of scrolling, even on touchpads with a higher scroll increment.

The first scroll event has no usable scroll information

This is a more significant issue. The smooth scrolling information is part of the valuator data, but axis data is only sent in some events, and then only to some clients. Once a pointer leaves the window, the original client loses track of what happens to the valuators. Once the pointer re-enters the client's window it will receive the new axis value once more scrolling happens. That value is an absolute value but clients need the delta between it and the previous scroll event. That information is only available once the second event is sent to the client.

For example, the axis data may be 3000 when the pointer leaves the window. Then the pointer re-enters the window and the first scroll event sends has a value of 5500. Since we don't know how much the axis increased elsewhere, we cannot act on this scroll event, we have to wait for the next one to calculate a delta.

I confirmed with Carlos that this is indeed what GTK does: reset the scroll valuators on enter and then track the valuators for future events.

Tuesday, June 5, 2012

git branch-info

I have too many branches. And sometimes I forgot which one is which. So yesterday I sat down and wrote a git alias to tell me:
branch-info = "!sh -c 'git branch --list --no-color | \
    sed -e \"s/*/ /\" | \
    while read branch; do \
    git log -1 --format=format:\"%Cred$branch:%Cblue %s %Cgreen%h%Creset (%ar)\" $branch; \
    done'"
Gives me a nice output like this:
:: whot@yabbi:~/xorg/xserver (for-keith)> git branch-info
dtrace-input-abi:  dix: add dtrace probes to input API  c0b0a9b (3 months ago)
extmod-changes:  DRI2: Remove prototype for DRI2DestroyDrawable  8ba2980 (11 months ago)
fedora-17-branch:  os: make timers signal-safe 7089841 (6 weeks ago)
master:  Xext: include dix-config.h 594b4a4(12 days ago)
mt-devel: Switch to new listener handling for touch events 3ee84fc (6 months ago)
mt-devel2: dix: conditionally update the cursor sprite 9edb3fd(6 months ago)
multitouch: blah 8ecec2e(4 months ago)
...
Or, the same thing aligned in columns, though without colours (column doesn't parse the escape sequences for colors so the columns are misaligned).
branch-info = "!sh -c 'git branch --list --no-color | \
    sed -e \"s/*/ /\" | \
    while read branch; do \
    git log -1 --format=format:\"$branch:|%s|%h|%ar\n\" $branch; done | \
    column -t -s\"|\"'"

pkg-config and empty CFLAGS

I ran into this a few days ago and it took me a while to find. One of the gnome modules failed to build with an error of "cannot find <gtk/gtk.h>". At first I thought it was a Makefile.am/configure.ac error but it appears the issue sits deeper. Cause of the problem was a stale gtk installation, but finding that was tricky. The configure.ac file contains essentially
PKG_CHECK_MODULES(GTK, gtk+-2.0)
AC_SUBST(GTK_CFLAGS)
AC_SUBST(GTK_LIBS)
Turns out that PKG_CHECK_MODULES in configure.ac gets translated to
pkg-config --exists --print-errors gtk+-2.0
This returns 0 (success) on my machine - the gtk+-2.0.pc file is there. However, to get the CFLAGS, configure runs a different command and that returned an error:
$> pkg-config --cflags gtk+-2.0
Package libpng12 was not found in the pkg-config search path.
Perhaps you should add the directory containing `libpng12.pc'
to the PKG_CONFIG_PATH environment variable
Package 'libpng12', required by 'GdkPixbuf', not found
Alas, this error isn't shown anywhere and it doesn't end up in the log either. What does happen though is that GTK_CFLAGS gets replaced with an empty string, causing the build error above. Once I installed the required libpng everything works fine. But if you ever run into issues where the CFLAGS aren't quite what they should be, check the pkg-config commands for success.

[Update Jun 07 2012]
As pointed out in the comments, looks like we're looking at https://bugs.freedesktop.org/show_bug.cgi?id=4738 and https://bugs.freedesktop.org/show_bug.cgi?id=43149 here.

Friday, May 18, 2012

Home-made 4WD rooftop awning

This article describes how to build your own rooftop awning. I recommend that if you do want an awning, purchase one from a store. They have come down in price considerably since I built mine. If you do go ahead, make sure to check with your local transportation authority and check whether such a device is legal in your country/state.

Anyway, let's start. A while ago I wanted a rooftop awning but I didn't want to pay that much money. In Australia, these awnings go for several hundred dollars, and the small ones are indeed quite small. So I built my own, instructions are below. This is how it looks like:


Sometimes it's a tough life...

The ingredients are:
  • tarp with metal eyelets. IIRC mine is 2.4 x 3.0m (similar one)
  • 3m 25x25mm aluminium square tube (or width of tarp otherwise)
  • Two plastic caps for the square tube
  • Four 1.5 inch 3/8 bolts + nuts + washers
  • Four 2 inch 3/8 bolts + nuts + washers
  • Four poles, one crossbar, ropes, pegs
  • Wide (~5cm) velcro, 1 m is sufficient (makes 3 strips)

Most of the stuff I got from Bunnings, the poles from the camping store and the velcro from a textile store next to the BCF at Cannon Hill.

The basic design is simple. The tarp is held onto the square tube by a bunch of vertical bolts - one per tarp eyelet. The crossbar, which is too long for my tray, is held in place by two horizontal bolts, on the inside (towards the car). The tarp is folded in half, then rolled up and held in place by three strips of velcro.

Tarp when rolled up for transport.

I have Rhino Rack Aero bars and the bolts I got had a head exactly the size of the groove. So I don't need mountings, I simply push the screw into the groove, then sit the awning on it and tighten the nut up the top. And, second time lucky, the eyelets are the distance of my roof racks, so I don't have to do anything there either.


The bolt fits exactly into the groove - lucky me :)
Obscured by the rolled-up tarp is the top end of the bolt - simply a washer and a nut.

Assembly instructions


Drill a hole a few cm in from one ond of the square tube. Now take a bolt and nut, screw the tarp's corner to the tube. Stretch it to the other end, drill another hole, bolt, nut, done. The tarp should be tightly stretched. Now mark each eyelets in the middle for more holes. Take the tarp off, drill the remaining holes, then saw the excess square tube off.

Bolt shown from the side

As said above, my roof racks are exactly the distance of the eyelets, so I don't need extra mountings. The professional awnings have an L-shaped attachment that goes into the roof racks and the awning then hangs off the side. This photo shows the attachment, the same should be possible on this home-made model.


The final bit are two horizontal drillings through the tube. They are for mounting the crossbar next to the tube. I transport the four poles, the ropes and the pegs in the tray.

Crossbar mount. The bolt is horizontal when mounted.

The front bit of the rolled-up awning, taken from the other side of the car, showing the crossbar mount.

Setting it up is simple enough, though it does require 2 people. It takes a few minutes to set up, a few minutes to pack up again. If the tarp gives up, I can (hopefully) buy another one and replace it.

Costs for me was just over AUD 100, including the crossbar and the poles. If you already have those, you're obviously able to build it for much less.

Side notes

As I have the handyman's knowledge of a five-year-old, I did a few things wrong. First, Bunnings sells bolts in imperial and metric. Had I known that, I'd have bought metric screws. I ended up just getting the first set that matched the size I needed. The metric section was a few metres further down the aisle.

The first set of (round capped) nuts wasn't galvanised and started rusting (you can see it in the photos). Haven't been able to find galvanised ones for the imperial bolts, so I've since replaced them with galvanised normal nuts.

Pros and Cons

  • It's freaking awesome! Having a shady spot next to the car wherever you roll up is great!
  • The awning is big. Not much of an issue once it's mounted but it's pretty unwieldy in the garage.
  • The surface area covered is perfect for two people and a table, so no issues there.
  • The awning is not free-standing. I believe the commercial models are, but this one needs to be pegged down. Which means that moving the car once it's set up is a no-go, you have to tear it down.
  • The way I roll it results in rain collecting in the rolled-up tarp. I could roll it the other way, but that's a bit awkward. No big issue though, except after a good rain when you break at the first set of lights, a few litres of water will run down your windscreen :)
  • I can't yet claim how it will hold up in a typical Queensland wind or thunderstorm. I think it will be alright, but so far I've only had it up during light winds and light rain.

Thursday, May 10, 2012

Testing X servers from git

Every so-often I get asked the question of how to test the X server (or drivers) from git. The setup I have is quite simple: I have a full tree in /opt/xorg, next to the system-installed binaries in /usr. A symlink and some environment variables allow me to switch between git versions of the server and clients and the system-installed ones.

Installing the tree

Getting that setup is quite easy these days:
mkdir -p xorg/util
git clone git://anongit.freedesktop.org/git/xorg/util/modular xorg/util/modular
cd xorg
./util/modular/build.sh --clone --autoresume built.modules /opt/xorg
That takes a while but if any component fails to build (usually due to missing dependencies) just re-run the last command. The built.modules file contains all successfully built modules and the script will simply continue from the last component. Despite the name, build.sh will also install each component into the specified prefix.

You get everything here, including a shiny new copy of xeyes. Yes, what you always wanted, I know

Note that build.sh is just a shell script, so you can make changes to it. Disable the parts you don't want (fonts, for example) by commenting them out. Or alternatively, generate a list of all modules, remove the ones you don't want or need and build with that set only:

./util/modular/build.sh -L > module_list
vim module_list # you can skip fonts, apps (except xkbcomp) and exotic drivers
./util/modular/build.sh --clone --autoresume built.modules --modfile module_list /opt/xorg

Either way, you end up with /opt/xorg/bin/Xorg, the X server binary. I just move my system-installed and then symlink the new one.

sudo mv /usr/bin/Xorg /usr/bin/Xorg_old
sudo ln -s /opt/xorg/bin/Xorg /usr/bin/Xorg
Next time when gdm starts the server, it'll start the one from git. You can now update modules from git one-by-one as you need to and just run make install in all of them. Alternatively, running the build.sh script again without the --clone parameter will simply git pull in each module.

Setting up the environment

What I then define is a few environment variables. In my .zshrc I have
alias mpx=". $HOME/.exportrc.xorg"
and that file contains
export PKG_CONFIG_PATH=/opt/xorg/lib/pkgconfig:/opt/xorg/share/pkgconfig
export LD_LIBRARY_PATH=/opt/xorg/lib/
export PATH=/opt/xorg/bin:$PATH
export ACLOCAL="aclocal -I /opt/xorg/share/aclocal"
export MANPATH=/opt/xorg/share/man/
So running "mpx" will start git versions of the clients, link clients against git versions of the libraries, or build against git versions of the protocol.

Why this setup?

The biggest advantage of this setup is simple: the system install doesn't get touched at all and if the git X server breaks changing the symlink back to /usr/bin/Xorg_old gives me a working X again. And it's equally quick to test Fedora rpms, just flick the symlink back and restart the server. I have similar trees for gnome, wayland, and a few other large projects.

It also makes it simple to test if a specific bug is a distribution bug or an upstream bug. Install the matching X server branch instead of master and with a bit of symlink flicking you can check if the bug reproduces in both. For example, only a few weeks ago I noticed that xinput got BadAtom errors when run from /usr/bin but not when run from /opt/xorg/bin. Turns out it was a thing fixed in the upstream libXi but not backported to Fedora yet.

The drawback of this setup is that whenever the xorg-x11-server-Xorg module is updated, I need to move and symlink again. That could be automated with a script but so far I've just been too lazy to do it.

[Update 11.05.12: typo and minor fixes, explain build.sh -L]

Wednesday, May 9, 2012

vimdir for project-specific vim settings

Sometimes it feels that each project I work on has different indentation settings. Not quite true but still annoying. I don't know of a way to tell vim to auto-detect the indentation settings based on the current file (which, for X.Org projects wouldn't work anyway) but what has been incredibly useful is the vimdir script. It simply scans the directory tree upwards to find a .vimdir file and loads the settings from there. So I keep files like this around:
setlocal noexpandtab shiftwidth=8 tabstop=8
The alternative is to add a snippet to the file itself but not every maintainer is happy with that.
/* vim: set noexpandtab tabstop=8 shiftwidth=8: */

Friday, May 4, 2012

Copy/paste of code is not ok

Much too often, I see patches that add code copied from other sections of the same repository. The usual excuse is that, well, we know that block of code works, it's easy to copy and we immediately get the result we need.

This is rather short-sighted. Whenever code is copied, the two instances will go and live separate lives. Code is never static, over time that copy becomes a partial reimplementation of the original.

There are a few conditions when copy-paste is acceptable:

  • You can guarantee that the original code does not have any bugs and thus the copy does not have any bugs, now or in the future. Otehr

  • You can guarantee that anyone making changes to this code in the future is aware of the copy and the original and their respective contexts.

  • You can guarantee that the context of the original and the copy never changes in a different manner.

  • You are happy to reimburse testers and developers for the time wasted tracking down bugs caused by ignoring any of the three above.
If the above are true, copying code is ok. And you probably get some price for having found an impossible piece of code. In all other cases, write a helper function and share the code. If the helper function is to unwieldy, maybe it's time to think about the design and refactor some things.

Thursday, May 3, 2012

What's new in Synaptics 1.6.0

xf86-input-synaptics 1.6.0 was released today and it packs a bunch of changes. This post outlines some of these changes and future directions for the drive.

Clickpad support

Clickpads are touchpads without physical buttons. Instead, the user has to press the whole pad down to trigger a click. These pads are becoming increasingly common in laptops but need a bunch of software workarounds to make them useful with traditional interaction methods. I've got a post dedicated to Clickpad support in the synaptics driver, so best to read that for more details.

The important bit about clickpad support is that it's only available on multi-touch capable X servers. If you're running an older server, you won't see clickpad-y goodness. Sorry.

Smooth scrolling

Smooth scrolling is now supported by the driver as well. On XI 2.1-compatible servers (read: server 1.12 [1]), the driver will submit scrolling valuators instead of scroll events. This makes for a smoother scrolling experience on clients that support it.

We're in a transition period where clients start to enable smooth scrolling but many other clients don't support it yet. For now, we left coasting enabled by default in the driver to provide a consistent experience. This will likely be disabled once smooth scrolling support and client-side coasting becomes more prevalent.

Meanwhile, clients can disable it by setting the "Synaptics Coasting Speed" property to 0, 0.

Bugs, bugs, bugs

Luckily I was able to spend quite some time on synaptics over the last weeks to fix various bugs and I think this is the most solid release of synaptics in quite a while. A few bugs that you may have seen that have now been fixed are:
  • touchpad unresponsive after suspend  #49161
  • weird cursor jumps while dragging #48777
  • broken double-tap #31854
  • slow or too fast scrolling on pre-1.12 servers #46617
and a few more.

Future of synaptics

What becomes more and more obvious is that the driver is on the edge of being unmaintainable. Pushing new features into the driver always always means that some other feature is broken and sometimes we don't notice for months. Having around 75 options in the driver doesn't help here, testing all combinations is impossible.

I can't claim that I have big plans because I tend to get pre-empted by misc things all the time but the rough plan is to keep a 1.6.x series while re-doing some of the internals of the driver for the 1.7 release. Expect the git master branch to see a few big changes though.


[1] Ubuntu 12.04 ships a 1.11 with the 1.12 input stack, so the above applies to that server too.

Tuesday, May 1, 2012

Drive-by learning through patch reviews

This came up on the linuxwacom-devel list today and I think it warrants further spread through this post.

Different projects have different patch review requirements but the biggest difference is pre-review and post-review. That is, do patches get reviewed before or after they hit the repositories. Not too long ago, the X server employed a post-review process. Everyone with access pushed and bugs would get discovered by those reading commit logs. Patches that ended up on the list were mainly from those that didn't have commit access. Beginning with server 1.8 we introduced a hard review requirement and every patch to make it into the repos ended up on the list, so we switched from post-review to pre-review.

Aside from enforcing that someone gives the formal ACK for a patch, a side-effects is to allow for a passive "drive-by" learning of the code base. Rather than having to explicitly look up commit logs, patches are delivered into one's inbox, outlining where the development currently happens, what it is about and - perhaps most importantly - issues that may have been caused bugs in rejected patches. Ideally that is then archived, so you can link to that discussion later.

The example I linked to from above is automake's INCLUDES versus AM_CPPFLAGS. I wouldn't have know about them if it wasn't for skimming through Gaetan's patches to various X.Org-related projects. That again allowed me to contribute a (in this case admittedly minor) patch to another project. And since that patch ended up on another list, the knowledge can spread on.

Next time when you think of committing directly to a repo, consider sending the patches out. Not just for review, but also to make learning easier for others.

Clickpad kernel bugs in 3.3.x and 3.4

In my last post I talked about Clickpad support in the synaptics driver. Unfortunately, right around the same time a kernel bug surfaced that messed with the events from some clickpads (the Synaptics-branded ones, Apple and others were unaffected). Affected was the 3.3.x series and obviously 3.4. Thanks to +Benjamin Herrenschmidt, that bug was fixed and it is now available in a variety of kernels. The upstream commit is in Linus tree now, still making it into stable (not in 3.3.4 yet). Fedora users can grab Fedora 17 or Fedora 16 kernels that carry this patch.

Thursday, April 19, 2012

ClickPad support in the synaptics driver

[update 20 Apr: clarify middle finger emulation]
One of the features added to the upcoming xf86-input-synaptics version 1.6 is support for clickpad-style devices. This post outlines what clickpads are, how they are supported and how you go about enabling the new features.

What are clickpads

The name ClickPad comes from the Synaptics product of the same name. It describes a touchpad that does not have physical buttons. Instead, the whole touchpad works as a button. Devices like this have been around for a while, most notably the touchpads found in Apple laptops and the Dell Mini 10 series. The challenges for us were to handle the data streams correctly. Most of this work was done by Chase Douglas.

Clickpads give us the position for each finger, and a button 1 event when the pad is pressed. The design of the hardware however means that whenever you click the pad, you always add one more finger to the mix. Decoupling that finger from the ones that actually matter is the challenge. And integrating it with all the other whacky features that the synaptics driver currently has.

Clickpad Support

Clickpad support requires server 1.12 [0]. It heavily relies on multitouch support.
Central to the new feature is the "ClickPad" option and property. It is enabled if the kernel sets the INPUT_PROP_BUTTONPAD property on the device, otherwise you can enable it in an xorg.conf.d snippet or at runtime with either xinput or synclient:
xinput set-prop "device name" "Synaptics ClickPad" 1
synclient ClickPad=1
This toggles a few driver behaviours to make the clickpad much more usable. Most notably, you can use one finger to press the button and another finger to move the cursor around.

Word of warning here: if you enable clickpad support manually at runtime, you will also have to manually disable traditional middle mouse button emulation (synclient EmulateMidButtonTime=0). For autoconfigured devices or xorg.conf.d configured devices this is done automatically.

The second big feature for clickpads is support for software buttons. Since the device only gives us left button clicks, we expose an option to allow for right and middle button clicks. By default, we ship this xorg.conf.d snippet:
Section "InputClass"
        Identifier "Default clickpad buttons"
        MatchDriver "synaptics"
        Option "SoftButtonAreas" "50% 0 82% 0 0 0 0 0"
EndSection
The order of soft button edges is left, right, top, bottom for the right button, then left, right, top, bottom for the middle button. So the above snippet sets the right half of the bottom 18% to be a right button, with no middle button present. A coordinate of 0 means "stretch to edge of touchpad". [1]

Traditional middle mouse button emulation is disabled by default and we don't handle it for clickpads. Traditional middle mouse button emulation refers to the method of generating a button 2 press when both button 1 and button 3 are pressed simultaneously. Mostly because, well, I'm not sure how you would even trigger middle mouse button emulation for clickpads given that all buttons except the first are already emulated anyway. You can still emulate middle mouse button emulations through clickfingers (see below), tapping, or the soft button areas as described above.

Tapping works as on any other touchpad.

ClickFinger functionality works, albeit slightly different. In the default ClickFinger setting, if you have 2 fingers on the touchpad and you press the left button, you will see a ClickFinger2 action performed (right button by default). On clickpads we guess the number of fingers by proximity (since you need one finger to actually press the button). Fingers closer together than 30% of the touchpad's size [2] will count towards ClickFinger actions, others will be skipped. So in the default use-case, where you have two fingers down on the touchpad in the middle and you use a third to click the button, you will still get a ClickFinger2 action. Likewise, if you press with two fingers down, you will also get a ClickFinger2 action.

All other functions are not affected by clickpad support and behave as before.

  • [0] Ubuntu 12.04 will ship a 1.11/1.12 mix, that will work as well
  • [1] "edge of touchpads" should be 100% but it isn't due to a years-old mishandling of the coordinate ranges. So 0 is better to use here.
  • [2] All touchpads lie about their size, so we just hardcode 30% since anything we infer is going to be wrong anyways

Tuesday, April 10, 2012

New layout template

Short summary: layout of the blog changed. Long summary: One of the issues I ran into repeatedly was that any code postings would overrun the rather narrow column width. Now I've done the lazy thing and applied a new standard template that appears to be wider. So in the future, code snippets are hopefully easier to read.

Sunday, April 8, 2012

Chrome's multitask mode - an April fool's 2 years too late?

Ok, I was away on holidays for the last week so I missed everything. Thanks to Simon Thum, I found the Chrome team's April fools joke: Chrome's Multitask Mode. Allegedly a mode for Chrome that allows to use multiple pointers simultaneously.

Great, except that it's just an April fool's joke, they didn't actually implement it. Even though, well, it's been a standard feature in the X.Org X server since September 2009. Yep, right. Fedora 12 had it, for example, and every version since. It's ubiquitous on the GNU/Linux desktop. GTK3 has support for it.

Being able to use two hands is quite useful, research papers on the benefits go back well over 20 years. Use cases are generally as dominant hand/non-dominant hand methods (e.g. an artist holding the palette with one hand while painting with the other one) or as equal bimanual interaction (aligning a rectangular mask around an object). All this isn't new, and as I said above this is all a standard feature in X since 2009. You really just [1] need to add it in your application. So Chrome's April fool's joke is pretty much a joke about not implementing a feature. Which, well, uhm... ok. Haha.

The video is a bit outlandish, with a guy playing golf and a shooter simultaneously. However, listening to just the audio on the video largely makes sense (except the switching off your computer part). Using two mice became natural for me quite quickly. I even conducted a user-study about users using two browser windows simultaneously to research and gather information about a specific topic. Yep, they could do it, including typing up the results in a decidedly single-user Abiword without major conflicts.

Now I'm waiting for next year's joke, maybe it's about how we drive cars with, wait for it, steering wheels.

[1] I say "just", but implementing it is really hard. It opens a can of worms about assumptions in user interface paradigms that aren't quite that simple to deal with. From a technical point of view, it's a "just" though...

Thursday, March 15, 2012

ssh and shared connections

My job requires me to ssh into various boxes often, with several connections to the target host. Some people use screen on the target host but I work better if I have multiple terminal windows. But re-connecting to the same host can be annoying, a connection does take time and it should be instantaneous. Luckily, SSHv2 allows to share a connection, making reconnection a lot faster. Also, if you have password-authenticated connection instead of a key-based one you won't have to type the password for each new connection (but really, you should be using keys anyway). The few lines you'll need in your $HOME/.ssh/config:
ControlMaster auto
ControlPath ~/.ssh/sockets/ssh_mux_%h_%p_%r
ControlPersist 60
All three are extensively described in the ssh_config(5) man page, but here's a summary:
  • ControlMaster auto will create a new ssh connection when no matching socket exists. Otherwise, it will just use the existing connection.
  • ControlPath is simply the path to the control socket, with %h, %p and %r being replaced with target host, port and username to keep the socket name unique. Having this in a user-specific location instead of /tmp is generally a good idea.
  • ControlPersist defines how long the master connection should be kept open after exit. You can specify "yes" for indefinite or a number of idle seconds. If you reconnect within that idle time, it will again re-use the existing connection. Note that if you do not have ControlPersist and you quit the master connection, you will terminate all other connections too! ControlPersist was added in OpenSSH 5.6.
You can provide these options globally or inside a Host section of the config, depending on your needs. A few final notes: since you essentially only have one connection now, you can only forward one X11 display, one ssh agent, etc. at a time. If you need a separate connection for a otherwise shared host, use "ssh -S none". Also, if you're doing heavy data transfer on laggy connections you're probably better off having separate connections.

Tuesday, February 7, 2012

Multitouch in X - Multitouch-touchpads

This post is part of a series on multi-touch support in the X.Org X server.
  1. Multitouch in X - Getting events
  2. Multitouch in X - Pointer emulation
  3. Multitouch in X - Touch grab handling
  4. Multitouch in X - Multitouch-touchpads
In this post, I'll outline the handling of touch events for multitouch-capable touchpads. Multi-touch touchpads that are supported are those that provide position information for more than one finger. The current version of the synaptics X driver does some tricks to pretend two-finger interaction on single-finger touchpads - such devices are not applicable here.

Touchpads are primarily pointer devices and any multi-touch interaction is usually a gesture. In the protocol, such devices are of the type XIDependentDevice and the server does adjust touch event delivery. I've already hinted at this here but this time I'll give a more detailed explanation.

Touch event delivery

Unlike for direct touch devices such as touchscreens, dependent devices have a different picking mechanism in the server. We assume that all gestures are semantically associated with the cursor position. For example, for scrolling, you would move the cursor on top of the window to be scrolled, then you would start scrolling. The server thus adjusts event delivery accordingly. Whereas for direct touch devices the touch events are delivered to whichever window is at the position of the touch, touch events from dependent devices are always delivered to the window underneath the pointer (grab semantics are adjusted to follow the same rules). So if you start a gesture in the top-left corner of the touchpad, the window underneath the cursor gets the events with the top-left coordinates. Note that the event and root coordinates always reflect the pointer position.

The average multi-touch touchpad has two modes of operation: single-finger operation where the purpose is to move the visible cursor and multi-finger operation which is usually interpreted into a gesture on the client. These two modes are important, as they too affect event delivery. The protocol specifies that any interaction with the device that serves to move the visible cursor only should not generate touch events, and that touch events will start once that interaction becomes a true multi-touch interaction. This leaves the drivers a little bit of leeway, but the current implementation in the synaptics driver is the following:
  1. A user places one finger on the touchpad and moves. The client will receive regular pointer events.
  2. The user places a second finger on the touchpad. The client will now receive a TouchBegin event for the first and the second touch, at their respective current positions in device coordinate range.
  3. Movement of either finger now will generate touch events, but no pointer events.
  4. Any other fingers will generate touch events only.
  5. When one of two remaining fingers on the touchpoint ceases the touch, a TouchEnd is sent for both the terminating touch and the remaining touch. The remaining finger will revert to sending pointer events.

Legacy in-driver gestures

As you are likely aware, the synaptics driver currently supports several pseudo gestures such as tap-to-click or two-finger scrolling. These gestures are interpreted in the driver, thus the server and client never see the raw data for them.

With proper multi-touch support these gestures are now somewhat misplaced. On the one hand, we want the clients to interpret multitouch, on the other hand we want the gestures to be handled in the same manner in all applications. (Coincidentally, this is also a problem that we need to solve for Wayland).

We toyed with ideas of marking emulated events so clients can filter but since we do need to be compatible to the core and XI 1.x behaviours, we only found one solution: any in-driver gestures that alter event deliver must not generate touch events. Thus, if the driver is set to handle two-finger scrolling, the clients will only see the pointer events and scroll events, they will not see touch events from two-fingers. To get two-finger scrolling handled by the client, the in-driver gesture must be disabled. The obvious side-effect of that is that you then cannot scroll in applications that don't support the gestures. Oh well, it's the price we have to pay for having integrated gesture support in the wrong place.

Friday, January 20, 2012

XKB breaking grabs - CVE-2012-0064

Given that there is a copious amount of misinformation being spread, here is a summary of CVE-2012-0064, straight from a horse's mouth.

Outline of the issue

The bug allows users to work around screen locking (e.g. gnome-screensaver) by hitting Control+Alt+keypad multiply or Control+Alt+keypad divide. This terminates the input grab the screensaver has and thus allows a user to interact with the desktop, skipping the password entry.

Affected versions

Affected is anyone running X server 1.11 or later (or release candidates thereof). So if "Xorg -version" shows something else on your box, stop worrying. I doubt any distribution would have back-ported the patches.

In Fedora/Red Hat land - the only distributions affected are Fedora 16 and current Fedora Rawhide. Both have been fixed, the F16 update is avaialble here. Note that the update is to xkeyboard-config, not to the server itself.

Fedora 15 is not affected. RHEL 4, 5, 6 and thus CentOS 4, 5, 6 and other derivatives are not affected. I believe that most other distributions have now pushed out updates as well, if you want to link to the respective updates, please do so in the comments.

Sergey has also pushed out xkeyboard-config 2.5 today with the fix included.

History of the feature

The X protocol does not allow the server to break grabs. Once a client has a grab, the server must wait for that client to release the grab, terminate, or the grab window to become unviewable. This is an issue when debugging applications - if your client has a keyboard grab, you cannot use the debugger since all key events will go to the client being debugged. To avoid this issue, the X server has had two combinations to break grabs: Control+Alt+Keypad multiply and Control+Alt+Keypad divide. They forced grab termination inside the X server and although against the protocol it made debugging possible. The option required explicit enabling in the xorg.conf.

These options were removed in server 1.4 and disabled since. Which made debugging hard, so last year we merged a patch to bring them back, together with some other features. They are triggered by XKB actions (as they used to be). The plan was to remove the XKB actions from the default keymap so that the action is available on request but not enabled by default. This is where a miscommunication happened, the removal from the default keymap never happened. So server 1.11 and vanilla xkeyboard-config ship with both the actions available and present in the current keymap. As a result, any user can break a grab from any application and thus get around screen locking.

Outline of the fix

To shoot yourself in the foot, you need two items: a gun and a trigger. We have removed the trigger. The fix we've now pushed into xkeyboard-config removes the actions from the default keymap and into an XKB option instead. So the fix does not remove the gun, but it requires the user to screw the trigger in themselves before trying to hurt themselves. In a default configuration, it is thus no longer possible to break the grab of your screensaver.

To re-enable grab debugging, run setxkbmap with "-option grab:break_actions" or enable "Allow breaking grabs with keyboard actions (warning: security risk)" in the "Miscellaneous compatibility options" in your keyboard layout configuration tool of choice.

Tuesday, January 3, 2012

Multitouch in X - Touch grab handling

This post is part of a series on multi-touch support in the X.Org X server.
  1. Multitouch in X - Getting events
  2. Multitouch in X - Pointer emulation
  3. Multitouch in X - Touch grab handling
  4. Multitouch in X - Multitouch-touchpads
In this post, I'll outline how grabs on touch events work. This post assumes basic knowledge of the XI2 Xlib interfaces.

Passive grabs

The libXi interface has one new passive grab call: XIGrabTouchBegin, which works pretty much like the existing passive grab APIs. As with event selection, you must set all three event masks XI_TouchBegin, XI_TouchUpdate and XI_TouchEnd or a BadValue error occurs. Once a passive grab activates in response to a touch, the client must choose to either accept or reject a touch. Details on that below.

Grabs activate on a TouchBegin event and due to the nature of multitouch, multiple touch grabs may be active at any time - some of them for different clients.

Active grabs

Active grabs do not have a new call, they are handled through the event masks of the existing XIGrabDevice(3) call. If a client has an active touch grab on the device, it is automatically the owner of the touch sequence (ownership is described below). If a client has an active pointer or keyboard grab on the device, it is the owner of the touch sequence for pointer emulated touch events only. Other touch events are unaffected by the grab and are processed normally.

Acceptance and rejection

Pointer grabs provide exclusive access to the device, but to some degree a client can opt to replay the event it received on the next client. We expect that touch sequences will often trigger gesture recognition, and a client may realise after a few events that it doesn't actually want that touch sequence. So we expanded the replay semantics. clients with a touch grab must choose to either accept or reject a touch.

Accepting a touch signals to the server that the touch sequence is meant for this client and no-one else. The server then exclusively delivers to that client until the terminating TouchEnd.

Rejecting a touch sequence signals that the touch sequence is not meant for this client. Once a client rejects a touch sequence, the server sends the TouchEnd event to that client (if the touch is still active) and replays the full touch sequence [1] on the next grab or window. We use the term owner of a touch sequence to talk about the current recipient.

The order of events for two clients Cg and Cw, with Cg having a grab and Cw having a regular touch event selection on a window, is thus:
TouchBegin to Cg    → 
TouchUpdate to Cg   → 
TouchUpdate to Cg   → 
                    ← Cg rejects touch
                    ← Cw becomes new owner
TouchEnd+ to Cg     →
TouchBegin* to Cw   → 
TouchUpdate* to Cw  → 
TouchUpdate* to Cw  → 
#### physical touch ends #### 
TouchEnd to Cw      →
Events with + mark an event created by the server, * mark events replayed by the server

For nested grabs, this sequence simply repeats for each client until either a grabbing client accepts the touch or the client with the event selection becomes the owner.

In the above case, the touch ended after Cg rejected the touch. If the touch ends before the current owner accepted or rejected it, the owner gets the TouchEnd event and the touch is left handing until the owner accepts or rejects it. If accepted, that's it. If rejected, the new owner gets the full sequence in one go, including the TouchEnd event. The sequence is thus:
TouchBegin to Cg    → 
TouchUpdate to Cg   → 
TouchUpdate to Cg   → 
#### physical touch ends #### 
TouchEnd to Cg      →
                    ← Cg rejects touch
                    ← Cw becomes new owner
TouchBegin* to Cw   → 
TouchUpdate* to Cw  → 
TouchUpdate* to Cw  → 
TouchEnd* to Cw     →

Touch ownership handling

One additional event type that XI 2.2 introduces is the XI_TouchOwnership event. Clients selecting for this event signal that they need to receive touch events before they're the owner of the touch sequence. This event type can be selected on both grabs and event selections.

First up: there are specific use-cases where you need this. If you don't fall into them, you're better off just skipping on ownership events, they make everything more complicated. And whether you need ownership events depends not only on you, but also the stack you're running under. On normal touch event selection, touch events are only delivered to the current owner of the touch. With multiple grabs, the delivery is sequential and delivery of touch events may be delayed.

Clients selecting for touch ownership events get the events as they occur, even if they are not the current owner. The XI_TouchOwnership event is delivered if and when they become the current owner. The last part is important: if you select for ownership events, you may receive touch events but you may not become the owner of that sequence. So while you can start reacting to that sequence, anything your app does must be undo-able in case the e.g. window manager claims the touch sequence.

If we look at the same sequence as above with two clients selecting for ownership, the sequence looks like this:
TouchBegin to Cg     → 
TouchBegin to Cw     → 
TouchOwnership to Cg →
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
                     ← Cg rejects touch
                     ← Cw becomes new owner
TouchEnd+ to Cg      →
TouchOwnership to Cw →
#### physical touch ends #### 
TouchEnd to Cw      →
Note: TouchOwnership events do not correspond to any physical event, they are always generated by the server

If a touch ends before the owner accepts, the current owner gets the TouchEnd, all others get a TouchUpdate event instead. That TouchUpdate has a flag XITouchPendingEnd set, signalling that no more actual events will arrive from this touch but the touch is still waiting for owner acceptance.
TouchBegin to Cg     → 
TouchBegin to Cw     → 
TouchOwnership to Cg →
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
#### physical touch ends #### 
TouchEnd to Cg       →
TouchUpdate to Cw    →  (XITouchPendingEnd flag set)
                     ← Cg rejects touch
                     ← Cw becomes new owner
TouchOwnership to Cw →
TouchEnd to Cw       →
In both cases, we dealt with a rejecting owner. For an accepting owner, the sequences look like this:
TouchBegin to Cg     → 
TouchBegin to Cw     → 
TouchOwnership to Cg →
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
                     ← Cg accepts touch
TouchEnd+ to Cw      →
TouchUpdate to Cg    → 
#### physical touch ends #### 
TouchEnd to Cg      →
or
TouchBegin to Cg     → 
TouchBegin to Cw     → 
TouchOwnership to Cg →
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
TouchUpdate to Cg    → 
TouchUpdate to Cw    → 
#### physical touch ends #### 
TouchEnd to Cg       →
TouchUpdate to Cw    →  (XITouchPendingEnd flag set)
                     ← Cg accepts touch
TouchEnd* to Cw      →
In the case of multiple grabs, the same strategy applies in order of grab activation. Ownership events may be selected by some clients but not others. In that case, each client is treated as requested, so the event sequence the server deals with may actually look like this:
TouchBegin to C1     → 
TouchBegin to C3     → 
TouchOwnership to C1 →
TouchUpdate to C1    → 
TouchUpdate to C3    → 
TouchUpdate to C1    → 
TouchUpdate to C3    → 
                     ← C1 rejects touch
                     ← C2 becomes new owner
TouchEnd+ to C1      →
TouchBegin* to C2    → 
TouchUpdate* to C2   → 
TouchUpdate* to C2   → 
                     ← C2 rejects touch
                     ← C3 becomes new owner
TouchEnd+ to C2      →
TouchOwnership to C3 →
#### physical touch ends #### 
TouchEnd to C3       →


[1] obviously we need to store these events so "full sequence" really means all events until the buffer was full