Linux UINPUT: простой пример?
У меня проблемы с получением обеих сторон кода с помощью uinput
за работой.
Основано на Приступая к работе с uinput: подсистема ввода пользовательского уровня [dead link; заархивировал ] я собрал следующий писатель (минус обработка ошибок):
int main(int ac, char **av)
{
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
int ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);
struct uinput_user_dev uidev = {0};
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-rotary");
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = 255;
ret = write(fd, &uidev, sizeof(uidev));
ret = ioctl(fd, UI_DEV_CREATE);
struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;
ret = write(fd, &ev, sizeof(ev));
getchar();
ret = ioctl(fd, UI_DEV_DESTROY);
return EXIT_SUCCESS;
}
Это, кажется, работает, по крайней мере, полный input_event
структура, кажется, написана.
Затем я написал самый наивный читатель событий, которые я мог придумать:
int main(int ac, char **av)
{
int fd = open(av[1], O_RDONLY);
char name[256] = "unknown";
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
printf("reading from %s\n", name);
struct input_event ev = {0};
int ret = read(fd, &ev, sizeof(ev));
printf("Read an event! %i\n", ret);
printf("ev.time.tv_sec: %li\n", ev.time.tv_sec);
printf("ev.time.tv_usec: %li\n", ev.time.tv_usec);
printf("ev.type: %hi\n", ev.type);
printf("ev.code: %hi\n", ev.code);
printf("ev.value: %li\n", ev.value);
return EXIT_SUCCESS;
}
К сожалению, читательская сторона вообще не работает; только удается прочитать 8 байтов каждый раз, что не является почти полным input_event
состав.
Какую глупую ошибку я делаю?
2 ответа
Вы также должны писать событие синхронизации после фактического события. В вашем коде стороны писателя:
struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;
usleep(1500);
memset(&ev, 0, sizeof(ev));
ev.type = EV_SYN;
ev.code = 0;
ev.value = 0;
ret = write(fd, &ev, sizeof(ev));
getchar();
TL;DR: ядро ожидает события кода, потому что отдельные события могут быть сгруппированы вместе, т. Е. Когда они происходят в один и тот же момент времени.
Вы можете думать об этом как о ядре, интерпретирующем группу событий, а не отдельные события, как указано в. Событие ограничивает эти группы событий кодом, т. Е. Это обеспечивает механизм группировки для сигнализации событий, которые происходят одновременно.
Например, представьте, что вы касаетесь пальцем поверхности тачпада:
Учитывая определение in
linux/input.h
:
struct input_event {
/* ... */
__u16 type; /* e.g., EV_ABS, EV_REL */
__u16 code; /* e.g., ABS_X for EV_ABS */
__s32 value; /* e.g., the value of the x coordinate for ABS_X */
};
Невозможно создать одно событие, которое может содержать как коды и, так и значения для указания координат x и y соответственно - есть только одно
code
член в 1 .
Вместо этого будут созданы два события 2 :
- Один с кодом и координатой x как.
- Другой с кодом и координатой y как
value
.
Было бы не совсем правильно интерпретировать эти два события как два события, разделенных во времени, то есть одно, которое указывает на изменение координаты x, а другое, которое происходит позже во времени и указывает на изменение координаты y.
Вместо двух событий, разделенных по времени, эти события должны быть сгруппированы вместе и интерпретированы ядром как единая единица изменения входных данных, которая указывает на изменение координат x и y одновременно . Вот почему существует описанный выше механизм: генерируя один
EV_SYN
мероприятие с
SYN_REPORT
код сразу после этих двух
EV_ABS
события (т. е.
ABS_X
а также
ABS_Y
), мы можем сгруппировать их как единое целое.
1 Это напрямую связано с тем, что
struct input_event
имеет фиксированный размер и не может увеличиваться произвольно.
2 Технически может быть создано больше событий. Например, другое событие типа
EV_KEY
с кодом
BTN_TOUCH
скорее всего будет создан. Однако это не имеет отношения к тому, о чем я хочу сказать.