处理多个/dev/input/eventX的情况

33 篇文章 0 订阅
19 篇文章 0 订阅

/dev/input/eventX实在是太多了,十几二十个。

而且每个人的电脑上鼠标、键盘对应的文件都不一样,可能插拔过后就变了。

给我们写程序带来很大不便,下面来解决它。

一、过滤出需要的键盘 / 鼠标文件

注意到还有/dev/input/by-path/,以及/proc/bus/input/devices,同时还有一些工具,比如evtest

这些都或多或少提供了一些信息,只是不完整

1. shell过滤

首先想到的思路肯定是shell,grep、awk什么的搞起来。

比如筛选键盘设备:

$ ls /dev/input/by-path | grep -- -kbd$
pci-0000:00:14.0-usb-0:5:1.0-event-kbd
platform-i8042-serio-0-event-kbd

然后,你可能直接就想:

#include <stdlib.h>
int main(void)
{
	system("ls /dev/input/by-path | grep -- -kbd$");
}

system()fork()一个子进程,这个子进程调用shell解释器来执行命令,但是它执行完就G了,最多留个返回值给我们(好吧,其实你可以把输出写到文件里,再读出来)。

这样可不行,就无法获取结果了,全输出到屏幕了。

正确做法是popen()系统调用:

#include <stdlib.h>
#include <stdio.h>
int main(void)
{
	FILE *pipe_out = 
		popen("ls /dev/input/by-path | grep -- -kbd$", "r");
	char buf[4096]
	fread(buf, 1, sizeof(buf), pipe_out);
	pclose(pipe_out);
}

它会开一个进程(管道),并且把进程的输出端重定向,通过返回值获取,成功完成父子进程通信。

下来按行解析即可。

2. glob()

从来没见到过这个函数,看stackoverflow上例子学到的。

这个函数真是用到地方了,也是首推的方式。

#include <glob.h>
#include <stddef.h>
#include <stdio.h>

int main (void)
{
	glob_t keyboard_dev;
	
	glob ("/dev/input/by-path/*-kbd", 0, NULL, &keyboard_dev);
	
	for (int i = 0; i < keyboard_dev.gl_pathc ; ++i) {
		puts (keyboard_dev.gl_pathv[i]);
	}
	
	globfree (&keyboard_dev);
    
}

大概懂了吧?详细内容请看手册。

二、I/O复用

虽然我只有一个键盘,但是里面还是有两个键盘设备。

不知道你们用来做什么,我是用来进行按键检测。如果一个键盘按键会被检测,另一个键盘不被检测——比如笔记本外接机械键盘的,

这样就体验不好。

所有肯定要处理多键盘的情况。


然而,这里的键盘检测,其实是依赖于read(),这样一个键盘读不出来东西就会卡在那里,可能后面的键盘数据已经到了,但是它执行不了。

你大概也不想每个键盘都开一个线程吧?我能预见,这样设计会很烂,它让问题过度复杂化了。

为什么不用多线程?理论上,你可能有100个键盘,这样你要再开99个线程。
实际上,你也要面临竞争的风险——不同键盘相同的按键(这极有可能,只要在一个结束之前按下另一个)会触发相同的处理程序,它们可能同时设置一份资源。

下面来解决它。

1. select

#include <glob.h>
#include <stddef.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <linux/input.h>

ssize_t readn (int fd, void *buf, size_t size)
{
	size_t nleft = size;
	while (nleft > 0) {
		int n = read (fd, & ( (char *) buf) [size - nleft], nleft);
		
		if (n > 0)
			nleft -= n;
		else if (n == -1) {
			if (errno == EINTR)
				continue;
			else
				return -1;
		} else if (n == 0)
			break;
	}
	return size - nleft;
}

static void solve_keyboard (int fd)
{
	static struct input_event event;
	
	int n = readn (fd, &event, sizeof (event));
	if (n != sizeof (event))
		perror ("read()"), exit (EXIT_FAILURE);
		
	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);
		case 2:
			puts ("repeat automatically");
	}
}

