The Input Stack
Touchpad input takes a long journey from physical finger movements to the application layer:
- Finger movements affect capacitive touch circuitry.
- Firmware embedded in the device at the time of manufacture interprets electrical signals. Some convey proximity, pressure, orientation, or simply absolute X/Y coordinates of fingers. A modern specification for touchpad firmware<->OS integration is the Windows Precision Touchpad spec.
- The linux kernel combines hardware-specific custom drivers and general Human Interface Device drivers (hid, hid-multitouch) to provide a stream of data that is in relative or absolute coordinates (usually absolute), and context-free or slotted (usually slotted, meaning each “finger” corresponds to a data slot).
- All touchpad events, including
ABS_MT_*multitouch events are emitted by the kernel and made available to userspace via one of the
/dev/input/event*file descriptors. Older touchpads may emit relative touch events, or multitouch events without contact tracking (meaning information about which of N fingers is creating the events is not conveyed).
- Gesture remappers take
libinputevents such as gestures as input, and remap them to user-defined or desktop environment norms. Their output is then looped back into
libinputat the next layer as well. Sometimes gesture remappers also extend into the User Interface domain by providing customization GUIs or showing graphics and animations (but not always).
The Linux graphical desktop world is currently divided into X.Org and Wayland, with the former being the “stable classic” and the latter being the “eventual replacement” for X. That said, both systems now depend on
libinputas part of their stack (see #8 below).
(a) Xserver employs a “driver” such as xf86-input-libinput to effectively mirror what
libinputproduces. Xserver used to have many device drivers for input, but libinput has consolidated them and there is usually no need for them any more. Xserver communicates touchpad events to client apps via Xprotocol.
(b) A Wayland compositor such as Weston, mutter, or KWin interfaces directly with
libinputto handle movement, taps, and gestures. Unlike X, it does not need a forwarding driver like
xf86-input-libinput. Wayland compositors communicate touchpad events to client apps via the Wayland protocol.
- The application responds to touch events (position, tap, scroll, gestures etc.)
- Finally we can talk about libinput!
libinputis a cornerstone library for the touchpad input stack that can be used by several layers of the stack at any given time. It contains a hardware database to provide physical measurements, normalize DPI, and handle device-specific quirks. In addition, it converts absolute position touchpad coordinates to relative mouse pointer movements, detects gestures such as two-finger scrolling and others, and improves quality of life through palm detection and DWT (“disable [the touchpad] while typing”). mtdev is also empoyed to convert to “slotted” contact tracking events if needed. Because
For most touchpad hackers, the 3 most important layers are “kernel”, “libinput”, and “application”:
kernelmust be aware of and compatible with the touchpad hardware & firmware. If your touchpad is not working at all and it’s a new model, perhaps you’ll need to work at the kernel level.
libinputlayer is the userland layer that unifies all of the differences in capabilities, DPI, and quirks so that subsequent layers don’t need to know about particular hardware. If your touchpad is “working” but you’re experiencing some annoyance with regard to reliable heuristics, it’s likely at the
libinputlevel that you’d like to focus your attention.
applicationneed only concern itself with wrapped events, e.g. GTK or QT library events.
Finding Your Touchpad Device
The kernel uses its
udev database system to track attributes and properties of various devices as they are initialized or added/removed.
$ find /dev/input/ -maxdepth 1 -name 'event*' \ | sudo xargs udevadm info \ | grep -20 -i touchpad P: /devices/pci0000:00/0000:00:15.3/i2c_designware.2/i2c-12/i2c-PIXA3854:00/0018:093A:0274.0002/input/input10/event8 N: input/event8 L: 0 S: input/by-path/pci-0000:00:15.3-platform-i2c_designware.2-event-mouse E: DEVPATH=/devices/pci0000:00/0000:00:15.3/i2c_designware.2/i2c-12\ /i2c-PIXA3854:00/0018:093A:0274.0002/input/input10/event8 E: DEVNAME=/dev/input/event8 E: MAJOR=13 E: MINOR=72 E: SUBSYSTEM=input E: USEC_INITIALIZED=4797212 E: ID_INPUT=1 E: ID_INPUT_TOUCHPAD=1 E: ID_INPUT_WIDTH_MM=111 E: ID_INPUT_HEIGHT_MM=73 E: ID_SERIAL=noserial E: ID_PATH=pci-0000:00:15.3-platform-i2c_designware.2 E: ID_PATH_TAG=pci-0000_00_15_3-platform-i2c_designware_2 E: LIBINPUT_DEVICE_GROUP=18/93a/274:i2c-PIXA3854:00 E: DEVLINKS=/dev/input/by-path\ /pci-0000:00:15.3-platform-i2c_designware.2-event-mouse
DEVNAME property above shows
/dev/input/event8 as the touchpad device.
You can also see what kernel module(s) may be involved in making this touchpad device work. The “MAJOR” and “MINOR” properties above map to 13, 72. Or, using
exa), you can aslo see the major, minor numbers of the device:
$ ls -l /dev/input/event8 crw-rw---- 1 root input 13, 72 Nov 28 10:42 /dev/input/event8
input 13, 72 tells us this is an input device (i.e. character device, not a block device) with major number 13 and minor number 72. These can be found in the
/sys/dev/char (character) directory:
$ ls /sys/dev/char/13\:72/ dev device/ power/ subsystem/ uevent
device/ folder, you should be able to see what kernel module is driving:
$ ls -l /sys/dev/char/13\:72/device/device/driver lrwxrwxrwx 1 root root 0 Dec 2 09:28 \ /sys/dev/char/13:72/device/device/driver -> \ ../../../../../../../bus/hid/drivers/hid-multitouch
More info can be found at How to find the driver associated with a device on Linux.
Another tool you can use to find what’s driving your device at the kernel level is
$ lsmod | grep touch hid_multitouch 28672 0 hid 139264 6 i2c_hid,usbhid,hid_multitouch,hid_sensor_hub,intel_ishtp_hid,hid_generic
The comma-separated list indicates what modules depend on this module. So
hid_multitouch depends on
If you’re using Xorg, you can introspect your input devices like so:
$ xinput list ⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ FRMW0001:00 32AC:0006 Consumer Control id=10 [slave pointer (2)] ⎜ ↳ PIXA3854:00 093A:0274 Touchpad id=11 [slave pointer (2)] ⎜ ↳ PIXA3854:00 093A:0274 Mouse id=12 [slave pointer (2)] ⎜ ↳ keyd virtual pointer id=15 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] [...snip...]
To see what
libinput settings are enabled and disabled for your touchpad, e.g.:
$ xinput --list-props "PIXA3854:00 093A:0274 Touchpad" Device 'PIXA3854:00 093A:0274 Touchpad': Device Enabled (177): 1 Coordinate Transformation Matrix (179): 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 libinput Tapping Enabled (316): 1 libinput Tapping Enabled Default (317): 0 libinput Tapping Drag Enabled (318): 1 libinput Tapping Drag Enabled Default (319): 1 libinput Tapping Drag Lock Enabled (320): 0 [...snip...]
Hardware-reported Capabilities: Report Descriptors
Each HID device has a hardware “report descriptor” that is sent from the device itself to the kernel to describe its capabilities. The data is stored in a binary format called a HID report descriptor.
Find your device’s
report_descriptor file, and then dump as hex:
cat /sys/devices/pci0000\:00/0000\:00\:15.3/\ i2c_designware.2/i2c-12/i2c-PIXA3854\:00/0018\:093A\:0274.0002\ /report_descriptor \ | hexdump -e '16/1 "%02x " "\n"' 05 01 09 02 a1 01 85 02 05 01 09 01 a1 00 05 09 19 01 29 02 15 00 25 01 75 01 95 02 81 02 05 0d [...snip...]
Copy-paste the entire hex byte sequence to Frank’s USB Descriptor and Request Parser. Click “USB HID Report Descriptor” and you’ll get output something like this:
[...snip mouse collection...] 0x05, 0x0D, // Usage Page (Digitizer) 0x09, 0x05, // Usage (Touch Pad) 0xA1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID (1) 0x05, 0x0D, // Usage Page (Digitizer) 0x09, 0x22, // Usage (Finger) 0xA1, 0x02, // Collection (Logical) 0x09, 0x47, // Usage (0x47) 0x09, 0x42, // Usage (Tip Switch) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x02, // Report Count (2) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x95, 0x06, // Report Count (6) 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x51, // Usage (0x51) 0x25, 0x0F, // Logical Maximum (15) 0x75, 0x08, // Report Size (8) 0x95, 0x01, // Report Count (1) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x30, // Usage (X) 0x75, 0x10, // Report Size (16) 0x55, 0x0E, // Unit Exponent (-2) 0x65, 0x11, // Unit (System: SI Linear, Length: Centimeter) 0x35, 0x00, // Physical Minimum (0) 0x46, 0x5A, 0x04, // Physical Maximum (1114) 0x27, 0x39, 0x05, 0x00, 0x00, // Logical Maximum (1336) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0x09, 0x31, // Usage (Y) 0x46, 0xDA, 0x02, // Physical Maximum (730) 0x27, 0x6C, 0x03, 0x00, 0x00, // Logical Maximum (875) 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 0xC0, // End Collection 0x05, 0x0D, // Usage Page (Digitizer) 0x09, 0x22, // Usage (Finger) 0xA1, 0x02, // Collection (Logical) 0x09, 0x47, // Usage (0x47) 0x09, 0x42, // Usage (Tip Switch) [...snip additional finger collections...]
While the hardware may report one thing, the kernel may report another, depending on the compatibility and state of the driver. Here is a method to inspect what the kernel is saying the device is capable of.
# Note: `cat *` would work, but we want to see the filenames next to their # contents, so we use `tail -n +1`. We cd to the capabilities dir so that # filenames aren't so long: $ (cd /sys/devices/pci0000\:00/0000\:00\:15.3/\ i2c_designware.2/i2c-12/i2c-PIXA3854\:00/0018\:093A\:0274.0002/\ input/input10/capabilities/; tail -n +1 *) ==> abs <== 2e0800000000003 ==> ev <== 1b ==> ff <== 0 ==> key <== e520 30000 0 0 0 0 ==> led <== 0 ==> msc <== 20 ==> rel <== 0 ==> snd <== 0 ==> sw <== 0
The above files and their numeric contents represent HID “capability bits”. The Linux Humain Interface Devices spec has several categories of devices that correspond to the short names above. See the kernel.org input event-codes documentation.
For our touchpad hacking purposes, we’re mostly interested in the non-zero capabilities:
- abs: ABS_ prefix, indicates a device capable of sending absolute coordinates (like a touchpad!)
- ev: EV_ prefix, indicates a device capable of sending generic events
- key: KEY_ prefix, indicates a device capable of sending key events (key down, key up, key repeat).
- msc: MSC_ prefix, indicates a device capable of sending “misc” events, such as MSC_TIMESTAMP (a piece of information that can be sent as a kind of heartbeat, as in the case of fingers not moving but still touching the touchpad–for example when two fingers are touching but not moving it can be a signal that means “put on the brakes” for inertial scrolling).
In my case, the above bits work out to:
ev: 1b 7654 3210 0001 1011 Set Bits: 0, 1, 3, 4 => EV_SYN, EV_KEY, EV_ABS, EV_MSC key: e520 30000 0 0 0 0 #TODO: work out which keys/buttons this represents abs: 2e0800000000003 0010 1110 0000 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 Set Bits: 0, 1, 47, 53, 54, 55, 57 => ABS_X, ABS_Y, ABS_MT_SLOT, ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_TOOL_TYPE, ABS_MT_TRACKING_ID msc: 20 0010 0000 Set Bits: 5 => MSC_TIMESTAMP
Quick Hack Steps on Debian/Ubuntu
If you’re on a Debian-based system like Ubuntu, you can quite easily modify libinput source code, compile, and test your changes with the following commands:
sudo apt build-dep libinput sudo apt install fakeroot apt source libinput cd libinput-1.19.2/ # edit code! e.g. `vim src/evdev-mt-touchpad.c` dpkg-buildpackage -b sudo dpkg -i -O ../libinput*.deb