Showing posts with label evdev. Show all posts
Showing posts with label evdev. Show all posts

Monday, January 8, 2018

python-libevdev - a python wrapper for libevdev

Edit 2018-02-26: renamed from libevdev-python to python-libevdev. That seems to be a more generic name and easier to package.

Last year, just before the holidays Benjamin Tissoires and I worked on a 'new' project - python-libevdev. This is, unsurprisingly, a Python wrapper to libevdev. It's not exactly new since we took the git tree from 2016 when I was working on it the first time round but this time we whipped it into a better shape. Now it's at the point where I think it has the API it should have, pythonic and very easy to use but still with libevdev as the actual workhorse in the background. It's available via pip3 and should be packaged for your favourite distributions soonish.

Who is this for? Basically anyone who needs to work with the evdev protocol. While C is still a thing, there are many use-cases where Python is a much more sensible choice. The python-libevdev documentation on ReadTheDocs provides a few examples which I'll copy here, just so you get a quick overview. The first example shows how to open a device and then continuously loop through all events, searching for button events:

import libevdev

fd = open('/dev/input/event0', 'rb')
d = libevdev.Device(fd)
if not d.has(libevdev.EV_KEY.BTN_LEFT):
     print('This does not look like a mouse device')
     sys.exit(0)

# Loop indefinitely while pulling the currently available events off
# the file descriptor
while True:
    for e in d.events():
        if not e.matches(libevdev.EV_KEY):
            continue

        if e.matches(libevdev.EV_KEY.BTN_LEFT):
            print('Left button event')
        elif e.matches(libevdev.EV_KEY.BTN_RIGHT):
            print('Right button event')
The second example shows how to create a virtual uinput device and send events through that device:
import libevdev
d = libevdev.Device()
d.name = 'some test device'
d.enable(libevdev.EV_REL.REL_X)
d.enable(libevdev.EV_REL.REL_Y)
d.enable(libevdev.EV_KEY.BTN_LEFT)
d.enable(libevdev.EV_KEY.BTN_MIDDLE)
d.enable(libevdev.EV_KEY.BTN_RIGHT)

uinput = d.create_uinput_device()
print('new uinput test device at {}'.format(uinput.devnode))
events = [InputEvent(libevdev.EV_REL.REL_X, 1),
          InputEvent(libevdev.EV_REL.REL_Y, 1),
          InputEvent(libevdev.EV_SYN.SYN_REPORT, 0)]
uinput.send_events(events)
And finally, if you have a textual or binary representation of events, the evbit function helps to convert it to something useful:
>>> import libevdev
>>> print(libevdev.evbit(0))
EV_SYN:0
>>> print(libevdev.evbit(2))
EV_REL:2
>>> print(libevdev.evbit(3, 4))
ABS_RY:4
>>> print(libevdev.evbit('EV_ABS'))
EV_ABS:3
>>> print(libevdev.evbit('EV_ABS', 'ABS_X'))
ABS_X:0
>>> print(libevdev.evbit('ABS_X'))
ABS_X:0
The latter is particularly helpful if you have a script that needs to analyse event sequences and look for protocol bugs (or hw/fw issues).

More explanations and details are available in the python-libevdev documentation. That doc also answers the question why python-libevdev exists when there's already a python-evdev package. The code is up on github.

Monday, September 19, 2016

Understanding evdev

This post explains how the evdev protocol works. After reading this post you should understand what evdev is and how to interpret evdev event dumps to understand what your device is doing. The post is aimed mainly at users having to debug a device, I will thus leave out or simplify some of the technical details. I'll be using the output from evemu-record as example because that is the primary debugging tool for evdev.

What is evdev?

evdev is a Linux-only generic protocol that the kernel uses to forward information and events about input devices to userspace. It's not just for mice and keyboards but any device that has any sort of axis, key or button, including things like webcams and remote controls. Each device is represented as a device node in the form of /dev/input/event0, with the trailing number increasing as you add more devices. The node numbers are re-used after you unplug a device, so don't hardcode the device node into a script. The device nodes are also only readable by root, thus you need to run any debugging tools as root too.

evdev is the primary way to talk to input devices on Linux. All X.Org drivers on Linux use evdev as protocol and libinput as well. Note that "evdev" is also the shortcut used for xf86-input-evdev, the X.Org driver to handle generic evdev devices, so watch out for context when you read "evdev" on a mailing list.

Communicating with evdev devices

Communicating with a device is simple: open the device node and read from it. Any data coming out is a struct input_event, defined in /usr/include/linux/input.h:

struct input_event {
 struct timeval time;
 __u16 type;
 __u16 code;
 __s32 value;
};
I'll describe the contents later, but you can see that it's a very simple struct.

Static information about the device such as its name and capabilities can be queried with a set of ioctls. Note that you should always use libevdev to interact with a device, it blunts the few sharp edges evdev has. See the libevdev documentation for usage examples.

evemu-record, our primary debugging tool for anything evdev is very simple. It reads the static information about the device, prints it and then simply reads and prints all events as they come in. The output is in machine-readable format but it's annotated with human-readable comments (starting with #). You can always ignore the non-comment bits. There's a second command, evemu-describe, that only prints the description and exits without waiting for events.

Relative devices and keyboards

The top part of an evemu-record output is the device description. This is a list of static properties that tells us what the device is capable of. For example, the USB mouse I have plugged in here prints:

# Input device name: "PIXART USB OPTICAL MOUSE"
# Input device ID: bus 0x03 vendor 0x93a product 0x2510 version 0x110
# Supported events:
#   Event type 0 (EV_SYN)
#     Event code 0 (SYN_REPORT)
#     Event code 1 (SYN_CONFIG)
#     Event code 2 (SYN_MT_REPORT)
#     Event code 3 (SYN_DROPPED)
#     Event code 4 ((null))
#     Event code 5 ((null))
#     Event code 6 ((null))
#     Event code 7 ((null))
#     Event code 8 ((null))
#     Event code 9 ((null))
#     Event code 10 ((null))
#     Event code 11 ((null))
#     Event code 12 ((null))
#     Event code 13 ((null))
#     Event code 14 ((null))
#   Event type 1 (EV_KEY)
#     Event code 272 (BTN_LEFT)
#     Event code 273 (BTN_RIGHT)
#     Event code 274 (BTN_MIDDLE)
#   Event type 2 (EV_REL)
#     Event code 0 (REL_X)
#     Event code 1 (REL_Y)
#     Event code 8 (REL_WHEEL)
#   Event type 4 (EV_MSC)
#     Event code 4 (MSC_SCAN)
# Properties:
The device name is the one (usually) set by the manufacturer and so are the vendor and product IDs. The bus is one of the "BUS_USB" and similar constants defined in /usr/include/linux/input.h. The version is often quite arbitrary, only a few devices have something meaningful here.

We also have a set of supported events, categorised by "event type" and "event code" (note how type and code are also part of the struct input_event). The type is a general category, and /usr/include/linux/input-event-codes.h defines quite a few of those. The most important types are EV_KEY (keys and buttons), EV_REL (relative axes) and EV_ABS (absolute axes). In the output above we can see that we have EV_KEY and EV_REL set.

As a subitem of each type we have the event code. The event codes for this device are self-explanatory: BTN_LEFT, BTN_RIGHT and BTN_MIDDLE are the left, right and middle button. The axes are a relative x axis, a relative y axis and a wheel axis (i.e. a mouse wheel). EV_MSC/MSC_SCAN is used for raw scancodes and you can usually ignore it. And finally we have the EV_SYN bits but let's ignore those, they are always set for all devices.

Note that an event code cannot be on its own, it must be a tuple of (type, code). For example, REL_X and ABS_X have the same numerical value and without the type you won't know which one is which.

That's pretty much it. A keyboard will have a lot of EV_KEY bits set and the EV_REL axes are obviously missing (but not always...). Instead of BTN_LEFT, a keyboard would have e.g. KEY_ESC, KEY_A, KEY_B, etc. 90% of device debugging is looking at the event codes and figuring out which ones are missing or shouldn't be there.

Exercise: You should now be able to read a evemu-record description from any mouse or keyboard device connected to your computer and understand what it means. This also applies to most special devices such as remotes - the only thing that changes are the names for the keys/buttons. Just run sudo evemu-describe and pick any device in the list.

