It seems like @meuh wins the cookie for the best advice: libevdev
is exactly on point.
I found this answer that solved a related problem of binding a key (ALT-X) to automatically launch a program, and the overall structure was really easy to adapt (my modified code is below).
In each struct input_event
you get, you look for ev.type == EV_KEY
to pick off keyboard events (as opposed to mouse or other events), ev.code
contains the code for the key (KEY_UP
, KEY_0
, KEY_KP5
, KEY_BACKSPACE
, etc.). I'm testing with just a number pad so I don't get the shifts or alt or the like, but I suspect it's straightforward.
You also look at ev.value
, which can be:
EV_KEY
- key down
EV_REL
- key repeat values (optional, can be more than one)
EV_SYN
- key up
I imagine for some applications you could just ignore all except for the EV_SYN
to capture the key-up event; that's what I'll be doing.
$ sudo ./evtest
Device /dev/input/event1 is open and associated w/ libevent
KEY: Value=EV_KEY; Code=KEY_KP7 <-- KP = keypad
KEY: Value=EV_SYN; Code=KEY_KP7
KEY: Value=EV_KEY; Code=KEY_KP8
KEY: Value=EV_SYN; Code=KEY_KP8
KEY: Value=EV_KEY; Code=KEY_KP9
KEY: Value=EV_SYN; Code=KEY_KP9
Note that the key values are not ASCII and are also not traditional keyboard scan codes - these are a whole new namespace, and there's probably some other abstraction layer that translates to regular ASCII, but I haven't looked for it as the KEY_* codes are fine for my application. But it's way, way better than the lousy /dev/hidraw0
mechanism I was using before.
It does require root permission, which makes sense because otherwise your user-mode program could lay in wait for the superuser to login on the console, snagging their password. For an embedded application I'm sure this is not going to be an issue.
Thank you, @meuh for the great tip. And I didn't even have to write a device driver!
The code below runs on a BeagleBone running Debian Buster.
// hack test for working with events
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <err.h>
#include <libevdev-1.0/libevdev/libevdev.h>
#define COUNTOF(x) (int) ( ( sizeof(x) / sizeof((x)[0]) ) )
static void setupKeyCodes(void);
static const char *printableEventType(int t);
static const char *keycodes[64 * 1024] = { 0 }; // hack
int main(void) {
setupKeyCodes();
const char *eventDevice = "/dev/input/event1";
const int fd = open(eventDevice, O_RDONLY | O_NONBLOCK);
if (fd < 0) errx(EXIT_FAILURE, "ERROR: cannot open device %s [%s]", eventDevice, strerror(errno));
struct libevdev *dev;
int err = libevdev_new_from_fd(fd, &dev);
if (err < 0) errx(EXIT_FAILURE, "ERROR: cannot associate event device [%s]", strerror(-err));
printf("Device %s is open and associated w/ libevent\n", eventDevice);
do {
struct input_event ev;
err = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
if (err == 0 && ev.type == EV_KEY)
{
printf("KEY: Value=%s; Code=%s\n",
printableEventType(ev.value),
keycodes[ev.code]);
}
} while (err == 1 || err == 0 || err == -EAGAIN);
return 0;
}
// HACK: populate the whole array of possible keycodes with their strings
// so we can see what we're reading.
static void setupKeyCodes(void)
{
for (int i = 0; i < COUNTOF(keycodes); i++)
keycodes[i] = "-unknown-";
// these from /usr/include/linux/input-event-codes.h
keycodes[KEY_RESERVED] = "KEY_RESERVED";
keycodes[KEY_ESC] = "KEY_ESC";
keycodes[KEY_1] = "KEY_1";
keycodes[KEY_2] = "KEY_2";
keycodes[KEY_3] = "KEY_3";
keycodes[KEY_4] = "KEY_4";
keycodes[KEY_5] = "KEY_5";
keycodes[KEY_6] = "KEY_6";
keycodes[KEY_7] = "KEY_7";
keycodes[KEY_8] = "KEY_8";
keycodes[KEY_9] = "KEY_9";
keycodes[KEY_0] = "KEY_0";
// ... many many more
keycodes[KEY_STOP_RECORD] = "KEY_STOP_RECORD";
keycodes[KEY_PAUSE_RECORD] = "KEY_PAUSE_RECORD";
keycodes[KEY_VOD] = "KEY_VOD";
keycodes[KEY_UNMUTE] = "KEY_UNMUTE";
keycodes[KEY_FASTREVERSE] = "KEY_FASTREVERSE";
keycodes[KEY_SLOWREVERSE] = "KEY_SLOWREVERSE";
}
#define STRCASE(x) case x: return #x
static const char *printableEventType(int t)
{
switch (t)
{
STRCASE(EV_SYN);
STRCASE(EV_KEY);
STRCASE(EV_REL);
STRCASE(EV_ABS);
STRCASE(EV_MSC);
STRCASE(EV_SW);
STRCASE(EV_LED);
STRCASE(EV_SND);
STRCASE(EV_REP);
STRCASE(EV_FF);
STRCASE(EV_PWR);
STRCASE(EV_FF_STATUS);
default: return "-?-";
}
}