Monday, September 5, 2011

What's new in XI 2.1 - smooth scrolling

This post is the last part of a series about new changes in XI 2.1. See also:

The core protocol allows for up to 255 buttons but only two axes: x and y. When scroll wheels became ubiquitous, the solution was to send button events for the four scroll wheel directions. Even today, a logical button click 4 results in scroll down, 5, 6, and 7 result in scroll up, left, and right, respectively.

However, scrolling is more complex than that. Velocity information and subpixel scrolling are handy to have on scroll axes. With XI 2.1, servers may now mark some axes as axes providing scrolling information. These axes may represent real axes on the device (e.g. a mouse wheel) or be virtual axes (e.g. the edge scrolling on synaptics devices). For each scrolling axis, XIQueryDevice(3) provides an XIScrollClassInfo.

XIDeviceInfo *info;

int ndevices;

info = XIQueryDevice(display, XIAllDevices, &ndevices);
for (i = 0; i < ndevices; i++) {
XIScrollClassInfo *scroll;
for (j = 0; j < info[i].num_classes; j++) {
scroll = info[i].classes[j];
if (scroll->type != XIScrollClass)
continue; /* Not interested in others */

printf("Valuator %d is a valuator for ", scroll->number);
switch(scroll->scroll_type) {
case XIScrollTypeVertical: printf("vertical"); break;
case XIScrollTypeHorizontal: printf("horizontal"); break;
printf(" scrolling\n");
printf("Increment of %d is one scroll event\n", scroll->increment);
if (scroll->flags & XIScrollFlagsNoEmulation)
printf("No emulated button events for this axis\n");
if (scroll->flags & XIScrollFlagsPreferred)
printf("No emulated button events for this axis\n");


The above code example runs through each class on each device, printing only the scrolling information bit. Note that for each XIScrollClassInfo, a XIValuatorClassInfo with the same axis number must be present. That valuator info then contains the actual details of the device.

Let's say a XIScrollClassInfo is available for valuator 3. This simply means that any coordinates on valuator 3 should be interpreted as scrolling events. The increment defines what delta the driver considers to be one scroll event. For an increment of +5, each delta of 5 should be regarded as one scroll unit down. For an increment of -3, each delta of 3 should be regarded as one scroll unit up (i.e. inverted).

Note that a driver may change this at run-time. As always, a client should listen to XIDeviceChangedEvents.

Scroll button emulation

If a driver provides smooth-scrolling valuators, the server provides two-way emulation of scroll events to remain backwards-compatible to existing X clients. For any button 4-7 event from the device, the server emulates a scroll event with the right increment on the respective axis. (Note that drivers are discouraged to use button events for scrolling if they support smooth scrolling). For any scroll event on a scroll axis axis, the server emulates the matching button 4-7 events. If the scroll event has an abs(value) less than the increment on this axis, the server only emulates once the cumulative value of several events hits a multiple of the increment (positive or negative).

For clients, this means they get two events for each hardware scrolling event. The one that was emulated by the server has the XIPointerEmulated flag set and can be safely ignored by the client. The same flag is set for XIRawEvents as well.

A device may have multiple scoll axes of the same type. If so, one of them is marked as XIScrollFlagsPreferred and any legacy button events will be emulated on that axis. This bit is informative only, I don't think clients need to care about it. Plus, we're aiming for drivers to do smooth scrolling only, so hopefully we don't need to do button → scroll valuator emulation.

Potential side-effects

  • Pre-2.1 clients may now get valuator information from a scroll axis that they don't identify as scroll axis. For 1.x clients, this is not much of an issue. 1.x clients never knew what an axis represented anyway and needed a UI for users to select what an axis meant. 2.0 clients have axis labels and should act on those. So I think this is mostly a non-issue.

  • Clients that implement smooth-scrolling may act on information less than one increment. Permanent up/down movement of values less than one increment may then cause smooth scrolling events but no legacy events. I don't think this is an issue, users usually trigger the scrolling axis until they see the effect on-screen. The worst-case scenario here is that scrolling in one client requires more finger movement than in another client.

  • Clients cannot yet change the increment. I don't know if this will be an issue in the real world given that the actual interpretation is in the client anyway.

1 comment:

Anonymous said...

How is floating point loss of precision dealt with once you have scrolled very very much? At some point, adding 1.0 to to the accumulated value will just stop having an effect ;) Do we assume this will never happen?