У меня есть код обработки событий, который читает Linux / dev / input / для моей сенсорной панели и выводит результат на основе того, какая кнопка нажата / отпущена.
Несмотря на то что. на данный момент мой код ожидает нажатия кнопки во время работы на терминале. Мой следующий шаг - запустить этот поток обработки событий вместе с другим потоком (не на основе событий). Если я продолжу обрабатывать событие, читая ввод с терминала, я не смогу выполнять другие потоки как часть моей main (), поскольку main () продолжает ждать нажатия кнопки:
int main(int argc, char** argv)
{
*Mouse event handling code here*
return 0;
}
Есть ли другой подход, например, прерывание чтения? Или я все же могу использовать этот подход и внести исправления в свой код, чтобы эта работа работала как часть потока (например, могу ли я заставить свой поток ждать этих входных данных в качестве аргументов)?





Если вы сделаете дескрипторы устройства событий неблокирующими (открыв их с помощью флага 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;
}
в Linux все является файлом, просто посмотрите событие в файле, который обрабатывает вашу мышь, например, с помощью epoll.