我有一个检测键盘的程序。
起初,我用
read()
从键盘设备读取struct input_event
,效果很好。
然后我尝试用
read()
替换fread()
。它得到了错误的数据。
演示如下。要重现错误,您需要与您的键盘开发人员交换
/dev/input/event0
并使用sudo
运行它。
#include <unistd.h>
#include <stdio.h>
#include <sys/fcntl.h>
#include <linux/input.h>
#include <stdlib.h>
#define USE_FREAD
static void check_keyboard (int fd)
{
static struct input_event event;
#ifdef USE_READ
{
//use read(). it works well.
if (read (fd, &event, sizeof (event)) == -1)
perror ("read()"), exit (EXIT_FAILURE);
}
#else
#ifdef USE_FREAD
{
//use fread(). it gets wrong data
int cloned = dup (fd);
if (cloned == -1)
perror ("dup()"), exit (EXIT_FAILURE);
FILE *file = fdopen (cloned, "r");
if (file == NULL)
perror ("fdopen()"), exit (EXIT_FAILURE);
if (fread (&event, sizeof (event), 1, file) != 1) {
if (feof (file))
fprintf (stderr, "waring: fread() get EOF");
else if (ferror (file))
fprintf (stderr, "fread() failed\n"), exit (EXIT_FAILURE);
}
fclose (file);
puts ("fread() over");
}
#endif
#endif
if (event.type != EV_KEY)
return;
switch (event.value) {
case 0:
puts ("key released");
break;
case 1:
printf ("key pressed, code:%d\n", event.code);
break;
case 2:
puts ("repeat automatically");
break;
}
}
int main()
{
int fd = open ("/dev/input/event0", O_RDONLY);
if (fd == -1)
perror ("open()"), exit (EXIT_FAILURE);
for (; ;) {
check_keyboard (fd);
}
close (fd);
}
如果定义了
USE_READ
,它可以检测键盘事件。如果定义了USE_FREAD
,它只会打印“fread() over”。
我试着用gdb来观察
event
的值。但是还有许多其他事件混淆了数据。
我的环境:debian 12, gcc 12.2.0 , kernel 6.1.0-7-amd64 .
我已经按照评论中的建议解决了
只需使用
setvbuf()
禁用缓冲区:
#ifdef USE_FREAD
{
//use fread(). it gets right data now.
int cloned = dup (fd);
if (cloned == -1)
perror ("dup()"), exit (EXIT_FAILURE);
FILE *file = fdopen (cloned, "r");
if (setvbuf (file, NULL, _IONBF, 0) != 0)
perror ("setvbuf()"), exit (EXIT_FAILURE);
if (fread (&event, sizeof (event), 1, file) != 1) {
if (feof (file))
fprintf (stderr, "waring: fread() get EOF");
else if (ferror (file))
fprintf (stderr, "fread() failed\n"), exit (EXIT_FAILURE);
}
fclose (file);
}
#endif
顺便检查一下返回值。