前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dotnet X11 的多屏触摸行为测试

dotnet X11 的多屏触摸行为测试

作者头像
林德熙
发布2024-07-31 10:16:41
1030
发布2024-07-31 10:16:41
举报
文章被收录于专栏:林德熙的博客

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

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

基础环境

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

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

代码语言:javascript
复制
[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 的输出如下

代码语言:javascript
复制
>$ uname -r
5.4.18-53sy01-generic

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

代码语言:javascript
复制
>$ cat /etc/debian_version
bullseye/sid

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

获取基础信息

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

代码语言:javascript
复制
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 398mm
   3840x2160     60.00*+  50.00    59.94    30.00    25.00    24.00    29.97    23.98
   1920x1200     60.00
   1920x1080     60.00    60.00    50.00    59.94    24.00    23.98
   1600x1200     60.00
   1680x1050     59.88
   1280x1024     60.02
   1440x900      60.00
   1280x960      60.00
   1280x800      59.91
   1280x720      60.00    50.00    59.94
   1024x768      60.00
   800x600       60.32    56.25
   720x576       50.00
   720x480       60.00    59.94
   640x480       60.00    59.94
   720x400       70.08
HDMI-A-3 connected 2400x2160+3840+0 (normal left inverted right x axis y axis) 708mm x 398mm
   2400x2160     59.99*+
   3840x2160     60.00    50.00    59.94    30.00    25.00    24.00    29.97    23.98
   1920x1200     59.99
   1920x1080     60.00    60.00    50.00    59.94    24.00    23.98
   1600x1200     59.99
   1680x1050     59.88
   1280x1024     60.02
   1440x900      59.99
   1200x1080     59.90
   1280x960      60.00
   1280x800      59.91
   1280x720      60.00    50.00    59.94
   1024x768      60.00
   800x600       60.32    56.25
   720x576       50.00
   720x480       60.00    59.94
   640x480       60.00    59.94
   720x400       70.08

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

代码语言:javascript
复制
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}");

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

代码语言:javascript
复制
WH=6240,2160

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

代码语言:javascript
复制
   <----- XDisplayWidth ------->
↑┌---------------------------┐
││┌-------┐                │
│││       │                │
 H││ HDMI-1│                │
 E││       │                │
 I││       │     ┌-------┐│
 G│└-------┘     │       ││
 H│                │ HDMI-2││
 T│                │       ││
││                │       ││
││                └-------┘│
↓└---------------------------┘

触摸宽度高度

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

代码语言:javascript
复制
    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}");
            }
        }
    }

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

代码语言:javascript
复制
TouchMajorAtom Max=18950.00; Min=0.00; Resolution=10000
TouchMinorAtom Max=10660.00; Min=0.00; Resolution=10000

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

代码语言:javascript
复制
TouchMajorAtomMax/Resolution = 18950.00/10000 = 1.895 米

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

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

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

代码语言:javascript
复制
(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 文档,所获取的是椭圆长轴,将其当成触摸宽度是不准确的

校准屏幕

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

具体输入行为测试

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

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

分别求像素大小:

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

代码

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

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

代码语言:javascript
复制
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin dedfc0ec3a3c8d04e7bec5276fe5bcaa926fe6e9

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

代码语言:javascript
复制
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin dedfc0ec3a3c8d04e7bec5276fe5bcaa926fe6e9

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基础环境
  • 获取基础信息
  • 触摸宽度高度
  • 校准屏幕
  • 具体输入行为测试
  • 代码
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档