这是我在学习 CPF 和 Avalonia 过程中,编写的 X11 触摸测试程序所测试到的一些行为
前置博客: dotnet 学习 CPF 框架笔记 了解 X11 里如何获取触摸信息
X11 触摸测试程序
测试程序开源代码路径: https://github.com/dotnet-campus/ManipulationDemo/tree/master/ManipulationDemoCpfX11
此测试程序基于 CPF 的源代码进行编写
XI_Leave 行为
以下是我测试到的 XI_Leave 的行为逻辑
当存在别的窗口在当前的窗口之上时,触摸先进入当前的窗口,让当前的进程收到了 X11 的 Down 事件。接着在不断移动,不断收到 Move 事件。当移动到别的窗口之上时,将可以收到 XiEventType.XI_Leave 类型的事件。接着在别的窗口移动过程中,继续收到 Move 事件。抬手时,可以收到 End 事件
本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
获取代码之后,进入 BujeeberehemnaNurgacolarje 文件夹,即可获取到源代码
触摸宽度高度
在我的一台设备里面,使用的是统信 UOS 系统,复现出丢失触摸宽度高度信息
执行名为 CaijawhejiJoballbarwi 的测试程序,可以看到如下控制台输出。此 CaijawhejiJoballbarwi 测试程序代码可在后文获取到
从以上的输出日志可以看到,只是拿到了 XiValuatorClassInfo 为 Rel X
和 Rel Y
等信息,而没有触摸宽度高度需要的 Abs MT Touch Major
和 Abs MT Touch Minor
信息
此时尝试触摸一下屏幕,从 CaijawhejiJoballbarwi 程序里面的 XNextEvent 收到的 GenericEvent 里,取出的 XIDeviceEvent 事件参数里面的 valuators 集合里面,也是只有 Rel X
和 Rel Y
等信息,没有 Abs MT Touch Major
和 Abs MT Touch Minor
信息
此时执行 xinput list 可见如下输出信息
在触摸没有宽度高度信息时,使用 xinput list 2
所见也是没有 Abs MT Touch Major
和 Abs MT Touch Minor
信息,只有 Rel X
和 Rel Y
等信息
如此可以证明这是从 X11 底层就没有拿到触摸的宽度高度信息,和任何上层 UI 框架都没有关系,和应用程序本身没有关系
如果此时触摸一下触摸屏,则再次执行 CaijawhejiJoballbarwi 测试程序,可以获取到触摸宽度高度信息
再次执行 xinput list 2
命令,也是可以看到触摸宽度高度信息
此问题复现步骤:
- 挂机,不要碰屏幕,等待一段时间,也许是半个小时以上
- 使用 SSH 远程、或键盘在终端,启动 CaijawhejiJoballbarwi 测试程序或空 Avalonia 程序。即一定不要用触摸双击打开
- 此时可见 CaijawhejiJoballbarwi 测试程序和 xinput 都报告没有触摸宽度高度信息
如果此时触摸了一下触摸屏,则再次启动 CaijawhejiJoballbarwi 测试程序或使用 xinput 都能拿到触摸宽度高度信息
预计是一段时间没有碰触摸屏,导致触摸进入某个状态
本文以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
获取代码之后,进入 X11/CaijawhejiJoballbarwi 文件夹,即可获取到源代码
这里必须额外说明的是,本文以上的 CaijawhejiJoballbarwi 测试程序没有处理 XI_DeviceChanged 事件,但在本文描述的情况下,即使带上 XI_DeviceChanged 处理也不能解决问题
为什么带上 XI_DeviceChanged 更新 Valuator 内容也不能处理?因为从 XInput 2 收到的事件 XIDeviceEvent 的 valuators 就没有带上 Abs MT Touch Major 和 Abs MT Touch Minor 的内容
如以下我的控制台所示,先收到 XI_DeviceChanged 事件,最后我使用 XIQueryDevice 将 XIMasterPointer 设备的 XiValuatorClassInfo 进行输出,日志内容如下
通过以上日志信息可以看到 Abs MT Touch Major 对应的 Num 刚好是 3 的值。正好在丢失触摸宽度高度信息的时候的 XiValuatorClassInfo 只有 Rel X
和 Rel Y
和 Rel Vert Wheel
的值。于是我就通过读取 XIDeviceEvent 的 valuators 的数量来确定是否出错
读取 XIDeviceEvent 的 valuators 的数量的代码是从 Avalonia 里面抄的,代码如下
出现问题时的 Valuators 字典只有三个元素,也就是 Num 刚好是 3 的 Abs MT Touch Major 是不存在的,丢失触摸宽度高度信息。尝试从 Valuators 字典获取 TouchMajorXIValuatorClassInfo 的 Number 对应信息,也是获取不到,获取的代码如下
如此证明了从 XIDeviceEvent 的 valuators 里面就不存在 Touch Major 信息,丢失触摸宽度高度
这就是为什么即使在 XI_DeviceChanged 事件里面更新 valuators 信息也是不能修复此问题的原因,核心原因不在于 XI_DeviceChanged 事件里面拿不到更新的 XiValuatorClassInfo 信息,而是在于收到的 XIDeviceEvent 参数里面的 valuators 没有宽度高度信息
我尝试追踪代码,根据阅读代码,可以看到在 XIQueryDevice 里面发送消息给到 XServer 端,收到消息再返回。以下是在 XIQueryDevice.c 的 XIQueryDevice 方法的部分代码
如此可以看到 XIQueryDevice 没有进行额外的处理,继续阅读在 XExtInt.c 的 copy_classes 方法,可以发现里面只是做拷贝,证明信息是从 XServer 存放的
继续追到 XServer 里面,又发现实际实现是从 xf86-input-evdev 过来的数据,再继续我就跟丢了
多次调用 XIQueryDevice 方法,第二次将会卡住
我尝试使用如下代码执行多次对 XIQueryDevice 方法的调用:
运行程序,按下回车,在触摸/鼠标点击屏幕之前 XIQueryDevice 方法不会返回
似乎没有点击屏幕时,是不会完成 XIQueryDevice 方法的
这与上一次的 XIQueryDevice 是否调用 XIFreeDeviceInfo 进行释放没有关系,即使调用了 XIFreeDeviceInfo 依然也会让第二次 XIQueryDevice 需要等待鼠标或触摸点击屏幕才能返回
以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码
获取代码之后,进入 X11/CaijawhejiJoballbarwi 文件夹,即可获取到源代码
参考文档
Linux Mint 19/Ubuntu18 多点触摸和手势 - Techyou labs
x11 - Touch screen relative coordinates - Stack Overflow
原文链接: http://blog.lindexi.com/post/%E8%AE%B0-X11-%E9%87%8C%E9%9D%A2%E8%A7%A6%E6%91%B8%E7%9A%84%E4%B8%80%E4%BA%9B%E8%A1%8C%E4%B8%BA
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。