我怎么知道 X server 何时完成绘制?

How do I know when X server has completed the drawing?

假设我想一个接一个地画矩形。我如何知道 X 服务器何时完成绘制一个矩形?有没有办法从 X 服务器获得任何确认? 在下面的代码中,我在 500,500 处绘制了第一个矩形,并在公开处理程序中重新绘制了相同的矩形。之后,我在 1000,1000 处绘制了一个新矩形。问题是第一个矩形从未被绘制。我必须通过 Expose 事件处理程序多少次? 多少次才够?

#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <X11/Xutil.h>
#include <iostream>

using namespace std;

int main(){
    Display *disp;
    int screen;
    Window win;
    GC gc;

    disp = XOpenDisplay(NULL);
    screen = DefaultScreen(disp);

    XColor color;
    Colormap colormap;
    char black[] = "#ffffff";
    colormap = DefaultColormap(disp, 0);
    XParseColor(disp, colormap, black, &color);
    XAllocColor(disp, colormap, &color);

    win = XCreateSimpleWindow(disp, RootWindow(disp, screen), 10, 
        10, 1500, 1500, 0, WhitePixel(disp, screen), color.pixel);

    XSizeHints    my_hints = {0};
    my_hints.flags  = PPosition | PSize;        
    my_hints.x      = 10;                        
    my_hints.y      = 10;
    my_hints.width  = 1500;
    my_hints.height = 1500;
    XSetNormalHints(disp, win, &my_hints);   
    XMapWindow(disp, win);  

    gc = XCreateGC(disp, win, 0, 0);

    XColor color1;
    Colormap colormap1;
    char red[] = "#000000";
    colormap1 = DefaultColormap(disp, 0);
    XParseColor(disp, colormap1, red, &color1);
    XAllocColor(disp, colormap1, &color1);  

    XSetForeground(disp, gc, color1.pixel);  

    XSelectInput(disp, win, ExposureMask);

    int x1 = 500;
    int x2 = 500;

    XEvent event;
    int i = 0;
    while(true){
        XDrawRectangle(disp, win, gc, x1, x1, 500, 500);
        XNextEvent(disp, &event);
        if(event.xany.window == win){
            if(event.type == Expose){
                cout << i << endl;
                XDrawRectangle(disp, win, gc, x1, x2, 500, 500);
            }
        }
        i++;
        x1 += 500;
        x2 += 500;
    }

    XFreeGC(disp, gc);
    XDestroyWindow(disp, win);
    XCloseDisplay(disp);
    return 0;
}

输出

0
1
2
3

您似乎希望 XDrawRectangle() 生成 Expose 事件,但它永远不会发生,因为它不是这样工作的。 Expose 事件在 window 被映射、调整大小、移动或模糊 window 未映射时生成,但绘制到 window 时不会生成。实际上,您唯一感兴趣的 Expose 事件是由 XMapWindow() 生成的事件。收到第一个 Expose 事件后,您知道 window 已映射并可以绘制到。您还应该在映射 window 之前调用 XClearWindow() 以确保它不包含任何垃圾。有时您可能还想检查 if (event.xexpose.count == 0) 以仅处理队列中的最新事件,但在您的示例中没有必要这样做。然后只需绘制到 window 并可能在每次绘制后用 XFlush()XSync() 刷新缓冲区。这应该是你想要的(我稍微修改了坐标,但原理是一样的):

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
#include <chrono>
#include <iostream>
#include <thread>

using namespace std;

int main()
{
    Display* disp;
    int screen;
    Window win;
    GC gc;

    disp = XOpenDisplay(NULL);
    screen = DefaultScreen(disp);

    XColor color;
    Colormap colormap;
    char black[] = "#ffffff";
    colormap = DefaultColormap(disp, 0);
    XParseColor(disp, colormap, black, &color);
    XAllocColor(disp, colormap, &color);

    win = XCreateSimpleWindow(disp,
                              RootWindow(disp, screen),
                              10,
                              10,
                              1500,
                              1500,
                              0,
                              WhitePixel(disp, screen),
                              color.pixel);

    XSizeHints my_hints = {0};
    my_hints.flags = PPosition | PSize;
    my_hints.x = 10;
    my_hints.y = 10;
    my_hints.width = 1500;
    my_hints.height = 1500;
    XSetNormalHints(disp, win, &my_hints);

    gc = XCreateGC(disp, win, 0, 0);

    XColor color1;
    Colormap colormap1;
    char red[] = "#000000";
    colormap1 = DefaultColormap(disp, 0);
    XParseColor(disp, colormap1, red, &color1);
    XAllocColor(disp, colormap1, &color1);

    XSetForeground(disp, gc, color1.pixel);

    XSelectInput(disp, win, ExposureMask);

    int x = 10;
    int y = 10;

    int i = 0;
    XClearWindow(disp, win); // Clear window from possible garbage
    XMapWindow(disp, win);
    XEvent event;

    while (true)
    {
        // Wait for window to be mapped
        XNextEvent(disp, &event);
        if (event.xany.window == win && event.type == Expose)
        {
            break;
        }
    }

    for (int k = 0; k < 20; ++k)
    {
        std::cout << i << '\n';
        XDrawRectangle(disp, win, gc, x, y, 500, 500);
        XFlush(disp);
        ++i;
        x += 10;
        y += 10;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    XFreeGC(disp, gc);
    XDestroyWindow(disp, win);
    XCloseDisplay(disp);
    return 0;
}