我从网络上获得了这个XWindows“你好,世界”。我的行为在更复杂的程序中无法理解,但是这里的简单程序也会显示它:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
Display *d;
Window w;
XEvent e;
const char *msg = "Hello, World!";
int s;
int x;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask | KeyPressMask);
XMapWindow(d, w);
XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));
//XFlush(d);
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) {
// XDrawString(d, w, DefaultGC(d, s), 10, 50, msg, strlen(msg));
}
if (e.type == KeyPress) break;
}
XCloseDisplay(d);
return 0;
}
因此,第一个XDrawString()调用实际上不会写入窗口,但是在暴露事件中取消对它的注释。我发现这种行为令人困惑。在第一个XDrawString()时,显示,屏幕和窗口都已设置。但这并没有画出来,而出现在事件处理程序中的那画了。我还尝试了XFlush()进入队列,这没什么区别。
此效果与我正在处理的(许多)更复杂的代码有关,该代码将字符放置在屏幕上。我将它们放置在支持屏幕的像素图中的所需位置,然后对真实屏幕进行相同的绘制以使其保持同步。暴露处理程序会将像素图复制到屏幕上,以对其进行更新,但是暴露事件要等到以后才会发生,此外,将整个像素图复制回屏幕比放置单个字符要昂贵。
我怀疑我需要跟随绘制到缓冲区的操作,将字符下的矩形标记为无效(这将是MS Windows方式),并让事件处理程序来处理它,但是我想了解什么在X11内继续进行以实现这一目标。
答案 0 :(得分:5)
X服务器不需要自己提供后备存储,因此在需要绘制窗口时会发送Expose
事件。这与其他环境中的功能相同。一开始您可以进行绘制,但是X服务器可能尚未正确设置窗口,稍后会在准备将内容绘制到屏幕上时向您报告。绘图是直接显示在屏幕上的,而不是后备缓冲区,因此,每次将窗口置于前面时,都必须进行绘制。
关于您自己请求重画,您可以使用XSendEvent
自己发送一个Expose
事件,然后X会将其中继到您的事件循环中。因此,它实际上与Windows完全相同。
答案 1 :(得分:2)
请记住您处于客户端/服务器环境中。意味着在您的代码请求与其实现之间存在延迟。从调用返回到XMapWindow
并不意味着您的窗口仅准备好接受XServer必须接受的图形,因此您需要等待第一个Expose
事件进行绘制,这意味着XServer完成了这项工作。这是强制性的,因为Expose
是表示该窗口(或其一部分)在屏幕上可见的事实的事件。
永远记住:UI是事件驱动的。
答案 2 :(得分:1)
只需在Sami的良好答复中加点。
深思熟虑,当使用多个可以相互重叠的窗口(不计算很多其他事情)时,X11的工作方式是唯一正确的方法。这就是为什么许多框架(X11,Windows,gtk,qt ...)都使用这种机制(无效并公开|绘制|绘制)。
所有有关优化图形的事情。您不应盲目绘画,因为您的绘画可能会被丢弃;可能是您的窗口不可见,或者是用户在不重绘其内容的情况下移动了该窗口,或者其他任何原因。
相反,X11(或其他工具包/框架)非常了解何时必须显示数据,并且可以显示 。在这种情况下,X11告诉您重绘(通过暴露事件)。当X11要求重新粉刷时,您的绘图操作将不会被丢弃。
另一方面,应该准备一个好的X11程序,以便在需要时进行重新绘制:如果用户在周围拖动窗口或更改虚拟桌面,对应用程序进行图标化等操作,它将经常发生。
合并两个方面:程序必须在需要时绘制,并且在创建其第一个窗口后将要求确定。因此,仅在暴露事件中绘制是完美的。
可以通过支持存储来改进此基本机制:X11可以缓存图形数据,并在以后使用它们,而不必一次又一次地向程序询问相同的数据。有时后备存储速度更快,但有时只会浪费内存。
答案 3 :(得分:1)
好吧,我昨晚想到了这一点,并意识到一个叫做“ lines”的古老而经典的程序说明了我正在谈论的问题:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int randr(int upper)
{
return rand() % (upper+1);
}
int main(void) {
Display *d;
Window w;
XEvent e;
const char *msg = "Hello, World!";
int s;
XEvent se;
d = XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(d);
w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 400, 400, 1,
BlackPixel(d, s), WhitePixel(d, s));
XSelectInput(d, w, ExposureMask | KeyPressMask);
XMapWindow(d, w);
se.type = Expose;
se.xexpose.type = Expose;
se.xexpose.serial = 0;
se.xexpose.send_event = 1;
se.xexpose.window = w;
se.xexpose.x = 0;
se.xexpose.y = 0;
se.xexpose.width = 400;
se.xexpose.height = 400;
se.xexpose.count = 0;
while (1) {
XNextEvent(d, &e);
if (e.type == Expose) {
XSetForeground(d, DefaultGC(d, s), randr(255)<<16 | randr(255)<<8 | randr(255));
XDrawLine(d, w, DefaultGC(d, s), randr(400), randr(400), randr(400), randr(400));
XSendEvent(d, w, 0, ExposureMask, &se);
}
if (e.type == KeyPress) break;
}
XCloseDisplay(d);
return 0;
}
Lines是一个古老的程序,可以追溯到1970年代后期的Cromemco Dazzler。它只是画随机线,而且速度很快。它不需要输入,它只是画图,也可以用作性能测试。
正如您在这里看到的,我通过在暴露事件本身中发布另一个事件来欺骗X11连续处理暴露事件。在(例如)MS窗口中,这不是必需的。您可以随时在窗口上绘制,并且不读取事件队列不是很好,但是程序仍然可以工作。
该程序可以在X11中运行,并且(在我的机器上)运行如此之快,以至于基本上可以立即填满窗口。
现在,如果我想使该演示尽快进行,则需要考虑该程序中的主要时间,据我所知,这是需要持续发布新的公开事件。