Skip to main content

Basics

Devices

Wacom tablets generally create one or more of the following five types of X Input devices:

  • Stylus devices that represent features from the tip side of Wacom styli, which include side switches and wheel if the stylus supports them.
  • Eraser devices that represent features from the revert side of the styli.
  • Cursor devices that represent features from Wacom Len Cursor or 4D Mouse.
  • Touch Devices that represent touches from a single finger or multiple fingers.
  • Pad Devices that cover the features on the tablets, such as Expresskeys, touch rings, touch keys, etc.

Running xinput list with an Intuos Pro Medium attached to the system would see the following Wacom devices:

⎡ Virtual core pointer                    id=2 [master pointer (3)]  

⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]

⎜ ↳ USB OPTICAL MOUSE id=10 [slave pointer (2)]

⎜ ↳ Wacom Intuos Pro M Finger touch id=15 [slave pointer (2)]

⎜ ↳ Wacom Intuos Pro M Pen stylus id=16 [slave pointer (2)]

⎜ ↳ Wacom Intuos Pro M Pen eraser id=17 [slave pointer (2)]

⎜ ↳ Wacom Intuos Pro M Pen cursor id=18 [slave pointer (2)]

⎜ ↳ Wacom Intuos Pro M Pad pad id=19 [slave pointer (2)]

Devices like Wacom tablets are defined as Extension Input Devices in the X11 server. X Input has two major releases, X Input 1 and X Input 2 (XI2). X Input 2 was introduced in 2009, which supports Multi-Touch events. The X Input Extension 2.x protocol was finalized in 2012. It is quite stable and hasn't been changed much in the past 8 years. Since most of Wacom devices are multitouch-enabled digitizers, we recommend applications are designed and implemented based on XI2. If your application was developed before XI 2, please upgrade it to support XI2. This document only discuss XI2 events.

XI2 Data Types

BUTTONMASK
A binary mask defined as (1 << button number).
A SETofBUTTONMASK is a binary OR of zero or more BUTTONMASK.

DEVICE { DEVICEID, AllDevices, AllMasterDevices }
A DEVICE specifies either a DEVICEID or AllDevices or
AllMasterDevices.

DEVICEID { CARD16 }
A DEVICEID is a numerical ID for a device currently available in the
server. The server may re-use a device ID after a device's removal.
The device IDs 0 and 1 are reserved.
AllDevices ........ 0
AllMasterDevices .. 1

DEVICEUSE { MasterPointer, MasterKeyboard, SlavePointer,
SlaveKeyboard, FloatingSlave }
A DEVICEUSE field specifies the current use of a device in the MD/SD
device hierarchy. See Section "The Master/Slave device hierarchy"
for more information.

EVTYPEMASK
An EVTYPEMASK is a binary mask defined as (1 << event type).
A SETofEVTYPEMASK is a binary OR of zero or more EVTYPEMASK.

XI2 Event Types

There are 27 types of XI2 Event. Each event type has an event mask to set on/off for client to listen to. The block of code below shows how each event is set through its mask for different XI2 minor versions. Refer to XI2 Initialization for the original description.

 /* Select XI events */
m = &mask[0];
m->deviceid = (deviceid == -1) ? XIAllDevices : deviceid;
m->mask_len = XIMaskLen(XI_LASTEVENT);
m->mask = calloc(m->mask_len, sizeof(char));
XISetMask(m->mask, XI_ButtonPress);
XISetMask(m->mask, XI_ButtonRelease);
XISetMask(m->mask, XI_KeyPress);
XISetMask(m->mask, XI_KeyRelease);
XISetMask(m->mask, XI_Motion);
XISetMask(m->mask, XI_DeviceChanged);
XISetMask(m->mask, XI_Enter);
XISetMask(m->mask, XI_Leave);
XISetMask(m->mask, XI_FocusIn);
XISetMask(m->mask, XI_FocusOut);
#if HAVE_XI22
XISetMask(m->mask, XI_TouchBegin);
XISetMask(m->mask, XI_TouchUpdate);
XISetMask(m->mask, XI_TouchEnd);
#endif
if (m->deviceid == XIAllDevices)
XISetMask(m->mask, XI_HierarchyChanged);
XISetMask(m->mask, XI_PropertyEvent);

m = &mask[1];
m->deviceid = (deviceid == -1) ? XIAllMasterDevices : deviceid;
m->mask_len = XIMaskLen(XI_LASTEVENT);
m->mask = calloc(m->mask_len, sizeof(char));
XISetMask(m->mask, XI_RawKeyPress);
XISetMask(m->mask, XI_RawKeyRelease);
XISetMask(m->mask, XI_RawButtonPress);
XISetMask(m->mask, XI_RawButtonRelease);
XISetMask(m->mask, XI_RawMotion);
#if HAVE_XI22
XISetMask(m->mask, XI_RawTouchBegin);
XISetMask(m->mask, XI_RawTouchUpdate);
XISetMask(m->mask, XI_RawTouchEnd);
#endif

