如何更精准读取/dev/input/mice设备的鼠标移动数据?
问题原因
你遇到的快速移动鼠标时计数不准的问题,根源是没有正确解析PS/2鼠标协议的扩展数据包:
/dev/input/mice遵循PS/2鼠标协议,默认发送3字节数据包,位移范围限制在-128到127之间。- 当鼠标快速移动导致位移超过单字节范围时,会自动发送6字节的扩展数据包,用来传递更大范围的16位位移值。
- 你的代码直接读取6字节数据,且只提取buf[1]和buf[2]这两个低字节位移值,完全忽略了扩展包中的高字节数据,导致快速移动时丢失了大部分位移信息,累加的x_total自然远低于预期值。
解决方案
需要严格按照PS/2鼠标协议规范解析数据包:
- 先读取第一个字节(标志位),通过标志位的第3位判断是否为扩展数据包。
- 根据数据包类型,读取完整的3字节(普通包)或6字节(扩展包)数据。
- 对扩展包,组合低字节和高字节得到完整的16位位移值,再进行累加计算。
修改后的代码
#include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sys/select.h> #include <stdio.h> int main() { int fd; fd_set readfds; int screen_x = 1920; int screen_y = 1080; int x_total = 0, y_total = 0; fd = open("/dev/input/mice", O_RDONLY); if (fd == -1) { printf("Error Opening /dev/input/mice\n"); return 1; } while(1) { FD_ZERO(&readfds); FD_SET(fd, &readfds); select(fd+1, &readfds, NULL, NULL, NULL); if(FD_ISSET(fd, &readfds)) { unsigned char flags; // 先读取标志位字节 if(read(fd, &flags, 1) <= 0) { continue; } int packet_size = (flags & 0x08) ? 6 : 3; // 扩展包6字节,普通包3字节 unsigned char buf[6]; buf[0] = flags; // 读取剩余字节,确保数据包完整 if(read(fd, buf + 1, packet_size - 1) != packet_size - 1) { continue; } int x, y; if(packet_size == 3) { // 普通3字节包,直接取带符号单字节位移 x = (signed char)buf[1]; y = (signed char)buf[2]; } else { // 6字节扩展包,组合低字节与高字节为完整16位位移 x = (signed char)buf[1] | (buf[4] << 8); y = (signed char)buf[2] | (buf[5] << 8); } printf("x=%d, y=%d\n", x, y); x_total += x; y_total += y; printf("x_total: %d; y_total: %d\n", x_total, y_total); } } close(fd); return 0; }
额外说明
- PS/2鼠标6字节扩展包的固定格式为:标志位字节 + x低字节 + y低字节 + 固定0x00字节 + x高字节 + y高字节。
- 读取时必须确保完整读取整个数据包,避免因分段读取导致解析错误。
- 测试时仍可使用
xdotool mousemove 960 540将光标定位到屏幕中心,再快速移动验证结果准确性。
内容的提问来源于stack exchange,提问作者Xuan
相关产品推荐
相关产品推荐

