You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确设置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跨平台移植工具包的一部分,有两个使用场景:

  1. 生成无边框的组件窗口(widget),该场景仅需在窗口首次map前执行一次关闭边框操作,原有实现可正常工作
  2. 动态切换边框状态,例如点击时钟程序可移除边框实现更小展示体积和固定位置,再次点击恢复边框,该场景原有实现无法正常运行

可行解决方案

通过修改_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 08:42:01