GtkSharp 获取触摸宽度高度面积尺寸信息

准确来说本文的方法是我在 lsj 的帮助下试出来的,我没有找到正式的文档对此有描述。本文测试的机器是在 UOS 系统搭配兆芯的 CPU 的机器上,这台机器采用的是希沃的某款触摸框的设备。更具体的 UOS 内核版本号是 4.19-amd64-desktop 版本,处理器是 ZHAOXIN KaiXian KX-U6780A 型号

核心获取方法是通过在 EventTouch 的 Axes 里面读取值。这里的 EventTouch 对应 GTK 官方文档记录的 https://docs.gtk.org/gdk3/struct.EventTouch.html

尽管在 GTK 官方文档 对 axes 的描述似乎不是这个意思,以下是官方原文

x, y translated to the axes of device, or NULL if device is the mouse.

从官方的定义上可以看到 axes 是一个 double 类型的数组,我就从数组里面的第 3、4 项分别获取到 [0-1] 范围内的宽度和高度的值。从网上一些不权威的文档里面,我看到了第 3、4 项表示的是 Xtilt 和 Ytilt 的含义,即 X 倾斜和 Y 倾斜的含义。但从实际测试上看,这个值却能够和触摸的宽度和高度对应

接下来我将使用一个简单的项目告诉大家具体如何获取触摸宽度高度信息

先新建一个控制台项目,然后编辑 csproj 项目文件,替换为如下代码

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="GtkSharp" Version="3.24.24.95" />
  </ItemGroup>

</Project>

以上代码安装了 3.24.24.95 版本的 GtkSharp 库,同时开启不安全代码

由于当前 GtkSharp 的官方定义里面,没有开放 EventTouch 的 Axes 属性,于是咱先自己定义一个 EventTouch 结构体。为什么自己定义的结构体也能生效?这是因为官方定义的结构体的作用仅仅只是在 Marshal.PtrToStructure 从指针进行转换的,于是自己定义的结构体也能通过相同的方式从指针进行转换,代码如下

[StructLayout(LayoutKind.Sequential)]
public partial struct EventTouch
{
    public Gdk.EventType Type;
    private IntPtr _window;
    public Gdk.Window Window
    {
        get
        {
            return GLib.Object.GetObject(_window) as Gdk.Window;
        }
        set
        {
            _window = value == null ? IntPtr.Zero : value.Handle;
        }
    }
    public sbyte SendEvent;
    public uint Time;
    public double X;
    public double Y;
    public IntPtr Axes;
    public uint State;
    private IntPtr _sequence;
    public Gdk.EventSequence? Sequence
    {
        get
        {
            return _sequence == IntPtr.Zero ? null : (Gdk.EventSequence) GLib.Opaque.GetOpaque(_sequence, typeof(Gdk.EventSequence), false);
        }
        set
        {
            _sequence = value == null ? IntPtr.Zero : value.Handle;
        }
    }
    public bool EmulatingPointer;
    private IntPtr _device;
    public Gdk.Device Device
    {
        get
        {
            return GLib.Object.GetObject(_device) as Gdk.Device;
        }
        set
        {
            _device = value == null ? IntPtr.Zero : value.Handle;
        }
    }
    public double XRoot;
    public double YRoot;

    public static EventTouch Zero = new EventTouch();

    public static EventTouch New(IntPtr raw)
    {
        if (raw == IntPtr.Zero)
            return EventTouch.Zero;
        return (EventTouch) Marshal.PtrToStructure(raw, typeof(EventTouch));
    }

    private static GLib.GType GType
    {
        get { return GLib.GType.Pointer; }
    }
}

接下来创建一个继承 DrawingArea 的类型,如以下代码的 F 类型

class F : DrawingArea
{
   ... // 忽略其他代码
}

在 F 的构造函数里面,先使用 AddEvents 函数,告诉 GTK 层,当前的 F 类型所感兴趣的事件。如果没有调用此方法,那将监听事件时不会触发。这里为了简单起见,就添加了足够多的事件

class F : DrawingArea
{
    public F()
    {
        AddEvents((int) RequestedEvents);
        ... // 忽略其他代码
    }

