[译]输入系统如何工作——指针输入
翻译自 How input works – pointer input。 在上一篇博文中,我讨论了键盘输入。这篇博文将会讨论指针设备——多被称为鼠标。就像本系列文章一样,本文仅仅讨论了KWin/Wayland上面的情形。
不同的硬件类型
有好几种不同的设备被识别为指针设备。有轨迹球这样的传统鼠标,也有在笔记本上的触摸板。此外,还有可绝对定位的设备,比如触摸屏。 在libinput中,对于不同的设备,有很多不同的选项可供配置。比如指针加速等其他选项。我们正在开发Wayland的触摸板配置模块,看起来会合并到Plasma 5.9中。像第一篇博文描述的,一旦设备创建,一系列配置选项会被立马设置。
指针运动
指针设备生成不同种类的事件,其中一种就是指针运动事件。通常来说,有两种指针运动事件:绝对运动和相对运动。大多数设备,比如鼠标,产生相对运动事件,所以我们在本文中只关注相对运动事件。
确定新的位置
相对运动是一组带x和y坐标的距离向量,它描述了指针位置该如何移动。 所以,一旦事件从KWin队列中读取出来,需要确定新的指针位置。这并不单单是最后的指针位置再加上一个运动向量。这会导致指针离开可视区域。 相反,指针运动得验证和约束。我们确保光标不会离开可视区域,并且还要遵守应用程序窗口对指针的约束。 这是KWin在Plasma 5.9中支持的新协议。它允许Wayland窗口将指针锁定到某个位置或将指针限制在某个区域。在第一种情况下,指针根本不移动,在第二种情况下,只允许指针在窗口的特定区域移动。
处理新的位置
即使在光标被限定的情况下,不需要移动鼠标指针,事件也会得到进一步处理。即使光标不移动,应用程序也可能对相对运动感兴趣,并对其做出反应。 像键盘的情况一样,为了进一步处理,会生成QMouseEvent并发送到KWin的输入过滤器。指针运动可能在KWin内部处理,例如:指针位置超出了当前的屏幕边缘。或者通过KWayland将指针运动转发到窗口。
更新获取焦点的窗口
如果指针移动,光标可能从一个窗口移动到另一个窗口,或从窗口移动到Wayland服务器端的窗口装饰上。这意味着对于每个指针位置的变化,KWin需要计算当前位置的窗口。 与键盘输入相比,KWin只需要考虑活动窗口,但这也是一个相当复杂的任务。我们需要考虑将输入变换应用于屏幕还是窗口,我们需要在窗口上应用输入掩码,考虑窗口装饰,检查屏幕是否被锁定,Xwayland 1.18之前的临时解决方案等一大堆问题。 最后,该方法可能已经确定了一个获得指针焦点的新平面。KWin使用KWayland来更新聚焦的指针表面,确保离开旧表面和进入新平面的事件触发。 当然,当移动鼠标时,应该更新焦点。如果拖放(按下指针按钮并移动),则不会更新焦点。
更新指针图案
如果指针移动到新的位置,指针图标有可能会改变。不幸的是,这可能需要多次往返消息才能确定。窗口管理器不知道窗口想要使用哪个指针图标,直到动作被发送到窗口。一个窗口可以通过两种方式做出反应:它可以更新光标图像,也可以不更新。在第一种情况下,KWin通过KWayland通知图像发生变化,在第二种情况下根本没有通知。 这意味着移动光标时,KWin不知道光标图标是否是正确的。所以在指针运动时,KWin更新光标到新的位置,但是光标图标仍然是旧的,但可能在一帧之后,客户端就会更新光标图案了。不得不说,在Wayland的显示下,并不是每一帧都是完美的,这是我遇到的唯一一个此类情况:光标可能显示错误的图标。 进入窗口时,光标尚未定义。直到窗口设置光标,否则光标还是未设置的状态。此时,KWin不会渲染光标;这意味着当进入冻结窗口(程序无响应的窗口)时,我们没有光标。我们以后必须改进这一点。目前,我们没有检测挂起的应用程序,因为我认为我们无法检测到它们:客户端使用专用的Wayland事件线程,因此程序可以永远给一切事件回复OK,即使事件并没有处理。 但是,KWin也可能需要设置一个光标图像。例如,当鼠标在窗户装饰上悬停,或进入特殊模式以选择窗口时,就使用KWin提供的光标图像。KWin从主题中加载这个光标图案。在内部,KWin记录光标图案应该使用的源图像。无论是Wayland窗口还是窗口选择界面,还是使用特定的光标图像。
更新实际光标
通过KWin的内部平台API实现光标位置和图标的实际更新。每个平台以不同的方式设置光标图像。对于我们的主要平台,我们使用DRM API来更新位置并更新图标——如果有新图标的话。 对于Wayland中嵌套的X11这样的嵌套平台,这通过窗口系统内的特定的调用处理。嵌套平台不允许更新光标位置——这是由窗口系统之间通过指针运动间接更新的。但是,光标图案可以更新。 虚拟出来的平台只知道软件指针的概念。这是在合成器渲染过程中渲染出来的光标。目前这只有在QPainter合成器中实现,在OpenGL合成器中还没有实现。
按钮事件
libinput支持指针设备产生的指针按钮的按下、释放事件。这些事件携带着它们触发的指针按钮。 和指针运动事件相比,按钮事件的处理更加直接。指针事件被事件过滤器拦截(比如Alt+Tab)或者被转发到当前获取焦点的窗口。 这相比于X11,是一个巨大的进步。在X11下,如果KWin希望独占的处理指针事件,则需要锁定指针。这意味着屏幕无法锁定。如果当前窗口处于活动状态,屏幕不锁定。在Wayland,这并不重要。如果当前窗口处于活动状态,屏幕可以锁定,也可以获取所有指针事件。一旦解锁当前的窗口仍然是活动窗口。“我”很轻松地为这种情况添加了自动测试。
轴事件
许多指针设备有一个或者两个轴。在X11的核心协议中,轴事件被实现为指针按钮。在Wayland和libinput的情况下,我们有了自己的轴事件,能告诉我们哪个轴被移动了,移动了多少。这是一个很大的提升,相比于X11,这意味着“平滑滚动”是标准的一部分了,在滚动事件的处理过程中,额外的事件处理部分也不会执行。 其他事件的处理也非常简单。KWin创建了一个QWheelEvent并将其传递给许多事件过滤器,如果过滤器没有拦截事件,它会被转发到KWayland::Server,KWayland::Server再把事件发送到获得焦点的指针平面。
触摸板手势
对于触摸板,我们还有别的事件。对于触摸板,Libinput不仅仅识别运动事件和按压事件,还能识别多指手势。它支持两种手势:滑动手势,捏合/旋转手势。KWin获取手势事件,转发给正常的事件系统。我们在Wayland协议中添加了特殊的支持。这允许转发鼠标手势Wayland应用程序,如视频所见。 很不幸,我们还没有真正地使用这些手势。QtWayland没有实现这个协议,所以转发的消息并不会到达应用程序,我们还没有使用它,比如全局手势就没有使用,我们仍在努力界定我们要支持的手势。我希望我们在Plasma 5.9能够改进这点,但是这也不确定。
假期愉快
这是“我”今年的最后一篇博客。下一年我将继续这一系列博客,会谈谈触摸屏事件,或许会包括数位版。 “我”希望每个人都有一个愉快的假期,在2017年有一个不错的开始。 如果你想支持“我”的工作,请考虑捐款。让世界变得更好!——KDE 2016年末募捐。
[译]输入系统如何工作——指针输入