Wednesday, September 18, 2013

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.

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:

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.

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 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:

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.

1 comment:

I set some calibration/settings on input device (turn device wheel to left. I did this with write() function) while this happens input system gathers data. So when I first call libevdev_next_event it gives me old garbage data. But I dont want those.