前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dotnet 如何从 Gtk 3 的窗口到对应的 X11 窗口

dotnet 如何从 Gtk 3 的窗口到对应的 X11 窗口

作者头像
林德熙
发布2024-05-16 16:50:04
580
发布2024-05-16 16:50:04
举报
文章被收录于专栏:林德熙的博客林德熙的博客

本文将告诉大家如何在 Gtk3 的 Gtk.Window 或 Gdk.Window 里面获取到对应的 X11 窗口 XID 号

记录本文是因为我在这里踩了很多坑,核心问题就是 GTK 有很多个版本,我开始找的全是使用 GTK 2 的 gdk_x11_drawable_get_xid 方法,而不是 GtkSharp 3.24 对应的 GTK 3 的方法

以上的 gdk_x11_drawable_get_xid 方法需要构建传入 GdkDrawable 指针,让我弄错为使用 gtk_widget_get_window 方法去获取其 gdk 窗口,于是错误就更加诡异

通过阅读文档发现了以下的 gtk 架构图,即 gtk 的窗口和 gdk 窗口是不相同的,可以通过 gtk_widget_get_window 方法获取,在 C# dotnet 里面可直接使用 Gtk.Window 的 Window 属性,更多请参阅:https://en.wikipedia.org/wiki/GDK

从 Gtk 的 Window 窗口获取 Gdk 的 Window 窗口,可使用以下简单代码获取

代码语言:javascript
复制
        Gtk.Window window = xxx;
        Gdk.Window gdkWindow = window.Window;

获取 Gdk 窗口指针,可通过 Handle 属性获取,如以下代码

代码语言:javascript
复制
        Gdk.Window gdkWindow = window.Window;

        var handle = gdkWindow.Handle;

以上获取的 handle 指针与 var windowHandle = gtk_widget_get_window(gtkWindow.Handle); 获取的 windowHandle 是相同的,因为在 GtkSharp 的 Widget.cs 就是如此实现的

代码语言:javascript
复制
namespace Gtk;

public partial class Widget 
{
    ... // 忽略其他代码

		[GLib.Property ("window")]
		public Gdk.Window Window 
		{
			get  
			{
				IntPtr raw_ret = gtk_widget_get_window(Handle);
				Gdk.Window ret = GLib.Object.GetObject(raw_ret) as Gdk.Window;
				return ret;
			}
			set  
			{
				gtk_widget_set_window(Handle, value == null ? IntPtr.Zero : value.Handle);
			}
		}

		[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
		delegate IntPtr d_gtk_widget_get_window(IntPtr raw);
		static d_gtk_widget_get_window gtk_widget_get_window = FuncLoader.LoadFunction<d_gtk_widget_get_window>(FuncLoader.GetProcAddress(GLibrary.Load(Library.Gtk), "gtk_widget_get_window"));
}

public partial class Container : Gtk.Widget
{
   ... // 忽略其他代码
}

public partial class Bin : Gtk.Container
{
   ... // 忽略其他代码
}

public partial class Window : Gtk.Bin
{
   ... // 忽略其他代码
}

使用 gdk_x11_window_get_xid 方法即可正确的从 gdk 窗口获取到对应的 X11 窗口的 XID 值

为了方便使用 gdk_x11_window_get_xid 方法,以下照 GtkSharp 进行一些代码定义

代码语言:javascript
复制
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate IntPtr d_gdk_x11_window_get_xid(IntPtr gdkWindow);

    private static d_gdk_x11_window_get_xid gdk_x11_window_get_xid =
        LoadFunction<d_gdk_x11_window_get_xid>("libgdk-3.so.0", "gdk_x11_window_get_xid");

    private static T LoadFunction<T>(string libName, string functionName)
    {
        if (!OperatingSystem.IsLinux())
        {
            // 防止炸调试
            return default(T)!;
        }

        // LoadLibrary
        var libPtr = Linux.dlopen(libName, RTLD_GLOBAL | RTLD_LAZY);

        Console.WriteLine($"Load {libName} ptr={libPtr}");

        // GetProcAddress
        var procAddress = Linux.dlsym(libPtr, functionName);
        Console.WriteLine($"Load {functionName} ptr={procAddress}");
        return Marshal.GetDelegateForFunctionPointer<T>(procAddress);
    }

    private const int RTLD_LAZY = 0x0001;
    private const int RTLD_GLOBAL = 0x0100;

    private class Linux
    {
        [DllImport("libdl.so.2")]
        public static extern IntPtr dlopen(string path, int flags);

        [DllImport("libdl.so.2")]
        public static extern IntPtr dlsym(IntPtr handle, string symbol);
    }

接着在窗口的 Show 方法之后,即可获取到对应的 X11 窗口

代码语言:javascript
复制
    protected override void OnShown()
    {
        base.OnShown(); // 在这句话调用之前 window.Window 是空

        Window window = this;
        Gdk.Window gdkWindow = window.Window;

        if (gdkWindow is null)
        {
            // 确保 base.OnShown 调用
            Console.WriteLine($"gdkWindow is null");
            return;
        }

        var x11 = gdk_x11_window_get_xid(gdkWindow.Handle);
        Console.WriteLine($"X11 窗口 0x{x11:x2}");
    }

通过以上代码输出的 X11 窗口的 XID 号,可以同步在命令行输入进 xwininfo 命令里面。比如我这里输出的是 X11 窗口 0x5600003 的值

打开另一个命令行,输入以下命令,将 XID 传入 xwininfo 命令,即可看到显示的窗口标题和当前运行的窗口是相同的

我核心踩坑就是搜到的是 GTK 2 的使用方法,以及将 gtk 的窗口当成 gdk 的窗口传入方法

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

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

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

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

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

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

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档