Wayland环境下uinput虚拟设备如何设置光标绝对位置?
问题描述
我参考Linux uinput文档中的代码示例,实现了一个虚拟指针设备,现有代码可以成功让光标向右下方做相对移动。但我想把光标绝对定位到屏幕x=500、y=500的位置,仅将代码中的REL相关常量替换为ABS后并未生效。我没找到相关功能的详细文档,想了解在Wayland(Gnome Mutter)环境下,是否可以通过uinput实现该功能,或者有没有其他替代方案。我注意到libinput replay具备绝对定位的能力,希望能复现这个行为。
原相对移动代码如下:
#include <linux/uinput.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> void emit(int fd, int type, int code, int val) { struct input_event ie; struct input_event ie; ie.type = type; ie.code = code; ie.value = val; /* timestamp values below are ignored */ ie.time.tv_sec = 0; ie.time.tv_usec = 0; write(fd, &ie, sizeof(ie)); } /* emit function is identical to of the first example */ void emit(int fd, int type, int code, int val) { struct input_event ie; ie.type = type; ie.code = code; ie.value = val; /* timestamp values below are ignored */ ie.time.tv_sec = 0; ie.time.tv_usec = 0; write(fd, &ie, sizeof(ie)); } int main(void) { struct uinput_setup usetup; int i = 50; int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); /* enable mouse button left and relative events */ ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); ioctl(fd, UI_SET_EVBIT, EV_REL); ioctl(fd, UI_SET_RELBIT, REL_X); ioctl(fd, UI_SET_RELBIT, REL_Y); memset(&usetup, 0, sizeof(usetup)); usetup.id.bustype = BUS_USB; usetup.id.vendor = 0x1234; /* sample vendor */ usetup.id.product = 0x5678; /* sample product */ strcpy(usetup.name, "Example device"); ioctl(fd, UI_DEV_SETUP, &usetup); ioctl(fd, UI_DEV_CREATE); /* * On UI_DEV_CREATE the kernel will create the device node for this * device. We are inserting a pause here so that userspace has time * to detect, initialize the new device, and can start listening to * the event, otherwise it will not notice the event we are about * to send. This pause is only needed in our example code! */ sleep(1); /* Move the mouse diagonally, 5 units per axis */ while (i--) { emit(fd, EV_REL, REL_X, 5); emit(fd, EV_REL, REL_Y, 5); emit(fd, EV_SYN, SYN_REPORT, 0); usleep(15000); } /* * Give userspace some time to read the events before we destroy the * device with UI_DEV_DESTROY. */ sleep(1); ioctl(fd, UI_DEV_DESTROY); close(fd); return 0; }
解决方案
一、修正uinput绝对定位的代码实现
仅替换REL为ABS无法生效的原因是:绝对坐标设备需要告知系统轴的取值范围,并且需要正确初始化设备的事件掩码。以下是修正后的代码:
#include <linux/uinput.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> 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)); } int main(void) { struct uinput_setup usetup; struct uinput_abs_setup abs_setup; int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); if (fd < 0) { perror("Failed to open /dev/uinput"); return EXIT_FAILURE; } /* 启用按键事件(保留左键) */ ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); /* 启用绝对坐标事件 */ ioctl(fd, UI_SET_EVBIT, EV_ABS); ioctl(fd, UI_SET_ABSBIT, ABS_X); ioctl(fd, UI_SET_ABSBIT, ABS_Y); /* 设置X轴范围(根据你的屏幕分辨率调整,这里示例为1920x1080) */ memset(&abs_setup, 0, sizeof(abs_setup)); abs_setup.code = ABS_X; abs_setup.absinfo.min = 0; abs_setup.absinfo.max = 1919; abs_setup.absinfo.fuzz = 0; abs_setup.absinfo.flat = 0; ioctl(fd, UI_ABS_SETUP, &abs_setup); /* 设置Y轴范围 */ abs_setup.code = ABS_Y; abs_setup.absinfo.max = 1079; ioctl(fd, UI_ABS_SETUP, &abs_setup); /* 配置设备信息 */ memset(&usetup, 0, sizeof(usetup)); usetup.id.bustype = BUS_USB; usetup.id.vendor = 0x1234; usetup.id.product = 0x5678; strcpy(usetup.name, "Absolute Pointer Device"); ioctl(fd, UI_DEV_SETUP, &usetup); ioctl(fd, UI_DEV_CREATE); sleep(1); /* 等待系统识别设备 */ /* 发送绝对定位到(500,500)的事件 */ emit(fd, EV_ABS, ABS_X, 500); emit(fd, EV_ABS, ABS_Y, 500); emit(fd, EV_SYN, SYN_REPORT, 0); /* 提交事件 */ sleep(1); /* 等待事件被处理 */ ioctl(fd, UI_DEV_DESTROY); close(fd); return 0; }
关键修改点:
- 删除了重复定义的
emit函数 - 替换
EV_REL为EV_ABS,并启用ABS_X、ABS_Y事件位 - 通过
UI_ABS_SETUP设置X/Y轴的最小、最大值(必须和屏幕分辨率匹配,否则坐标会被缩放) - 直接发送目标绝对坐标,最后通过
SYN_REPORT提交事件
二、Wayland(Gnome Mutter)下的适配注意事项
Mutter等Wayland compositor对输入设备的识别有以下要求:
- 确保设备被标记为绝对定位设备(示例中用
BUS_USB即可) - 轴范围必须准确,否则Mutter会自动缩放坐标导致定位偏移
- 确保当前用户有权限访问
/dev/uinput(可将用户加入input组)
三、替代方案:基于libinput实现
如果uinput方式在Wayland下有兼容性问题,可以参考libinput replay的实现逻辑:
- libinput提供了更上层的输入设备抽象,能自动适配X11和Wayland环境
- 可以使用libinput的API创建虚拟绝对指针设备,直接发送绝对坐标事件
- 也可以直接调用
libinput-record和libinput-replay工具,录制一次绝对定位事件后重放,无需自己编写代码
内容的提问来源于stack exchange,提问作者ElijaRock
相关产品推荐
相关产品推荐

