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

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).
Заметили ошибку или не согласны с информацией в статье? Напишите нам support@bfometr.ru