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; } } }
修改说明
- 调整事件掩码:将
XSelectInput的参数改为StructureNotifyMask | ExposureMask,同时监听窗口结构事件和暴露事件。 - 重构事件循环:不再在MapNotify后直接退出循环,而是持续处理所有事件,当收到Expose事件时执行绘制,确保窗口真正准备好接收绘制指令。
- 资源清理:在窗口关闭时释放GC资源并关闭显示连接,避免内存泄漏。
这样修改后,无需依赖延迟,完全遵循Xlib的事件驱动模型,绘制操作会在合适的时机自动执行。
内容的提问来源于stack exchange,提问作者Player_X_YT
相关产品推荐
相关产品推荐

