当您应该只考虑该事件
read()的回报
== sizeofev,因为我们是从输入设备读取这里。如果返回零,则意味着不再发生任何事件(也许设备已分离?)。如果返回
-1,请检查
errno。如果
read()返回任何其他值,则说明内核驱动程序已经崩溃了,您可以将其视为致命错误。
errno == EINTR是正常的(发生在传递信号时),这本身不是错误。它不应该在这里发生,但是忽略它(将其视为打ic,而不是错误)是非常安全的。
errno == EAGAIN``O_NONBLOCK在
open()标记中使用时发生,并且尚无新事件可用。
绝对没有理由在
O_NONBLOCK这里使用。它所做的是使你的代码浪费CPU周期,从返回的每秒几万次
read()的调用只是返回
-1用
errno== EAGAIN。只需将其放下即可,以便
read()将仅等待新事件到达并返回它。
请参阅我对input_event结构描述问题的回答。
总而言之,适用于
ev_type == 1 == EV_KEY:
ev_value == 0
:释放键(向上键)ev_value == 1
:按下键(按下键)ev_value == 2
:自动重复(自动重复键)ev_pre == 1 == KEY_ESC
ev_pre == 2 == KEY_1
ev_pre == 3 == KEY_2
ev_pre == 10 == KEY_9
ev_pre == 11 == KEY_0
ev_pre == 28 == KEY_ENTER
实际提供的设备的按键
1
2
3
4
5
6
7
8
9
Enter。
(请注意,您只显示了按键释放事件;实际上,您应该分别看到两个,一个带有
ev_value == 1,然后带有一个
ev_value ==0,
ev_pre)。
我尝试过的一个中国人很好,尽管便宜。它具有一本手册,其中包含一些条形码,包括一些可以在条形码格式(和位数)之间切换的手册。我隐约记得使用两个条形码切换到另一种模式,并使用最小音量来发出哔声。即使分离后,它似乎仍保留设置。
这是我将使用哪种实施方式读取条形码的示例。我显然将条形码读取部分拆分为一个单独的文件。
以下代码专用于公共领域(在CC0下许可),因此可以随意使用它。没有任何形式的保证,所以不要怪我有任何破损。(欢迎进行任何错误修复;如果报告了错误,则将进行检查并包含在下面的代码中。建议在下面添加注释;每两天左右都会阅读所有答案中的注释。)
头文件
barpre.h:
#ifndef BARCODE_H#define BARCODE_H#include <stdlib.h>#include <signal.h>extern volatile sig_atomic_t done;int install_done(const int signum);typedef struct { int fd; volatile int timeout; timer_t timer;} barpre_dev;int barpre_close(barpre_dev *const dev);int barpre_open(barpre_dev *const dev, const char *const device_path);size_t barpre_read(barpre_dev *const dev, char *const buffer, const size_t length, const unsigned long maximum_ms);#endif 以下
barpre.c文件中的实现当前仅接受数字(0到1),但是添加任何其他必要的键(例如
KEY_A到
KEY_Z)应该很简单。当前的操作忽略了移位,控制等功能,因为据我所知,任何扫描仪都没有提供这些功能。它使用
SIGRTMAX-0实时信号和每个条形码设备的自定义计时器来读取条形码,因此您需要将其链接到
librt(
-lrt):
#define _POSIX_C_SOURCE 200809L#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <signal.h>#include <time.h>#include <linux/input.h>#include <string.h>#include <stdio.h>#include <errno.h>#define UNUSED __attribute__((unused))#define TIMEOUT_SIGNAL (SIGRTMAX-0)volatile sig_atomic_t done = 0;static void handle_done(int signum UNUSED){ done = 1;}int install_done(const int signum){ struct sigaction act; sigemptyset(&act.sa_mask); act.sa_handler = handle_done; act.sa_flags = 0; if (sigaction(signum, &act, NULL) == -1) return errno; return 0;}typedef struct { int fd; volatile int timeout; timer_t timer;} barpre_dev;static void handle_timeout(int signum UNUSED, siginfo_t *info, void *context UNUSED){ if (info && info->si_pre == SI_TIMER && info->si_value.sival_ptr)#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) __atomic_add_fetch((int *)info->si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);#else __sync_add_and_fetch((int *)info->si_value.sival_ptr, 1);#endif}static int install_timeouts(void){ static int installed = 0; if (!installed) { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_sigaction = handle_timeout; act.sa_flags = SA_SIGINFO; if (sigaction(TIMEOUT_SIGNAL, &act, NULL) == -1) return errno; installed = 1; } return 0;}int barpre_close(barpre_dev *const dev){ int retval = 0; if (!dev) return 0; if (dev->fd != -1) if (close(dev->fd) == -1) retval = errno; dev->fd = -1; if (dev->timer) if (timer_delete(dev->timer) == -1) if (!retval) retval = errno; dev->timer = (timer_t)0; while (1) { struct timespec t; siginfo_t info; sigset_t s; t.tv_sec = (time_t)0; t.tv_nsec = 0L; sigemptyset(&s); if (sigtimedwait(&s, &info, &t) != TIMEOUT_SIGNAL) break; if (info.si_pre != SI_TIMER || !info.si_value.sival_ptr) continue;#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) __atomic_add_fetch((int *)info.si_value.sival_ptr, 1, __ATOMIC_SEQ_CST);#else __sync_add_and_fetch((int *)info.si_value.sival_ptr, 1);#endif } return errno = retval;}int barpre_open(barpre_dev *const dev, const char *const device_path){ struct sigevent event; int fd; if (!dev) return errno = EINVAL; dev->fd = -1; dev->timeout = -1; dev->timer = (timer_t)0; if (!device_path || !*device_path) return errno = EINVAL; if (install_timeouts()) return errno; do { fd = open(device_path, O_RDonLY | O_NOCTTY | O_CLOEXEC); } while (fd == -1 && errno == EINTR); if (fd == -1) return errno; errno = 0; if (ioctl(fd, EVIOCGRAB, 1)) { const int saved_errno = errno; close(fd); return errno = (saved_errno) ? errno : EACCES; } dev->fd = fd; memset(&event, 0, sizeof event); event.sigev_notify = SIGEV_SIGNAL; event.sigev_signo = TIMEOUT_SIGNAL; event.sigev_value.sival_ptr = (void *)&(dev->timeout); if (timer_create(CLOCK_REALTIME, &event, &dev->timer) == -1) { const int saved_errno = errno; close(fd); return errno = (saved_errno) ? errno : EMFILE; } return errno = 0;}size_t barpre_read(barpre_dev *const dev, char *const buffer, const size_t length, const unsigned long maximum_ms){ struct itimerspec it; size_t len = 0; int status = ETIMEDOUT; if (!dev || !buffer || length < 2 || maximum_ms < 1UL) { errno = EINVAL; return (size_t)0; } it.it_value.tv_sec = maximum_ms / 1000UL; it.it_value.tv_nsec = (maximum_ms % 1000UL) * 1000000L; it.it_interval.tv_sec = 0; it.it_interval.tv_nsec = 10000000L; if (timer_settime(dev->timer, 0, &it, NULL) == -1) return (size_t)0; #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR >= 7) __atomic_store_n((int *)&(dev->timeout), 0, __ATOMIC_SEQ_CST);#else __sync_fetch_and_and((int *)&(dev->timeout), 0);#endif while (!dev->timeout) { struct input_event ev; ssize_t n; int digit; n = read(dev->fd, &ev, sizeof ev); if (n == (ssize_t)-1) { if (errno == EINTR) continue; status = errno; break; } else if (n == sizeof ev) { if (ev.type != EV_KEY || (ev.value != 1 && ev.value != 2)) continue; switch (ev.pre) { case KEY_0: digit = '0'; break; case KEY_1: digit = '1'; break; case KEY_2: digit = '2'; break; case KEY_3: digit = '3'; break; case KEY_4: digit = '4'; break; case KEY_5: digit = '5'; break; case KEY_6: digit = '6'; break; case KEY_7: digit = '7'; break; case KEY_8: digit = '8'; break; case KEY_9: digit = '9'; break; default: digit = ' '; } if (digit == ' ') { if (!len) continue; status = 0; break; } if (len < length) buffer[len] = digit; len++; continue; } else if (n == (ssize_t)0) { status = ENOENT; break; } else { status = EIO; break; } } if (len + 1 < length) buffer[len + 1] = ' '; else buffer[length - 1] = ' '; it.it_value.tv_sec = 0; it.it_value.tv_nsec = 0; it.it_interval.tv_sec = 0; it.it_interval.tv_nsec = 0L; (void)timer_settime(dev->timer, 0, &it, NULL); errno = status; return len;}这是一个示例程序
example.c。您提供输入事件设备(我建议使用符号链接,
/dev/input/by-id/或者
/dev/input/by-path/如果您
udev提供了符号链接,因为事件设备索引在内核版本和硬件启动之间可能不稳定),以及您愿意等待/直到下一个条形码的最大持续时间。
#include <stdlib.h>#include <string.h>#include <signal.h>#include <stdio.h>#include <errno.h>#include "barpre.h"#define BARCODE_MAXLEN 1023int main(int argc, char *argv[]){ barpre_dev dev; unsigned long ms; int status, exitpre; if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { fprintf(stderr, "n"); fprintf(stderr, "Usage: %s [ -h | --help ]n", argv[0]); fprintf(stderr, " %s INPUT-EVENT-DEVICE IDLE-TIMEOUTn", argv[0]); fprintf(stderr, "n"); fprintf(stderr, "This program reads barpres from INPUT-EVENT-DEVICE,n"); fprintf(stderr, "waiting at most IDLE-TIMEOUT seconds for a new barpre.n"); fprintf(stderr, "The INPUT-EVENT-DEVICE is grabbed, the digits do not appear asn"); fprintf(stderr, "inputs in the machine.n"); fprintf(stderr, "You can at any time end the program by sending it an"); fprintf(stderr, "SIGINT (Ctrl+C), SIGHUP, or SIGTERM signal.n"); fprintf(stderr, "n"); return EXIT_FAILURE; } if (install_done(SIGINT) || install_done(SIGHUP) || install_done(SIGTERM)) { fprintf(stderr, "Cannot install signal handlers: %s.n", strerror(errno)); return EXIT_FAILURE; } { double value, check; char dummy; if (sscanf(argv[2], " %lf %c", &value, &dummy) != 1 || value < 0.001) { fprintf(stderr, "%s: Invalid idle timeout value (in seconds).n", argv[2]); return EXIT_FAILURE; } ms = (unsigned long)(value * 1000.0); check = (double)ms / 1000.0; if (value < check - 0.001 || value > check + 0.001 || ms < 1UL) { fprintf(stderr, "%s: Idle timeout is too long.n", argv[2]); return EXIT_FAILURE; } } if (barpre_open(&dev, argv[1])) { fprintf(stderr, "%s: Cannot open barpre input event device: %s.n", argv[1], strerror(errno)); return EXIT_FAILURE; } while (1) { char pre[BARCODE_MAXLEN + 1]; size_t len; if (done) { status = EINTR; break; } len = barpre_read(&dev, pre, sizeof pre, ms); if (errno) { status = errno; break; } if (len < (size_t)1) { status = ETIMEDOUT; break; } printf("%zu-digit barpre: %sn", len, pre); fflush(stdout); } if (status == EINTR) { fprintf(stderr, "Signaled to exit. Complying.n"); fflush(stderr); exitpre = EXIT_SUCCESS; } else if (status == ETIMEDOUT) { fprintf(stderr, "Timed out, no more barpres.n"); fflush(stderr); exitpre = EXIT_SUCCESS; } else { fprintf(stderr, "Error reading input event device %s: %s.n", argv[1], strerror(status)); fflush(stderr); exitpre = EXIT_FAILURE; } if (barpre_close(&dev)) { fprintf(stderr, "Warning: Error closing input event device %s: %s.n", argv[1], strerror(errno)); fflush(stderr); exitpre = EXIT_FAILURE; } return exitpre;}如您所见,它只是将条形码打印到标准输出(以及任何对标准错误的错误消息和警告)。要进行编译,我建议使用以下命令
Makefile(缩进必须使用
Tab,而不是空格):
CC := gccCFLAGS := -Wall -Wextra -O2LDFLAGS := -lrt.PHONY: all cleanall: clean exampleclean: rm -f example *.o%.o: %.c $(CC) $(CFLAGS) -c $^example: example.o barpre.o $(CC) $(CFLAGS) $^ $(LDFLAGS) -o example
要进行编译,请创建上面列出的四个文件,然后运行
make clean example
例如运行
./example /dev/input/event4 5.0
将从中读取条形码
/dev/input/event4,但会在
Ctrl+
C(INT信号),HUP信号,TERM信号处退出,或者在5秒钟内没有条形码出现时退出。
请注意,如果在5秒钟之内只读取了部分条形码,我们确实会得到该部分(可以尝试读取其余部分),但是上面的示例程序将忽略该部分,仅显示超时。
有什么问题吗



