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

Xlib中MapNotify事件触发过早问题求助

解决Xlib中MapNotify事件触发过早导致绘制失效的问题

问题说明

首次使用C语言开发Xlib程序,想要在屏幕上绘制一个测试矩形,但需要等待Xorg完成窗口创建。目前设置的MapNotify事件触发时机过早,添加3秒延迟后矩形可以正常绘制,但这种方案仅适用于测试,无法实现回调的正常工作。

原始代码

#include <time.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#define white 0xFFFFFF
#define black 0

#define red 0xFF0000
#define green 0xFF00
#define blue 0xFF

void delay(unsigned int millis) {
    clock_t start = clock();
    while(clock() < start + millis);
}

int main(int argc, char** argv) {
    Display* dpy = XOpenDisplay(NULL); // 获取默认显示设备
    if(dpy == NULL) {
        fprintf(stderr, "无法打开显示设备\n");
        return 1;
    }

    Window win = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), 0, 0, 800, 800, 0, 0, black); // 创建一个简单窗口

    XMapWindow(dpy, win);
    XSelectInput(dpy, win, StructureNotifyMask); // 监听MapNotify事件

    GC gc = XCreateGC(dpy, win, 0, 0);
    if(gc == NULL) {
        fprintf(stderr, "创建GC失败\n");
        return 1;
    }

    XSetBackground(dpy, gc, white);
    XSetForeground(dpy, gc, white);
    XSync(dpy, true);

    while(true) { // 等待窗口就绪,目前这个事件触发太早
        XEvent e;
        XNextEvent(dpy, &e);
        printf("收到事件: %d\n", e.type);
        if(e.type == MapNotify)
            break;
    }
    //delay(3000);

    XFillRectangle(dpy, win, gc, 5, 5, 100, 100);
    XFlush(dpy);

    while(true) { // 等待窗口关闭,目前这个逻辑不生效,暂时忽略
        XEvent e;
        XNextEvent(dpy, &e);
        if(e.type == UnmapNotify)
            return 0;
    }
}

问题原因

MapNotify事件只是通知窗口已被映射到屏幕,但此时窗口可能还没有完成所有初始化步骤(比如窗口管理器还没完成窗口的布局和渲染准备),直接绘制的内容可能会被后续的窗口刷新操作覆盖,或者根本无法被正确渲染。

解决方案

在Xlib中,正确的绘制时机是响应Expose事件——当窗口需要重绘(包括首次显示)时,Xorg会发送Expose事件。我们需要修改代码,监听Expose事件,并在收到该事件时执行绘制操作:

修改后的代码

#include <string.h>
#include <unistd.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>

#define white 0xFFFFFF
#define black 0

#define red 0xFF0000
#define green 0xFF00
#define blue 0xFF

int main(int argc, char** argv) {
    Display* dpy = XOpenDisplay(NULL);
    if(dpy == NULL) {
        fprintf(stderr, "无法打开显示设备\n");
        return 1;
    }

    Window win = XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy), 0, 0, 800, 800, 0, 0, black);

    // 同时监听窗口结构事件和暴露事件
    XSelectInput(dpy, win, StructureNotifyMask | ExposureMask);
    XMapWindow(dpy, win);

    GC gc = XCreateGC(dpy, win, 0, 0);
    if(gc == NULL) {
        fprintf(stderr, "创建GC失败\n");
        return 1;
    }

    XSetBackground(dpy, gc, white);
    XSetForeground(dpy, gc, white);
    XSync(dpy, true);

    bool window_mapped = false;
    while(true) {
        XEvent e;
        XNextEvent(dpy, &e);

        switch(e.type) {
            case MapNotify:
                window_mapped = true;
                printf("窗口已映射\n");
                break;
            case Expose:
                // 只有窗口已映射且需要重绘时执行绘制
                if(window_mapped) {
                    XFillRectangle(dpy, win, gc, 5, 5, 100, 100);
                    XFlush(dpy);
                    printf("执行绘制操作\n");
                }
                break;
            case UnmapNotify:
                // 处理窗口关闭,清理资源
                XFreeGC(dpy, gc);
                XCloseDisplay(dpy);
                return 0;
            default:
                break;
        }
    }
}

修改说明

  1. 调整事件掩码:将XSelectInput的参数改为StructureNotifyMask | ExposureMask,同时监听窗口结构事件和暴露事件。
  2. 重构事件循环:不再在MapNotify后直接退出循环,而是持续处理所有事件,当收到Expose事件时执行绘制,确保窗口真正准备好接收绘制指令。
  3. 资源清理:在窗口关闭时释放GC资源并关闭显示连接,避免内存泄漏。

这样修改后,无需依赖延迟,完全遵循Xlib的事件驱动模型,绘制操作会在合适的时机自动执行。

内容的提问来源于stack exchange,提问作者Player_X_YT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:30:16