The Input Stack

Touchpad input takes a long journey from physical finger movements to the application layer:

  1. Finger movements affect capacitive touch circuitry.
  2. 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.
  3. 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).
  4. 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).
  5. Gesture remappers take libinput events such as gestures as input, and remap them to user-defined or desktop environment norms. Their output is then looped back into libinput at 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).
  6. 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 libinput as part of their stack (see #8 below).

    (a) Xserver employs a “driver” such as xf86-input-libinput to effectively mirror what libinput produces. 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 libinput to 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.

  7. The application responds to touch events (position, tap, scroll, gestures etc.)
  8. Finally we can talk about libinput! libinput is 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 libinput

For most touchpad hackers, the 3 most important layers are “kernel”, “libinput”, and “application”:

  • The kernel must 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.
  • The libinput layer 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 libinput level that you’d like to focus your attention.
  • The application need 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\
E: DEVNAME=/dev/input/event8
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\

The 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 ls (or 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

The 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 

Within the 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 -> \

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:

$ 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 hid.


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)]

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

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/\
    /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

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...]

Kernel-reported Capabilities

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/\
  tail -n +1 *)

==> abs <==

==> ev <==

==> ff <==

==> key <==
e520 30000 0 0 0 0

==> led <==

==> msc <==

==> rel <==

==> snd <==

==> sw <==

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 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).

How do you decode the exact capability bits in each non-zero category of HID input even types? See this stackexchange contribution by Runium.

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

msc: 20
   0010 0000
   Set Bits: 5

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


See resources.