C语言 Linux 中的鼠标事件处理?



我有一个事件处理代码,它为我的触摸板读取Linux的/dev/input/并打印基于哪个按钮被按下/释放的结果。

虽然。 截至目前,我的代码在终端上运行时正在等待按钮按下。我的下一步是将此事件处理线程与另一个线程(不基于事件(一起运行。如果我通过在终端读取输入继续处理事件,我将无法执行其他线程作为我的 main(( 的一部分,因为 main(( 一直在等待按钮按下:

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

有没有不同的方法,比如读取中断?或者我仍然可以采用这种方法并在我的代码中进行修改以使其作为线程的一部分工作(例如我可以让我的线程等待这些输入作为参数(?

如果使事件设备描述符不受阻塞(通过使用O_NONBLOCK标志打开它们(,则可以轻松地使用 'poll(( 等待,直到其中一个具有可以读取的事件。

请考虑以下示例程序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 pressedn", keys[i], keys[i], keys[i]);
else
if (keys[i])
printf("Key '\%03o' = 0x%02x = %u pressedn", keys[i], keys[i], keys[i]);
else
printf("NUL key (0) pressedn");
}
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 pressedn");
else
printf("Left mouse button releasedn");
}
if (event.type == EV_KEY && event.code == BTN_RIGHT) {
if (event.value > 0)
printf("Right mouse button pressedn");
else
printf("Right mouse button releasedn");
}
}
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: %sn" "$DEV" "$NAME" ;
done

在 Bash 或 Dash 或 POSIX shell 中,查看您可以尝试哪些事件设备。

上面的示例程序必须从终端或控制台运行,因为它也从终端获取输入。它将终端设置为非阻塞非规范模式,在该模式下可以接收单个按键。请注意,某些按键(如光标键和功能键(实际上有几个字符长,以 ESC (33( 开头。

将该

输入事件循环拆分为单独的线程也很常见。它只是多了十几行,但"问题"变成了单独的线程如何通知主(或其他(线程新的输入事件/命令已经到达。上面的非阻塞 poll(( 方法通常更容易以非常健壮、直接的方式实现。

我的简单民意调查。 事件例程尝试从两个非阻塞 FD(一个用于鼠标,一个用于键盘(获取数据。事件例程在未准备就绪时返回 -1 或设备繁忙,低于该值的任何错误都被事件捕获。这里的 if 语句首先尝试 fmd、mouse,然后尝试 fkd。返回值小于 1 或 0 表示数据未就绪,线程休眠。

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

最新更新