如何使用XCB绘制标题栏

5

我正在使用libxcb编写一个简单的窗口管理器,尝试给窗口添加标题栏、图标和最小化/最大化/关闭按钮。

我在Xephyr环境中测试我的窗口管理器。我可以产生一个新的xterm窗口,移动它并调整它的大小。但现在我想装饰这个新的xterm窗口(或其他任何应用程序),让它有一个标题栏、图标和最小化/最大化/关闭按钮。

在我的Linux机器中,我刚刚安装了一个Gtk主题,如果我启动Firefox,例如,在我的设置中设置那个主题之后,窗口就会被装饰起来。所以在这种情况下,我认为是Gtk应用了窗口装饰。这是怎么做到的呢?

我读到EWMH窗口属性_NET_WM_WINDOW_TYPE,可以用于确定如何处理你的窗口装饰。所以我想我可以检查窗口类型是否为_NET_WM_WINDOW_TYPE_NORMAL,获取应用程序的WM_NAME,然后手动在其上绘制一个标题栏。

这是正常情况下如何绘制窗口装饰吗?还是我可以使用Gtk(或其他什么东西)来实现这一点?

1个回答

7
所以在这种情况下,我认为是Gtk应用了窗口装饰。这是怎么做到的呢?
没错。GTK应用程序通过将边框宽度设置为0,告诉窗口管理器不要对其进行装饰。现在我的建议是只实现这一点:如果窗口设置了边框宽度为0,则忽略其装饰。起初我不会费心去做其他事情。实际上,你甚至可以暂时忽略这个提示。
我读到了EWMH窗口属性[...]。
暂时不要关注EWMH。只需为所有未设置边框为0的受管理窗口进行装饰。此外,我不认为像对话框这样的其他窗口类型不应该被装饰;我不认为窗口管理器真正使用这个属性来确定这一点,但我只能确切地说几个。
这是你通常应该绘制窗口装饰的方式吗?还是我可以使用Gtk(或其他东西)来完成?
虽然你没有明确要求这样做,但这个引文中的最后一句话告诉我你可能并没有完全理解装饰的工作原理。
最常见的方法,我强烈建议你这样做,叫做重新父化。
重新父化意味着当你管理一个窗口时,你创建一个新窗口(当然你不应该像正常客户端窗口那样管理它)称为框架窗口,然后将客户端窗口重新父化到你的框架窗口中。所以实际的顶层窗口是由窗口管理器拥有的框架窗口;客户端窗口(用户交互的窗口)是其直接子代。
现在,你只需要使框架窗口比客户端窗口稍微大一点,并在其中正确定位客户端窗口。当然,你需要跟踪客户端窗口的大小调整并对其进行操作。
那么我们为什么要创建这个框架窗口呢?很简单!因为你可以为它创建一个像素图,并在上面绘制你的标题栏。这比直接绘制子窗口好,因为你不会干扰你实际上不拥有的窗口。
绘图可以使用“原始”和简单的调用,如xcb_poly_fill_rectangle,或者你可以使用更复杂的方法,例如使用cairo这样的库(我建议这样做)。例如,i3窗口管理器使用一个简单的抽象层,通过编译标志支持两种方法(libi3/draw_util.c)。
这种重新父化的方法是工具如xwininfo或xprop具有“-frame”选项的原因。默认情况下,这些工具实际上会忽略框架窗口并进入客户端窗口,几乎隐藏了框架窗口的存在事实。只需在同一窗口上尝试xprop和xprop -frame,你就会发现框架窗口附加的信息要少得多。

一旦您已经实现了重新父级和绘图,您可以考虑那些不需要/不想装饰窗口的情况。鉴于这里要跟踪的内容相当多,我认为首先实施这个功能将让您忙碌一段时间。我强烈建议您学习其他简单窗口管理器的代码,其中包括重新父级。


啊,我之前不知道有重新设置父级控件的功能,谢谢。我会使用它来绘制自己的标题栏。但是我不明白如何让GTK主题在GTK应用程序中显示出来。如果应用程序将边框宽度设置为0,除了不绘制自己的装饰外,我需要做些什么吗? - Carlito
不,GTK只是客户端使用的一个库。窗口管理器不涉及渲染客户端窗口本身,这取决于客户端,而在这种情况下,客户端使用GTK使事情更容易。你不需要担心任何问题。 - Ingo Bürk
最常见的方法是重复。还有哪些绘制标题栏的方式? - oel_runs_the_world
@oel_runs_the_world 从技术上讲,窗口管理器可以渲染到紧邻客户端窗口(甚至是根窗口)的窗口中。但这样做并没有太多意义,反而会引起很多问题,因此不建议这么做。 - Ingo Bürk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接