C ++ на Linux: прослушивание ввода с клавиатуры при работе в качестве службы systemd - PullRequest
1 голос
/ 27 мая 2020

Итак, по этому топу было несколько вопросов c, но без действительно качественных ответов.

Моя проблема заключается в следующем: у меня есть встроенное приложение, работающее на Raspberry Pi, которое запущено через init.d. Вся установка не имеет экрана и, как правило, предполагается, что она работает с сетью, отключенной в производственном использовании (b c это будет go в среде, где помехи от Wi-Fi / Bluetooth могут быть проблематичными c).

Я хочу использовать обычную c USB-клавиатуру / цифровую клавиатуру в качестве устройства ввода для настройки и устранения неполадок. Естественно, я не могу просто читать из cin, потому что моя программа не запущена на терминале (и, фактически, терминала нет).

Как я могу это сделать, желательно на уровне, где i не нужно беспокоиться о деталях раскладки клавиатуры и / или количестве подключенных устройств ввода?

edit Моим вероятным обходным решением будет что-то, связанное с автоматическим входом и скриптом .profile. Тем не менее, если у кого-нибудь есть решение этой проблемы, мы будем признательны.

1 Ответ

1 голос
/ 28 мая 2020

Похоже, что @meuh побеждает повара ie за лучший совет: libevdev точно в точку.

Я нашел этот ответ , который решил связанную проблему привязки клавиша (ALT-X) для автоматического запуска программы, и общую структуру было действительно легко адаптировать (мой измененный код ниже).

В каждом полученном struct input_event вы ищите ev.type == EV_KEY для захвата событий клавиатуры (в отличие от событий мыши или других событий) ev.code содержит код для клавиши (KEY_UP, KEY_0, KEY_KP5, KEY_BACKSPACE, et c.). Я тестирую только цифровую клавиатуру, поэтому я не получаю сдвиги, alt или что-то подобное, но я подозреваю, что это просто.

Вы также смотрите ev.value, что может быть:

  • EV_KEY - клавиша вниз
  • EV_REL - значения повторения клавиш (необязательно, может быть больше одного)
  • EV_SYN - клавиша вверх

Я полагаю, что для некоторых приложений вы могли бы просто игнорировать все, кроме EV_SYN для захвата события нажатия клавиши; это то, что я буду делать.

$ 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

Обратите внимание, что значения клавиш не являются ASCII и также не являются традиционными кодами сканирования клавиатуры - это совершенно новое пространство имен, и, вероятно, есть какой-то другой уровень абстракции, который переводит на обычный ASCII, но я не искал его, так как коды KEY_ * подходят для моего приложения. Но это намного лучше, чем паршивый механизм /dev/hidraw0, который я использовал раньше.

Он требует разрешения root, что имеет смысл, потому что в противном случае ваша программа пользовательского режима могла бы ждать, пока суперпользователь войдите в консоль, получив свой пароль. Я уверен, что для встроенного приложения это не будет проблемой.

Спасибо, @ meuh за отличный совет. И мне даже не пришлось писать драйвер устройства!

Приведенный ниже код работает на BeagleBone под управлением 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 "-?-";
    }
}
...