【问题标题】:How do I know when X server has completed the drawing?我如何知道 X 服务器何时完成了绘图?
【发布时间】:2021-11-18 14:07:34
【问题描述】:

假设我想一个接一个地绘制矩形。我如何知道 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

【问题讨论】:

  • 渲染是独立和异步的。如果一个窗口被揭开,它会在某个时候被绘制出来。这与窗口中运行的进程无关。获取所有事件是没有意义的,因为这包括太多其他的东西。相反,请定期使用计时器绘制矩形。

标签: c++ graphics xlib


【解决方案1】:

您似乎期望XDrawRectangle() 生成Expose 事件,但它永远不会发生,因为它不是这样工作的。 Expose 事件会在窗口被映射、调整大小、移动或未映射模糊窗口时生成,但在绘制到窗口时不会生成。实际上,您感兴趣的唯一Expose 事件是XMapWindow() 生成的事件。在收到第一个Expose 事件后,您知道该窗口已映射并且可以绘制到。您还应该在映射窗口之前调用XClearWindow(),以确保它不包含任何垃圾。有时您可能还想检查if (event.xexpose.count == 0) 以仅处理队列中的最新事件,但在您的示例中它不应该是必需的。然后只需在窗口中绘制,并可能在每次绘制后用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;
}

【讨论】:

  • 我运行代码时将初始矩形坐标设置为 (x=0,y=0) 并注意到第一个矩形没有绘制。您的代码生成 4 个公开事件。当第二个暴露事件从事件队列复制到 XEvent 结构时,绘制 (x=0,y=0) 处的矩形。这可以通过“等待窗口被映射”循环中的计数器来完成。无需经历所有这 4 个暴露事件。我很想知道你是否得到相同的结果。无论哪种方式,我都接受你的回答。
  • 在示例中,您是否只更改了坐标?我没有看到您描述的问题,我只看到一个 Expose 事件,并且始终为我绘制第一个矩形 (0, 0)。根据古老的“Xlib 编程手册l”: 顶层窗口的初始映射是一种特殊情况,因为窗口的可见性可能会被窗口管理器延迟。由于复杂的原因,应用程序必须等待第一个 Expose 事件,然后才能假定其窗口可见并绘制到其中。
  • 我不是 X 专家,但我猜可能是你的 DE 对窗口执行了一些额外的处理,生成了额外的 Expose 事件。我正在使用一个没有全功能 DE 的简单窗口管理器,上面的示例对我来说可以正常工作。
  • 顺便说一句,似乎 首先公开事件 他们实际上是指计数 == 0 的那些,这解释了为什么您会观察到这种奇怪的行为。换句话说,总是等待event.xexpose.count == 0
猜你喜欢
  • 2015-03-11
  • 1970-01-01
  • 1970-01-01
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多