The events from relative devices and keyboards

evdev is a serialised protocol. It sends a series of events and then a synchronisation event to notify us that the preceeding events all belong together. This synchronisation event is EV_SYN SYN_REPORT, is generated by the kernel, not the device and hence all EV_SYN codes are always available on all devices.

Let's have a look at a mouse movement. As explained above, half the line is machine-readable but we can ignore that bit and look at the human-readable output on the right.

E: 0.335996 0002 0000 0001      # EV_REL / REL_X                1
E: 0.335996 0002 0001 -002      # EV_REL / REL_Y                -2
E: 0.335996 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
This means that within one hardware event, we've moved 1 device unit to the right (x axis) and two device units up (y axis). Note how all events have the same timestamp (0.335996).

Let's have a look at a button press:

E: 0.656004 0004 0004 589825    # EV_MSC / MSC_SCAN             589825
E: 0.656004 0001 0110 0001      # EV_KEY / BTN_LEFT             1
E: 0.656004 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
E: 0.727002 0004 0004 589825    # EV_MSC / MSC_SCAN             589825
E: 0.727002 0001 0110 0000      # EV_KEY / BTN_LEFT             0
E: 0.727002 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
For button events, the value 1 signals button pressed, button 0 signals button released.

And key events look like this:

E: 0.000000 0004 0004 458792    # EV_MSC / MSC_SCAN             458792
E: 0.000000 0001 001c 0000      # EV_KEY / KEY_ENTER            0
E: 0.000000 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
E: 0.560004 0004 0004 458976    # EV_MSC / MSC_SCAN             458976
E: 0.560004 0001 001d 0001      # EV_KEY / KEY_LEFTCTRL         1
E: 0.560004 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
[....]
E: 1.172732 0001 001d 0002      # EV_KEY / KEY_LEFTCTRL         2
E: 1.172732 0000 0000 0001      # ------------ SYN_REPORT (1) ----------
E: 1.200004 0004 0004 458758    # EV_MSC / MSC_SCAN             458758
E: 1.200004 0001 002e 0001      # EV_KEY / KEY_C                1
E: 1.200004 0000 0000 0000      # ------------ SYN_REPORT (0) ----------
Mostly the same as button events. But wait, there is one difference: we have a value of 2 as well. For key events, a value 2 means "key repeat". If you're on the tty, then this is what generates repeat keys for you. In X and Wayland we ignore these repeat events and instead use XKB-based key repeat.

Now look at the keyboard events again and see if you can make sense of the sequence. We have an Enter release (but no press), then ctrl down (and repeat), followed by a 'c' press - but no release. The explanation is simple - as soon as I hit enter in the terminal, evemu-record started recording so it captured the enter release too. And it stopped recording as soon as ctrl+c was down because that's when it was cancelled by the terminal. One important takeaway here: the evdev protocol is not guaranteed to be balanced. You may see a release for a key you've never seen the press for, and you may be missing a release for a key/button you've seen the press for (this happens when you stop recording). Oh, and there's one danger: if you record your keyboard and you type your password, the keys will show up in the output. Security experts generally reocmmend not publishing event logs with your password in it.

Exercise: You should now be able to read a evemu-record events list from any mouse or keyboard device connected to your computer and understand the event sequence.This also applies to most special devices such as remotes - the only thing that changes are the names for the keys/buttons. Just run sudo evemu-record and pick any device listed.

Absolute devices

Things get a bit more complicated when we look at absolute input devices like a touchscreen or a touchpad. Yes, touchpads are absolute devices in hardware and the conversion to relative events is done in userspace by e.g. libinput. The output of my touchpad is below. Note that I've manually removed a few bits to make it easier to grasp, they will appear later in the multitouch discussion.

# Input device name: "SynPS/2 Synaptics TouchPad"
# Input device ID: bus 0x11 vendor 0x02 product 0x07 version 0x1b1
# Supported events:
#   Event type 0 (EV_SYN)
#     Event code 0 (SYN_REPORT)
#     Event code 1 (SYN_CONFIG)
#     Event code 2 (SYN_MT_REPORT)
#     Event code 3 (SYN_DROPPED)
#     Event code 4 ((null))
#     Event code 5 ((null))
#     Event code 6 ((null))
#     Event code 7 ((null))
#     Event code 8 ((null))
#     Event code 9 ((null))
#     Event code 10 ((null))
#     Event code 11 ((null))
#     Event code 12 ((null))
#     Event code 13 ((null))
#     Event code 14 ((null))
#   Event type 1 (EV_KEY)
#     Event code 272 (BTN_LEFT)
#     Event code 325 (BTN_TOOL_FINGER)
#     Event code 328 (BTN_TOOL_QUINTTAP)
#     Event code 330 (BTN_TOUCH)
#     Event code 333 (BTN_TOOL_DOUBLETAP)
#     Event code 334 (BTN_TOOL_TRIPLETAP)
#     Event code 335 (BTN_TOOL_QUADTAP)
#   Event type 3 (EV_ABS)
#     Event code 0 (ABS_X)
#       Value   2919
#       Min     1024
#       Max     5112
#       Fuzz       0
#       Flat       0
#       Resolution 42
#     Event code 1 (ABS_Y)
#       Value   3711
#       Min     2024
#       Max     4832
#       Fuzz       0
#       Flat       0
#       Resolution 42
#     Event code 24 (ABS_PRESSURE)
#       Value      0
#       Min        0
#       Max      255
#       Fuzz       0
#       Flat       0
#       Resolution 0
#     Event code 28 (ABS_TOOL_WIDTH)
#       Value      0
#       Min        0
#       Max       15
#       Fuzz       0
#       Flat       0
#       Resolution 0
# Properties:
#   Property  type 0 (INPUT_PROP_POINTER)
#   Property  type 2 (INPUT_PROP_BUTTONPAD)
#   Property  type 4 (INPUT_PROP_TOPBUTTONPAD)
We have a BTN_LEFT again and a set of other buttons that I'll explain in a second. But first we look at the EV_ABS output. We have the same naming system as above. ABS_X and ABS_Y are the x and y axis on the device, ABS_PRESSURE is an (arbitrary) ranged pressure value.

Absolute axes have a bit more state than just a simple bit. Specifically, they have a minimum and maximum (not all hardware has the top-left sensor position on 0/0, it can be an arbitrary position, specified by the minimum). Notable here is that the axis ranges are simply the ones announced by the device - there is no guarantee that the values fall within this range and indeed a lot of touchpad devices tend to send values slightly outside that range. Fuzz and flat can be safely ignored, but resolution is interesting. It is given in units per millimeter and thus tells us the size of the device. in the above case: (5112 - 1024)/42 means the device is 97mm wide. The resolution is quite commonly wrong, a lot of axis overrides need the resolution changed to the correct value.

The axis description also has a current value listed. The kernel only sends events when the value changes, so even if the actual hardware keeps sending events, you may never see them in the output if the value remains the same. In other words, holding a finger perfectly still on a touchpad creates plenty of hardware events, but you won't see anything coming out of the event node.

Finally, we have properties on this device. These are used to indicate general information about the device that's not otherwise obvious. In this case INPUT_PROP_POINTER tells us that we need a pointer for this device (it is a touchpad after all, a touchscreen would instead have INPUT_PROP_DIRECT set). INPUT_PROP_BUTTONPAD means that this is a so-called clickpad, it does not have separate physical buttons but instead the whole touchpad clicks. Ignore INPUT_PROP_TOPBUTTONPAD because it only applies to the Lenovo *40 series of devices.

