Linux下模拟输入设备发送event事件
Linux下的键盘输入设备上报event事件来让系统处理按键的按下和释放动作。实际应用中,会有需要判断外部信号后,模拟event事件,来让上层应用处理这个事件对应的业务功能。此时,可以使用uinput设备来实现这个功能,向uinput设备写入input_event结构的数据,会被模拟为标准的Linux event事件数据,从而让上层业务应用直接处理对应功能。
uinput
即Userspace Input
,uinput 的实现是基于 Linux input子系统(Input Subsystem),允许用户空间程序创建虚拟的输入设备并向内核发送输入事件,比如键盘敲击、鼠标移动等,就像这些事件来自真实的物理设备一样。对于开发自定义输入设备驱动、自动化测试、游戏控制模拟以及各种人机交互实验等场景非常有用。
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main() {
int fd;
struct uinput_user_dev uidev;
struct input_event ev;
fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (fd < 0) {
perror("Error opening /dev/uinput");
return 1;
}
memset(&uidev, 0, sizeof(uidev));
strncpy(uidev.name, "Virtual Keyboard", UINPUT_MAX_NAME_SIZE);
uidev.id.bustype = BUS_USB;
uidev.id.vendor = 0x1234;
uidev.id.product = 0x5678;
uidev.id.version = 1;
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_KEYBIT, KEY_A);
write(fd, &uidev, sizeof(uidev));
ioctl(fd, UI_DEV_CREATE);
while(1) {
sleep(2);
memset(&ev, 0, sizeof(ev));
ev.type = EV_KEY;
ev.code = KEY_A;
ev.value = 1; // press
write(fd, &ev, sizeof(ev));
ev.value = 0; // release
write(fd, &ev, sizeof(ev));
memset(&ev, 0, sizeof(ev));
ev.type = EV_SYN;
ev.code = SYN_REPORT;
ev.value = 0;
write(fd, &ev, sizeof(ev));
}
ioctl(fd, UI_DEV_DESTROY);
close(fd);
return 0;
}
运行程序后,系统会生成一个虚拟的input设备,可以使用cat命令来查看当前系统中的虚拟设备。
cat /proc/bus/input/devices
I: Bus=0003 Vendor=1234 Product=5678 Version=0001
N: Name="Virtual Keyboard"
P: Phys=
S: Sysfs=/devices/virtual/input/input0
U: Uniq=
H: Handlers=kbd event0
B: PROP=0
B: EV=3
B: KEY=40000000
然后使用evtest命令来查看这个设备是否有上传event事件。
evtest /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x1234 product 0x5678 version 0x1
Input device name: "Virtual Keyboard"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 30 (KEY_A)
Properties:
Testing ... (interrupt to exit)
Event: time 1651254109.394687, type 1 (EV_KEY), code 30 (KEY_A), value 1
Event: time 1651254109.394687, type 1 (EV_KEY), code 30 (KEY_A), value 0
Event: time 1651254109.394687, -------------- SYN_REPORT ------------
Event: time 1651254111.394875, type 1 (EV_KEY), code 30 (KEY_A), value 1
Event: time 1651254111.394875, type 1 (EV_KEY), code 30 (KEY_A), value 0
Event: time 1651254111.394875, -------------- SYN_REPORT ------------
这里可以看到/dev/input/event0设备有上传了,类型为EV_KEY,键值为KEY_A的event事件。