dotnet 设置 X11 建立窗口之间的父子关系

news/2024/11/19 20:09:42/文章来源:https://www.cnblogs.com/lindexi/p/18197088

在 X11 里面有和 Win32 类似的窗口之间的关系机制,如 Owner-Owned 关系,以及 Parent-Child 关系。本文将告诉大家如何进行设置以及其行为

本文将大量使用到 new bing 提供的回答内容,感谢 new bing 人工智能提供的内容

Owner-Owned 关系

  • 在这种关系中,一个窗口可以被另一个窗口拥有(owner)。
  • 被拥有的窗口永远显示在拥有它的那个窗口的前面。
  • 当所有者窗口最小化时,它所拥有的窗口也会被隐藏。
  • 当所有者窗口被销毁时,它所拥有的窗口也会被销毁。
  • 当子窗口最小化时,不会影响到所有者窗口
  • 子窗口可以超过所有者窗口的范围

被拥有的窗口 = 子窗口

所有者窗口 = “在拥有它的那个窗口”

即与 WPF 的 ChildWindow.Owner = MainWindow 的效果类似。以上的 ChildWindow 为子窗口,而 MainWindow 为 所有者窗口

核心 C# 代码如下

        // 我们使用XSetTransientForHint函数将窗口a设置为窗口b的子窗口。这将确保窗口a始终在窗口b的上方XSetTransientForHint(Display, a, b);

通过关系的描述可以了解到,使用上面代码即可设置 a 窗口一定在 b 窗口上方

以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

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

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

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

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

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

Parent-Child 关系

  • 在这种关系中,一个窗口是另一个窗口的父窗口。
  • 子窗口只能显示在父窗口的客户区内。
  • 当父窗口被隐藏时,它的所有子窗口也会被隐藏。
  • 当父窗口被销毁时,它所拥有的子窗口也会被销毁。

核心 C# 代码如下

        // 设置父子关系XReparentWindow(display, childWindowHandle, mainWindowHandle, 0, 0);XMapWindow(display, childWindowHandle);

需要记住在 XMapWindow 之前调用 XReparentWindow 方法,否则关系设置无效

以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

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

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

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

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

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

建立 Parent-Child 关系之后,如果子窗口没有调用 XSelectInput 方法时,那所有在子窗口上的消息都能被所有者窗口收到,如果调用了 XSelectInput 则子窗口收到子窗口的消息,即所有者窗口被子窗口遮挡的部分将不能收到消息,被子窗口遮挡的部分的触摸或鼠标消息会被子窗口接收

简单的测试代码逻辑如下

var xDisplayWidth = XDisplayWidth(display, screen) / 2;
var xDisplayHeight = XDisplayHeight(display, screen) / 2;
var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,32,(int) CreateWindowArgs.InputOutput,visual,(nuint) valueMask, ref xSetWindowAttributes);XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |XEventMask.PointerMotionHintMask;
var mask = new IntPtr(0xffffff ^ (int) ignoredMask);
XSelectInput(display, handle, mask);var mainWindowHandle = handle;// 再创建另一个窗口设置 Owner-Owned 关系var childWindowHandle = XCreateSimpleWindow(display, rootWindow, 0, 0, 300, 300, 5, white, black);XSelectInput(display, childWindowHandle, mask);// 设置父子关系XReparentWindow(display, childWindowHandle, mainWindowHandle, 50,50);XMapWindow(display, childWindowHandle);while (true)
{var xNextEvent = XNextEvent(display, out var @event);if(@event.type == XEventName.MotionNotify){if (@event.MotionEvent.window == handle){Console.WriteLine($"Window1 {DateTime.Now:HH:mm:ss}");}else{Console.WriteLine($"Window2 {DateTime.Now:HH:mm:ss}");}}
}

配置了以上代码,运行项目,可以看到鼠标在子窗口上时,只能收到子窗口的消息,如下图

以上代码有所忽略,全部的代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

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

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

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

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

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

设置 Parent-Child 关系之后,将限制子窗口只能在主窗口的客户区范围内,即子窗口不能超过主窗口范围,如下图所示

