dotnet X11 的多屏触摸行为测试

news/2024/11/16 0:39:54/文章来源:https://www.cnblogs.com/lindexi/p/18333725

故事的背景是我在给 Avalonia 加上触摸尺寸的支持时,代码审查过程中大佬提出了在多屏上的 X11 行为问题,为此我找了两个触摸屏进行测试 X11 的多屏触摸行为。由于我的设备有限,本文只记录我所测试到的行为

给 Avalonia 加上触摸尺寸支持的功能的代码: https://github.com/AvaloniaUI/Avalonia/pull/16498

基础环境

本次测试是我在麒麟 Kylin 系统搭配 CVT 厂商的双屏进行测试

在我的设备上使用 cat /etc/.kyinfo 获取麒麟系统的版本的输出信息如下

[dist]
name=Kylin
milestone=Desktop-V10-SP1-General-Release-TSM-lindexi-20230217
arch=arm64
beta=False
time=2023-02-17 19:01:29

根据 定昌电子 记录的文档,这里的 Desktop V10 SP1 General Release 版本就是银河麒麟桌面操作系统V10 SP1版本

运行 uname -r 的输出如下

>$ uname -r
5.4.18-53sy01-generic

在麒麟系统上运行 cat /etc/debian_version 获取 debian 版本号,输出信息如下

>$ cat /etc/debian_version
bullseye/sid

bullseye 是 debian 11 的发布代号,详细请看 https://www.debian.org/releases/bullseye/

获取基础信息

输入 xrandr 获取屏幕信息,可见内容如下

Screen 0: minimum 320 x 200, current 6240 x 2160, maximum 16384 x 16384
HDMI-A-0 disconnected (normal left inverted right x axis y axis)
HDMI-A-1 disconnected (normal left inverted right x axis y axis)
HDMI-A-2 connected primary 3840x2160+0+0 (normal left inverted right x axis y axis) 708mm x 398mm3840x2160     60.00*+  50.00    59.94    30.00    25.00    24.00    29.97    23.981920x1200     60.001920x1080     60.00    60.00    50.00    59.94    24.00    23.981600x1200     60.001680x1050     59.881280x1024     60.021440x900      60.001280x960      60.001280x800      59.911280x720      60.00    50.00    59.941024x768      60.00800x600       60.32    56.25720x576       50.00720x480       60.00    59.94640x480       60.00    59.94720x400       70.08
HDMI-A-3 connected 2400x2160+3840+0 (normal left inverted right x axis y axis) 708mm x 398mm2400x2160     59.99*+3840x2160     60.00    50.00    59.94    30.00    25.00    24.00    29.97    23.981920x1200     59.991920x1080     60.00    60.00    50.00    59.94    24.00    23.981600x1200     59.991680x1050     59.881280x1024     60.021440x900      59.991200x1080     59.901280x960      60.001280x800      59.911280x720      60.00    50.00    59.941024x768      60.00800x600       60.32    56.25720x576       50.00720x480       60.00    59.94640x480       60.00    59.94720x400       70.08

以上代码的 Screen 0: minimum 320 x 200, current 6240 x 2160, maximum 16384 x 16384 中的 current 6240 x 2160 就是对应 XDisplayWidth 和 XDisplayHeight 所获取的值,如以下代码所示

var display = XOpenDisplay(IntPtr.Zero);
var screen = XDefaultScreen(display);var xDisplayWidth = XDisplayWidth(display, screen);
var xDisplayHeight = XDisplayHeight(display, screen);var width = xDisplayWidth;
var height = xDisplayHeight;Console.WriteLine($"WH={width},{height}");

在我的设备上执行以上代码,可以看到如下输出

WH=6240,2160

由此可见 Screen 的总宽度是多个触摸屏的外接矩形,如下字符图所示

   <----- XDisplayWidth ------->
↑┌---------------------------┐
││┌-------┐                │
│││       │                │H││ HDMI-1│                │E││       │                │I││       │     ┌-------┐│G│└-------┘     │       ││H│                │ HDMI-2││T│                │       ││
││                │       ││
││                └-------┘│
↓└---------------------------┘

触摸宽度高度