Ok, back to the buttons: aside from BTN_LEFT, we have BTN_TOUCH. This one signals that the user is touching the surface of the touchpad (with some in-kernel defined minimum pressure value). It's not just for finger-touches, it's also used for graphics tablet stylus touchpes (so really, it's more "contact" than "touch" but meh).

The BTN_TOOL_FINGER event tells us that a finger is in detectable range. This gives us two bits of information: first, we have a finger (a tablet would have e.g. BTN_TOOL_PEN) and second, we may have a finger in proximity without touching. On many touchpads, BTN_TOOL_FINGER and BTN_TOUCH come in the same event, but others can detect a finger hovering over the touchpad too (in which case you'd also hope for ABS_DISTANCE being available on the touchpad).

Finally, the BTN_TOOL_DOUBLETAP up to BTN_TOOL_QUINTTAP tell us whether the device can detect 2 through to 5 fingers on the touchpad. This doesn't actually track the fingers, it merely tells you "3 fingers down" in the case of BTN_TOOL_TRIPLETAP.

Exercise: Look at your touchpad's description and figure out if the size of the touchpad is correct based on the axis information [1]. Check how many fingers your touchpad can detect and whether it can do pressure or distance detection.

The events from absolute devices

Events from absolute axes are not really any different than events from relative devices which we already covered. The same type/code combination with a value and a timestamp, all framed by EV_SYN SYN_REPORT events. Here's an example of me touching the touchpad:

E: 0.000001 0001 014a 0001      # EV_KEY / BTN_TOUCH            1
E: 0.000001 0003 0000 3335      # EV_ABS / ABS_X                3335
E: 0.000001 0003 0001 3308      # EV_ABS / ABS_Y                3308
E: 0.000001 0003 0018 0069      # EV_ABS / ABS_PRESSURE         69
E: 0.000001 0001 0145 0001      # EV_KEY / BTN_TOOL_FINGER      1
E: 0.000001 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.021751 0003 0018 0070      # EV_ABS / ABS_PRESSURE         70
E: 0.021751 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +21ms
E: 0.043908 0003 0000 3334      # EV_ABS / ABS_X                3334
E: 0.043908 0003 0001 3309      # EV_ABS / ABS_Y                3309
E: 0.043908 0003 0018 0065      # EV_ABS / ABS_PRESSURE         65
E: 0.043908 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +22ms
E: 0.052469 0001 014a 0000      # EV_KEY / BTN_TOUCH            0
E: 0.052469 0003 0018 0000      # EV_ABS / ABS_PRESSURE         0
E: 0.052469 0001 0145 0000      # EV_KEY / BTN_TOOL_FINGER      0
E: 0.052469 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +9ms
In the first event you see BTN_TOOL_FINGER and BTN_TOUCH set (this touchpad doesn't detect hovering fingers). An x/y coordinate pair and a pressure value. The pressure changes in the second event, the third event changes pressure and location. Finally, we have BTN_TOOL_FINGER and BTN_TOUCH released on finger up, and the pressure value goes back to 0. Notice how the second event didn't contain any x/y coordinates? As I said above, the kernel only sends updates on absolute axes when the value changed.

Ok, let's look at a three-finger tap (again, minus the ABS_MT_ bits):

E: 0.000001 0001 014a 0001      # EV_KEY / BTN_TOUCH            1
E: 0.000001 0003 0000 2149      # EV_ABS / ABS_X                2149
E: 0.000001 0003 0001 3747      # EV_ABS / ABS_Y                3747
E: 0.000001 0003 0018 0066      # EV_ABS / ABS_PRESSURE         66
E: 0.000001 0001 014e 0001      # EV_KEY / BTN_TOOL_TRIPLETAP   1
E: 0.000001 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.034209 0003 0000 2148      # EV_ABS / ABS_X                2148
E: 0.034209 0003 0018 0064      # EV_ABS / ABS_PRESSURE         64
E: 0.034209 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +34ms
[...]
E: 0.138510 0003 0000 4286      # EV_ABS / ABS_X                4286
E: 0.138510 0003 0001 3350      # EV_ABS / ABS_Y                3350
E: 0.138510 0003 0018 0055      # EV_ABS / ABS_PRESSURE         55
E: 0.138510 0001 0145 0001      # EV_KEY / BTN_TOOL_FINGER      1
E: 0.138510 0001 014e 0000      # EV_KEY / BTN_TOOL_TRIPLETAP   0
E: 0.138510 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +23ms
E: 0.147834 0003 0000 4287      # EV_ABS / ABS_X                4287
E: 0.147834 0003 0001 3351      # EV_ABS / ABS_Y                3351
E: 0.147834 0003 0018 0037      # EV_ABS / ABS_PRESSURE         37
E: 0.147834 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +9ms
E: 0.157151 0001 014a 0000      # EV_KEY / BTN_TOUCH            0
E: 0.157151 0003 0018 0000      # EV_ABS / ABS_PRESSURE         0
E: 0.157151 0001 0145 0000      # EV_KEY / BTN_TOOL_FINGER      0
E: 0.157151 0000 0000 0000      # ------------ SYN_REPORT (0) ---------- +10ms
In the first event, the touchpad detected all three fingers at the same time. So get BTN_TOUCH, x/y/pressure and BTN_TOOL_TRIPLETAP set. Note that the various BTN_TOOL_* bits are mutually exclusive. BTN_TOOL_FINGER means "exactly 1 finger down" and you can't have exactly 1 finger down when you have three fingers down. In the second event x and pressure update (y has no event, it stayed the same).

In the event after the break, we switch from three fingers to one finger. BTN_TOOL_TRIPLETAP is released, BTN_TOOL_FINGER is set. That's very common. Humans aren't robots, you can't release all fingers at exactly the same time, so depending on the hardware scanout rate you have intermediate states where one finger has left already, others are still down. In this case I released two fingers between scanouts, one was still down. It's not uncommon to see a full cycle from BTN_TOOL_FINGER to BTN_TOOL_DOUBLETAP to BTN_TOOL_TRIPLETAP on finger down or the reverse on finger up.

Exercise: test out the pressure values on your touchpad and see how close you can get to the actual announced range. Check how accurate the multifinger detection is by tapping with two, three, four and five fingers. (In both cases, you'll likely find that it's very much hit and miss).

Multitouch and slots

Now we're at the most complicated topic regarding evdev devices. In the case of multitouch devices, we need to send multiple touches on the same axes. So we need an additional dimension and that is called multitouch slots (there is another, older multitouch protocol that doesn't use slots but it is so rare now that you don't need to bother).

First: all axes that are multitouch-capable are repeated as ABS_MT_foo axis. So if you have ABS_X, you also get ABS_MT_POSITION_X and both axes have the same axis ranges and resolutions. The reason here is backwards-compatibility: if a device only sends multitouch events, older programs only listening to the ABS_X etc. events won't work. Some axes may only be available for single-touch (ABS_MT_TOOL_WIDTH in this case).

Let's have a look at my touchpad, this time without the axes removed:

# Input device name: "SynPS/2 Synaptics TouchPad"
# Input device ID: bus 0x11 vendor 0x02 product 0x07 version 0x1b1
# Supported events:
#   Event type 0 (EV_SYN)
#     Event code 0 (SYN_REPORT)
#     Event code 1 (SYN_CONFIG)
#     Event code 2 (SYN_MT_REPORT)
#     Event code 3 (SYN_DROPPED)
#     Event code 4 ((null))
#     Event code 5 ((null))
#     Event code 6 ((null))
#     Event code 7 ((null))
#     Event code 8 ((null))
#     Event code 9 ((null))
#     Event code 10 ((null))
#     Event code 11 ((null))
#     Event code 12 ((null))
#     Event code 13 ((null))
#     Event code 14 ((null))
#   Event type 1 (EV_KEY)
#     Event code 272 (BTN_LEFT)
#     Event code 325 (BTN_TOOL_FINGER)
#     Event code 328 (BTN_TOOL_QUINTTAP)
#     Event code 330 (BTN_TOUCH)
#     Event code 333 (BTN_TOOL_DOUBLETAP)
#     Event code 334 (BTN_TOOL_TRIPLETAP)
#     Event code 335 (BTN_TOOL_QUADTAP)
#   Event type 3 (EV_ABS)
#     Event code 0 (ABS_X)
#       Value   5112
#       Min     1024
#       Max     5112
#       Fuzz       0
#       Flat       0
#       Resolution 41
#     Event code 1 (ABS_Y)
#       Value   2930
#       Min     2024
#       Max     4832
#       Fuzz       0
#       Flat       0
#       Resolution 37
#     Event code 24 (ABS_PRESSURE)
#       Value      0
#       Min        0
#       Max      255
#       Fuzz       0
#       Flat       0
#       Resolution 0
#     Event code 28 (ABS_TOOL_WIDTH)
#       Value      0
#       Min        0
#       Max       15
#       Fuzz       0
#       Flat       0
#       Resolution 0
#     Event code 47 (ABS_MT_SLOT)
#       Value      0
#       Min        0
#       Max        1
#       Fuzz       0
#       Flat       0
#       Resolution 0
#     Event code 53 (ABS_MT_POSITION_X)
#       Value      0
#       Min     1024
#       Max     5112
#       Fuzz       8
#       Flat       0
#       Resolution 41
#     Event code 54 (ABS_MT_POSITION_Y)
#       Value      0
#       Min     2024
#       Max     4832
#       Fuzz       8
#       Flat       0
#       Resolution 37
#     Event code 57 (ABS_MT_TRACKING_ID)
#       Value      0
#       Min        0
#       Max    65535
#       Fuzz       0
#       Flat       0
#       Resolution 0
#     Event code 58 (ABS_MT_PRESSURE)
#       Value      0
#       Min        0
#       Max      255
#       Fuzz       0
#       Flat       0
#       Resolution 0
# Properties:
#   Property  type 0 (INPUT_PROP_POINTER)
#   Property  type 2 (INPUT_PROP_BUTTONPAD)
#   Property  type 4 (INPUT_PROP_TOPBUTTONPAD)
We have an x and y position for multitouch as well as a pressure axis. There are also two special multitouch axes that aren't really axes. ABS_MT_SLOT and ABS_MT_TRACKING_ID. The former specifies which slot is currently active, the latter is used to track touch points.

Slots are a static property of a device. My touchpad, as you can see above ony supports 2 slots (min 0, max 1) and thus can track 2 fingers at a time. Whenever the first finger is set down it's coordinates will be tracked in slot 0, the second finger will be tracked in slot 1. When the finger in slot 0 is lifted, the second finger continues to be tracked in slot 1, and if a new finger is set down, it will be tracked in slot 0. Sounds more complicated than it is, think of it as an array of possible touchpoints.

The tracking ID is an incrementing number that lets us tell touch points apart and also tells us when a touch starts and when it ends. The two values are either -1 or a positive number. Any positive number means "new touch" and -1 means "touch ended". So when you put two fingers down and lift them again, you'll get a tracking ID of 1 in slot 0, a tracking ID of 2 in slot 1, then a tracking ID of -1 in both slots to signal they ended. The tracking ID value itself is meaningless, it simply increases as touches are created.

Let's look at a single tap:

E: 0.000001 0003 0039 0387 # EV_ABS / ABS_MT_TRACKING_ID   387
E: 0.000001 0003 0035 2560 # EV_ABS / ABS_MT_POSITION_X    2560
E: 0.000001 0003 0036 2905 # EV_ABS / ABS_MT_POSITION_Y    2905
E: 0.000001 0003 003a 0059 # EV_ABS / ABS_MT_PRESSURE      59
E: 0.000001 0001 014a 0001 # EV_KEY / BTN_TOUCH            1
E: 0.000001 0003 0000 2560 # EV_ABS / ABS_X                2560
E: 0.000001 0003 0001 2905 # EV_ABS / ABS_Y                2905
E: 0.000001 0003 0018 0059 # EV_ABS / ABS_PRESSURE         59
E: 0.000001 0001 0145 0001 # EV_KEY / BTN_TOOL_FINGER      1
E: 0.000001 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.021690 0003 003a 0067 # EV_ABS / ABS_MT_PRESSURE      67
E: 0.021690 0003 0018 0067 # EV_ABS / ABS_PRESSURE         67
E: 0.021690 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +21ms
E: 0.033482 0003 003a 0068 # EV_ABS / ABS_MT_PRESSURE      68
E: 0.033482 0003 0018 0068 # EV_ABS / ABS_PRESSURE         68
E: 0.033482 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +12ms
E: 0.044268 0003 0035 2561 # EV_ABS / ABS_MT_POSITION_X    2561
E: 0.044268 0003 0000 2561 # EV_ABS / ABS_X                2561
E: 0.044268 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +11ms
E: 0.054093 0003 0035 2562 # EV_ABS / ABS_MT_POSITION_X    2562
E: 0.054093 0003 003a 0067 # EV_ABS / ABS_MT_PRESSURE      67
E: 0.054093 0003 0000 2562 # EV_ABS / ABS_X                2562
E: 0.054093 0003 0018 0067 # EV_ABS / ABS_PRESSURE         67
E: 0.054093 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +10ms
E: 0.064891 0003 0035 2569 # EV_ABS / ABS_MT_POSITION_X    2569
E: 0.064891 0003 0036 2903 # EV_ABS / ABS_MT_POSITION_Y    2903
E: 0.064891 0003 003a 0059 # EV_ABS / ABS_MT_PRESSURE      59
E: 0.064891 0003 0000 2569 # EV_ABS / ABS_X                2569
E: 0.064891 0003 0001 2903 # EV_ABS / ABS_Y                2903
E: 0.064891 0003 0018 0059 # EV_ABS / ABS_PRESSURE         59
E: 0.064891 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +10ms
E: 0.073634 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID   -1
E: 0.073634 0001 014a 0000 # EV_KEY / BTN_TOUCH            0
E: 0.073634 0003 0018 0000 # EV_ABS / ABS_PRESSURE         0
E: 0.073634 0001 0145 0000 # EV_KEY / BTN_TOOL_FINGER      0
E: 0.073634 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +9ms
We have a tracking ID (387) signalling finger down, as well as a position plus pressure. then some updates and eventually a tracking ID of -1 (signalling finger up). Notice how there is no ABS_MT_SLOT here - the kernel buffers those too so while you stay in the same slot (0 in this case) you don't see any events for it. Also notice how you get both single-finger as well as multitouch in the same event stream. This is for backwards compatibility [2]

Ok, time for a two-finger tap:

E: 0.000001 0003 0039 0496 # EV_ABS / ABS_MT_TRACKING_ID   496
E: 0.000001 0003 0035 2609 # EV_ABS / ABS_MT_POSITION_X    2609
E: 0.000001 0003 0036 3791 # EV_ABS / ABS_MT_POSITION_Y    3791
E: 0.000001 0003 003a 0054 # EV_ABS / ABS_MT_PRESSURE      54
E: 0.000001 0003 002f 0001 # EV_ABS / ABS_MT_SLOT          1
E: 0.000001 0003 0039 0497 # EV_ABS / ABS_MT_TRACKING_ID   497
E: 0.000001 0003 0035 3012 # EV_ABS / ABS_MT_POSITION_X    3012
E: 0.000001 0003 0036 3088 # EV_ABS / ABS_MT_POSITION_Y    3088
E: 0.000001 0003 003a 0056 # EV_ABS / ABS_MT_PRESSURE      56
E: 0.000001 0001 014a 0001 # EV_KEY / BTN_TOUCH            1
E: 0.000001 0003 0000 2609 # EV_ABS / ABS_X                2609
E: 0.000001 0003 0001 3791 # EV_ABS / ABS_Y                3791
E: 0.000001 0003 0018 0054 # EV_ABS / ABS_PRESSURE         54
E: 0.000001 0001 014d 0001 # EV_KEY / BTN_TOOL_DOUBLETAP   1
E: 0.000001 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.012909 0003 002f 0000 # EV_ABS / ABS_MT_SLOT          0
E: 0.012909 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID   -1
E: 0.012909 0003 002f 0001 # EV_ABS / ABS_MT_SLOT          1
E: 0.012909 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID   -1
E: 0.012909 0001 014a 0000 # EV_KEY / BTN_TOUCH            0
E: 0.012909 0003 0018 0000 # EV_ABS / ABS_PRESSURE         0
E: 0.012909 0001 014d 0000 # EV_KEY / BTN_TOOL_DOUBLETAP   0
E: 0.012909 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +12ms
This was a really quick two-finger tap that illustrates the tracking IDs nicely. In the first event we get a touch down, then an ABS_MT_SLOT event. This tells us that subsequent events belong to the other slot, so it's the other finger. There too we get a tracking ID + position. In the next event we get an ABS_MT_SLOT to switch back to slot 0. Tracking ID of -1 means that touch ended, and then we see the touch in slot 1 ended too.

Time for a two-finger scroll:

E: 0.000001 0003 0039 0557 # EV_ABS / ABS_MT_TRACKING_ID   557
E: 0.000001 0003 0035 2589 # EV_ABS / ABS_MT_POSITION_X    2589
E: 0.000001 0003 0036 3363 # EV_ABS / ABS_MT_POSITION_Y    3363
E: 0.000001 0003 003a 0048 # EV_ABS / ABS_MT_PRESSURE      48
E: 0.000001 0003 002f 0001 # EV_ABS / ABS_MT_SLOT          1
E: 0.000001 0003 0039 0558 # EV_ABS / ABS_MT_TRACKING_ID   558
E: 0.000001 0003 0035 3512 # EV_ABS / ABS_MT_POSITION_X    3512
E: 0.000001 0003 0036 3028 # EV_ABS / ABS_MT_POSITION_Y    3028
E: 0.000001 0003 003a 0044 # EV_ABS / ABS_MT_PRESSURE      44
E: 0.000001 0001 014a 0001 # EV_KEY / BTN_TOUCH            1
E: 0.000001 0003 0000 2589 # EV_ABS / ABS_X                2589
E: 0.000001 0003 0001 3363 # EV_ABS / ABS_Y                3363
E: 0.000001 0003 0018 0048 # EV_ABS / ABS_PRESSURE         48
E: 0.000001 0001 014d 0001 # EV_KEY / BTN_TOOL_DOUBLETAP   1
E: 0.000001 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +0ms
E: 0.027960 0003 002f 0000 # EV_ABS / ABS_MT_SLOT          0
E: 0.027960 0003 0035 2590 # EV_ABS / ABS_MT_POSITION_X    2590
E: 0.027960 0003 0036 3395 # EV_ABS / ABS_MT_POSITION_Y    3395
E: 0.027960 0003 003a 0046 # EV_ABS / ABS_MT_PRESSURE      46
E: 0.027960 0003 002f 0001 # EV_ABS / ABS_MT_SLOT          1
E: 0.027960 0003 0035 3511 # EV_ABS / ABS_MT_POSITION_X    3511
E: 0.027960 0003 0036 3052 # EV_ABS / ABS_MT_POSITION_Y    3052
E: 0.027960 0003 0000 2590 # EV_ABS / ABS_X                2590
E: 0.027960 0003 0001 3395 # EV_ABS / ABS_Y                3395
E: 0.027960 0003 0018 0046 # EV_ABS / ABS_PRESSURE         46
E: 0.027960 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +27ms
E: 0.051720 0003 002f 0000 # EV_ABS / ABS_MT_SLOT          0
E: 0.051720 0003 0035 2609 # EV_ABS / ABS_MT_POSITION_X    2609
E: 0.051720 0003 0036 3447 # EV_ABS / ABS_MT_POSITION_Y    3447
E: 0.051720 0003 002f 0001 # EV_ABS / ABS_MT_SLOT          1
E: 0.051720 0003 0036 3080 # EV_ABS / ABS_MT_POSITION_Y    3080
E: 0.051720 0003 0000 2609 # EV_ABS / ABS_X                2609
E: 0.051720 0003 0001 3447 # EV_ABS / ABS_Y                3447
E: 0.051720 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +24ms
[...]
E: 0.272034 0003 002f 0000 # EV_ABS / ABS_MT_SLOT          0
E: 0.272034 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID   -1
E: 0.272034 0003 002f 0001 # EV_ABS / ABS_MT_SLOT          1
E: 0.272034 0003 0039 -001 # EV_ABS / ABS_MT_TRACKING_ID   -1
E: 0.272034 0001 014a 0000 # EV_KEY / BTN_TOUCH            0
E: 0.272034 0003 0018 0000 # EV_ABS / ABS_PRESSURE         0
E: 0.272034 0001 014d 0000 # EV_KEY / BTN_TOOL_DOUBLETAP   0
E: 0.272034 0000 0000 0000 # ------------ SYN_REPORT (0) ---------- +30ms
Note that "scroll" is something handled in userspace, so what you see here is just a two-finger move. Everything in there i something we've already seen, but pay attention to the two middle events: as updates come in for each finger, the ABS_MT_SLOT changes before the upates are sent. The kernel filter for identical events is still in effect, so in the third event we don't get an update for the X position on slot 1. The filtering is per-touchpoint, so in this case this means that slot 1 position x is still on 3511, just as it was in the previous event.

That's all you have to remember, really. If you think of evdev as a serialised way of sending an array of touchpoints, with the slots as the indices then it should be fairly clear. The rest is then just about actually looking at the touch positions and making sense of them.

Exercise: do a pinch gesture on your touchpad. See if you can track the two fingers moving closer together. Then do the same but only move one finger. See how the non-moving finger gets less updates.

That's it. There are a few more details to evdev but much of that is just more event types and codes. The few details you really have to worry about when processing events are either documented in libevdev or abstracted away completely. The above should be enough to understand what your device does, and what goes wrong when your device isn't working. Good luck.

[1] If not, file a bug against systemd's hwdb and CC me so we can put corrections in
[2] We treat some MT-capable touchpads as single-touch devices in libinput because the MT data is garbage

Wednesday, September 18, 2013

libevdev - creating uinput devices

This post describes how to create uinput devices through the new libevdev library. For more information about libevdev, please refer to the first post in this series.

What is uinput?

uinput is the kernel interface to create evdev devices that, for most purposes, look the same as real devices. This goes so far that around 80% (well, I'm guessing. actually, make this 83.45%) of all testing I do now is with emulated devices only. There are a few bits that can't be emulated, a few things that are different, but generally I found uinput devices to be close enough to the real thing. As the evdev interface, the uinput interface requires you to handle a few structs and ioctls, not necessarily in an obvious way. libevdev wraps that for you.

Creating a device

The simplest way to create a uinput device is to duplicate an existing device.

struct libevdev *dev;
struct libevdev_uinput *uidev;
int rc;

rc = libevdev_new_from_fd(fd, &dev);
if (rc 

The above code will create a device from a fd, duplicate that device as a uinput device and then post a x/y relative event through that uinput device. Because we opened the uinput device as LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev will handle access to the /dev/uinput node.

Duplicating devices is useful, but a more likely use-case is to create a device from scratch:

int fd;

dev = libevdev_new();
libevdev_set_name(dev, "my device");
libevdev_enable_event_type(dev, EV_REL);
libevdev_enable_event_code(dev, EV_REL, REL_X);
libevdev_enable_event_code(dev, EV_REL, REL_Y);
libevdev_enable_event_type(dev, EV_KEY);
libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT);
libevdev_enable_event_code(dev, EV_KEY, BTN_MIDDLE);
libevdev_enable_event_code(dev, EV_KEY, BTN_RIGHT);

fd = open("/dev/uinput", O_RDWR);

rc = libevdev_uinput_create_from_device(dev, fd, &uidev);
if (rc 

This time we created a blank device, set a few bits and created a uinput device from that. The result should be a device that looks like a normal three-button mouse to most of the stack.

As you can see, because this time we opened /dev/uinput ourselves, we need to close it ourselves too. libevdev won't touch the fd unless it's in LIBEVDEV_UINPUT_OPEN_MANAGED mode. Note that you can only ever have one active uinput device per fd, and closing the fd will destroy the uinput device (but won't free the memory, you'll still have to call libevdev_uinput_destroy).

Accessing uinput devices

We just created a uinput device, but how do we actually use it? Well, as shown above events are just written to the device directly. But sometimes we have to create a device and re-open it through libevdev.

int fd;
struct libevdev *dev;
const char *devnode;

devnode = libevdev_uinput_get_devnode(uidev);
fd = open(devnode, O_RDWR|O_NONBLOCK);
rc = libevdev_new_from_fd(fd, &dev);
if (rc 

Voila. That's all there is to it to complete the circle. You can now use that device to create a uinput device again, and so on, and so forth.

A word of warning: the kernel does not (yet) provide an ioctl to get the device number from a newly created uinput device. libevdev has to guess what the device is going to be. In some cases, this guess may come up with the wrong device. This can happen if you create multiple uinput devices with the same name at the same time. So, don't do that. Either change the name, or delay creation so that the timestamp (one-second resolution!) differs for each device.

libevdev - accessing and modifying devices

This post describes how to change the appearance of a device through the new libevdev library. For more information about libevdev, please refer to the previous post on libevdev.

Changing a device

So you have a device but for some reason it doesn't exactly reflect what you actually need. This can happen for broken devices that export random axes, or it can happen if the software stack needs certain bits that the device doesn't actually provide. The code below is C-style pseudocode, don't expect to be able to directly take and compile it.

struct libevdev *dev;
int rc;

rc = libevdev_new_from_fd(fd, &dev));
if (rc 

Simple enough - we've disabled ABS_RX, so we'll never get an event from this instance. Note that this is a local change only, so anyone else reading the device will still receive ABS_RX events. Likewise, we enabled ABS_PRESSURE so that future calls to libevdev_has_event_code will return true, including the axis range we've provided. This too is a local change only and won't affect anyone else. For obvious reasons, enabling a bit on the device doesn't actually make the device generate events of that type. Otherwise, the HW industry would be out of business quickly.

For local changes, libevdev provides setters for almost every field. I won't go into more details here, the API should be obvious enough so that e.g. changing the device name is straightforward.

Modifying the kernel device

A few calls can actually modify the kernel device, so that other readers of the device will see modified data.

if (libevdev_has_event_code(dev, EV_ABS, ABS_PRESSURE))
     rc = libevdev_kernel_set_abs_info(dev, ABS_PRESSURE, &abs));

This call would actually change the axis ranges on the device. A future reader of the device would thus see the new range. Note that the kernel won't enable the bits as you upload the new data, so you can't actually create new axes on the device, only modify existing ones.

Modifying LEDs

A slightly more common scenario is toggling LEDs on a device:

if (libevdev_has_event_code(dev, EV_LED, LED_NUML))
   rc = libevdev_set_led_value(dev, LED_NUML, LIBEVDEV_LED_ON);

/* for the lazy: */
rc = libevdev_set_led_values(dev, LED_NUML, LIBEVDEV_LED_ON,
                                  LED_CAPSL, LIBEVDEV_LED_OFF,
                                  LED_SCROLLL, LIBEVDEV_LED_ON,
                                  -1);

Again, this shouldn't need much explanation. The first call toggles a single LED, the second call toggles multiple LEDs in one go.

libevdev - handling input events

This post describes how to read input events from the kernel through the new libevdev library.

What is libevdev?

libevdev is a wrapper library to access /dev/input/eventX devices and provide their events through a C API. It buffers the device and is essentially a read(2) on steriods. Instead of read(2) on the file descriptor, you'd call libevdev_next_event() to fetch the next event that is waiting on the fd. And the buffering allows a process to access device data easily.

Why use a library though? The kernel interface is relatively simple, but it has a few pitfalls. For one, device data is accessed through ioctl(2) and can cause weird bugs [1]. Second, not all events work in the same way. e.g. EVIOCGABS doesn't work the same for multi-touch axes, simply because the slot protocol has different semantics than the normal EV_ABS protocol. EV_REP has different handling as EV_ABS, EV_SYN is a special case anyway, etc. libevdev tries to avoid having to think about the differences and does sanity checks for the various calls.

Repositories and documentation

Status of libevdev

libevdev is currently in version 0.4, and the current API is expected stable. That is, we don't foresee any changes unless we discover some severe bug. If that is the case, I will update the blog post here.

Example code

The code snippets below are in C-style pseudocode. You won't be able to just take them and compile them, but look at libevdev-events for a real tool that does almost everything described below.

Initializing a device

The first step to get a device is to open it. That is not actually handled by libevdev directly, rather it expects an already opened file descriptor. The reason is simple: reading /dev/input/event devices usually requires root and the process accessing the device may not have these permissions. In weston for example, the fd is passed from the suid weston-launch binary. Ok, enough talk, let's see some code:

struct libevdev *dev;
int fd;
int rc;

fd = open("/dev/input/event0", O_RDONLY|O_NONBLOCK);
if (fd 

Fairly straightforward. Open a new device from the file descriptor and initialize it. On error, the return value is a negative errno.

printf("Device: %s\n", libevdev_get_name(dev));
printf("vendor: %x product: %x\n",
       libevdev_get_id_vendor(dev),
       libevdev_get_id_product(dev));

if (libevdev_has_event_type(dev, EV_REL) &&
    libevdev_has_event_code(dev, EV_REL, REL_X) &&
    libevdev_has_event_code(dev, EV_REL, REL_Y) &&
    libevdev_has_event_code(dev, EV_KEY, BTN_LEFT) &&
    libevdev_has_event_code(dev, EV_KEY, BTN_MIDDLE) &&
    libevdev_has_event_code(dev, EV_KEY, BTN_RIGHT))
    printf("Looks like we got ourselves a mouse\n");

libevdev_free(dev);
close(fd);

Getting information about the device is done by simply calling the various getters. And checking the device for functionality is done by checking the various event codes we care about. Note that the above code checks for the EV_REL event type first, then for the actual axes bits. This is just for completeness, it is not necessary. Checking for an event code also checks for the event type so we can skip libevdev_has_event_type(). Both approaches are allowed of course, whichever makes you feel more comfortable about the code.

Finally, cleaning up: Because we don't handle the fd in libevdev, we just use it, you'll have to close that separately.

Ok, the gist of how to access a device should be clear. Let's move on to reading events from the device

Reading events

In the standard case, we just want to get the next event and process it.

struct input_event ev;

rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
if (rc 

The error handling should be clear by now: negative errno means something has gone wrong. Except -EAGAIN, wich indicates that there are no events to read at the moment. A return value of LIBEVDEV_READ_STATUS_SYNC is special, it signals a SYN_DROPPED event which I'll describe later.

A return value of LIBEVDEV_READ_STATUS_SUCCESS means success, so we know we have an event and we can print it. libevdev provides some helper functions to print the string value of an event type or code. The code above could, for example print something like this:

We have an event!
2 (EV_REL) 0 (REL_X) value -1

As you can see, all this effort just to read the same thing off the kernel device that you would've otherwise with a read(2) call. But wait! There's more!

Event buffering

libevdev buffers events internally and always tries to read the maximum number of events off the kernel device. So when you call libevdev_next_event, libevdev may read 50 events off the fd (or whatever is available) and only give you the first. On the next call, it will simply give you the second event of those first 50, but try to read more again to keep the kernel buffer as empty as possible.

Whenever you request an event, libevdev will update its internal state to match the current device state so the client doesn't have to. So if you need to keep track of button states, you can rely on libevdev:

if (!libevdev_has_event_code(dev, EV_KEY, BTN_LEFT))
        return;

if (libevdev_get_event_value(dev, EV_KEY, BTN_LEFT) == 0)
   printf("Button is up\n");
else
   printf("Button is down\n");

/* BTN_LEFT event happens */

rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev))
...

if (libevdev_get_event_value(dev, EV_KEY, BTN_LEFT) == 0)
   printf("Button is up\n");
else
   printf("Button is down\n");

If no button is pressed, then pressed before the next event is read, this snippet would print "Button is up" and "Button is down". Fairly obvious, I think.

Important to point out is that the device state is always the state as seen by the client, i.e. if the client would keep track of the device state based on the events libevdev hands to it, libevdev and the client would always have the same state. Why is this important? libevdev reads multiple events off the wire whenever a client calls libevdev_next_event, but these events do not update the state of the device until passed to the client. So again, since libevdev reflects the state as seen by the client, the client doesn't need to keep track of the state itself. Winners all 'round.

SYN_DROPPED device syncing

A EV_SYN/SYN_DROPPED event is relatively recent (kernel 2.6.39). If a device sends events faster than userspace can read it, eventually the kernel buffers are full and the kernel drops events. When it does so, it sends a EV_SYN/SYN_DROPPED event to notify userspace. The userspace process then needs to stop what it's doing, re-sync the device (i.e. query all axis, key, LED, etc. values), update the internal state accordingly and then it can start reading events again.

libevdev handles all this for you. In the example code above, you saw that a return value of LIBEVDEV_READ_STATUS_SYNC signals a SYN_DROPPED event and we called handle_syn_dropped(). This function is actually incredibly easy:

void handle_syn_dropped(struct libevdev *dev) {
    struct input_event ev;
    int rc = LIBEVDEV_READ_STATUS_SYNC;

    while (rc == LIBEVDEV_READ_STATUS_SYNC) {
        rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
        if (rc 

You notice there is almost no difference to the normal event loop. A different read flag, and instead of an rc of 0, we're now expecting an rc of LIBEVDEV_READ_STATUS_SYNC. libevdev will give us events that all reflect the state change since the SYN_DROPPED so we can update the client accordingly. Once the device is fully synced, libevdev_next_event returns -EAGAIN to indicate there are no more events to sync. The client can go back to reading events normally with LIBEVDEV_READ_FLAG_NORMAL.

This is a lot simpler than having to ioctl the device and calculating the state manually.

The state handling is the same as described above. Even though libevdev knows that there are e.g. a few button events waiting in the sync queue it will not update the client-visible state until it passed the respective event to you.

Finally: you don't have to sync the device after a SYN_DROPPED event. You can chose to keep reading with LIBEVDEV_READ_FLAG_NORMAL as if nothing happened. If you do so, libevdev will drop the sync event queue, update the internal state to match the sync status and pass you the next real event. So even if you didn't get that button down event because you dropped the sync, libevdev_get_event_value(dev, EV_KEY, BTN_LEFT) will now return 1 to reflect the state of the device. So libevdev's device state still matches what the client would otherwise see (had it processed all events).

This is a base overview of how libevdev works. In the next post, I'll show how to manipulate the device.

[1] look the kernel source, drivers/input/evdev.c:handle_eviocgbit, supplying the wrong size was common enough to warrant a warning in the kernel.

Monday, April 18, 2011

GNOME 3.0 middle mouse button emulation

This is essentially a GNOME 3.0-specific update to New evdev middle mouse button emulation defaults. In recent evdev versions, middle mouse button emulation is disabled by default and can be enabled with Option "Emulate3Buttons" "on" in the xorg.conf file or an xorg.conf.d snippet.

GNOME 3.0 has a gsettings key to also trigger this behaviour on a per-user basis (see bug 633863). The default value is off (false). Settings applied from the desktop environment overwrite xorg.conf(.d) settings, so to get middle button emulation in GNOME simply toggle the gsettings key and don't worry about xorg.conf(.d). The command to toggle the key is:


$> gsettings set org.gnome.settings-daemon.peripherals.mouse \
middle-button-enabled true


Since this gsettings are persistent, setting the key once is sufficient. It will be applied after login, for each device and as new devices are hot-plugged.

Tuesday, June 1, 2010

New evdev middle mouse button emulation defaults

As a heads-up, the next version of evdev (2.5) has a changed default for the middle mouse button emulation code. It is now off by default.

First of all - what is middle mouse button emulation?
Back in the olden days, when oil was cheap and mouse buttons were the peak of technology and thus expensive, mice only had two buttons. Possibly also to confuse user because two buttons for a (on average) five-finger hand seems just the right thing to do. Anyway, I digress.

One version of copy/paste in X relies on the middle mouse button, so a way to emulate that button was implemented in the driver. When this emulation was on, a left button was held back for a timeout and if during this timeout the right button was pressed, the driver would instead post a middle button event. If the right button didn't get pressed in time, the left button event was sent after the timeout expiry.

Coffee addicts could set this timeout to multiple minutes enough to have an excuse to fuel their addiction whenever they tried to do anything with the GUI. Now, two things have happened. The price of oil has surpassed the price of mouse buttons and on virtually all mice we have shiny things like scroll wheels, side buttons and whatnot. Also, computers have gotten a lot faster and people get annoyed about lag in the UI. Holding back a mouse button event by a few milliseconds seriously reduces the number of tweets one can write.

For the last couple of releases, we had the emulation on "auto" by default. It was on, until a physical middle mouse button click was detected. At this point the driver realised the emulation wasn't needed and turned it off. Which made for interesting side-effects: if you didn't know this feature, the GUI felt snappier after a middle mouse button press - for no obvious reason.
The "auto" option was a permanent false positive, there's no really reliable method to detect if a mouse has a middle mouse button.

So I've removed that "auto" feature and defaulted to off. Result - a slightly snappier feel of the UI for a quite underused feature. Of course, there are plenty of users of this feature, so it wasn't removed completely. If you need middle button emulation, please enable it with an xorg.conf snippet or ask your favourite desktop environment to provide a convenient checkbox.


Section "InputClass"
Identifier "middle button emulation class"
MatchIsPointer "on"
Option "Emulate3Buttons" "on"
EndSection

Wednesday, November 11, 2009

evtest-capture and replaying events

Many of you are probably familiar with evtest, a small debugging utility written by Vojtek Pavlik to check what input device data is leaving the kernel. I use it a lot and one of the standard requests I make in bugreports is run evtest against the device file. The information it prints tells us the capabilities a device has and the events being sent when a certain action is performed.

I've used this information to add uinput device to my uinput test devices collection to debug various server and/or driver failures.
That worked for some cases but suffered from a major caveat: it was incredibly hard to reproduce issues that resulted from complex interactions. For months, I've been meaning to fix this and last weekend I finally had time to sit down and hack something up. That work is now in the evtest repository.

It works quite simple:
The user runs evtest-capture against the device and performs the action that reproduces the bug. evtest-capture saves the event stream into an xml file, this xml file can then be converted into a standalone uinput-based C program that resembles both the device and the interaction. I can re-create and run that program on my test box and reproduce and hopefully debug the issue.

The full set of steps is (as root where necessary):


$> evtest-capture /dev/input/event0 pointer-crashes.xml
$> xsltproc evtest-create-device.xsl pointer-crashes.xml > pointer-crash-device.c
$> gcc -o pointer-crash-device pointer-crash-device.c
$> ./pointer-crash-device
#verify the issue happens with this test device.


Like so many things, it's not perfect but it will hopefully help us reproduce a lot more bugs and thus make it simpler to find and address these bugs.

Thursday, February 26, 2009

Tip of the day - wheel emulation

[update 30 May 2012: add xorg.conf.d snippet] First, run xinput --list --short to identify your mouse device. Let's assume that your mouse is called "USB Mouse".
Then, run the following command:

xinput --set-int-prop "USB Mouse" "Evdev Wheel Emulation" 8 1
xinput --set-int-prop "USB Mouse" "Evdev Wheel Emulation Button" 8 3

Now, holding the right mouse button and moving up and down will scroll. Very useful for reading long articles.

Requirements: evdev 2.1, X server 1.6 and xinput 1.4. Don't forget to check the evdev man page, there's a few extra tweaks related to wheel emulation. xinput is not persistent, but the above can be achieved with the following xorg.conf.d snippet:
Section "InputClass"
   Identifier     "Wheel Emulation"
   MatchIsPointer "on"
   MatchProduct   "USB Mouse"
   Option         "EmulateWheelButton" "3"
   Option         "EmulateWheel" "on"
EndSection
Save this as /etc/X11/xorg.conf.d/99-wheel-emulation.conf and you're good to go. Requires X Server 1.8 or later, for older servers you can do the same by merging the keys input.x11_options.EmulateWheel and input.x11_options.EmulateWheelButton your HAL fdi file.

Tuesday, February 17, 2009

Generic axis support in evdev

After the groundwork by Matt Helsley we now have generic axis support in evdev. So evdev finally sets the device up according to what the kernel reports rather than limiting itself to x/y and potentially pressure.

A truly generic driver. Rejoice.

And because we can, it does axis labeling too (if you have xserver git master, that is).
No more guessing what axis 7 actually is.

The obligatory "screenshot":


whot@roo:~> xinput --list-props "PIXART USB OPTICAL MOUSE" | grep "Axis Labels"
Axis Labels (242): "Rel X", "Rel Y", "Rel Vert Wheel"
whot@roo:~> xinput --list-props "Wacom Intuos3 4x6" | grep "Axis Labels"
Axis Labels (242): "Abs X", "Abs Y", "Abs Z", "Abs Rotary X", "Abs Rotary Z", "Abs
Throttle", "Abs Wheel", "Abs Pressure", "Abs Distance", "Abs Tilt X", "Abs Tilt Y", "Abs Misc"


Expect this to be in evdev 2.2, once it has matured and potential glitches are ironed out.

Sunday, October 19, 2008

Avoid evdev devices in xorg.conf

Quick addition to my previous blog post:

If you have an xorg.conf that references evdev devices, these will generate duplicate events with the recent changes (they get added twice). Just get rid of the matching entry in your xorg.conf (you most likely don't need it anyway).

Otherwise, add Option "GrabDevice" "True" to the respective input device. This way you restore the old behaviour that forces a kernel grab. But then again, having the device in your xorg.conf devoids you of any hotplugging (for this device) anyway, so what's the point.

There was one thing I missed when testing: if you can CTRL+C the server or VT switch with Alt+cursor keys, then update to xorg-x11-server-1.5.2-6. A one-line fix that is needed if no xorg.conf is present.

Thursday, October 16, 2008

New keyboard configuration handling

Note that the changes I describe here are currently available in Fedora rawhide, but I expect they will be available soon in a similar form both in X.Org git and in other distributions.

We finally closed the last big chapter in the evdev input driver transition - the handling of device grabs (in the kernel). As described previously, evdev used to put a kernel grab on the device so no-one else could open the same device. This avoided duplicate events but the grab hindered other programs that needed access to the device too: lirc, rfkill, HAL and others.
The need for the grab was removed with quite a simple patch. However, this isn't the whole story, and another patch changes server's default behaviour.

So here's the rundown how it may affect you:

In short, evdev and kbd/mouse are mutually exclusive as they produce duplicate events if both are active. In any configuration where both are specified, kbd/mouse are ignored.


If you don't have an xorg.conf
, your devices were hotplugged through HAL. Nothing changes for you.

If you have an xorg.conf with input devices using mouse or kbd as driver, they are now ignored.
Effectively nothing changes though: due to the evdev grab these mouse/kbd devices were previously "mute", i.e. the would never send events anyway. You might as well remove the respective sections from your xorg.conf. However, having the devices in your xorg.conf provided a fallback: if HAL was not working, kbd/mouse would pick up the slack. This fallback is gone.

If you disable AllowEmptyInput, AutoAddDevices or AutoEnableDevices, then you get the traditional X server behaviour:

  1. Devices referenced in the ServerLayout are available in the server, or if none are specified,

  2. The first pointer or keyboard device in the xorg.conf become available in the server, or if none are specified,

  3. The standard configs become available (/dev/console and /dev/input/mice).



Disabling AllowEmptyInput is discouraged and should not be necessary on virtually all standard desktop systems.

Update: If you are using evdev 2.1 and you only disable AllowEmptyInput, the devices will still be added and you will get duplicate events (key repeat handling will then make each key press emit three characters). AEI defaults to (AutoAddDevices && AutoEnableDevices), so if you want to disable HAL, the best option is to set AutoAddDevices to "off".

Thursday, July 24, 2008

Input configuration in a nutshell.

Once upon a time, we had a number of free operating systems. Linux, BSDs, emacs, XFree86, you name it. Then one of them chose to be less like one and become a windowing system instead (which was supposed to it's job from the beginning, albeit it kinda forgot about it halfway there). The principle of DTRT was embraced and the almighty xorg.conf file was given the handshake and shown the door. These days, X starts without a config file these days, and everything just works.

Most of the time. When it doesn't it's usually ugly. So here's a guide on how to configure input devices with recent servers. This guide includes
  • a quick overview of the evdev input driver
  • how to set up input hotplugging.
  • what to do in the xorg.conf
First, if you're not running Linux you can stop reading. Keep your config file, make sure you reference your input devices in the ServerLayout section, and start porting the evdev kernel module to your OS of choice.


A quick overview of the evdev input driver
xf86-input-evdev is the hotplugging-capable input driver to be used for mice and keyboards (and other devices, if appropriate). One main difference is that it uses devices files such as /dev/input/event0 instead of /dev/input/mouse0 (like the mouse driver does for example). The other big difference is that it can query the kernel about the capabilities about each device and set up itself correctly. So if you point a device to /dev/input/event0, it will automatically set itself up as mouse or keyboard, as appropriate.
The second important thing is that it puts a grab on the device file (not the same as a pointer/keyboard grab in X!). This grab gives us two benefits: the device will not send events to the collector devices (e.g. /dev/input/mice) anymore. And if anyone else tries to grab the device they will fail. So we can ensure that only one process receives events from this device.

What does all this mean to us now?
We can change our mice and keyboards in the xorg.conf to use the evdev driver instead. As device, we can either specify /dev/input/eventX, but these numbers may change on reboot. Look into the /dev/input/by-id and /dev/input/by-path directory. I recommend to always use /dev/input/by-id/MyMouseName-event-mouse as device instead of the eventX files. Note that you'll find MyMouseName-mouse and MyMouseName-event-mouse, of which only the latter will work with evdev. Ignore the former.
The big benefit is simply that you can configure multiple devices quite easily now. Simply set up InputDevice sections for each device and the X server will treat them as different devices. This is quite useful if you're using MPX.


How to set up input hotplugging

Writing xorg.conf sections is not only boring, it's also quite pointless in most cases. So let's let the server find input devices automagically. The two ingredients are HAL and DBus. At startup, the server queries HAL over DBus for a list of devices and adds them one-by-one.

All you need to do is configure HAL so the server adds the devices with the right options.
You must merge the key input.x11_driver. Look at x11-input.fdi. This file should reside in /etc/hal/fdi/policy/, and it tells HAL to merge the evdev driver for each mouse and keyboard It also merges some Xkb options for keyboards.
Important: the server will not add devices unless they have input.x11_driver set. If you end up with no devices at all, then your HAL setup is probably broken.

To test your installation, run hal-device to obtain a list of all devices and look for the input.x11* keys. If they are not present, modify the fdi file and restart HAL.

Your X server is now hot-plug capable. It will add all mice and keyboards automatically, even if you add/remove them at runtime. There's modified fdi files floating around to add hotplug for wacom tablets and synaptics touchpad, finding them is left as an exercise for the reader.

What to do with the xorg.conf
We have two configuration files now. Yay. One is xorg.conf, the other one is the fdi file. And they both don't know about each other, leading to interesting results. The server parses xorg.conf first, then gets the devices through HAL. And here we have a big culprit: if add a InputDevice section with the mouse driver, it will conflict with the evdev driver. Evdev grabs the files, remember? So when all mice are added through the HAL evdev hotplugging mechanism, all mice stop sending to the /dev/input/mice file. So although you still have a valid input device in the server, it will never emit any events.
The only solution here? Either stop hotplugging alltogether by adding Option "AutoAddDevices" "off" in your ServerLayout, or modify the fdi file that it doesn't merge input.x11_driver for the specific mice you don't want hotplugged (or all mice).

Troubleshooting list:
  • I have no input devices and compiled the server myself. Install libhal-dev and libdbus-dev, re-run configure and recompile.
  • I have no input devices. HAL setup needs to be configured. Make sure the x11-input.fdi file is in /etc/hal/fdi/policy, and that hal-device lists mice and keyboards with input.x11_driver = "evdev".
  • My xorg.conf settings are ignored. HAL adds your devices without settings, and evdev grabs all devices away. Modify the fdi and add your settings there, or add Option "AutoAddDevices" "off" to your ServerLayout. Note that this will stop input hotplugging.
  • I'm getting a "Grab failed" in the logs. You have two evdev input devices pointing to the same device file. This usually only happens if you have a xorg.conf entry with an evdev, and then HAL tries to add the same device again. You can ignore this warning.
  • I have devices in my xorg.conf but still don't get any devices. Your ServerLayout doesn't reference the input devices and your HAL setup is broken. Add Option "AllowEmptyInput" "off" to your ServerLayout or simply reference the InputDevice sections in the ServerLayout. While you're at it, you may also want to configure HAL.
  • My touchpad is slow. Check the logs if the touchpad is added with the evdev driver. If so, modify the fdi file to merge synaptics as input.x11_driver, or add a InputDevice section in your xorg.conf (must be referenced by the ServerLayout).
  • Evdev doesn't support option "XYZ". Add a feature request to X.Org Bug 16699. Bugs with patches get preference.