如何正确设置XWindows属性实现窗口边框的动态开启与关闭
X11窗口边框动态切换实现方案
原有实现及存在的问题
最初尝试通过修改_NET_WM_WINDOW_TYPE属性实现边框开关,代码如下:
/* * XWindows study on if we can turn the frame on and off. */ #include <X11/Xlib.h> #include <X11/Xatom.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAPIT #define REPEAT void waitxevt(Display* d, int type) { XEvent e; /* XEvent holder */ do { XNextEvent(d, &e); } while (e.type != type); } void frame(Display* d, Window w, int e) { Atom window_type; long value; #ifdef MAPIT XUnmapWindow(d, w); waitxevt(d, UnmapNotify); #endif window_type = XInternAtom(d, "_NET_WM_WINDOW_TYPE", False); if (e) value = XInternAtom(d, "_NET_WM_WINDOW_TYPE_NORMAL", False); else value = XInternAtom(d, "_NET_WM_WINDOW_TYPE_DOCK", False); XChangeProperty(d, w, window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &value, 1); #ifdef MAPIT XMapWindow(d, w); waitxevt(d, MapNotify); #endif } int main(void) { Display* d; Window w; XEvent e; const char* msg = "Hello, World!"; int s; GC gracxt; int frmenb = 0; 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|StructureNotifyMask); XMapWindow(d, w); waitxevt(d, MapNotify); while (1) { XNextEvent(d, &e); if (e.type == Expose) XDrawString(d, w, gracxt, 10, 50, msg, strlen(msg)); if (e.type == KeyPress) { frame(d, w, frmenb); frmenb = !frmenb; #ifdef REPEAT frame(d, w, frmenb); frmenb = !frmenb; #endif } } XCloseDisplay(d); return 0; }
该实现存在以下问题:
- 不执行unmap/map操作时,关闭边框可正常生效,窗口变为无边框状态,无法调整大小、无标题栏;但重新开启边框时标题栏不会自动显示,仅支持窗口大小调整,手动调整窗口尺寸后标题栏才会重新出现
- 若添加unmap/map流程,会导致无边框窗口无法获得焦点,无法接收按键事件切回有边框状态,同时map操作后窗口位置会被重置
以上测试均运行于Ubuntu 20.04系统。
该功能属于Windows与Linux跨平台移植工具包的一部分,有两个使用场景:
- 生成无边框的组件窗口(widget),该场景仅需在窗口首次map前执行一次关闭边框操作,原有实现可正常工作
- 动态切换边框状态,例如点击时钟程序可移除边框实现更小展示体积和固定位置,再次点击恢复边框,该场景原有实现无法正常运行
可行解决方案
通过修改_MOTIF_WM_HINTS属性控制窗口装饰状态,无需unmap/map操作即可实现边框动态切换,完整实现代码如下:
/* * XWindows study on if we can turn the frame on and off. */ #include <X11/Xlib.h> #include <X11/Xatom.h> #include <stdio.h> #include <stdlib.h> #include <string.h> struct MwmHints { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; }; enum { MWM_HINTS_FUNCTIONS = (1L << 0), MWM_HINTS_DECORATIONS = (1L << 1), MWM_FUNC_ALL = (1L << 0), MWM_FUNC_RESIZE = (1L << 1), MWM_FUNC_MOVE = (1L << 2), MWM_FUNC_MINIMIZE = (1L << 3), MWM_FUNC_MAXIMIZE = (1L << 4), MWM_FUNC_CLOSE = (1L << 5) }; void frame(Display* d, Window w, int e) { Atom mwmHintsProperty; struct MwmHints hints; mwmHintsProperty = XInternAtom(d, "_MOTIF_WM_HINTS", 0); hints.flags = MWM_HINTS_DECORATIONS; hints.decorations = !!e; XChangeProperty(d, w, mwmHintsProperty, mwmHintsProperty, 32, PropModeReplace, (unsigned char *)&hints, 5); } int main(void) { Display* d; Window w; XEvent e; const char* msg = "Hello, World!"; int s; GC gracxt; int frmenb = 0; 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|StructureNotifyMask); XMapWindow(d, w); while (1) { XNextEvent(d, &e); if (e.type == Expose) XDrawString(d, w, gracxt, 10, 50, msg, strlen(msg)); if (e.type == KeyPress) { frame(d, w, frmenb); frmenb = !frmenb; } } XCloseDisplay(d); return 0; }
内容的提问来源于stack exchange,提问作者Scott Franco
相关产品推荐
相关产品推荐

