linux下鼠标事件丢失与evdev缓冲区溢出问题

探讨在性能较差的Linux平台上,Qt程序中鼠标事件丢失的原因及解决方案。文章深入分析了Qt鼠标事件的生成与分发过程,指出问题源于内核与Qt循环之间的evdev缓冲区溢出,并提出通过处理SYN_DROPPED事件来解决。

之前遇到这样一个bug:在一个性能较差的linux平台上的一个Qt程序,当UI线程在执行耗时操作时,界面会卡顿,而这时频繁点击滑动鼠标,会出现鼠标事件丢失的问题。举个例子:某个控件收到一个鼠标按下的事件,但再也没有收到鼠标弹起事件,而此时鼠标按键实际上已经弹起了,这就导致程序进入了一种异常状态,除非再次点击鼠标,否则无法恢复。上面这个问题是在QApplication的事件过滤器中确定的,全局的QApplication事件过滤器能记录下所有的Qt事件。

那么如何确定事件是在哪里丢失的呢?首先要大概的了解一下Qt鼠标事件的生成与分发过程,由于上述平台没有窗口管理系统(一般是Xorg),所以大致流程是这样的:

  1. 鼠标产生动作
  2. 内核响应中断,将鼠标事件放入evdev缓冲区
  3. Qt从evdev缓冲区中读取鼠标事件
  4. Qt分发鼠标事件
  5. Qt处理鼠标事件

那么事件是在哪一步丢失的呢?经过分析,第3步是由QEvdevMouseHandler::readMouseData函数完成的,通过记录日志发现,鼠标弹起事件在这一步就已经不见了。而这一步呢,是Qt能接触到鼠标事件的第一现场,也就是说问题的直接原因并不在于Qt,而在于内核。

回过头来看一下,上面的1-5步看起来像是串行的,实际上并不是:内核的中断响应与Qt的事件读取是两个独立的流程,1-2是一个内核的循环,3-5是一个Qt的循环,而这两个循环共享了同一个evdev缓冲区。如果内核的循环跑的很快,而Qt的循环由于什么原因被卡住而跑的很慢时会发生什么呢?自然evdev缓冲区会发生溢出。以我手上的4.16.0版本的内核源码为例,其中evdev.c中的__pass_event函数用来向evdev缓冲区中压入事件:

static void __pass_event(struct evdev_client *client,
			 const struct input_event *event)
{
	client->buffer[client->head++] = *event;
	client->head &= client->bufsize - 1;

	if (unlikely(client->head == client->tail)) {
		/*
		 * This effectively "drops" all unconsumed events, leaving
		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
		 */
		client->tail = (client->head - 2) & (client->bufsize - 1);

		client->buffer[client->tail].input_event_sec =
						event->input_event_sec;
		client->buffer[client->tail].input_event_usec =
						event->input_event_usec;
		client->buffer[client->tail].type = EV_SYN;
		client->buffer[client->tail].code = SYN_DROPPED;
		client->buffer[client->tail].value = 0;

		client->packet_head = client->tail;
	}

	if (event->type == EV_SYN && event->code == SYN_REPORT) {
		client->packet_head = client->head;
		kill_fasync(&client->fasync, SIGIO, POLL_IN);
	}
}

可以看出来evdev缓冲区是一个环状缓冲区,如果插入事件导致head与tail相等,即可判定缓冲区发生了溢出,会丢弃缓冲区中的所有事件,同时内核会向缓冲区放入一个SYN_DROPPED事件,通知用户程序事件有丢失。不巧的是,Qt的QEvdevMouseHandler::readMouseData对这个事件没有做任何处理,也就无法自动从异常状态恢复了。如果希望解决这个问题,可以考虑从这个特殊的SYN_DROPPED事件下手。

PS:虽然事件是被内核丢掉的,但Qt和用户程序对此也要负一半责任:为什么Qt要把事件读取和事件响应放在一个UI线程里面去跑呢?用户代码为什么要把耗时操作直接在UI线程中跑呢?

PPS:如果事件源源不断产生,而响应事件平摊耗时总是大于生成事件耗时,不管缓冲区有多大,总有一天会溢出的,不过这种极端情况似乎并不容易出现。

