如何为X11程序的子窗口添加带标题栏的标准窗口装饰?
XWindow子窗口标题栏默认行为覆盖及MDI窗中窗实现问题
我了解到XWindow会根据窗口的子级属性调整其标题栏样式,请问能否覆盖该默认行为?示例程序如下:
/* * Study for child windows. */ #include <X11/Xlib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> Display* d; int main(void) { Window w, w2; XEvent e; const char* msg = "Hello, window 1"; const char* msg2 = "Hello, window 2"; int s; int x; GC gracxt; d = XOpenDisplay(NULL); if (d == NULL) { fprintf(stderr, "Cannot open display\n"); exit(1); } s = DefaultScreen(d); gracxt = XDefaultGC(d, s); w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 640, 480, 5, BlackPixel(d, s), WhitePixel(d, s)); XSelectInput(d, w, ExposureMask|KeyPressMask|PointerMotionMask|StructureNotifyMask); XMapWindow(d, w); w2 = XCreateSimpleWindow(d, w, 100, 100, 100, 100, 5, BlackPixel(d, s), WhitePixel(d, s)); XSelectInput(d, w2, ExposureMask|KeyPressMask|PointerMotionMask|StructureNotifyMask); XMapWindow(d, w2); while (1) { XNextEvent(d, &e); if (e.type == Expose) { if (e.xany.window == w) XDrawString(d, e.xany.window, gracxt, 10, 50, msg, strlen(msg)); else XDrawString(d, e.xany.window, gracxt, 10, 50, msg2, strlen(msg2)); } if (e.type == KeyPress) break; /* exit on any key */ } XCloseDisplay(d); return 0; }

运行生成的子窗口丢失了标题栏,仅显示黑色外框。我希望实现窗中窗效果,即子窗口保留标题栏、缩放边框等完整装饰,支持在父窗口内移动、调整尺寸,用于多文档(MDI)类界面,方便用户在父窗口内打开多个可操作的文档。我的运行环境为Ubuntu 20.04操作系统,窗口管理器为Gnome/GDM3。请问是否有方法可以实现该效果?
解答
首先要明确:X11体系下窗口的标题栏、缩放边框等装饰不是XServer原生提供的,是由系统的窗口管理器(Gnome对应的窗口管理器是Mutter)负责绘制的,默认只有父级为根窗口的顶层窗口才会被窗口管理器识别并添加装饰,你直接创建父级为主窗口的子窗口,不会被窗口管理器处理,所以没有装饰是正常行为,不存在所谓“覆盖默认属性”的原生参数可以直接改出带装饰的子窗口。
可行的实现方案有两种:
- 自主实现MDI子窗口逻辑:自己绘制子窗口的标题栏、缩放边框,监听鼠标事件手动处理拖动移动、边缘缩放、按钮交互的逻辑。这是最通用的实现方式,不依赖窗口管理器的特性,绝大多数成熟UI框架的MDI组件都是这么实现的,比如GTK的GtkMDI、Qt的QMdiArea,不需要从零手写Xlib底层逻辑。
- 窗口重定向实现:先把需要作为子窗口的窗口创建为父级是根窗口的顶层窗口,等待窗口管理器给它加上装饰后,调用
XReparentWindow接口将其重定向到你的主窗口内。需要注意Mutter窗口管理器对这类非标准窗口操作的兼容性,要额外处理重定向后的坐标校准、事件转发逻辑,还要设置合适的窗口类型Hint避免窗口管理器做异常拦截。
如果你的应用要适配Gnome环境,优先选择第一种方案,兼容性更好。
内容的提问来源于stack exchange,提问作者Scott Franco
相关产品推荐
相关产品推荐

