The
use of Xlib for XInput has changed a bit, but not that much. This
mini-tutorial shows you how to get the list of devices and how to
register for events. For many applications, this is pretty much all you
need.
Let's get started. The two most important changes to previous
versions of XInput are:
- applications have to announce support for XI 2 (MPX).
- the list of input devices contains multiple core devices.
Announcing support for XI 2 is easy, just issue an XQueryInputVersion()
before
you use any other XI requests. The reason why this is necessary is
simple: the behaviour of XInput has changed, and the server must know
which clients suports XI 1.x, and which clients support XI 2. This way,
it can change some replies accordingly to make XI 1.x clients not
break. In practice, your code should look something like this:
int main (int argc, char* * argv) {
Display * dpy;
dpy = XOpenDisplay(NULL);
XQueryInputVersion(dpy, XI_2_Major, XI_2_Minor);
/* do stuff */
XCloseDisplay(dpy);
return 0;
}
Next, we want to know which devices are actually available. XListInputDevices()
does this for us. It returns an array with XDeviceInfo structs,
each of which specifies the name, id and the capabilities of the
devices. In addition, the XDeviceInfo also contains a "use"
field, which is of some importance. The use field can have one of 5
values:
- IsXPointer for any master pointer (i.e. cursor).
- IsXKeyboard for any master keyboard (i.e. keyboard
focus)
- IsXExtensionPointer for any physical pointer device.
- IsXExtensionKeyboard for any physical keyboard
device.
- IsXExtensionDevice for any physical device that is
neither pointer nor keyboard (this is fairly uncommon).
As a rule, you never register for events on the physical
devices.
Really. The cursor and the keyboard focus are the user's input points,
so focus on them. Only special applications like the GIMP or
configuration apps have to worry about the others. Again some code:
XDeviceInfo* info;
int ndevices;
int i;
info = XListInputDevices(dpy, &ndevices);
for (i = 0; i < ndevices; i++) {
XDeviceInfo* current = &info[i];
printf("Found device: %s (%d)\n", current->name, current->id);
switch(current->use) {
case IsXExtensionPointer:
case IsXExtensionKeyboard:
case IsXExtensionDevice:
/* foo() */
break;
case IsXPointer:
case IsXKeyboard:
/* bar() */
break;
}
}
XFreeDeviceList(info);
Try calling XListInputDevices before and after a call
to XQueryInputVersion().You'll notice that the device list is
significantly shorter if the server thinks you do not support XI 2.
Ok. Now we know the devices, let's start using them. Writing
most
multi-device apps is incredibly easy: Open the device, register for
events, wait for events to roll in.
XDevice *pointer1;
XDevice *pointer2;
XEvent ev;
pointer1 = XOpenDevice(id1); /* get the ID from ListInputDevices */
pointer2 = XOpenDevice(id2);
/* register for events, see below */
while(1) {
XNextEvent(dpy, &ev);
printf("Event type %d received\n", ev.type);
}
Now, I left out the registering for event for now because this
is
historically painful. You see, the X Protocol specification requires
that extension event codes are dynamically assigned at runtime. That
is, while the core protocol's type ButtonPress is always the
same, the XI type DeviceButtonPress
can change from server to server (and even when you restart the
server). So we need to get the type. In addition, because we only want
to register for certain device's events, we need to get the EventClass.
It's easiest to think of the EventClass as the mask to register for a
specific event on a specific device. Xlib provides some macros for us
here:
int type_bpress;
XEventClass cls[2];
DeviceButtonPress(pointer1, type_bpress, cls[0]);
DeviceButtonPress(pointer2, type_bpress, cls[1]);
Now, type_bpress is set to the event type for DeviceButtonPress
events. We can pass the same variable in both times since the type
remains static for the lifetime of the server. The class however is
device-specific. (Oh - and by the way, the macros re-use the arguments
twice, so don't try to pass in something like (evclass++), it can be
pain to debug...)
Now we can complete the above example:
XDevice *pointer1;
XDevice *pointer2;
XEvent ev;
int type_bpress;
XEventClass cls[2];
pointer1 = XOpenDevice(id1); /* get the ID from ListInputDevices */
pointer2 = XOpenDevice(id2);
/* register for events */
DeviceButtonPress(pointer1, type_bpress, cls[0]);
DeviceButtonPress(pointer2, type_bpress, cls[1]);
XSelectExtensionEvent(dpy, myWindow, cls, 2);
while(1) {
XNextEvent(dpy, &ev);
printf("Event type %d received\n", ev.type);
if (ev.type == type_bpress)
{
XDeviceButtonEvent* bev = (XDeviceButtonEvent*)&ev;
printf("Press received by device %d\n", bev->deviceid);
}
}
Here's another important change in XI 2. Events always contain
coordinates in the screen's coordinate system, only valuators are
device-specific. So for most apps, you don't have to worry about
coordinate conversion, just use x_root, y_root or x/y of the
XDeviceButtonEvent structure.
That's it for now. There's a number of other macros such as DeviceMotionNotify,
all to be found in XInput.h. All the functions described here have man
pages that explain the parameters better than I can.
|