Обработка событий мыши в Linux?

У меня есть код обработки событий, который читает Linux / dev / input / для моей сенсорной панели и выводит результат на основе того, какая кнопка нажата / отпущена.

Несмотря на то что. на данный момент мой код ожидает нажатия кнопки во время работы на терминале. Мой следующий шаг - запустить этот поток обработки событий вместе с другим потоком (не на основе событий). Если я продолжу обрабатывать событие, читая ввод с терминала, я не смогу выполнять другие потоки как часть моей main (), поскольку main () продолжает ждать нажатия кнопки:

int main(int argc, char** argv)
{
    *Mouse event handling code here*
    return 0; 
}

Есть ли другой подход, например, прерывание чтения? Или я все же могу использовать этот подход и внести исправления в свой код, чтобы эта работа работала как часть потока (например, могу ли я заставить свой поток ждать этих входных данных в качестве аргументов)?

в Linux все является файлом, просто посмотрите событие в файле, который обрабатывает вашу мышь, например, с помощью epoll.

Stargateur 08.09.2018 10:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
1 189
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы сделаете дескрипторы устройства событий неблокирующими (открыв их с помощью флага O_NONBLOCK), вы можете очень легко использовать `опрос (), чтобы дождаться, пока у одного из них появятся события, которые вы можете прочитать.

Рассмотрим следующий пример программы, example.c:

#define  _POSIX_C_SOURCE  200809L
#define  _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/input.h>
#include <termios.h>
#include <poll.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* Maximum number of input sources, including the terminal. */
#ifndef   MAX_INPUTS
#define   MAX_INPUTS  32
#endif

/* Maximum wait for events, in milliseconds (1000 ms = 1 second). */
#ifndef   INTERVAL_MS
#define   INTERVAL_MS  100
#endif

int main(int argc, char *argv[])
{
    unsigned char       keys[16];
    struct input_event  event;
    struct termios      config, oldconfig;
    struct pollfd       src[MAX_INPUTS];
    size_t              srcs, i, done;
    ssize_t             n;
    int                 arg, nsrcs;

    if (!isatty(STDIN_FILENO)) {
        fprintf(stderr, "Standard input is not a terminal.\n");
        return EXIT_FAILURE;
    }

    /* Save old terminal configuration. */
    if (tcgetattr(STDIN_FILENO, &oldconfig) == -1 ||
        tcgetattr(STDIN_FILENO, &config) == -1) {
        fprintf(stderr, "Cannot get terminal settings: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Set new terminal configuration. */
    config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
    config.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN | TOSTOP);
    config.c_cc[VMIN] = 0;
    config.c_cc[VTIME] = 0;
    config.c_cc[VSTART] = 0;
    config.c_cc[VSTOP] = 0;
    if (tcsetattr(STDIN_FILENO, TCSANOW, &config) == -1) {
        const int  saved_errno = errno;
        tcsetattr(STDIN_FILENO, TCSANOW, &oldconfig);
        fprintf(stderr, "Cannot set terminal settings: %s.\n", strerror(saved_errno));
        return EXIT_FAILURE;
    }

    /* The very first input source is the terminal. */
    src[0].fd = STDIN_FILENO;
    src[0].events = POLLIN;
    src[0].revents = 0;
    srcs = 1;

    /* Add input devices from command line. */
    for (arg = 1; arg < argc; arg++) {
        int  fd;

        fd = open(argv[arg], O_RDONLY | O_NOCTTY | O_NONBLOCK);
        if (fd == -1) {
            fprintf(stderr, "Skipping input device %s: %s.\n", argv[arg], strerror(errno));
            continue;
        }

        if (srcs >= MAX_INPUTS) {
            fprintf(stderr, "Too many event sources.\n");
            return EXIT_FAILURE;
        }

        /* Optional: Grab input device, so only we receive its events. */
        ioctl(fd, EVIOCGRAB, 1);

        src[srcs].fd = fd;
        src[srcs].events = POLLIN;
        src[srcs].revents = 0;
        srcs++;
    }

    printf("Ready. Press Q to exit.\n");
    fflush(stdout);

    done = 0;
    while (!done) {

        nsrcs = poll(src, srcs, INTERVAL_MS);
        if (nsrcs == -1) {
            if (errno == EINTR)
                continue;
            fprintf(stderr, "poll(): %s.\n", strerror(errno));
            break;
        }

        /* Terminal is not an input source. */
        if (src[0].revents & POLLIN) {
            n = read(src[0].fd, keys, sizeof keys);
            if (n > 0) {
                for (i = 0; i < n; i++) {
                    if (keys[i] == 'q' || keys[i] == 'Q')
                        done = 1;
                    if (keys[i] >= 32 && keys[i] <= 126)
                        printf("Key '%c' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
                    else
                    if (keys[i])
                        printf("Key '\\%03o' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
                    else
                        printf("NUL key (0) pressed\n");
                }
                fflush(stdout);
            }
            src[0].revents = 0;
        }

        /* Check the other input sources. */
        for (i = 1; i < srcs; i++) {
            if (src[i].revents & POLLIN) {
                while (1) {
                    n = read(src[i].fd, &event, sizeof event);
                    if (n != sizeof event)
                        break;

                    if (event.type == EV_KEY && event.code == BTN_LEFT) {
                        if (event.value > 0)
                            printf("Left mouse button pressed\n");
                        else
                            printf("Left mouse button released\n");
                    }

                    if (event.type == EV_KEY && event.code == BTN_RIGHT) {
                        if (event.value > 0)
                            printf("Right mouse button pressed\n");
                        else
                            printf("Right mouse button released\n");
                    }
                }
                fflush(stdout);
            }
            src[i].revents = 0;             
        }
    }

    /* Close input devices. */
    for (i = 1; i < srcs; i++)
        close(src[i].fd);

    /* Restore terminal settings. */
    tcsetattr(src[0].fd, TCSAFLUSH, &oldconfig);

    printf("All done.\n");
    return EXIT_SUCCESS;
}

Скомпилируйте его, например,

gcc -Wall -O2 example.c -o example

и запустите его, например,

sudo ./example /dev/input/event5

где /dev/input/event5 - устройство событий мыши. Обратите внимание, что вы можете прочитать /sys/class/input/event5/device/name, чтобы узнать имя устройства (насколько это известно ядру; это те же имена, которые evtest показывает при запуске от имени root).

Если вы не уверены, вы всегда можете запустить

for N in /sys/class/input/event*/device/name ; do 
    DEV = "${N%%/device/name}" ; DEV = "/dev/${DEV##/sys/class/}" ;
    NAME = "$(cat "$N" 2>/dev/null)" ;
    printf "%s: %s\n" "$DEV" "$NAME" ;
done

в оболочке Bash, Dash или POSIX, чтобы узнать, какие устройства событий вы можете попробовать.

Приведенный выше пример программы должен запускаться с терминала или консоли, потому что он также принимает ввод с терминала. Он устанавливает терминал в неблокирующий неканонический режим, в котором он может получать отдельные нажатия клавиш. Обратите внимание, что некоторые нажатия клавиш, такие как клавиши курсора и функциональные клавиши, на самом деле состоят из нескольких символов, начиная с ESC (\033).


Также часто этот цикл входных событий разделяют на отдельный поток. Это всего лишь на дюжину или около того строк больше, но "проблема" тогда заключается в том, как отдельный поток информирует основной (или другие) потоки о появлении новых событий / команд ввода. Неблокирующий подход poll (), описанный выше, обычно проще реализовать очень надежным и простым способом.

Мой простой опрос. процедура обработки событий пытается получить данные от двух неблокирующих файловых систем, один для мыши и один для клавиатуры. Подпрограмма обработки событий возвращает -1 или устройство занято, когда оно не готово, любая ошибка ниже этого перехватывается событием. Оператор if здесь сначала пробует fmd, mouse, затем fkd. Возврат меньше единицы или нуля означает, что данные не готовы, поток засыпает.

if ( ( ( imd = event(fmd,&ie) ) <=0)&& ( (  ikd = event(fkd,&ie)) <= 0)) 
    {
        usleep(TIMEOUT);
        continue;
    }

Другие вопросы по теме