使用如下代码查询主设备获取的信息如下

    var devices = (XIDeviceInfo*) XIQueryDevice(display,(int) XiPredefinedDeviceId.XIAllMasterDevices, out int num);XIDeviceInfo? pointerDevice = default;for (var c = 0; c < num; c++){Console.WriteLine($"XIDeviceInfo [{c}] {devices[c].Deviceid} {devices[c].Use}");if (devices[c].Use == XiDeviceType.XIMasterPointer){pointerDevice = devices[c];break;}}if (pointerDevice != null){var multiTouchEventTypes = new List<XiEventType>{XiEventType.XI_TouchBegin,XiEventType.XI_TouchUpdate,XiEventType.XI_TouchEnd,XiEventType.XI_Motion,XiEventType.XI_ButtonPress,XiEventType.XI_ButtonRelease,XiEventType.XI_Leave,XiEventType.XI_Enter,};XiSelectEvents(display, handle, new Dictionary<int, List<XiEventType>> { [pointerDevice.Value.Deviceid] = multiTouchEventTypes });for (int i = 0; i < pointerDevice.Value.NumClasses; i++){var xiAnyClassInfo = pointerDevice.Value.Classes[i];if (xiAnyClassInfo->Type == XiDeviceClass.XIValuatorClass){valuators.Add(*((XIValuatorClassInfo**) pointerDevice.Value.Classes)[i]);}else if (xiAnyClassInfo->Type == XiDeviceClass.XIScrollClass){scrollers.Add(*((XIScrollClassInfo**) pointerDevice.Value.Classes)[i]);}}foreach (var xiValuatorClassInfo in valuators){if (xiValuatorClassInfo.Label == touchMajorAtom){Console.WriteLine($"TouchMajorAtom Max={xiValuatorClassInfo.Max:0.00}; Min={xiValuatorClassInfo.Min:0.00}; Resolution={xiValuatorClassInfo.Resolution}");}else if (xiValuatorClassInfo.Label == touchMinorAtom){Console.WriteLine($"TouchMinorAtom Max={xiValuatorClassInfo.Max:0.00}; Min={xiValuatorClassInfo.Min:0.00}; Resolution={xiValuatorClassInfo.Resolution}");}}}

在我的设备上运行以上代码的输出信息如下

TouchMajorAtom Max=18950.00; Min=0.00; Resolution=10000
TouchMinorAtom Max=10660.00; Min=0.00; Resolution=10000

以上的 Max=18950.00 所获取的值是其物理尺寸,即 1.89 米宽度。这里的 Resolution 是一个比例,计算公式如下

TouchMajorAtomMax/Resolution = 18950.00/10000 = 1.895 米

由于 CVT 的设备报告的逻辑值和物理值都是相同的最大值最小值,因此以上代码我不确定拿到的是逻辑值还是物理值

通过以上代码也可以看到,我无法直接获取到正确的多屏不同尺寸的设备的多个屏幕的物理尺寸。这是因为我无法直接知道输入的是哪个屏幕以及其比例值

但像素值倒是很好获取到,只需获取到其触摸点报告的 TouchMajor 值,与 TouchMajorXIValuatorClassInfo 的最大值和最小值相比,压缩到 [0-1] 范围内,再乘以屏幕像素,即可获取到其像素值

(TouchMajorValuatorValue - TouchMajorXIValuatorClassInfo.Min)/(TouchMajorXIValuatorClassInfo.Max - TouchMajorXIValuatorClassInfo.Min) * Monitor.Width

以上伪代码的 Monitor.Width 指的是对应屏幕的像素宽度。由于 Min 常是 0 因此在计算中常被忽略

但值得一提的是在 X11 里面,根据 https://www.kernel.org/doc/html/latest/input/multi-touch-protocol.html 文档,所获取的是椭圆长轴,将其当成触摸宽度是不准确的

校准屏幕

在我的设备上,发现触摸屏的触摸输入和对应的屏幕显示没有对齐,需要根据以下大佬们的博客进行修复

  • Linux处理多触屏的终极解决方案 香风家的火柴盒
  • 【图形显示】扩展屏模式,触摸点较准不准确_90-touchscreen-map-CSDN博客

具体输入行为测试

我使用了相同的物理面积的物体触摸屏幕,两个屏幕分别是 3840x21602400x2160 分辨率

触摸左边 3840x2160 屏幕,获取到的 TouchMajorValuatorValue 是大概 100 的值。触摸右边 2400x2160 屏幕,获取到的 TouchMajorValuatorValue 是大概 160 的值

分别求像素大小:

  • 左边屏幕: 100/18950*3840=20.2638522427
  • 右边屏幕: 160/18950*2400=20.2638522427