以上代码是在 XReparentWindow 方法里面设置了子窗口的坐标,让子窗口超过主窗口的范围,代码如下

        var mainWindowHandle = handle;// 再创建另一个窗口设置 Owner-Owned 关系var childWindowHandle = XCreateSimpleWindow(display, rootWindow, 0, 0, 300, 300, 5, white, black);XSelectInput(display, childWindowHandle, mask);// 设置父子关系XReparentWindow(display, childWindowHandle, mainWindowHandle, 300, 50);XMapWindow(display, childWindowHandle);

以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

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

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

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

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

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

以下是 new bing 给出的 XReparentWindow 函数的更多信息

XReparentWindow 函数的作用是将一个窗口重新设置其父窗口。具体来说,如果指定的窗口已经被映射到屏幕上,XReparentWindow 会自动执行 UnmapWindow 请求,将其从当前层次结构中移除,并将其插入到指定父窗口的子级中。这个窗口会在兄弟窗口中的堆叠顺序中置于顶部。¹²

如果原始窗口已经被映射,XReparentWindow 还会导致 X 服务器生成一个 ReparentNotify 事件。在此事件中,override_redirect 成员被设置为窗口的相应属性。通常情况下,窗口管理器客户端应该忽略此窗口,如果此成员设置为 True。最后,如果原始窗口已经被映射,X 服务器会自动对其执行 MapWindow 请求。对于原先被遮挡的窗口,X 服务器会执行正常的曝光处理。但是,由于最终的 MapWindow 请求会立即遮挡初始 UnmapWindow 请求的某些区域,因此 X 服务器可能不会为这些区域生成 Expose 事件。¹

以下情况会导致 BadMatch 错误:

  • 新的父窗口不在与旧的父窗口相同的屏幕上。
  • 新的父窗口是指定窗口本身或指定窗口的下级。
  • 新的父窗口是 InputOnly 类型,而窗口不是。
  • 指定窗口具有 ParentRelative 背景,而新的父窗口与指定窗口的深度不同。

总之,XReparentWindow 允许您在 X 窗口系统中重新组织窗口的层次结构。

使用 XReparentWindow 设置的窗口关系时,子窗口将会挡住主窗口的渲染部分,即在子窗口范围内将看不到主窗口的绘制内容

其测试代码如下,先在主窗口和子窗口绘制内容

    if (@event.type == XEventName.Expose){if (@event.ExposeEvent.window == handle){XDrawLine(display, handle, gc, 2, 2, xDisplayWidth - 2, xDisplayHeight - 2);XDrawLine(display, handle, gc, 2, xDisplayHeight - 2, xDisplayWidth - 2, 2);}else if (childWindowHandle != 0 && @event.ExposeEvent.window == childWindowHandle){XDrawLine(display, childWindowHandle, gc, 1, 1, xDisplayWidth - 2, 1);XDrawLine(display, childWindowHandle, gc, 1, xDisplayHeight - 2, xDisplayWidth - 2, xDisplayHeight - 2);XDrawLine(display, childWindowHandle, gc, 1, 1, 1, xDisplayHeight - 2);XDrawLine(display, childWindowHandle, gc, xDisplayWidth - 2, xDisplayHeight - 2, xDisplayWidth - 2, xDisplayHeight - 2);}}

接着使用 XMoveWindow 设置子窗口坐标,此时可见子窗口所在地方将不可见主窗口绘制的内容

    while (true){await Task.Delay(TimeSpan.FromSeconds(1));await InvokeAsync(() =>{XMoveWindow(display, childWindowHandle, Random.Shared.Next(200), Random.Shared.Next(100));});}

全部的测试代码如下