static int find_max_int (const int *arr, size_t len)
{
	int max = arr[0];
	for (size_t i = 1; i < len ; ++i) {
		if (arr[i] > max)
			max = arr[i];
	}
	return max;
}
void detect_readonly_devices (const int *dev, size_t count, void (*solver) (int))
{
	fd_set readfds;
	while (1) {
		FD_ZERO (&readfds);
		for (int i = 0; i < count ; ++i)
			FD_SET (dev[i], &readfds);
		int n_readable = select (find_max_int (dev, count) + 1, &readfds, NULL, NULL, NULL);
		if (n_readable == -1) {
			if (errno == EINTR)
				continue;
			else
				perror ("select()"), exit (EXIT_FAILURE);
		} else if (n_readable > 0) {
		
			for (int i = 0; i < count ; ++i) {
				if (FD_ISSET (dev[i], &readfds))
					solver (dev[i]);
			}
			
		}
	}
}

int main (void)
{
	glob_t keyboard_devs;
	glob ("/dev/input/by-path/*-kbd", 0, NULL, &keyboard_devs);
	
	if (keyboard_devs.gl_pathc <= 0)
		return 0;
		
	const int fd_count = keyboard_devs.gl_pathc;
	int fds[fd_count];
	for (int i = 0 ; i < fd_count ; ++i) {
		int fd = open (keyboard_devs.gl_pathv[i], O_RDONLY);
		if (fd == -1)
			perror ("open()"), exit (EXIT_FAILURE);
		else
			fds[i] = fd;
	}
	globfree (&keyboard_devs);
	detect_readonly_devices (fds, fd_count, solve_keyboard);
}

2. poll

#include <glob.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <poll.h>
#include <errno.h>
#include <linux/input.h>

ssize_t readn (int fd, void *buf, size_t size)
{
	size_t nleft = size;
	while (nleft > 0) {
		int n = read (fd, & ( (char *) buf) [size - nleft], nleft);
		
		if (n > 0)
			nleft -= n;
		else if (n == -1) {
			if (errno == EINTR)
				continue;
			else
				return -1;
		} else if (n == 0)
			break;
	}
	return size - nleft;
}


static void check_keyboard (int fd)
{
	static struct input_event event;
	int n = readn (fd, &event, sizeof (event));
	if (n != sizeof (event))
		perror ("read()"), exit (EXIT_FAILURE);
		
	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 (void)
{
	int *fds;
	int fds_count;
	{
		glob_t kbd_dev;
		if (glob ("/dev/input/by-path/*-kbd", 0, NULL, &kbd_dev) != 0)
			perror ("glob()"), exit (EXIT_FAILURE);
			
		if (kbd_dev.gl_pathc < 1)
			exit (EXIT_SUCCESS);
			
		fds = malloc (sizeof (int) * kbd_dev.gl_pathc);
		fds_count = kbd_dev.gl_pathc;
		
		for (int i = 0; i < kbd_dev.gl_pathc; ++i) {
			int fd = open (kbd_dev.gl_pathv[i], O_RDONLY);
			if (fd == -1)
				perror ("open()"), exit (EXIT_FAILURE);
			else
				fds[i] = fd;
		}
		globfree (&kbd_dev);
	}
	
	struct pollfd poll_fds[fds_count];
	
	{
		for (int i = 0; i < fds_count ; ++i) {
			poll_fds[i].fd = fds[i];
			poll_fds->events = POLLIN;
		}
		
		free (fds);
	}
	
	while (1) {
		int n = poll (poll_fds, fds_count, -1);
		if (n == -1) {
			if (errno == EINTR)
				continue;
			else
				perror ("poll()"), exit (EXIT_FAILURE);
		} else if (n == 0) {
			printf ("warning: poll() timeout\n");
			continue;
		} else if (poll < 0) {
			perror ("poll()"), exit (EXIT_FAILURE);
		} else if (poll > 0) {
		
			for (int i = 0; i < fds_count ; ++i) {
				if (poll_fds[i].revents & POLLIN)
					check_keyboard (poll_fds[i].fd);
			}
		}
	}
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值