代码

本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快

先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin dedfc0ec3a3c8d04e7bec5276fe5bcaa926fe6e9

以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin dedfc0ec3a3c8d04e7bec5276fe5bcaa926fe6e9

获取代码之后,进入 X11/DacemciwarjurqofuKearwihiyi 文件夹,即可获取到源代码

更多技术博客,请参阅 博客导航

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/774421.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF 不带 TargetPlatformVersion 显示 Win10 的 Toast 通知的方法

本文将告诉大家如何在 WPF 不安装 WindowsAppSDK 包,且不在 TargetFramework 带上 TargetPlatformVersion 而弹出 Win10 的 Toast 通知的方法本文这里的 TargetPlatformVersion 指的是在 TargetFramework 里面的内容,如下面的代码里的 10.0.17763.0 就是 TargetPlatformVersi…

读零信任网络:在不可信网络中构建安全系统05网络代理

网络代理1. 网络代理 1.1. 安全策略在认证和授权环节都充分地利用了多个因子,综合考虑了用户及其使用的设备的信息 1.1.1. 允许员工通过企业发放的工作笔记本电脑提交源代码,但是禁止员工使用手机进行类似操作 1.1.2. 用户必须使用可信终端提交代码,并且这个终端必须属于用户…

Activity创建与跳转

layout目录下新建activity_main2.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.…

Linux工作原理14桌面和打印简介

14桌面和打印简介 本章简要介绍了典型 Linux 桌面系统中的组件。在 Linux 系统的各种软件中,桌面是最狂野、最丰富多彩的领域之一,因为有如此众多的环境和应用程序可供选择,而且大多数发行版都能让你比较容易地试用它们。 与 Linux 系统的其他部分(如存储和网络)不同,创建…

NDT论文翻译

The Normal Distributions Transform: A New Approach to Laser Scan Matching 正态分布变换:激光扫描匹配的新方法 摘要:匹配 2D 范围扫描是许多定位和建图算法的基本组成部分。大多数扫描匹配算法需要找到所使用的特征(即点或线)之间的对应关系。我们提出了范围扫描的替代…

Crypto 杂题选做

ctf stuff?apj 你在干神魔 目录W4terCTF 2024Merciful ZMJ4396d3Google CTF 2023LEAST COMMON GENOMINATORDeadSec CTF 2024Raul RosascorCTF 2024stepsmonkfish / anglerfish W4terCTF 2024 之前朋友给我看的题 Merciful ZMJ4396 找不到原来的 task.py 了,记得大概是这么个题…

我用Awesome-Graphs看论文:解读X-Stream

这次向大家分享发表在SOSP 2013上的另一篇经典图计算框架论文X-Stream,构建了单机上基于外存的Scatter-Gather图处理框架。X-Stream论文:《X-Stream: Edge-centric Graph Processing using Streaming Partitions》前面通过文章《论文图谱当如是:Awesome-Graphs用200篇图系统…

关于new、delete函数的错误处理(std::nothrow)

new、delete函数源码注释如下:无参数 无参数的new、delete函数,如果调用失败,会抛出bad_alloc异常,需要使用try{}catch(){}语句捕获异常从而进行异常处理。 #include <iostream>int main() {try {while (1){int *p = new int[100000000ul];}} catch (std::bad_alloc&…

了解GraphRAG

了解GraphRAG转载:从零实现大模型-GraphRAG,构建LLM中的关系数据库开源地址:https://github.com/microsoft/graphrag 论文:From Local to Global: A Graph RAG Approach to Query-Focused Summarization 博客介绍:https://microsoft.github.io/graphrag/传统RAGLLM预训练和…

Laconic Private Set-Intersection From Pairings (2022)

Laconic Private Set-Intersection From Pairings (2022)[!IMPORTANT] 论文地址:https://eprint.iacr.org/2022/529.pdf 代码地址:https://github.com/relic-toolkit/relic/tree/main/demo/psi-client-server 代码运行参考:RELIC 库学习Laconic 算法介绍 Laconic 适用于算力…

供热从清洁走向智慧

清洁供热是一种以末端需求为核心的拉动式供热方式,可以通过全面实施智慧供热,实现热电协同、多能互补,以打通供热最后一公里,使供热更加智慧化、高效化、绿色化。“清洁供热是一种以末端需求为核心的拉动式供热方式,可以通过全面实施智慧供热,实现热电协同、多能互补,以…