You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现抓取/dev/input/event#事件、替换后写入并取消抓取?

解决触摸屏事件拦截与修改的正确方案

你的核心问题在于错误地尝试将修改后的事件写回原输入设备,这违背了Linux输入子系统的设计逻辑。原输入设备是硬件事件的来源,修改后的事件应该通过虚拟输入设备(uinput)发送,同时通过EVIOCGRAB独占原设备,确保原始事件不会流向桌面。

正确流程步骤

    1. 以RDWR权限打开目标触摸屏设备(/dev/input/event#),调用ioctl(fd, EVIOCGRAB, 1)独占设备,此时所有原始触摸事件只会被你的程序读取,不会传递到桌面。
    1. 循环读取原设备的事件:
    • 若事件不需要修改,直接丢弃(因设备已被独占,事件不会流出)。
    • 若需要修改,调整事件的value或其他字段后,准备通过uinput发送。
    1. 初始化uinput虚拟设备:
    • 打开/dev/uinput(部分系统路径为/dev/input/uinput)。
    • 通过ioctl复制原触摸屏的事件类型(如EV_ABS、EV_KEY)、轴信息(如ABS_X、ABS_Y的范围)等属性,确保虚拟设备与原设备输入能力一致。
    • 调用ioctl(fd_uinput, UI_DEV_CREATE)创建虚拟设备。
    1. 将修改后的事件写入uinput设备,每次事件组结束后必须发送EV_SYN类型的SYN_REPORT事件,通知系统处理该组输入。
    1. 程序退出时,先调用ioctl(fd_uinput, UI_DEV_DESTROY)销毁虚拟设备,再取消原设备的抓取(ioctl(fd, EVIOCGRAB, 0))并关闭文件描述符。

关键代码片段示例

初始化uinput设备(复制原设备属性)

int setup_uinput(int original_fd) {
    int uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    if (uinput_fd < 0) return -1;

    // 复制原设备的事件位掩码
    unsigned long evbit[EV_MAX/8 + 1] = {0};
    ioctl(original_fd, EVIOCGBIT(0, EV_MAX), evbit);
    ioctl(uinput_fd, UI_SET_EVBIT, EV_SYN);
    for (int i = 0; i < EV_MAX; i++) {
        if (evbit[i/8] & (1 << (i%8))) {
            ioctl(uinput_fd, UI_SET_EVBIT, i);
            // 复制ABS事件的轴范围
            if (i == EV_ABS) {
                struct input_absinfo absinfo;
                for (int j = 0; j < ABS_MAX; j++) {
                    if (ioctl(original_fd, EVIOCGABS(j), &absinfo) == 0) {
                        ioctl(uinput_fd, UI_SET_ABSBIT, j);
                        ioctl(uinput_fd, UI_SET_ABSINFO, &absinfo);
                    }
                }
            }
        }
    }

    // 设置虚拟设备名称与总线类型
    struct uinput_setup setup = {0};
    strcpy(setup.name, "Modified Touchscreen");
    setup.id.bustype = BUS_USB;
    ioctl(uinput_fd, UI_DEV_SETUP, &setup);
    ioctl(uinput_fd, UI_DEV_CREATE);

    return uinput_fd;
}

发送修改后的事件

void send_modified_event(int uinput_fd, struct input_event *ev) {
    // 写入修改后的事件
    write(uinput_fd, ev, sizeof(struct input_event));
    // 发送同步事件触发系统处理
    struct input_event syn = {0};
    syn.type = EV_SYN;
    syn.code = SYN_REPORT;
    syn.value = 0;
    write(uinput_fd, &syn, sizeof(struct input_event));
}

原有方案失效原因

  • 用EVIOCGRAB独占原设备时,该设备的所有事件(包括你写入的)都会被程序的read调用捕获,无法传递到桌面。
  • 若先取消抓取再写入原设备,设备独占状态解除后,系统会恢复处理原设备事件流,写入的新事件会和后续原始事件一同流向桌面,导致输入混乱。

内容的提问来源于stack exchange,提问作者Thai Nguyen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:18:38