// See https://aka.ms/new-console-template for more informationusing CPF.Linux;using System;
using System.Diagnostics;
using System.Runtime;using static CPF.Linux.XLib;XInitThreads();
var display = XOpenDisplay(IntPtr.Zero);
var screen = XDefaultScreen(display);var rootWindow = XDefaultRootWindow(display);XMatchVisualInfo(display, screen, 32, 4, out var info);
var visual = info.visual;var valueMask =//SetWindowValuemask.BackPixmap0| SetWindowValuemask.BackPixel| SetWindowValuemask.BorderPixel| SetWindowValuemask.BitGravity| SetWindowValuemask.WinGravity| SetWindowValuemask.BackingStore| SetWindowValuemask.ColorMap//| SetWindowValuemask.OverrideRedirect;
var xSetWindowAttributes = new XSetWindowAttributes
{backing_store = 1,bit_gravity = Gravity.NorthWestGravity,win_gravity = Gravity.NorthWestGravity,//override_redirect = true, // 设置窗口的override_redirect属性为True,以避免窗口管理器的干预colormap = XCreateColormap(display, rootWindow, visual, 0),border_pixel = 0,background_pixel = 0,
};var xDisplayWidth = XDisplayWidth(display, screen) / 2;
var xDisplayHeight = XDisplayHeight(display, screen) / 2;
var handle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,32,(int) CreateWindowArgs.InputOutput,visual,(nuint) valueMask, ref xSetWindowAttributes);XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask |XEventMask.PointerMotionHintMask;
var mask = new IntPtr(0xffffff ^ (int) ignoredMask);
XSelectInput(display, handle, mask);XMapWindow(display, handle);
XFlush(display);var white = XWhitePixel(display, screen);
var black = XBlackPixel(display, screen);var gc = XCreateGC(display, handle, 0, 0);
XSetForeground(display, gc, white);
XSync(display, false);var invokeList = new List<Action>();
var invokeMessageId = new IntPtr(123123123);async Task InvokeAsync(Action action)
{var taskCompletionSource = new TaskCompletionSource();lock (invokeList){invokeList.Add(() =>{action();taskCompletionSource.SetResult();});}// 在 Avalonia 里面,是通过循环读取的方式,通过 XPending 判断是否有消息// 如果没有消息就进入自旋判断是否有业务消息和判断是否有 XPending 消息// 核心使用 epoll_wait 进行等待// 整个逻辑比较复杂// 这里简单处理,只通过发送 ClientMessage 的方式,告诉消息循环需要处理业务逻辑// 发送 ClientMessage 是一个合理的方式,根据官方文档说明,可以看到这是没有明确定义的// https://www.x.org/releases/X11R7.5/doc/man/man3/XClientMessageEvent.3.html// https://tronche.com/gui/x/xlib/events/client-communication/client-message.html// The X server places no interpretation on the values in the window, message_type, or data members.// 在 cpf 里面,和 Avalonia 实现差不多,也是在判断 XPending 是否有消息,没消息则判断是否有业务逻辑// 最后再进入等待逻辑。似乎 CPF 这样的方式会导致 CPU 占用略微提升var @event = new XEvent{ClientMessageEvent ={type = XEventName.ClientMessage,send_event = true,window = handle,message_type = 0,format = 32,ptr1 = invokeMessageId,ptr2 = 0,ptr3 = 0,ptr4 = 0,}};XSendEvent(display, handle, false, 0, ref @event);XFlush(display);await taskCompletionSource.Task;
}IntPtr childWindowHandle = 0;_ = Task.Run(async () =>
{await InvokeAsync(() =>{var mainWindowHandle = handle;// 再创建另一个窗口设置 Owner-Owned 关系// 创建无边框窗口valueMask =//SetWindowValuemask.BackPixmap0| SetWindowValuemask.BackPixel| SetWindowValuemask.BorderPixel| SetWindowValuemask.BitGravity| SetWindowValuemask.WinGravity| SetWindowValuemask.BackingStore| SetWindowValuemask.ColorMap| SetWindowValuemask.OverrideRedirect // [dotnet C# X11 开发笔记](https://blog.lindexi.com/post/dotnet-C-X11-%E5%BC%80%E5%8F%91%E7%AC%94%E8%AE%B0.html );xSetWindowAttributes = new XSetWindowAttributes{backing_store = 1,bit_gravity = Gravity.NorthWestGravity,win_gravity = Gravity.NorthWestGravity,override_redirect = true,colormap = XCreateColormap(display, rootWindow, visual, 0),border_pixel = 0,background_pixel = 0,};childWindowHandle = XCreateWindow(display, rootWindow, 0, 0, xDisplayWidth, xDisplayHeight, 5,32,(int) CreateWindowArgs.InputOutput,visual,(nuint) valueMask, ref xSetWindowAttributes);XSelectInput(display, childWindowHandle, mask);// 设置父子关系XReparentWindow(display, childWindowHandle, mainWindowHandle, 300, 50);XMapWindow(display, childWindowHandle);});while (true){await Task.Delay(TimeSpan.FromSeconds(1));await InvokeAsync(() =>{XMoveWindow(display, childWindowHandle, Random.Shared.Next(200), Random.Shared.Next(100));});}
});Thread.CurrentThread.Name = "主线程";while (true)
{var xNextEvent = XNextEvent(display, out var @event);if (xNextEvent != 0){Console.WriteLine($"xNextEvent {xNextEvent}");break;}if (@event.type == XEventName.Expose){if (@event.ExposeEvent.window == handle){XDrawLine(display, handle, gc, 2, 2, xDisplayWidth - 2, xDisplayHeight - 2);XDrawLine(display, handle, gc, 2, xDisplayHeight - 2, xDisplayWidth - 2, 2);}else if (childWindowHandle != 0 && @event.ExposeEvent.window == childWindowHandle){XDrawLine(display, childWindowHandle, gc, 1, 1, xDisplayWidth - 2, 1);XDrawLine(display, childWindowHandle, gc, 1, xDisplayHeight - 2, xDisplayWidth - 2, xDisplayHeight - 2);XDrawLine(display, childWindowHandle, gc, 1, 1, 1, xDisplayHeight - 2);XDrawLine(display, childWindowHandle, gc, xDisplayWidth - 2, xDisplayHeight - 2, xDisplayWidth - 2, xDisplayHeight - 2);}}else if (@event.type == XEventName.ClientMessage){var clientMessageEvent = @event.ClientMessageEvent;if (clientMessageEvent.message_type == 0 && clientMessageEvent.ptr1 == invokeMessageId){List<Action> tempList;lock (invokeList){tempList = invokeList.ToList();invokeList.Clear();}foreach (var action in tempList){action();}}}else if (@event.type == XEventName.MotionNotify){if (@event.MotionEvent.window == handle){Console.WriteLine($"Window1 {DateTime.Now:HH:mm:ss}");}else{Console.WriteLine($"Window2 {DateTime.Now:HH:mm:ss}");}}
}Console.WriteLine("Hello, World!");

运行代码之后的效果如下图

如上图,应用是透明窗口,可以看到背后的图片应用显示的内容。上述图片是使用 WPF 基础绘图 创建和加工图片 绘制的图片。可以看到无论是主窗口还是子窗口都能透过去。但是子窗口将会遮挡主窗口的绘制,即让子窗口直接显示窗口之后的部分内容,但不会与主窗口合成,即主窗口被子窗口挡住的部分就没有进行渲染

以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码

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

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

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

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

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

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

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

相关文章

学习 Avalonia 框架笔记 设置 X11 窗口从最小化状态还原为正常状态

本文记录在 dotnet 里面如何设置 X11 窗口从最小化状态还原为正常状态本文属于学习 Avalonia 框架系列笔记,更多请参阅 博客导航 核心设置方法如下 /// <summary> /// 代码从 Avalonia 抄的 https://github.com/AvaloniaUI/Avalonia/blob/5e323b8fb1e2ca36550ca6fe678e4…

[转帖]全网最硬核 JVM 内存解析 - 4.Java 堆内存大小的确认

https://cloud.tencent.com/developer/article/2277323 个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判。如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 issue,谢谢支持~…

[转帖]System Performance 读书笔记 - 操作系统(1)

https://cloud.tencent.com/developer/article/1927381 本系列是针对 Systems Performance: Enterprise and the Cloud, 2nd Edition (2020) 书籍的读书笔记,加入了一些个人理解以及拓展,并且针对一些难以理解的地方提供了一些额外的参考内核(Kernel) 经典模型中,内核在操…

读人工智能时代与人类未来笔记05_现代人工智能

读人工智能时代与人类未来笔记05_现代人工智能1. 图灵 1.1. 1950年,数学家和逻辑学家艾伦图灵的论文《计算机与智能》中,图灵建议把机器智能的问题完全搁置 1.2. 图灵认为,重要的不是智能的机制,而是智能的表现 1.2.1. 因为其他生命的内在体验仍然是不可知的,所以我们衡量…

[转帖]为什么我建议需要定期重建数据量大但是性能关键的表

https://cloud.tencent.com/developer/article/1999970 个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判。如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 issue,谢谢支持~…

尽微好物:从0到10亿+的抖音电商的TOP1“联盟团长”,如何使用NineData实现上云下云

杭州尽微供应链是抖⾳平台⽉均带货10E的TOP1“联盟团⻓”&#xff0c;是字节跳动⼀级代理商&#xff0c;巨量千川指定服务商&#xff0c;拥有商品库9万&#xff0c;是⾏业领先的电商供应链平台&#xff0c;达⼈陪跑机构。 杭州尽微供应链以天猫、京东抖音电商业务为依托&#x…

论文解读:Self-Promoted Supervision for Few-Shot Transformer

文章汇总 存在的问题 在没有归纳偏差的情况下&#xff0c;vit通常在只有少数标记训练数据可用的few-shot学习机制下学习低质量令牌依赖关系&#xff0c;这在很大程度上导致了上述性能下降。 动机 cnn的归纳偏置并不天生就适用于vit&#xff0c;也不能很好地增强和加速vit中…

Signal 即将成为JavaScript的一部分

什么是响应性&#xff1f; 在过去的几年中&#xff0c;响应性成为了所有现代前端框架以及React库的核心。 对于不熟悉前端开发的人来说&#xff0c;起初这可能是一个令人困惑的概念&#xff0c;因为它改变了常规的、自上而下的、从调用者到被调用者的顺序工作流。 在响应性范…

最近很火的粘土滤镜被玩坏了,教你用AI绘画SD免费无限制使用,附教程!

大家好&#xff0c;我是阿威。 最近在小红书上&#xff0c;“粘土特效”、“粘土滤镜”异常爆火&#xff0c;被网友玩出了花。 原来&#xff0c;一款海外修图工具——Remini&#xff08;类似妙鸭相机&#xff09;上线AI滤镜功能&#xff0c;其中就包括粘土滤镜&#xff0c;非常…

Adobe After Effects AE v24.3.0 解锁版 (视频合成及视频特效制作)

Adobe系列软件安装目录 一、Adobe Photoshop PS 25.6.0 解锁版 (最流行的图像设计软件) 二、Adobe Media Encoder ME v24.3.0 解锁版 (视频和音频编码渲染工具) 三、Adobe Premiere Pro v24.3.0 解锁版 (领先的视频编辑软件) 四、Adobe After Effects AE v24.3.0 解锁版 (视…

【线程创建】——三种方式➕多线程案例练习

02 线程创建 Thread , Runnable , Callable 三种创建方式 Thread class - 继承Thread类 (重点) Runnable接口 - 实现Runnable接口 (重点) Callable接口 - 实现Callable接口 (了解) Thread 类实现 它继承了老祖宗 Object java.lang.Object java.lang.Thread 它实现了 Runnab…

centOS忘记密码的处理办法

1、开机后在出现内核选项时&#xff0c;按 e&#xff1b; 2、在Linux 开头的这行&#xff0c;输入 rd.break 如下图&#xff1b; 3、然后&#xff0c;执行&#xff1a;CtrlX&#xff1b; 4、进入之后是 switch_root:/#输入 mount -o rw,remount /sysroot 以读写方式重新挂载 /s…