Открыть сервис

структура 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_ABSABS_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» на клавиатуре:

  1. Событие EV_KEY с кодом KEY_A и значением 1 (нажатие).
  2. Событие EV_SYN с кодом SYN_REPORT и значением 0 (синхронизация, сигнализирующая о завершении пакета).

При отпускании:

  1. Событие EV_KEY с кодом KEY_A и значением 0 (отпускание).
  2. Событие 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 и потере данных. Решение — увеличение размера буфера через ioctl EVIOCSGRAB или использование 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 →