is there a higher level (character?) based way to access a Linux keyboard other than /dev/input/eventx?

464 Views Asked by At

I'm trying to write a Linux daemon that needs to take input from a keyboard-like (HID) device - it's not the console keyboard, but a second device (it's actually a 1D barcode scanner if it makes any difference). The daemon will process the data it receives ("keystrokes" from alphanumeric barcodes).

I know the daemon can use ioctl(EVIOCGRAB) to grab that device from /dev/input/eventx and then read() events (struct input_event from <linux/input.h>), that works, but it's too low level for my needs. I don't need to know about every up and down key event and I would rather not have to decode/handle simultaneous keys, i.e. I don't want to know KEY_DOWN+KEY_RTSHIFT, KEY_DOWN+KEY_A, KEY_UP+KEY_RTSHIFT, KEY_UP+KEY_A, I just want to receive "A".

Using the input events needs a whole pile of extra code to be written, just to get "A" out of it - and that seems a waste of time and effort when there are almost certainly existing keyboard handlers (or something like that) that will do a far better job than me hacking code together - I just can't find them!

Is there any way to put an existing layer of (keyboard?) software onto /dev/input/eventx that the daemon can then exclusively read a stream of simple ascii from?

For this device, /proc/bus/input/devices reports ...

H: Handlers=sysrq kbd leds event9

... which might suggest that something has already put a "kbd" layer on it, but how to access it?

If I don't EVIOCGRAB, then when I scan a barcode I see syslogs about "failed login on /dev/tty1", so that keyboard input is clearly trying to login to a terminal/shell somewhere :-/ (There's no X, desktop etc on this machine either.)

2

There are 2 best solutions below

0
On

The libevdev library is not exactly what you ask for. But it is widely used (e.g. by the Xorg server evdev and libinput drivers).

As state on the home page:

libevdev is essentially a read(2) on steroids for /dev/input/eventX devices.

It manages ioctl calls for you, handles an events queue, etc.

One additional feature you didn't ask for, but it is really helpful: it can handles events from multiple devices at once. It allows you to listen on multiple devices located in /dev/input and handle the same event type in a single thread.

0
On

I am not aware of such library or daemon.

However, writing a daemon to do exactly that is not nearly as hard as you think. In my opinion/experience, if you do not need to implement autorepeat (that is, only do autorepeat if the keyboard-like device sends autorepeat keypresses), the daemon is surprisingly simple.

In particular, the main loop is just a blocking read followed by zero or more blocking writes, with an array look-up in the middle.

You can use either a named pipe (in which case you open the pipe before opening the device, because the open succeeds only after another process opens the named pipe or FIFO for reading), or a socket; either Unix Domain socket, or a TCP or UDP socket (on the loopback interface). I'd probably use a named pipe, /var/run/barcode-scanner.

I would use a separate configuration file for the input mapping, and I'd support the preprocessor macro names (KEY_) from /usr/include/linux/input-event-codes.h, parsed to an array using a helper awk script. The key mapping file would probably live in /etc/barcode-scanner/keymap, and contain definitions similar to

KEY_1 = 1
KEY_NUMERIC_1 = 1
KEY_E = e
shift KEY_E = E
ctrl KEY_E = \5
altgr KEY_E = €
KEY_UP = \033[A

and so on. Events not listed in the definition file would be ignored or optionally logged.

You can use an array, because there are up to 768 (0 to KEY_MAX, inclusive; although 0 is KEY_RESERVED) different keyboard events; as a two-dimensional array, you'll probably want something like 16 arrays, for supporting all kombinations of Shift, Ctrl, Alt, and AltGr (or right alt key), when each key is pressed. Internally, you'll just need to handle KEY_LEFTSHIFT, KEY_RIGHTSHIFT, KEY_CAPSLOCK (to manage the shift state); KEY_LEFTALT (to manage the alt state); KEY_RIGHTALT (to manage the altgr state); and KEY_LEFTCTRL and KEY_RIGHTCTRL (to manage the ctrl state). If you use a bit mask to indicate the current states, you can use it as an index to the outer dimension of the lookup array. It really is very straightforward.

Note that each array element is then a string, that is emitted when a key of that state is pressed (ev.value == 1 for keypress, ev.value == 2 for autorepeat). Aside from the above special keys, you do not need to handle key releases at all.

Hm. I wonder if there are others who would need such a daemon? Would you use a GPL3-licensed one, if the sources were at GitHub?