glXCreateContext 神奇地映射 Window
glXCreateContext Magically Maps Window
创建显示后,我用 XCreateWindow
创建了一个 window。然后,如 here 所述,我调用 XMapWindow
,然后立即调用 XUnmapWindow
;这让 X 服务器知道 window 以便命令(例如 XMoveWindow
)不会静默失败。
此时 window 是不可见的,这是应该的。我可以停止执行,例如getchar
。绝对看不见。
然后我调用glXCreateContext
,然后出现window,就像我再次调用XMapWindow
一样!巫术!我之前和之后立即停止了执行,所以我 知道 它是 glXCreateContext
.
这毫无意义。我浏览了文档,但确实不可能发生这种情况。有什么猜测吗?
编辑:这是一个简单的例子:
//Compile with "g++ <filename>.cpp -std=c++11 -lX11 -lGL"
#include <cassert>
#include <cstdio>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <GL/glx.h>
static Display* display;
static void _x11_map_window(Window window) {
printf("Mapping window %lu; RETURN to continue\n",window); getchar();
XMapWindow(display, window);
printf("Mapped window! RETURN to continue\n"); getchar();
}
static void _x11_unmap_window(Window window) {
printf("Unmapping window %lu; RETURN to continue\n",window); getchar();
XUnmapWindow(display, window);
printf("Unmapped window! RETURN to continue\n"); getchar();
}
int main(int argc, char* argv[]) {
/* ##### MAKE DISPLAY ##### */
display = XOpenDisplay(nullptr);
/* ##### MAKE VISUAL INFO. ##### */
int attributes[] = { //can't be const b/c X11 doesn't like it. Not sure if that's intentional or just stupid.
GLX_RGBA, //apparently nothing comes after this?
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
//Ideally, the size would be 32 (or at least 24), but I have actually seen
// this size (on a modern OS even).
GLX_DEPTH_SIZE, 16,
GLX_DOUBLEBUFFER, True,
None
};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast" //Because of X11's cruft in "DefaultScreen".
XVisualInfo* visual_info = glXChooseVisual(display, DefaultScreen(display), attributes);
#pragma GCC diagnostic pop
assert(visual_info!=nullptr);
/* ##### MAKE WINDOW ##### */
Window parent = XDefaultRootWindow(display);
Colormap colormap = XCreateColormap(display, parent, visual_info->visual, AllocNone);
XSetWindowAttributes window_attributes_set;
window_attributes_set.colormap = colormap;
window_attributes_set.background_pixel = 0; //This and next b/c of
window_attributes_set.border_pixel = 0; //especially resulting in BadMatch error on Raspberry Pi. Also changes bit fields below in XCreateWindow.
window_attributes_set.event_mask = ExposureMask | KeyPressMask;
int position[2]={50,50}, dimensions[2]={128,128};
Window window = XCreateWindow(
display, parent,
position[0],position[1], static_cast<unsigned int>(dimensions[0]),static_cast<unsigned int>(dimensions[1]), //Note: the documentation must be wrong; this thing wants unsigned ints.
0u,
visual_info->depth,
InputOutput,
visual_info->visual,
//CWColormap|CWEventMask,
CWBackPixel|CWColormap|CWBorderPixel | CWEventMask,
&window_attributes_set
);
assert(window!=0);
printf("Created window %lu\n",window);
XStoreName(display, window, "[default title]");
XSelectInput(display, window,
//http://www.tronche.com/gui/x/xlib/events/mask.html#NoEventMask
//http://www.tronche.com/gui/x/xlib/events/processing-overview.html
ExposureMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask | //ButtonMotionMask |
//EnterWindowMask | LeaveWindowMask |
PointerMotionMask |
//KeymapStateMask | FocusChangeMask | ColormapChangeMask |
StructureNotifyMask //Resizing, etc.
//PropertyChangeMask
);
Atom wm_delete = XInternAtom(display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(display, window, &wm_delete, 1);
XMoveWindow(display, window, 100,100);
//As described here:
// "the X server doesn't have to know about a window before it is mapped for the first time". Hence,
// map the window and then unmap it so that the X server knows about it. This is important because some
// functions silently fail (e.g. XMoveWindow) when the X server is oblivious.
_x11_map_window(window);
_x11_unmap_window(window);
/* ##### MAKE RENDER CONTEXT ##### */
GLXContext render_context = glXCreateContext(display, visual_info, nullptr, True);
assert(render_context!=nullptr);
/* ##### MORE STUFF WOULD GO HERE ##### */
while (1);
return 0;
}
还演示了 XCreateWindow
或 XMoveWindow
在 map/unmap 之前设置 window 位置的失败。
调查这个问题很困难,但我已经解决了它,TL;DR 是:
- 这实际上与
glXCreateContext(...)
无关。这是关于 X 的某些实现中明显的计时错误。
- 这个问题是我根据错误信息编写的解决方法暴露出来的。我想做的事情应该通过不同的解决方法来完成。
基本问题的描述
创建 window 时,window 管理器将其包装在新的 window 中,只要未设置覆盖重定向(属性 .override_redirect
并标记 CWOverrideRedirect
window 创作)。这样它就可以执行诸如添加框架和按钮之类的操作。
不幸的是,window 经理可以(并且至少在映射 window 之前)以此为借口忽略 XMoveWindow(...)
等行为。这导致 that one should map and then unmap the window 使得 X 服务器 "knows about it".
这暴露了明显的错误。在有问题的系统上(VirtualBox 中的库存 Ubuntu),映射然后立即取消映射 window 会导致 window 保持映射状态。
我尝试了很多东西,比如在 map/unmap 调用周围放置 XFlush(...)
或 XSync(...)
调用(这也让我能够证明 glXCreateContext(...)
不是问题).然而,最终让它按预期工作的是增加睡眠。 0.1 秒的延迟使 window 出现并消失。 0.01 秒的延迟使 window 保持映射状态。弄清楚这一点非常令人沮丧(我有前面提到的 getchar()
s 和 printf(...)
s,这在调试时引入了足够的延迟,导致问题无法重现)。
以下(可能不是最小的)代码按编写的方式工作,但删除 nanosleep(...)
调用将导致问题:
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 200000000;
XFlush(_display);
nanosleep(&ts, nullptr);
XMapWindow(display, window);
XFlush(display);
nanosleep(&ts, nullptr);
XUnmapWindow(display, window);
XFlush(_display);
大概延迟允许赶上 map/unmap 事件或其他事情。我不确定这是一个完整的错误,但它肯定是一个可用性缺陷。 (如果这为您提供了足够的信息来解释这里发生了什么,请随时编辑此答案。)
但是,如前所述,此解决方法是基于误解! X 服务器已经知道新 window。就是明目张胆的不理你。要解决此问题,we can hint 向 windowing 系统表明它不应该如此粗鲁。由于这不依赖于 map/unmap,错误行为不再发生。
创建显示后,我用 XCreateWindow
创建了一个 window。然后,如 here 所述,我调用 XMapWindow
,然后立即调用 XUnmapWindow
;这让 X 服务器知道 window 以便命令(例如 XMoveWindow
)不会静默失败。
此时 window 是不可见的,这是应该的。我可以停止执行,例如getchar
。绝对看不见。
然后我调用glXCreateContext
,然后出现window,就像我再次调用XMapWindow
一样!巫术!我之前和之后立即停止了执行,所以我 知道 它是 glXCreateContext
.
这毫无意义。我浏览了文档,但确实不可能发生这种情况。有什么猜测吗?
编辑:这是一个简单的例子:
//Compile with "g++ <filename>.cpp -std=c++11 -lX11 -lGL"
#include <cassert>
#include <cstdio>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <GL/glx.h>
static Display* display;
static void _x11_map_window(Window window) {
printf("Mapping window %lu; RETURN to continue\n",window); getchar();
XMapWindow(display, window);
printf("Mapped window! RETURN to continue\n"); getchar();
}
static void _x11_unmap_window(Window window) {
printf("Unmapping window %lu; RETURN to continue\n",window); getchar();
XUnmapWindow(display, window);
printf("Unmapped window! RETURN to continue\n"); getchar();
}
int main(int argc, char* argv[]) {
/* ##### MAKE DISPLAY ##### */
display = XOpenDisplay(nullptr);
/* ##### MAKE VISUAL INFO. ##### */
int attributes[] = { //can't be const b/c X11 doesn't like it. Not sure if that's intentional or just stupid.
GLX_RGBA, //apparently nothing comes after this?
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
//Ideally, the size would be 32 (or at least 24), but I have actually seen
// this size (on a modern OS even).
GLX_DEPTH_SIZE, 16,
GLX_DOUBLEBUFFER, True,
None
};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast" //Because of X11's cruft in "DefaultScreen".
XVisualInfo* visual_info = glXChooseVisual(display, DefaultScreen(display), attributes);
#pragma GCC diagnostic pop
assert(visual_info!=nullptr);
/* ##### MAKE WINDOW ##### */
Window parent = XDefaultRootWindow(display);
Colormap colormap = XCreateColormap(display, parent, visual_info->visual, AllocNone);
XSetWindowAttributes window_attributes_set;
window_attributes_set.colormap = colormap;
window_attributes_set.background_pixel = 0; //This and next b/c of
window_attributes_set.border_pixel = 0; //especially resulting in BadMatch error on Raspberry Pi. Also changes bit fields below in XCreateWindow.
window_attributes_set.event_mask = ExposureMask | KeyPressMask;
int position[2]={50,50}, dimensions[2]={128,128};
Window window = XCreateWindow(
display, parent,
position[0],position[1], static_cast<unsigned int>(dimensions[0]),static_cast<unsigned int>(dimensions[1]), //Note: the documentation must be wrong; this thing wants unsigned ints.
0u,
visual_info->depth,
InputOutput,
visual_info->visual,
//CWColormap|CWEventMask,
CWBackPixel|CWColormap|CWBorderPixel | CWEventMask,
&window_attributes_set
);
assert(window!=0);
printf("Created window %lu\n",window);
XStoreName(display, window, "[default title]");
XSelectInput(display, window,
//http://www.tronche.com/gui/x/xlib/events/mask.html#NoEventMask
//http://www.tronche.com/gui/x/xlib/events/processing-overview.html
ExposureMask |
KeyPressMask | KeyReleaseMask |
ButtonPressMask | ButtonReleaseMask | //ButtonMotionMask |
//EnterWindowMask | LeaveWindowMask |
PointerMotionMask |
//KeymapStateMask | FocusChangeMask | ColormapChangeMask |
StructureNotifyMask //Resizing, etc.
//PropertyChangeMask
);
Atom wm_delete = XInternAtom(display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(display, window, &wm_delete, 1);
XMoveWindow(display, window, 100,100);
//As described here:
// "the X server doesn't have to know about a window before it is mapped for the first time". Hence,
// map the window and then unmap it so that the X server knows about it. This is important because some
// functions silently fail (e.g. XMoveWindow) when the X server is oblivious.
_x11_map_window(window);
_x11_unmap_window(window);
/* ##### MAKE RENDER CONTEXT ##### */
GLXContext render_context = glXCreateContext(display, visual_info, nullptr, True);
assert(render_context!=nullptr);
/* ##### MORE STUFF WOULD GO HERE ##### */
while (1);
return 0;
}
还演示了 XCreateWindow
或 XMoveWindow
在 map/unmap 之前设置 window 位置的失败。
调查这个问题很困难,但我已经解决了它,TL;DR 是:
- 这实际上与
glXCreateContext(...)
无关。这是关于 X 的某些实现中明显的计时错误。 - 这个问题是我根据错误信息编写的解决方法暴露出来的。我想做的事情应该通过不同的解决方法来完成。
基本问题的描述
创建 window 时,window 管理器将其包装在新的 window 中,只要未设置覆盖重定向(属性 .override_redirect
并标记 CWOverrideRedirect
window 创作)。这样它就可以执行诸如添加框架和按钮之类的操作。
不幸的是,window 经理可以(并且至少在映射 window 之前)以此为借口忽略 XMoveWindow(...)
等行为。这导致
这暴露了明显的错误。在有问题的系统上(VirtualBox 中的库存 Ubuntu),映射然后立即取消映射 window 会导致 window 保持映射状态。
我尝试了很多东西,比如在 map/unmap 调用周围放置 XFlush(...)
或 XSync(...)
调用(这也让我能够证明 glXCreateContext(...)
不是问题).然而,最终让它按预期工作的是增加睡眠。 0.1 秒的延迟使 window 出现并消失。 0.01 秒的延迟使 window 保持映射状态。弄清楚这一点非常令人沮丧(我有前面提到的 getchar()
s 和 printf(...)
s,这在调试时引入了足够的延迟,导致问题无法重现)。
以下(可能不是最小的)代码按编写的方式工作,但删除 nanosleep(...)
调用将导致问题:
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 200000000;
XFlush(_display);
nanosleep(&ts, nullptr);
XMapWindow(display, window);
XFlush(display);
nanosleep(&ts, nullptr);
XUnmapWindow(display, window);
XFlush(_display);
大概延迟允许赶上 map/unmap 事件或其他事情。我不确定这是一个完整的错误,但它肯定是一个可用性缺陷。 (如果这为您提供了足够的信息来解释这里发生了什么,请随时编辑此答案。)
但是,如前所述,此解决方法是基于误解! X 服务器已经知道新 window。就是明目张胆的不理你。要解决此问题,we can hint 向 windowing 系统表明它不应该如此粗鲁。由于这不依赖于 map/unmap,错误行为不再发生。