struct input_event¶
struct input_event — это структура данных в ядре операционной системы Linux, определённая в заголовочном файле <linux/input.h>, которая используется для передачи информации о событиях, генерируемых устройствами ввода (клавиатурами, мышами, сенсорными панелями, джойстиками, тачскринами и другими). Каждое событие ввода, от нажатия клавиши до движения пальца по сенсору, представляется в виде экземпляра этой структуры, содержащего временную метку, тип события, код и значение. Структура является основой подсистемы ввода (input subsystem) в Linux, обеспечивая единообразный интерфейс между драйверами устройств и пользовательскими приложениями через файлы устройств /dev/input/event*.
¶Определение и синтаксис
В языке C структура input_event объявляется следующим образом:
``c struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; ``
time— поле типаstruct timeval, содержащее временную метку события (секунды и микросекунды, начиная с эпохи Unix). Позволяет точно определить момент возникновения события.type— 16-битное беззнаковое целое (__u16), указывающее категорию события. Основные типы:EV_KEY(0x01) — клавиши и кнопки,EV_REL(0x02) — относительные оси (например, движение мыши),EV_ABS(0x03) — абсолютные оси (например, положение тачскрина),EV_SYN(0x00) — синхронизация,EV_MSC(0x04) — прочие события.code— 16-битное беззнаковое целое (__u16), идентифицирующее конкретную клавишу, ось или кнопку в пределах типа. Например, дляEV_KEYкодKEY_A(30) обозначает клавишу A, дляEV_RELкодREL_X(0) — ось X, дляEV_ABSкодABS_X(0) — абсолютную координату X.value— 32-битное знаковое целое (__s32), содержащее значение события. ДляEV_KEYможет быть 0 (отпускание), 1 (нажатие) или 2 (автоповтор). ДляEV_REL— относительное смещение (например, +1 для движения мыши вправо). ДляEV_ABS— абсолютная координата (например, 500 для позиции на оси X).
¶История и развитие
Подсистема ввода Linux начала формироваться в середине 1990-х годов, когда возникла необходимость унифицировать обработку разнородных устройств ввода. Ранние драйверы использовали собственные форматы данных, что затрудняло разработку приложений. В 1999 году в ядре Linux 2.4 была представлена единая структура input_event как часть подсистемы ввода, разработанной Войцехом Домбровским (Vojtech Pavlik). С тех пор структура остаётся стабильной, хотя в ядро добавлялись новые типы событий (например, EV_SW для переключателей, EV_FF для обратной связи). В 2015 году в ядро 4.1 была добавлена поддержка расширенных временных меток (input_absinfo), но базовая структура input_event не изменилась.
¶Принцип работы
¶Генерация событий
Драйвер устройства ввода, работающий в пространстве ядра, при возникновении физического события (например, нажатия клавиши) формирует экземпляр struct input_event и передаёт его в подсистему ввода через вызов input_event(). Подсистема ввода направляет событие в соответствующий файл устройства /dev/input/eventN, где N — номер устройства. Приложения в пользовательском пространстве могут читать эти файлы как обычные файловые дескрипторы, получая последовательность структур input_event.
¶Синхронизация
Для группировки нескольких событий, относящихся к одному физическому действию (например, нажатие клавиши и её отпускание), используется тип EV_SYN. После отправки набора событий драйвер генерирует событие с type = EV_SYN, code = SYN_REPORT (0) и value = 0. Приложение, читающее события, может считать, что все события, полученные до EV_SYN, относятся к одному моменту времени. Это особенно важно для сенсорных устройств, где координаты X и Y передаются отдельными событиями, но должны обрабатываться как одно касание.
¶Чтение из пользовательского пространства
Приложение открывает файл устройства (например, /dev/input/event0) и читает из него данные блоками размером sizeof(struct input_event) (обычно 16 байт на 32-битных системах, 24 байта на 64-битных из-за выравнивания). Пример чтения на языке C:
```c
¶include <linux/input.h>
¶include <stdio.h>
¶include <fcntl.h>
¶include <unistd.h>
int main() { int fd = open("/dev/input/event0", O_RDONLY); struct input_event ev; while (read(fd, &ev, sizeof(ev)) > 0) { if (ev.type == EV_KEY && ev.value == 1) { printf("Key %d pressed\n", ev.code); } } close(fd); return 0; } ```
¶Типы событий
¶Основные типы
- EV_KEY (0x01) — события клавиш и кнопок. Коды определены в
<linux/input.h>какKEY_(например,KEY_ENTER,KEY_SPACE) иBTN_(например,BTN_LEFT,BTN_RIGHT). Значение: 0 — отпускание, 1 — нажатие, 2 — автоповтор. - EV_REL (0x02) — события относительных осей (мышь, трекбол). Коды:
REL_X(0),REL_Y(1),REL_WHEEL(8) и другие. Значение — относительное изменение (например, +3 для движения на 3 пикселя вправо). - EV_ABS (0x03) — события абсолютных осей (тачскрин, джойстик, планшет). Коды:
ABS_X(0),ABS_Y(1),ABS_PRESSURE(24) и другие. Значение — абсолютная координата в пределах диапазона, заданного вinput_absinfo. - EV_SYN (0x00) — события синхронизации. Код:
SYN_REPORT(0) — завершение группы событий,SYN_CONFIG(1) — изменение конфигурации,SYN_DROPPED(3) — потеря событий (введено в ядре 3.4). - EV_MSC (0x04) — прочие события (например,
MSC_SCAN— скан-код клавиши). - EV_SW (0x05) — события переключателей (например, крышка ноутбука, переключатель режима полёта). Коды:
SW_LID(0),SW_TABLET_MODE(1) и другие. Значение: 0 или 1. - EV_FF (0x15) — события обратной связи (вибромоторы, силовая обратная связь). Используется для управления эффектами.
- EV_LED (0x11) — управление светодиодами (например, Caps Lock, Num Lock).
- EV_SND (0x12) — управление звуковыми сигналами (например, динамик ПК).
¶Расширенные типы
В современных версиях ядра добавлены типы для специализированных устройств: EV_PWR (0x10) — кнопки питания, EV_REP (0x14) — параметры автоповтора клавиш, EV_FF_STATUS (0x17) — статус эффектов обратной связи.
¶Применение
¶Пользовательские приложения
- Серверы отображения и оконные менеджеры — X.Org, Wayland, KWin, Mutter — используют
input_eventдля обработки ввода от клавиатур, мышей и сенсорных устройств. - Эмуляторы — QEMU, VirtualBox — эмулируют устройства ввода, генерируя структуры
input_eventдля гостевой системы. - Игровые приложения — читают события джойстиков и геймпадов через
/dev/input/event*. - Специализированные утилиты —
evtest(отладка устройств ввода),input-utils,libinput(библиотека обработки ввода).
¶Ядро Linux
- Драйверы устройств — каждый драйвер ввода (например,
usbhid,psmouse,atkbd) формируетinput_eventи передаёт его в подсистему ввода. - Виртуальные устройства —
uinput(модуль ядра) позволяет пользовательским программам создавать виртуальные устройства ввода и генерировать события, что используется в автоматизации тестирования и эмуляции.
¶Ограничения и особенности
- Размер структуры — на 64-битных системах поле
timevalзанимает 16 байт (8 байт для секунд, 8 байт для микросекунд), что вместе сtype,codeиvalue(по 4 байта каждое, с учётом выравнивания) даёт 24 байта. На 32-битных системах — 16 байт. - Скорость передачи — при высокой частоте событий (например, сенсорный экран с частотой опроса 1000 Гц) может возникнуть переполнение буфера, что приводит к потере событий и генерации
SYN_DROPPED. - Зависимость от архитектуры — структура использует типы
__u16и__s32, которые гарантированно имеют указанный размер на всех платформах, поддерживаемых Linux. - Безопасность — чтение
/dev/input/event*требует прав суперпользователя или членства в группеinput, что ограничивает доступ к событиям ввода для обычных пользователей.
¶Сравнение с другими подсистемами
В других операционных системах существуют аналогичные механизмы: в Windows — структура RAWINPUT (для необработанного ввода) и INPUT_RECORD (для консольного ввода), в FreeBSD — структура input_event (заимствована из Linux), в macOS — CGEvent (Core Graphics Events). Однако struct input_event отличается простотой, стабильностью и широкой поддержкой в Linux-сообществе.
¶Интересные факты
- Структура
input_eventиспользуется не только для физических устройств, но и для виртуальных, создаваемых черезuinput. Например, программаxdotoolможет генерировать события ввода, используя этот механизм. - В ядре Linux 5.10 была добавлена поддержка расширенных временных меток с наносекундной точностью через
input_absinfo, но базовая структураinput_eventосталась неизменной для обратной совместимости. - Коды клавиш (
KEY_*) в Linux не соответствуют напрямую скан-кодам клавиатуры; они являются абстрактными кодами, определёнными в ядре, что позволяет единообразно обрабатывать клавиатуры разных производителей.
¶Источники
- Исходный код ядра Linux, файл
include/uapi/linux/input.h(версия 6.12). - Документация ядра Linux:
Documentation/input/input.rst. - Книга «Linux Device Drivers, 3rd Edition» (Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman), глава 14.
- Страница руководства
evtest(1). - Спецификация подсистемы ввода Linux (Linux Input Subsystem, Vojtech Pavlik, 1999–2001).