<think> 首先,用户要求我总结并详细讲解几个主题:Tp驱动报点、多模报点信息、事件发送、sceneboard进程事件、touch test相关知识。需要包含可能出现的错误和日志中可能出现的原文。 但用户提供的引用内容是关于Capistrano、RACObserve、Linux文件操作和touch命令的,这些似乎主题无关。用户说:“上述引用仅供参考,并不是用户真正的需求”,所以我不能依赖这些引用。相反,我应该基于自己的知识来回答,因为主题是关于触摸屏相关的技术(Tp驱动可能指触摸屏驱动)。 关键主题: - Tp驱动报点:这可能是指触摸屏(Touch Panel)驱动中的报点,即报告触摸点的坐标和信息。 - 多模报点信息:可能涉及多点触摸或多模式触摸信息的报告。 - 事件发送:将触摸事件发送到系统或应用程序。 - sceneboard进程事件:Sceneboard可能是一个特定进程或框架,处理场景板事件;可能是一个打字错误或特定术语。我认为可能是“Scene Board”或类似的东西,但不确定。或许是“Scene Board”进程事件,可能触摸事件处理相关。 - touch test:触摸测试,可能是指测试触摸屏功能。 我需要确保回答结构清晰,帮助用户逐步解决问题。回答要真实可靠,所以我必须基于常识和知识库内容。 系统指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 引用的段落末尾自然地添加引用标识(但用户提供的引用不相关,所以我可能不需要引用它们)。 - 回答结构清晰,真实可靠。 步骤: 1. **总结每个主题**:先简要介绍每个主题。 2. **详细讲解**:深入解释每个主题的工作原理、流程等。 3. **可能出现的错误和日志原文**:讨论常见问题、错误类型,并给出可能的日志原文示例。 4. **生成相关问题**:在末尾添加相关问题。 组织回答: - 引言:概述主题。 - 分节讲解每个主题。 - Tp驱动报点 - 多模报点信息 - 事件发送 - sceneboard进程事件(假设为Scene Board) - touch test - 在每个主题下包括错误和日志部分。 - 结论。 - 相关问题。 内容基于知识: - **Tp驱动报点**:触摸屏驱动负责检测触摸点,报告坐标、压力等。错误可能包括无效坐标、中断失败等。 - **多模报点信息**:多点触摸,报告多个触点信息。 - **事件发送**:驱动将事件发送到输入子系统或框架,如Linuxevdev。 - **sceneboard进程事件**:这可能是一个特定于某些系统的进程。或许是“Scene Board”或类似框架,处理触摸事件。假设它类似于Android的InputDispatcher或Linux的X server。 - **touch test**:测试工具或方法,验证触摸屏功能。 日志原文示例: - 使用真实感的日志片段,例如:"Error: Touch point out of range" 或 "Failed to send event to sceneboard"。 使用中文回答。 生成相关问题:在末尾添加2-3个相关问题。 最终结构: 1. 引言 2. 详细讲解每个主题 3. 错误和日志 4. 相关问题 </think> ### 触摸屏相关技术总结详细讲解 本回答基于我对触摸屏技术的理解和常见系统实现(如Linux输入子系统、Android框架等)进行总结。Tp驱动报点、多模报点信息、事件发送、sceneboard进程事件和touch test是触摸屏系统中的核心环节,涉及硬件驱动、数据处理和事件分发。我将逐一讲解每个主题的工作原理、常见流程、潜在错误及日志原文示例。讲解结构清晰,从底层驱动到上层应用逐步展开。 #### 1. Tp驱动报点 **定义原理**:Tp驱动(Touch Panel Driver)是触摸屏的硬件驱动程序,负责从触摸传感器读取原始数据(如坐标、压力、触点ID),并将其转换为标准化的“报点”(report points)。报点包括每个触摸点的详细信息,例如位置 $(x, y)$、触摸状态(按下、移动、抬起),以及时间戳。驱动通常运行在Linux内核空间,通过I2C或SPI总线获取数据。 **工作流程**: - **数据采集**:驱动轮询或中断方式读取传感器数据。 - **数据处理**:过滤噪声(如使用卡尔曼滤波),计算坐标偏移(考虑屏幕分辨率)。 - **报点生成**:将数据封装成输入事件结构,例如在Linux中使用 `input_event` 结构体。 - **上报内核**:通过 `input_report_abs()` 或类似函数上报到输入子系统。 **可能出现的错误**: - **坐标无效**:硬件噪声导致坐标超出屏幕范围(如 $x < 0$ 或 $y > 1080$)。 - **中断丢失**:高负载下中断未被正确处理,导致触摸点缺失。 - **传感器故障**:如开路或短路,导致数据错误。 - **驱动配置错误**:参数(如采样率)设置不当。 **日志原文示例**: - 常见日志条目(源自dmesg或syslog): ``` [ERROR] tp_drv: Touch point out of range: x=1200, y=2400 (expected max: 1080x1920) [WARN] tp_drv: Interrupt missed, touch events dropped at t=12345678 [DEBUG] tp_drv: Sensor read failure, I2C error code: -5 ``` #### 2. 多模报点信息 **定义原理**:多模报点(Multitouch Reporting)指支持多点触摸的报点机制,能同时处理多个触点信息(如5点或10点触摸)。每个触点包括独立属性:坐标 $(x_i, y_i)$、压力 $p_i$、触点ID(唯一标识符),以及手势信息(如捏合、旋转)。驱动使用多点触摸协议(如Linux的MT协议)组织数据。 **工作流程**: - **触点跟踪**:驱动为每个触点分配ID,并维护状态机(如按下、移动、抬起)。 - **数据打包**:多点数据打包为一个事件包(如通过 `input_mt_sync_frame()`)。 - **上报**:以数组形式上报所有触点信息,确保原子性(避免触点冲突)。 **可能出现的错误**: - **触点冲突**:ID分配错误导致触点混淆(如两个触点共享同一ID)。 - **数据不完整**:高触点密度下缓冲区溢出,部分触点丢失。 - **手势误判**:如将捏合误识别为滑动。 - **协议不兼容**:驱动上层框架协议版本不匹配。 **日志原文示例**: - 日志片段: ``` [ERROR] mt_report: Touch ID conflict detected: ID=2 duplicated at t=12345789 [INFO] mt_report: Buffer overflow, 3 touch points dropped [DEBUG] mt_report: Gesture misclassification: pinch detected as swipe ``` #### 3. 事件发送 **定义原理**:事件发送(Event Dispatching)指将驱动的报点数据发送到用户空间进程或框架。在Linux中,这通过输入子系统(如evdev)实现:驱动上报事件到 `/dev/input/eventX` 设备文件,然后由用户空间守护进程(如udev或X server)读取并转发到应用程序。事件包括类型(如 `EV_ABS` 表示绝对坐标)、代码(如 `ABS_MT_POSITION_X`)和值。 **工作流程**: - **内核到用户空间**:驱动调用 `input_event()` 写入事件队列。 - **用户空间处理**:守护进程(如evdev-reader)读取事件,通过socket或IPC发送到目标进程。 - **应用接收**:应用(如GUI框架)解析事件,触发回调函数。 **可能出现的错误**: - **队列满**:高事件速率下事件队列溢出事件被丢弃。 - **权限问题**:设备文件访问权限不足(如 `crw-rw----` 权限错误)。 - **进程死锁**:接收进程卡顿或崩溃,事件无法处理。 - **序列错误**:事件时间戳乱序,导致触摸轨迹断裂。 **日志原文示例**: - 日志记录: ``` [ERROR] evdev: Event queue full, 15 events discarded for device event3 [WARN] input_handler: Permission denied to /dev/input/event0, check udev rules [CRITICAL] dispatcher: Receiver process (PID 1234) not responding, events lost ``` #### 4. sceneboard进程事件 **定义原理**:sceneboard进程(可能指特定系统如Android中的“Scene Board”或类似输入管理服务)负责协调触摸事件的分发和处理。它从输入子系统接收事件,根据场景(如当前应用窗口)路由到正确目标(如焦点应用)。该进程通常处理事件优先级、手势识别和系统级响应。 **工作流程**: - **事件接收**:从输入守护进程获取事件流。 - **场景解析**:根据活动窗口或UI状态决定事件目的地。 - **事件分发**:发送到应用进程(如通过Binder或DBus)。 - **手势处理**:高级手势(如三指滑动)在此层识别。 **可能出现的错误**: - **路由失败**:场景判断错误,事件发送到错误应用。 - **资源耗尽**:进程内存或CPU过载,导致事件延迟。 - **死锁或崩溃**:进程自身故障(如段错误)。 - **兼容性问题**:应用的事件处理逻辑冲突。 **日志原文示例**: - 日志输出: ``` [ERROR] sceneboard: Event routing failed for window ID=567, no active focus [WARN] sceneboard: High CPU load (95%), event dispatch delayed by 200ms [FATAL] sceneboard: Segmentation fault at address 0x7f8a1c, stack trace: ... ``` #### 5. touch test **定义原理**:touch test是触摸屏功能的测试机制,包括硬件自检和软件测试工具。硬件测试验证传感器响应(如使用测试点阵),软件测试(如Linux的 `evtest` 或Android的 `getevent`)模拟或读取事件数据,检查坐标准确性、多点支持等。 **工作流程**: - **硬件测试**:通过固件命令触发测试模式,读取传感器输出。 - **软件工具**:运行命令行工具(如 `evtest /dev/input/eventX`),生成或解析事件。 - **校验指标**:如坐标误差范围(例如 $|\Delta x| < 5$ 像素)、触点一致性。 **可能出现的错误**: - **测试失败**:坐标偏差过大(如超过阈值)。 - **工具错误**:测试命令参数错误(如设备路径无效)。 - **环境干扰**:电磁噪声导致测试结果不稳定。 - **报告缺失**:未生成测试日志或报告。 **日志原文示例**: - 测试日志: ``` [FAIL] touch_test: Coordinate error: expected (100,200), actual (110,195), delta=11.18 pixels [ERROR] evtest: Device not found: /dev/input/event10 [INFO] selftest: Touch sensor noise detected, SNR below 20dB ``` ### 总结 以上主题相互关联:Tp驱动报点是起点,生成原始触摸数据;多模报点扩展为多点支持;事件发送将数据传输到用户空间;sceneboard进程处理高级路由;touch test确保整个链条的可靠性。错误通常源于硬件故障、配置错误或软件bug,日志中的原文可帮助诊断问题(如权限错误或坐标超限)。在真实系统中,这些问题可通过调试工具(如 `strace` 或内核日志)定位。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值