    internal const Gdk.EventMask RequestedEvents =
        Gdk.EventMask.EnterNotifyMask
        | Gdk.EventMask.LeaveNotifyMask
        | Gdk.EventMask.ButtonPressMask
        | Gdk.EventMask.ButtonReleaseMask
        | Gdk.EventMask.PointerMotionMask // Move
        | Gdk.EventMask.SmoothScrollMask
        | Gdk.EventMask.TouchMask // Touch
        | Gdk.EventMask.ProximityInMask // Pen
        | Gdk.EventMask.ProximityOutMask // Pen
        | Gdk.EventMask.KeyPressMask
        | Gdk.EventMask.KeyReleaseMask;
}

接着在构造函数监听触摸事件,如以下代码

class F : DrawingArea
{
    public F()
    {
        AddEvents((int) RequestedEvents);
        TouchEvent += F_TouchEvent;
    }

    private unsafe void F_TouchEvent(object o, TouchEventArgs args)
    {
    }
}

从 TouchEventArgs 里的指针可以转换出 EventTouch 结构体,如以下代码

class F : DrawingArea
{
    public F()
    {
        AddEvents((int) RequestedEvents);
        TouchEvent += F_TouchEvent;
    }

    private unsafe void F_TouchEvent(object o, TouchEventArgs args)
    {
        var eventTouch = EventTouch.New(args.Event.Handle);
    }
}

接下来通过 EventTouch 的 Device 可以获取 Axes 数组的长度。由于 EventTouch 定义的 Axes 是一个 double 类型的指针,如果没有从 Device 获取其长度,那可能出现越界问题

            var device = eventTouch.Device;
            var numAxes = device.NumAxes;

            Console.WriteLine($"NumAxes={numAxes} Id={eventTouch.Sequence?.Handle ?? -1}");

拿到数组长度,即可将整个数组的内容输出到控制台,如以下代码

            var axes = new Span<double>((void*) eventTouch.Axes, numAxes);

            Console.WriteLine("=================");
            for (int i = 0; i < numAxes; i++)
            {
                Console.WriteLine($"[{i}] {axes[i]}");
            }
            Console.WriteLine("=================");

根据实际实验,可以看到第 3、4 项就是触摸的尺寸,且是一个被压缩到 0-1 之间的浮点数。可以使用分辨计算 x 和 y 的缩放比从而获取到像素单位的触摸尺寸

            if (numAxes >= 5)
            {
                var radioX = eventTouch.XRoot / axes[0];
                var radioY = eventTouch.YRoot / axes[1];

                var rawWidth = axes[3];
                var rawHeight = axes[4];

                var width = rawWidth * radioX;
                var height = rawHeight * radioY;

                Console.WriteLine($"Width={width} Height={height}");
            }

通过以上方法即可获取到触摸点的宽度和高度

最后,将 F 类型的对象加入到窗口里面显示,代码如下

internal class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        Application.Init();

        _app = new Application("org.Samples.Samples", GLib.ApplicationFlags.None);
        _app.Register(GLib.Cancellable.Current);

        _win = new MainWindow("Demo Window");
        _app.AddWindow(_win);
        _win.ShowAll();
        Application.Run();
    }

    private static Application? _app;
    private static Window? _win;
}

class MainWindow : Window
{
    public MainWindow(string title) : base(WindowType.Toplevel)
    {
        WindowPosition = WindowPosition.Center;
        DefaultSize = new Size(600, 600);

        Child = new F();
    }
}

本文代码放在 githubgitee 上,可以使用如下命令行拉取代码

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

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码

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

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

posted @   lindexi  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
历史上的今天:
2020-09-12 WPF 点击按钮时更改按钮样式界面效果的 XAML 实现方法
2020-09-12 WPF 解决 Skia 因为找不到字体而绘制不出中文字符
2020-09-12 WPF 创建空白图片
2020-09-12 WPF 获取本机所有字体拿到每个字符的宽度和高度
2020-09-12 WPF 自己封装 Skia 差量绘制控件
2017-09-12 调试 ms 源代码
点击右上角即可分享
微信分享提示