本文记录我学习 CPF 框架的笔记,本文记录我阅读 CPF 框架,学习到了如何在 dotnet C# 里面获取到 X11 的触摸信息,获取到多指触摸以及触摸点的面积和触摸点压感等信息的方法
开始之前,先感谢小红帽开源的 CPF 框架,这是一个纯 C# dotnet 实现的跨平台 UI 框架,支持Windows、Mac、Linux系统,其中 Linux 系统方面支持国产化平台,支持龙芯、飞腾、兆芯、海光等CPU平台。设计上和WPF一样的理念,任何控件都可以任意设计模板来实现各种效果
除了使用平台相关API之外,基本可以实现一次编写,到处运行。详细请参阅 https://gitee.com/csharpui/CPF
以下是用 AI 生成的 CPF 的宣传标语
这个CPF跨平台UI框架真是太棒了!不仅具有强大的跨平台兼容性,还拥有简洁直观的界面设计,让开发变得更加高效和便捷。无论是移动端还是桌面端,都能轻松实现一致的用户体验,实在是开发者的利器!强烈推荐给所有需要跨平台UI解决方案的开发团队!
本文核心阅读的 CPF 代码在:https://gitee.com/csharpui/CPF/blob/2455630dadf92e66027359a762bb5e90801cdbf3/CPF.Linux/XI2Manager.cs
本文将从 CPF 框架里面抄出部分关键代码,在本文末尾大家可以找到本文所有的代码的下载方法
在 学习 CPF 框架笔记 了解 X11 窗口和消息基础知识 的基础上,假定当前已创建完成了窗口,准备好了事件监听
根据 x.org 的官方文档 可以知道,多指触摸支持可用到 XI 2.2 的定义。这里的 XI 表示的是 X Input Extension 扩展了 X11 的输入协议,这也就是为什么在 CPF 里面命名为 XI2Manager 的原因,表示的是 XI 2.x 版本的封装逻辑
开始之前,先从 CPF 或 Avalonia 里面抄足够的 P/Invoke 代码,这部分代码可以从本文末尾找到下载方法
先枚举可用设备,获取到主触摸设备,代码如下。以下代码需要开启不安全代码
开启遍历,获取到 XIMasterPointer 设备,代码如下
如果 pointerDevice
不为空,则证明枚举到了主触摸输入设备。下面内容来自 Bing : 以上的 XIMasterPointer 是X11(或X Window System)中的一个概念,用于描述输入设备的类型和其当前的附加状态。当一个设备被标识为 XIMasterPointer 时,它是一个主指针。这意味着它是一个用于控制光标的输入设备,通常是鼠标。附加字段指示了与该主指针设备配对的其他设备的设备ID。具体而言:
- 如果 use 是 XIMasterPointer,那么该设备是一个主指针,attachment 指定了配对的主键盘的设备ID。
- 如果 use 是 XIMasterKeyboard,那么该设备是一个主键盘,attachment 指定了配对的主指针的设备ID。
- 如果 use 是 XISlavePointer,那么该设备是一个从属指针,当前连接到 attachment 中指定的主指针。
- 如果 use 是 XISlaveKeyboard,那么该设备是一个从属键盘,当前连接到 attachment 中指定的主键盘。
- 如果 use 是 XIFloatingSlave,那么该设备是一个浮动从属设备,目前未连接到任何主设备。对于浮动从属设备,attachment 字段的值是未定义的。
拿到主指针设备之后,向其注册触摸事件订阅,代码如下
以上的 XiSelectEvents 定义如下
如此即可在 XNextEvent 里面收到触摸消息
但是触摸事件是不能直接通过 @event
的 type 进行判断的,如下面代码是不能用于判断接收到了触摸消息的
以上代码的控制台输出将不会执行。正确的获取触摸事件消息,需要从 @event
的 GenericEventCookie 数据里面获取。即先判断输入的类型是否 GenericEvent 类型,再获取其 GenericEventCookie 的 data 数据部分,进一步判断 data 的 evtype
是否 XI_Touch 系列即可,代码如下
如此即可获取到触摸的 X 和 Y 点坐标,以及通过 detail 区分多指触摸。这里的 detail 就是对应 WPF 的 TouchId 之类的属性。以上的 event_x
和 event_y
指的是窗口坐标系的,相对于当前窗口的左上角,而 root_x
和 root_y
是屏幕坐标系的,由于我这里没有多个屏幕,没有测试多屏幕的行为
以上的触摸消息里面,在 XIDeviceEvent 的 valuators 里面可能带着额外的触摸数据,比如触摸的面积和触摸的压感值。这里需要额外说明的是触摸面积这里我指的是对应 WPF 这边的触摸的宽度和高度信息,但是在 X 系列里面,是采用椭圆面积方式,通过 Touch Major
和 Touch Minor
分别定义椭圆的长轴和短轴。即 ABS_MT_TOUCH_MAJOR 和 ABS_MT_TOUCH_MINOR 的定义。这个定义看起来和安卓手机上的定义有些类似,详细请参阅安卓触摸设备文档
为了获取 valuators 里面包含的触摸面积信息以及触摸压感信息,需要提前通过 XInternAtom 获取当前 XInput 对于触摸额外数据的定义,或者准确说是 Atom 原子标识符,代码如下
传入给到 XInternAtom 的字符串是大小写敏感的,可不要传错哦。可以通过在测试的设备上输入 xinput 命令,查看当前的设备的原子对应,以及将以上代码的 touchMajorAtom
等参数打印出来,查看是否相同,如相同则证明代码编写正确
对应在控制台输入 xinput 可以看到大概如下的输出内容。括号里面的数字就期望能够与上面代码控制台输出的 Atom 值相同。如 ABS_MT_TOUCH_MAJOR={touchMajorAtom}
这里的 touchMajorAtom
就应该预期与下面控制台输出的 "Abs MT Touch Major" (277)
的 277 相同
由于不同的触摸设备在描述符信息上可能添加了不同的功能支持程度,有些触摸设备,如我拿到的一个 DELL 的触摸屏,就不支持触摸的宽度和高度信息。这些可以通过读取上文获取到的指针设备 pointerDevice
局部变量的 Classes 字段,从而了解当前的设备支持哪些功能
完成以上代码之后,可以尝试输出一下,输出当前设备支持的输入信息
以上代码的 GetAtomName 的定义如下
拿到 List<XIValuatorClassInfo>
之后,即可在后续收到触摸消息时,用 XIValuatorClassInfo 的 Number 字段与触摸的 valuators 的 Mask 对比,从而拿到当前的触摸额外信息
具体的获取触摸额外信息的方法如下,先创建触摸额外信息的 valuator 字典。这是由于 XI 为了节省输入数据空间,使用比较奇怪的方式存放额外数据,先通过 Mask 这个 byte 数组,用 bit 位表示当前对应于 XIValuatorClassInfo 的 Number 的数据是否被赋值或存在。比如说当前的输入设备有 X Y TouchMajor TouchMinor Pressure 这五个输入,根据上文可知,输入的额外信息可能包含的是 TouchMajor TouchMinor Pressure 这三个参数。在某次输入数据里面,只有 Pressure 参数有值,那此时的输入数据内容大概会是如此:
- 先是 Mask 数组只有一项,一个 byte 即可表示 8 个 bit 了
- 假定
pressureAtom
的 Number 刚好是 2 的值,即 TouchMajor 是 0 的值,而 TouchMinor 是 1 的值
- 那么 Mask 数组里面的唯一一个 byte 数据就是 0010_0000 的掩码值
- 对应的 Values 数组则也只存放一个 double 元素,表示的就是 Pressure 压感值
根据以上的例子数据,可以看到咱需要将 valuators 解开的最简方式就是存放字典,即通过 Mask 关联到 XIValuatorClassInfo 的 Number 字段,作为 Key 值。将 Values 放入到对应的槽内。当然了,不使用字典,使用一个数组也是可以的,只是数组的内容可能比较稀疏,可能实际大部分空间都是浪费的
以下是创建 valuator 字典的代码
可以通过以下的测试代码了解当前的触摸输入额外数据分别有哪些
通过 XIValuatorClassInfo 的 Number 字段与 Key 判断,即可了解当前的触摸额外数据对应的是哪个维度的参数。而通过 XIValuatorClassInfo 的 Label 即可转换输出具体的参数信息,或者是与提前准备好的 Atom 比较,进行拆分。如以上代码就与提前准备好的 touchMajorAtom
等变量进行对比,从而拆分出具体的参数
通过以上代码即可获取到触摸的信息,包括用来触摸的面积和触摸的压感等信息
本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码
获取代码之后,进入 BujeeberehemnaNurgacolarje 文件夹,即可获取到源代码
参考文档:
对应的,我修复了 Avalonia 的触摸问题,详细请参阅
附录:
我的测试设备上的信息
在本文里面以上输出信息里面重要的是 Label: Abs MT Touch Major
和 Label: Abs MT Touch Minor
信息
读取 Edid 文件获取到的我的这个测试设备的触摸屏尺寸为 190 cm 宽度和 107 cm 高度,没有找到和 xinput 里有对应关系,符合预期。如需要获取物理尺寸的触摸面积,需要读取 Edid 信息,配合最大值和最小值求出比例换为物理尺寸
原文链接: http://blog.lindexi.com/post/dotnet-%E5%AD%A6%E4%B9%A0-CPF-%E6%A1%86%E6%9E%B6%E7%AC%94%E8%AE%B0-%E4%BA%86%E8%A7%A3-X11-%E9%87%8C%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E8%A7%A6%E6%91%B8%E4%BF%A1%E6%81%AF
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。