структура input_event
input_event — это структура данных в ядре операционной системы Linux, предназначенная для передачи информации о событиях от устройств ввода (клавиатур, мышей, тачпадов, джойстиков, сенсорных экранов и других) в пользовательское пространство. Структура определена в заголовочном файле <linux/input.h> и является основным элементом подсистемы ввода Linux (Input Subsystem), обеспечивающей унифицированный интерфейс для обработки событий от разнородных устройств. Каждое событие кодируется в виде одного экземпляра структуры input_event, содержащего поля для типа события, кода и значения.
Определение и структура
Структура input_event в языке C объявлена следующим образом:
``c struct input_event { struct timeval time; // время события __u16 type; // тип события __u16 code; // код события (зависит от типа) __s32 value; // значение события }; ``
Поля структуры:
- time — структура
struct timeval, содержащая секунды (tv_sec) и микросекунды (tv_usec) с момента начала эпохи Unix (1 января 1970 года). В некоторых реализациях (например, в многопоточных приложениях) время может быть нулевым, если устройство не поддерживает аппаратные временные метки. - type — 16-битное беззнаковое целое, определяющее категорию события. Основные типы:
EV_KEY(0x01) — клавиши/кнопки,EV_REL(0x02) — относительное перемещение,EV_ABS(0x03) — абсолютные координаты,EV_SYN(0x00) — синхронизация,EV_MSC(0x04) — прочие события,EV_SW(0x05) — переключатели,EV_LED(0x11) — светодиоды,EV_SND(0x12) — звук,EV_REP(0x14) — автоповтор,EV_FF(0x15) — обратная связь (force feedback). - code — 16-битное беззнаковое целое, уточняющее конкретное событие внутри типа. Например, для типа
EV_KEYкод может бытьKEY_A(30),KEY_ENTER(28) илиBTN_LEFT(272). ДляEV_RELкод —REL_X(0),REL_Y(1),REL_WHEEL(8). ДляEV_ABS—ABS_X(0),ABS_Y(1),ABS_PRESSURE(24). - value — 32-битное знаковое целое, содержащее значение события. Для кнопок (
EV_KEY) значение 0 означает отпускание, 1 — нажатие, 2 — автоповтор. Для относительных осей (EV_REL) значение — это изменение позиции (например, +1 пиксель вправо). Для абсолютных осей (EV_ABS) значение — это абсолютная координата (например, 500 из 1024).
История и развитие
Подсистема ввода Linux начала формироваться в ранних версиях ядра (1990-е годы) для поддержки клавиатур и мышей через последовательные порты. С появлением USB-устройств в конце 1990-х потребовался универсальный интерфейс. В 2000 году была представлена структура input_event как часть драйвера input (разработчик — Войтек Павловский и другие). В 2002 году в ядро 2.4.x была включена стандартизированная подсистема ввода, где input_event стал основным форматом обмена данными между драйверами устройств и файлами устройств /dev/input/event*.
В 2010-х годах структура была расширена для поддержки мультитач-сенсоров (тип EV_ABS с кодами ABS_MT_POSITION_X, ABS_MT_TRACKING_ID и другими). В ядре 5.x (2020-е) добавлена поддержка 64-битных временных меток через поле input_event с расширенной структурой input_event_compat для обратной совместимости.
Принцип работы
Устройства ввода (например, клавиатура, подключенная через USB) генерируют прерывания, которые обрабатываются драйвером. Драйвер создаёт экземпляры input_event и передаёт их в подсистему ввода через функции input_event(), input_report_key(), input_report_abs() и другие. Подсистема ввода помещает события в буфер, связанный с файлом устройства /dev/input/eventX (где X — номер устройства). Пользовательские приложения (например, X.Org, Wayland, игровые движки) читают эти файлы, используя системный вызов read(), и получают массив структур input_event.
Каждое физическое действие (нажатие клавиши, движение мыши, касание сенсора) обычно генерирует несколько событий. Например, нажатие клавиши «A» на клавиатуре:
- Событие
EV_KEYс кодомKEY_Aи значением 1 (нажатие). - Событие
EV_SYNс кодомSYN_REPORTи значением 0 (синхронизация, сигнализирующая о завершении пакета).
При отпускании:
- Событие
EV_KEYс кодомKEY_Aи значением 0 (отпускание). - Событие
EV_SYNс кодомSYN_REPORTи значением 0.
События синхронизации (EV_SYN) необходимы для группировки связанных изменений (например, одновременное изменение X и Y при движении мыши). Без них приложение могло бы обработать неполный набор данных.
Классификация типов событий
EV_KEY (0x01)
События нажатия и отпускания клавиш, кнопок, переключателей. Коды определены в <linux/input-event-codes.h> и охватывают клавиши клавиатуры (KEY_), кнопки мыши (BTN_), кнопки джойстиков (BTN_JOYSTICK), кнопки телефонов (BTN_TOUCH). Всего поддерживается до 0x2FF (767) кодов.
EV_REL (0x02)
События относительного перемещения — мыши, трекболы, колёсики прокрутки. Коды: REL_X, REL_Y, REL_Z (для 3D-мышей), REL_HWHEEL (горизонтальная прокрутка), REL_DIAL, REL_MISC. Значение — целое число, обозначающее изменение позиции (обычно ±1 для мыши, ±120 для колёсика).
EV_ABS (0x03)
События абсолютных координат — сенсорные экраны, тачпады, джойстики, графические планшеты. Коды: ABS_X, ABS_Y, ABS_PRESSURE, ABS_DISTANCE, ABS_MT_POSITION_X (мультитач). Для каждого кода устройство сообщает минимальное (absmin) и максимальное (absmax) значения через ioctl EVIOCGABS.
EV_SYN (0x00)
События синхронизации — разделители между пакетами событий. Основной код: SYN_REPORT (0). Также существуют SYN_CONFIG (1), SYN_MT_REPORT (2), SYN_DROPPED (3). Событие SYN_DROPPED генерируется при переполнении буфера, сигнализируя о потере данных.
EV_MSC (0x04)
Прочие события — сканирование кодов, серийные номера, идентификаторы. Коды: MSC_SCAN (код сканирования клавиши), MSC_SERIAL, MSC_PULSELED, MSC_GESTURE, MSC_RAW, MSC_SCAN_RAW.
EV_SW (0x05)
События переключателей — крышка ноутбука (закрыта/открыта), переключатели питания, переключатели режимов. Коды: SW_LID, SW_TABLET_MODE, SW_HEADPHONE_INSERT, SW_MICROPHONE_INSERT. Значение 1 — активен, 0 — неактивен.
EV_LED (0x11), EV_SND (0x12), EV_REP (0x14), EV_FF (0x15)
Эти типы используются для обратной связи от системы к устройству (например, включение светодиода клавиатуры, воспроизведение звука, настройка автоповтора, обратная тактильная связь). Они редко появляются в потоке событий от устройств ввода, но могут быть отправлены через ioctl или write().
Чтение и обработка
Пользовательские приложения читают события из файлов устройств /dev/input/event* (обычно права доступа 660, группа input). Для неблокирующего чтения используется флаг O_NONBLOCK. Размер одного события — 16 байт (на 32-битных системах) или 24 байта (на 64-битных системах с учётом структуры timeval). Пример чтения на языке C:
```c
include <linux/input.h>
include <fcntl.h>
include <unistd.h>
include <stdio.h>
int main() { int fd = open("/dev/input/event0", O_RDONLY); if (fd < 0) { perror("open"); return 1; } 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; } ```
На практике приложения часто используют библиотеки-обёртки (libevdev, libinput), которые упрощают обработку событий, предоставляя функции для декодирования кодов и управления устройствами.
Применение
Структура input_event используется во всех операционных системах на базе Linux, включая Android (через драйверы uinput и evdev), встраиваемые системы, игровые консоли (Steam Deck), промышленные терминалы. Она лежит в основе:
- Графических серверов — X.Org (через драйвер
evdev), Wayland (черезlibinput). - Эмуляторов ввода — модуль
uinputпозволяет создавать виртуальные устройства ввода, генерирующиеinput_event(используется в автоматизации тестирования, удалённом управлении, игровых эмуляторах). - Игровых движков — SDL, GLFW, SFML читают события через
evdevдля поддержки геймпадов и джойстиков. - Специализированного ПО — системы управления роботами, медицинское оборудование, авиасимуляторы.
Критика и ограничения
Основные недостатки подхода на основе input_event:
- Отсутствие метаданных — структура не содержит информации о том, с какого устройства пришло событие (например, какой именно клавиатуры или мыши). Приложения вынуждены опрашивать устройства через ioctl (
EVIOCGNAME,EVIOCGID) или отслеживать идентификаторы файлов. - Ограниченная точность времени —
struct timevalиспользует микросекунды, что недостаточно для высокоточных приложений (например, профессиональные графические планшеты требуют наносекундной точности). В ядре 5.0+ добавлена поддержка 64-битных наносекундных меток черезinput_eventс расширенным полемinput_event_secиinput_event_usec, но обратная совместимость остаётся проблемой. - Переполнение буфера — при высокой частоте событий (например, мультитач с 10 касаниями) буфер может переполниться, что приводит к генерации
SYN_DROPPEDи потере данных. Решение — увеличение размера буфера через ioctlEVIOCSGRABили использованиеlibevdevс автоматическим восстановлением. - Сложность синхронизации — отсутствие гарантии атомарности пакетов (при многопоточном чтении возможно получение неполного набора событий). Разработчики должны явно обрабатывать
EV_SYN.
Связанные структуры и интерфейсы
- input_id — структура, возвращаемая через ioctl
EVIOCGID, содержит идентификаторы устройства (bus, vendor, product, version). - input_absinfo — структура для получения информации об абсолютных осях (минимум, максимум, разрешение, флаттер).
- ff_effect — структура для обратной связи (force feedback), передаваемая через ioctl
EVIOCSFF. - uinput — виртуальный драйвер ввода, позволяющий пользовательским программам создавать устройства, генерирующие
input_event.
Источники
- Linux kernel source tree, файл
include/uapi/linux/input.h(версия 6.x). - Документация ядра Linux:
Documentation/input/input.rst,Documentation/input/event-codes.rst. - Man-страницы:
evdev(4),input(4),uinput(4). - Книга «Linux Device Drivers», 3-е издание, глава 14 (Input Drivers).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →