问题
I have an event handling code that reads Linux's /dev/input/ for my touchpad and prints result on the basis of which button is pressed/released.
Although. as of now my code is waiting on a button press while running on terminal. My next step is to run this event handling thread along with another thread (not event based). If I continue handling event by reading input at terminal, I will not be able to execute other threads as a part of my main() as main() keeps on waiting for the button press:
int main(int argc, char** argv)
{
*Mouse event handling code here*
return 0;
}
Is there a different approach like reading interrupts instead? Or can I still take this approach and make amends in my code to make this work as a part of a thread (like can I make my thread to wait on these inputs as arguments)?
回答1:
If you make the event device descriptors nonblocking (by opening them with the O_NONBLOCK
flag), you can very easily use `poll() to wait until one of them has events you can read.
Consider the following example program, 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;
}
Compile it using e.g.
gcc -Wall -O2 example.c -o example
and run it using e.g.
sudo ./example /dev/input/event5
where /dev/input/event5
is a mouse event device. Note that you can read /sys/class/input/event5/device/name
to find out the name of the device (as far as the kernel knows it; these are the same names evtest
shows when run as root).
If you are not sure, you can always run
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
in a Bash or Dash or a POSIX shell, to see what event devices you can try.
The example program above must be run from a terminal or console, because it also takes input from the terminal. It sets the terminal into nonblocking non-canonical mode, where it can receive individual keypresses. Do note that some keypresses, like cursor and function keys, are actually several characters long, beginning with an ESC (\033
).
It is also common to split that input event loop into a separate thread. It is just a dozen or so lines more, but the "problem" then becomes how the separate thread informs the main (or other) threads that new input events/commands have arrived. The non-blocking poll() approach above is usually easier to implement in a very robust, straightforward manner.
回答2:
My simple poll. the event routine attempt to get data from the two non blocking fds, one for mouse and one for keyboard. The event routine returns -1 or device busy when not ready, anything error below that is trapped by event. The if statement here tries fmd, mouse, first, then fkd next. A return less than one or zero means data not ready, the thread sleeps.
if( ( ( imd = event(fmd,&ie) ) <=0)&& ( ( ikd = event(fkd,&ie)) <= 0))
{
usleep(TIMEOUT);
continue;
}
来源:https://stackoverflow.com/questions/52233626/mouse-event-handling-in-linux