Linux学习笔记--uinput
uinput 是 Linux 内核的一个模块,允许用户空间程序创建虚拟输入设备。这些虚拟设备的行为与真实硬件设备完全相同。
1. 头文件引入
#include <linux/uinput.h> // uinput 相关定义和 ioctl 命令
2. 事件发送函数
void emit(int fd, int type, int code, int val)
{struct input_event ie;ie.type = type;ie.code = code;ie.value = val;ie.time.tv_sec = 0;ie.time.tv_usec = 0;write(fd, &ie, sizeof(ie));
}
封装了输入事件的构造和发送
input_event
结构与内核中的定义一致时间戳被忽略(设为0)
1. 打开 uinput 设备
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
打开 uinput 字符设备
O_WRONLY
: 只写模式(用于发送事件)O_NONBLOCK
: 非阻塞模式
2. 配置设备属性
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
设置设备为直接输入设备(触摸屏类设备)。
3. 配置设备能力
按键事件支持:
ioctl(fd, UI_SET_EVBIT, EV_KEY); // 启用按键事件类型
ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); // 启用触摸按键
绝对坐标事件支持:
ioctl(fd, UI_SET_EVBIT, EV_ABS); // 启用绝对坐标事件类型
ioctl(fd, UI_SET_ABSBIT, ABS_X); // 启用 X 坐标轴
ioctl(fd, UI_SET_ABSBIT, ABS_Y); // 启用 Y 坐标轴
4. 设备信息设置
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1234; /* 示例厂商 ID */
usetup.id.product = 0x5678; /* 示例产品 ID */
strcpy(usetup.name, "Example device");
5. 创建设备
ioctl(fd, UI_DEV_SETUP, &usetup); // 应用设备设置
ioctl(fd, UI_DEV_CREATE); // 创建设备节点
执行 UI_DEV_CREATE
后,内核会在 /dev/input/
目录下创建新的输入设备节点。
事件模拟流程
1. 等待校准程序
printf("Will sleep 60s, in this time you should run ts_calibreate\n");
sleep(60);
给用户时间运行触摸屏校准程序。
2. 五点校准模拟
程序模拟了标准的五点触摸校准:
左上角点:
emit(fd, EV_KEY, BTN_TOUCH, 1); // 触摸开始
emit(fd, EV_ABS, ABS_X, 100); // X 坐标
emit(fd, EV_ABS, ABS_Y, 100); // Y 坐标
emit(fd, EV_SYN, SYN_REPORT, 0); // 同步事件
emit(fd, EV_KEY, BTN_TOUCH, 0); // 触摸结束
emit(fd, EV_SYN, SYN_REPORT, 0); // 同步事件
sleep(5);
事件序列说明:
BTN_TOUCH=1
: 触摸按下ABS_X
,ABS_Y
: 坐标位置SYN_REPORT
: 同步,表示一组事件完成BTN_TOUCH=0
: 触摸释放再次
SYN_REPORT
: 同步
五个校准点:
左上角: (100, 100)
右上角: (1000, 100)
右下角: (1000, 1000)
左下角: (100, 1000)
中心点: (550, 550)
3. 对角线绘制测试
emit(fd, EV_KEY, BTN_TOUCH, 1); // 触摸开始
emit(fd, EV_ABS, ABS_X, 200);
emit(fd, EV_ABS, ABS_Y, 200);
emit(fd, EV_SYN, SYN_REPORT, 0);
sleep(5);i = 200;
while (i < 1000)
{emit(fd, EV_ABS, ABS_X, i);emit(fd, EV_ABS, ABS_Y, i);emit(fd, EV_SYN, SYN_REPORT, 0);i += 10;sleep(5);
}emit(fd, EV_KEY, BTN_TOUCH, 0); // 触摸结束
emit(fd, EV_SYN, SYN_REPORT, 0);
模拟从 (200,200) 到 (1000,1000) 的对角线绘制。
4. 清理资源
ioctl(fd, UI_DEV_DESTROY); // 销毁虚拟设备
close(fd); // 关闭文件描述符
技术细节
uinput ioctl 命令详解:
命令 作用 参数
UI_SET_EVBIT 启用事件类型 EV_KEY, EV_ABS 等
UI_SET_KEYBIT 启用具体按键 BTN_TOUCH, KEY_A 等
UI_SET_ABSBIT 启用坐标轴 ABS_X, ABS_Y 等
UI_SET_PROPBIT 设置设备属性 INPUT_PROP_DIRECT 等
UI_DEV_SETUP 配置设备信息 uinput_setup 结构体
UI_DEV_CREATE 创建设备 无
UI_DEV_DESTROY 销毁设备 无
输入事件流协议:
正确的输入事件序列:
EV_KEY, BTN_TOUCH, 1 // 触摸开始
EV_ABS, ABS_X, x_value // X 坐标
EV_ABS, ABS_Y, y_value // Y 坐标
EV_SYN, SYN_REPORT, 0 // 同步(上报事件)
... // 可能的多点触控数据
EV_KEY, BTN_TOUCH, 0 // 触摸结束
EV_SYN, SYN_REPORT, 0 // 同步
运行要求
1. 内核支持
# 检查 uinput 模块
lsmod | grep uinput# 加载模块
sudo modprobe uinput
2. 设备权限
# 检查设备权限
ls -l /dev/uinput# 可能需要 root 权限或设置 udev 规则
sudo chmod 666 /dev/uinput
3. 编译运行
gcc -o virtual_touch virtual_touch.c
sudo ./virtual_touch