This is about my effort to write a Python version of xev, to snoop on XInput2 events from a touchpad.
Context
You have:
- a user input device (e.g. mouse, trackpad, touchscreen. Specifically a Wacom Bamboo Pen and Touch.)
- Linux (or OSX)
- X11 (not Wayland)
- Qt
- PyQt
- a Python app
You want to debug why your input device is not working with your app as you expect (specifically, not yielding QGestureEvent to my app.)
Architecture of the software stack
The above list is a stack, or pipe, of software.
The user input device has a microprocessor and firmware inside it, converting your physical actions to a stream of data, here to a USB connector. Here, the standard for the input stream is ‘HID’.
The Linux kernel has ‘kernel drivers’ that understand physical devices and interface them to ‘logical devices.’ A ‘device’ looks like a file, e.g. /dev/input/mouse0. Here ‘evdev’ is one name of the ‘kernel driver’ that understands input devices that follow the HID standard.
X11 has ‘input drivers’ that read a ‘logical device’ and interface to a queue of events, internal to the X Server. Here xf86-input-wacom is one name of the ‘input driver.’
Part of the X Server reads from the internal queue and interfaces to X Clients. That part of the XServer dispatches events to windows on your screen. Here we are mainly concerned with events of the XInput2 protocol.
Qt on Linux is an X Client.
And so forth, more later as I update this post.
A debugging strategy
Any part of the stack could be failing to transmit events as you expect.
One strategy is to read about and understand the code for the whole stack. Few people have the time to do that.
A common debugging strategy for such a pipeline is to snoop on the pipeline. You use tools that tap (tee off) the interfaces between parts of the pipe. The tools typically display the events on the console.
You can use a binary strategy, starting in the middle and continuing on the half that seems to be failing, and recurse until you locate the problem.
Or you can start at the beginning of the pipe, snooping towards the other end until you find the problem.
Snooping the kernel driver
>ls /dev/input
Will show you what input logical devices you have.
Plug in your input device. Here it is a hot-pluggable USB interface device. So the kernel should recognize it and create a ‘device’ file for it.
‘>ls /dev/input’ again will show a new entry: ‘wacom-touch’.
>cat /dev/input/wacom-touch
will display that file on your console. SInce the file is a stream without end (EOF) the above command will not return until you hit Ctl-C. If you now physically manipulate your input device (move the mouse or touch it), you will see the output from the device. It will be gobbledy-gook since the data is binary and the console wants printable characters (ASCII.) Anyway, you know the kernel driver is working with the input device, more or less.
Snooping the X input driver
There are two tools for snooping X events:
- xev – displays events from a window of an XServer
- xscope – displays all events from an XServer
These read events from the XServer and display them.
Unfortunately, these tools are not commonly distributed in binary form with most Linux distributions, so these commands may not be on your machine. You might need to download source and build the tools.
Worse, these tools seem to be outdated: they don’t display touch events from XInput2.2, which was released only in the last few years.
Also, the source for these tools is in the C language, so it will be hard for most people to update the tools themselves. (That explains why they are not updated already: C programmers are rare, and don’t have time to update tools that few people use, and for a system that many people consider ancient, X11.)
Rewriting xev in Python
So I thought I would rewrite xev in Python, at least enough to display touch events. If rewritten in Python, maybe it will be kept up to date, since more programmers can read and fix it.
Tools for rewriting xev in Python
xcb is a simplified interface to the X Protocol
xpyb is a Python binding to xcb
pyxev is a Python app (which I am writing) that does the same thing as xev: draws a window and snoops on X11 protocol events from the window, writing them to the console.
Basics of xpyb
Your Python app must do something like:
import xcb
from xcb.xproto import *
You must also import optional modules for the X extensions you will use:
import xcb.render
import xcb.xinput
(I had to look in the configure and Makefile to figure this out.)
Installing xpyb
xpyb is the colloquial name for the package. It is known as python-xpyb package in Ubuntu Software Center. It is known as ‘xcb’ when you import it in Python.
Unfortunately, it does not include a submodule for interfacing the to XInput extension (only for the XCore (core) protocol and the XRender extension. Thus we must download the source for xpyb and build it.
>./autogen.sh
>.configure --enable-xinput
>make
>sudo make install
Note the option to build the module ‘xinput’.
Understanding a xpyb binding to extension protocol
In a Python app using xpyb, importing a module of the xpyb package lets you call methods on the module, which in effect are message/replies to the extension e.g. the AllowDeviceEvents() of the XInputExtension.
Such methods control the extension, or query attributes of the extension.
But that’s secondary to my purpose: to snoop the XInput2 (touch) events coming from an input device.
Documentation of XInput2
There it says that my app (an X client) needs to subscribe to touch events by calling QueryVersion.
(So I am stuck here, not sure whether to call xcb.xinput.GetExtensionVersion() or xcb.core.QueryVersion.)
(Also, explain the need to call methods on an object the binding module returns versus calling methods on the binding module.)
Generic Events from an X Extension
I assumed that something like ‘TouchEvent’ and ‘TouchEventMask’ would be defined by xpyb. Not so. Apparently, such events are ‘Generic Events’, i.e. events wrapped inside a core event?
X11 Generic Events
But ‘Generic Event’ doesn’t seem to be defined in xpyb. Stuck here also.
More later…at this point I am reading about the X11 protocol and trying to understand how to translate from its documentation into xpyb calls to XCB.
The XCB documentation is very abstract: basically it says: “we don’t document every call, the translation from X11 calls to XCB calls is canonical, and you should be able to translate yourself.’
If you want to help, I will put the pyxev code on github and we can work on it together.