故事
我制作了一个简单的绘图程序,使用X11/Xlib在Linux系统上显示图形。
它有一个菜单和一个编辑器用来绘图。
它需要知道窗口的大小,因此起初我使用了XGetWindowAttributes()
,但我发现它太慢了。
由于我每个帧都要执行这个函数,所以我的程序很慢。
然后,我尝试更少地运行函数,例如每4帧运行一次,但是这引起了闪烁。
我想要一个事件,可以在窗口大小改变时生成,以便仅在大小更改时运行该函数。
原始解决方案:
我发现了ResizeRequest
,它符合我的要求,并额外输出了窗口大小。
所以我不再需要XGetWindowAttributes()
。这大大加快了程序的速度。
当缩小窗口时,它运行得很好。
新问题:
我发现,当窗口调整为比原始大小更大时,每个在原始范围外绘制的对象都会像图像中所示一样被裁剪。
我们可以看到灰色边框本应该围绕整个窗口但被裁剪了全部变黑了。这对于右上角的颜色调色板尤其明显,它有16种颜色,但是4种颜色的一半被裁剪了,而8种颜色根本看不见。
我想做什么:
我想知道如何通过解决以下问题之一来解决裁剪问题:
- 如果必须在事件发送时批准调整大小,我该怎么做?
- 如果必须弄乱缓冲区,我该怎么做?
追踪:
我的程序有一个菜单,仍然使用XGetWindowAttributes()
,而编辑器(在图像中显示)使用ResizeRequest
事件,它比XGetWindowAttributes()
更快。预期的是,裁剪发生在编辑器中而不是菜单中。但我注意到,如果我先在菜单中改变窗口大小,然后在编辑器中改变窗口大小,即使设置了ResizeRedirectMask
,也可以通过而不裁剪。 我发现只有当ResizeRedirectMask
在XSelectInput()
函数中设置时才会发生裁剪。引用自Xlib编程手册第10.11.4页ResizeRequest事件:
任何其他客户端尝试更改大小都将被重定向。
我猜这意味着它不会影响窗口大小(我使用XGetWindowAttributes()
并行测试过是真的)。那么,我必须批准调整大小,但如何实现?引用自X11窗口调整大小的答案:
我会清除前缓冲区并等待调整大小完成。
我应该如何做到这一点?代码示例:我编写了一个简短的程序,随机在窗口外面和里面显示彩色方块。如果您复制它,该程序将运行。你可以更改
#if
语句来查看期望(false
)和实际(true
)之间的差异。#include <X11/Xlib.h>
#include <cstdint>
#include <unistd.h>
#if true //Make it true to set ResizeRedirectMask and false to disable.
#define Masks KeyPressMask|ResizeRedirectMask
#else
#define Masks KeyPressMask
#endif
int main(){
uint32_t RDM=0;
uint16_t i=0;
bool RunLoop=true;
Display* XDisplay=XOpenDisplay(0);//Create a display
Window XWindow=XCreateSimpleWindow(XDisplay,DefaultRootWindow(XDisplay),0,0,480,360,0,0,0);//Create a Window
XMapWindow(XDisplay,XWindow);//Make the Window visible
GC XGraphicCTX=XCreateGC(XDisplay,XWindow,0,0);//Create a Graphics Context
//v Wait for a MapNotify XEvent for next commands
XSelectInput(XDisplay,XWindow,StructureNotifyMask);
while(1){
XEvent e;
XNextEvent(XDisplay,&e);
if(e.type==MapNotify)break;
}
XSelectInput(XDisplay,XWindow,Masks); //Here is part of the magic error
while(RunLoop){
while(XPending(XDisplay)){//Get key changes
XEvent Event;
XNextEvent(XDisplay,&Event);
if(Event.type==KeyPress){
RunLoop=false;
}
}
for(i=0;i<4096;i++){
RDM=(RDM+1841)*9245; //Not perfect but good enough
XSetForeground(XDisplay,XGraphicCTX,RDM&0xFFFFFF);
RDM=(RDM+1841)*9245; //Not perfect but good enough
XFillRectangle(XDisplay,XWindow,XGraphicCTX,RDM&0xFFFF,(RDM>>16),64,64);
}
XFlush(XDisplay);
usleep(16667); //AHHH there is 666!
}
return 0;
}
以下是重新调整大小后的结果:
![新程序的意外结果](https://cdn.discordapp.com/attachments/571335694471921684/741670810103578674/unknown.png)
ResizeRedirectMask
,就会发生裁剪,即使不需要响应事件也会发生!
将#if
语句切换为false
,就能获得期望的行为,因为这将禁用ResizeRedirectMask
。
那个特定的程序不关心窗口的大小,我的大多数程序需要知道大小否则失败。一些细节: 我之前使用
XGetWindowAttributes()
来获取窗口的大小,速度有点慢,但在菜单中仍然使用它 因为它足够好用。因此,在编辑器中,我使用ResizeRequest
事件。
ConfigureNotify
事件似乎工作得很好,但是XGetWindowAttributes()
比它闪烁更多。
为什么会闪烁?
它会闪烁,因为在调整大小完成后会发送ConfigureNotify
,这意味着它通常在调整大小之后一帧发送。
这与X11窗口调整大小的抖动有关。
至少它不会浪费太多性能。
我正在寻找解决闪烁的方法,因为这可能更容易。需要注意的是,问题已经完全改变,但仍然与最初的问题有关。