XISelectEvents(display, win, &mask[0], use_root ? 2 : 1);
if (!use_root) {
XISelectEvents(display, DefaultRootWindow(display), &mask[1], 1);
XMapWindow(display, win);
}
XSync(display, False);

free(mask[0].mask);
free(mask[1].mask);

if (!use_root) {
XEvent event;
XMaskEvent(display, ExposureMask, &event);
XSelectInput(display, win, 0);
}

After the targeted events are selected, the block of code below shows how to wait in a loop to get the XI Events:

while(1)
{
XEvent ev;
XGenericEventCookie *cookie = (XGenericEventCookie*)&ev.xcookie;
XNextEvent(display, (XEvent*)&ev);

if (XGetEventData(display, cookie) &&
cookie->type == GenericEvent &&
cookie->extension == xi_opcode) {
printf("EVENT type %d (%s)\n", cookie->evtype, type_to_name(cookie->evtype));
switch (cookie->evtype)
{
case XI_DeviceChanged:
print_devicechangedevent(display, cookie->data);
break;
case XI_HierarchyChanged:
print_hierarchychangedevent(cookie->data);
break;
case XI_RawKeyPress:
case XI_RawKeyRelease:
case XI_RawButtonPress:
case XI_RawButtonRelease:
case XI_RawMotion:
case XI_RawTouchBegin:
case XI_RawTouchUpdate:
case XI_RawTouchEnd:
print_rawevent(cookie->data);
break;
case XI_Enter:
case XI_Leave:
case XI_FocusIn:
case XI_FocusOut:
print_enterleave(cookie->data);
break;
case XI_PropertyEvent:
print_propertyevent(display, cookie->data);
break;
default:
print_deviceevent(cookie->data);
break;
}
}

Multi-Touch Events

Multi-Touch events life cycle

         TouchBegin - TouchUpdate - TouchUpdate - ... - TouchEnd;

Ownership of touch sequences

  • Once a grabbing client becomes the owner of a touch, it must either "accept" or "reject" the touch sequence using the XIAllowEvents request;

  • If a touch sequence is rejected, a TouchEnd event is sent to the rejecting client, and it will not receive any more events from this touch;

  • If the touch sequence physically ends while the owner of the touch sequence has not yet accepted or rejected ownership, the owner receives a TouchEnd event and all other clients receive a TouchUpdate event with the TouchPendingEnd flag set.

Touch device modes

DirectTouch and DependentTouch modes. Please refer to the protocol for the definition of the two modes and their examples. A device is identified as only one of the device modes at any given time, and the touch mode may change at any time.

Touch event delivery

For direct touch devices, the window set for event propagation is the set of windows from the root window to the topmost window lying at the co-ordinates of the touch.

For dependent devices, the window set for event propagation is the set of windows from the root window to the window that contains the device's pointer. A dependent device may only have one window set at a time, for all touches. Any future touch sequence will use the same window set. The window set is cleared when all touch sequences on the device end.

A window set is calculated on TouchBegin and remains constant until the end of the sequence. Modifications to the window hierarchy, new grabs, or changed event selection do not affect the window set.

Pointer emulation from Multi-Touch events

Touch sequences from direct touch devices may emulate pointer events. Only one touch sequence from a device may emulate pointer events at a time; which touch sequence emulates pointer events is implementation-dependent.

Pointer events are emulated as follows

A TouchBegin event generates a pointer motion event to the location of the touch with the same axis values of the touch event, followed by a button press event for button 1.

A TouchUpdate event generates a pointer motion event to the location of the touch and/or to update axis values of the pointer device. The button state as seen from the protocol includes button 1 set.

A TouchEnd event generates a pointer motion event to the location of the touch and/or to update the axis values if either have changed, followed by a button release event for button 1. The button state as seen from the protocol includes button 1 set.

If a touch sequence emulates pointer events and an emulated pointer event triggers the activation of a passive grab, the grabbing client becomes the owner of the touch sequence.

The touch sequence is considered to have been accepted if

  • The grab mode is asynchronous, or;
  • The grab mode is synchronous and the device is thawed as a result of AllowEvents with AsyncPointer or AsyncDevice.

Otherwise, if the button press is replayed by the client, the touch sequence is considered to be rejected.

Touch event delivery precedes pointer event delivery. A touch event emulating pointer events is delivered:

  • As a touch event to the top-most window of the current window set if a client has a touch grab on this window;
  • Otherwise, as a pointer event to the top-most window of the current window set if a client has a pointer grab on this window;
  • Otherwise, to the next child window in the window set until a grab has been found.

If no touch or pointer grab on any window is active and the last window in the window set has been reached, the event is delivered:

  • As a touch event to the window if a client has selected for touch events on this window;
  • Otherwise, as a pointer event to the window if a client has selected for pointer events;
  • Otherwise, to the next parent window in the window set until a selection has been found.

Emulated pointer events will have the PointerEmulated flag set. A touch event that emulates pointer events has the TouchEmulatingPointer flag set.

All the details are described in the protocol. Sample for retrieving Multi-Touch events can be found at Multi-Touch sample.