Cleanup window handling a little bit

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
David Oberhollenzer 2022-07-17 02:28:06 +02:00
parent 75baf054e5
commit 319b283868
2 changed files with 118 additions and 71 deletions

View file

@ -22,6 +22,7 @@
typedef struct { typedef struct {
Window wnd; Window wnd;
GLXContext gl; GLXContext gl;
bool visible;
} window; } window;
window *window_create(unsigned int width, unsigned int height, window *window_create(unsigned int width, unsigned int height,
@ -35,7 +36,7 @@ void window_set_vsync(window *wnd, int enable);
void window_destroy(window *wnd); void window_destroy(window *wnd);
int window_handle_events(void); bool window_handle_events(void);
GLuint shader_program_load(const char *fsh); GLuint shader_program_load(const char *fsh);

180
window.c
View file

@ -18,13 +18,16 @@ typedef GLXContext (*CREATECONTEXTATTRIBSPROC)(Display *, GLXFBConfig,
typedef void (*SWAPINTERVALEXTPROC)(Display *, GLXDrawable, int); typedef void (*SWAPINTERVALEXTPROC)(Display *, GLXDrawable, int);
static int glversions[][2] = { {4,5}, {4,4}, {4,3}, {4,2}, {4,1}, {4,0}, static int glversions[][2] = { {4,6}, {4,5}, {4,4}, {4,3}, {4,2}, {4,1}, {4,0},
{3,3}, {3,2}, {3,1}, {3,0} }; {3,3}, {3,2}, {3,1}, {3,0} };
static Display *dpy; static struct {
static int dpy_ref = 0; Display *dpy;
static Atom atom_wm_delete; Atom atom_wm_delete;
static XContext xctx; XContext xctx;
size_t vis_wnd_count;
size_t refcount;
} x11_globals;
static int xlib_swallow_errors(Display *display, XErrorEvent *event) static int xlib_swallow_errors(Display *display, XErrorEvent *event)
{ {
@ -32,6 +35,36 @@ static int xlib_swallow_errors(Display *display, XErrorEvent *event)
return 0; return 0;
} }
static int xlib_grab_ref(void)
{
if (x11_globals.refcount == 0) {
x11_globals.dpy = XOpenDisplay(0);
if (!x11_globals.dpy)
return -1;
XSetErrorHandler(xlib_swallow_errors);
x11_globals.atom_wm_delete = XInternAtom(x11_globals.dpy,
"WM_DELETE_WINDOW",
True);
x11_globals.xctx = XUniqueContext();
}
x11_globals.refcount += 1;
return 0;
}
static void xlib_drop_ref(void)
{
x11_globals.refcount -= 1;
if (x11_globals.refcount == 0) {
XCloseDisplay(x11_globals.dpy);
memset(&x11_globals, 0, sizeof(x11_globals));
}
}
/****************************************************************************/ /****************************************************************************/
window *window_create(unsigned int width, unsigned int height, window *window_create(unsigned int width, unsigned int height,
@ -46,38 +79,26 @@ window *window_create(unsigned int width, unsigned int height,
XVisualInfo *vi; XVisualInfo *vi;
window *this; window *this;
if (xlib_grab_ref())
return NULL;
this = calloc(1, sizeof(*this)); this = calloc(1, sizeof(*this));
if (!this) if (!this) {
return NULL; xlib_drop_ref();
if (dpy) {
++dpy_ref;
} else {
dpy = XOpenDisplay(0);
dpy_ref = 1;
if (!dpy) {
free(this);
return NULL; return NULL;
} }
XSetErrorHandler(xlib_swallow_errors); /* get a valid GLX framebuffer configuration; create an X11 visual */
atom_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
xctx = XUniqueContext();
}
attr[ 0] = GLX_BUFFER_SIZE; attr[ 1] = 32; attr[ 0] = GLX_BUFFER_SIZE; attr[ 1] = 32;
attr[ 2] = GLX_RED_SIZE; attr[ 3] = 8; attr[ 2] = GLX_RED_SIZE; attr[ 3] = 8;
attr[ 4] = GLX_GREEN_SIZE; attr[ 5] = 8; attr[ 4] = GLX_GREEN_SIZE; attr[ 5] = 8;
attr[ 6] = GLX_BLUE_SIZE; attr[ 7] = 8; attr[ 6] = GLX_BLUE_SIZE; attr[ 7] = 8;
attr[ 8] = GLX_ALPHA_SIZE; attr[ 9] = 8; attr[ 8] = GLX_ALPHA_SIZE; attr[ 9] = 8;
attr[10] = GLX_DEPTH_SIZE; attr[11] = 24; attr[10] = GLX_DOUBLEBUFFER; attr[11] = True;
attr[12] = GLX_STENCIL_SIZE; attr[13] = 8; attr[12] = None;
attr[14] = GLX_DOUBLEBUFFER; attr[15] = True;
attr[16] = None;
fbl = glXChooseFBConfig(dpy, DefaultScreen(dpy), attr, &fbcount); fbl = glXChooseFBConfig(x11_globals.dpy, DefaultScreen(x11_globals.dpy),
attr, &fbcount);
if (!fbl || !fbcount) { if (!fbl || !fbcount) {
window_destroy(this); window_destroy(this);
@ -85,7 +106,7 @@ window *window_create(unsigned int width, unsigned int height,
} }
fbc = fbl[0]; fbc = fbl[0];
vi = glXGetVisualFromFBConfig(dpy, fbl[0]); vi = glXGetVisualFromFBConfig(x11_globals.dpy, fbl[0]);
XFree(fbl); XFree(fbl);
if (!vi) { if (!vi) {
@ -93,9 +114,10 @@ window *window_create(unsigned int width, unsigned int height,
return NULL; return NULL;
} }
swa.colormap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), /* Get a colormap ID from the visual. Needed for legacy reasons. */
swa.colormap = XCreateColormap(x11_globals.dpy,
RootWindow(x11_globals.dpy, vi->screen),
vi->visual, AllocNone); vi->visual, AllocNone);
if (!swa.colormap) { if (!swa.colormap) {
XFree(vi); XFree(vi);
window_destroy(this); window_destroy(this);
@ -104,9 +126,11 @@ window *window_create(unsigned int width, unsigned int height,
swa.border_pixel = 0; swa.border_pixel = 0;
this->wnd = XCreateWindow(dpy, RootWindow(dpy,vi->screen), 0, 0, /* create a window, and get rid of the X11 visual */
this->wnd = XCreateWindow(x11_globals.dpy,
RootWindow(x11_globals.dpy, vi->screen), 0, 0,
width, height, 0, vi->depth, InputOutput, width, height, 0, vi->depth, InputOutput,
vi->visual, CWBorderPixel|CWColormap, &swa); vi->visual, CWBorderPixel | CWColormap, &swa);
XFree(vi); XFree(vi);
if (!this->wnd) { if (!this->wnd) {
@ -114,6 +138,7 @@ window *window_create(unsigned int width, unsigned int height,
return NULL; return NULL;
} }
/* try to create a GLX context, the new modern way */
CreateContextAttribs = (CREATECONTEXTATTRIBSPROC) CreateContextAttribs = (CREATECONTEXTATTRIBSPROC)
glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
@ -133,14 +158,15 @@ window *window_create(unsigned int width, unsigned int height,
attr[1] = glversions[i][0]; attr[1] = glversions[i][0];
attr[3] = glversions[i][1]; attr[3] = glversions[i][1];
this->gl = CreateContextAttribs(dpy, fbc, 0, this->gl = CreateContextAttribs(x11_globals.dpy, fbc, 0,
True, attr); True, attr);
} }
} }
/* fallback: create a GLX context, the legacy way */
if (!this->gl) { if (!this->gl) {
this->gl = glXCreateNewContext(dpy, fbc, GLX_RGBA_TYPE, this->gl = glXCreateNewContext(x11_globals.dpy, fbc,
0, GL_TRUE); GLX_RGBA_TYPE, 0, GL_TRUE);
} }
if (!this->gl) { if (!this->gl) {
@ -148,38 +174,51 @@ window *window_create(unsigned int width, unsigned int height,
return NULL; return NULL;
} }
/* make the window non-resizable */
hints.flags = PSize | PMinSize | PMaxSize; hints.flags = PSize | PMinSize | PMaxSize;
hints.min_width = hints.max_width = hints.base_width = width; hints.min_width = hints.max_width = hints.base_width = width;
hints.min_height = hints.max_height = hints.base_height = height; hints.min_height = hints.max_height = hints.base_height = height;
XSetWMNormalHints(dpy, this->wnd, &hints); XSetWMNormalHints(x11_globals.dpy, this->wnd, &hints);
XSelectInput(dpy, this->wnd, StructureNotifyMask | /* X11 should tell us about pretty much everything */
XSelectInput(x11_globals.dpy, this->wnd, StructureNotifyMask |
KeyPressMask | KeyReleaseMask | KeyPressMask | KeyReleaseMask |
PointerMotionMask | PropertyChangeMask | PointerMotionMask | PropertyChangeMask |
ButtonPressMask | ButtonReleaseMask); ButtonPressMask | ButtonReleaseMask);
XSetWMProtocols(dpy, this->wnd, &atom_wm_delete, 1); XSetWMProtocols(x11_globals.dpy, this->wnd,
XFlush(dpy); &x11_globals.atom_wm_delete, 1);
XStoreName(dpy, this->wnd, caption); /* store the object pointer with the window,
XMapWindow(dpy, this->wnd); so we can get it back later */
XSaveContext(dpy, this->wnd, xctx, (XPointer)this); XSaveContext(x11_globals.dpy, this->wnd,
XFlush(dpy); x11_globals.xctx, (XPointer)this);
/* set the caption and make the window visible */
XStoreName(x11_globals.dpy, this->wnd, caption);
XMapWindow(x11_globals.dpy, this->wnd);
this->visible = true;
x11_globals.vis_wnd_count += 1;
/* flush any pending requests to the X Server */
XFlush(x11_globals.dpy);
return this; return this;
} }
void window_make_current(window *this) void window_make_current(window *this)
{ {
if (this) { if (this) {
glXMakeContextCurrent(dpy, this->wnd, this->wnd, this->gl); glXMakeContextCurrent(x11_globals.dpy, this->wnd,
this->wnd, this->gl);
} else { } else {
glXMakeContextCurrent(dpy, 0, 0, 0); glXMakeContextCurrent(x11_globals.dpy, 0, 0, 0);
} }
} }
void window_swap_buffers(window *this) void window_swap_buffers(window *this)
{ {
glXSwapBuffers(dpy, this->wnd); glXSwapBuffers(x11_globals.dpy, this->wnd);
} }
void window_set_vsync(window *this, int enable) void window_set_vsync(window *this, int enable)
@ -190,48 +229,55 @@ void window_set_vsync(window *this, int enable)
glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT"); glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT");
if (SwapIntervalEXT) if (SwapIntervalEXT)
SwapIntervalEXT(dpy, this->wnd, enable ? 1 : 0); SwapIntervalEXT(x11_globals.dpy, this->wnd, enable ? 1 : 0);
} }
void window_destroy(window *this) void window_destroy(window *this)
{ {
if (this->gl) { if (this->gl) {
glXMakeContextCurrent(dpy, 0, 0, 0); glXMakeContextCurrent(x11_globals.dpy, 0, 0, 0);
glXDestroyContext(dpy, this->gl); glXDestroyContext(x11_globals.dpy, this->gl);
} }
if (this->wnd) { if (this->wnd) {
XDeleteContext(dpy, this->wnd, xctx); XDeleteContext(x11_globals.dpy, this->wnd, x11_globals.xctx);
XDestroyWindow(dpy, this->wnd); XDestroyWindow(x11_globals.dpy, this->wnd);
} }
if ((--dpy_ref) <= 0) { if (this->visible)
XCloseDisplay(dpy); x11_globals.vis_wnd_count -= 1;
dpy_ref = 0;
dpy = NULL;
}
xlib_drop_ref();
free(this); free(this);
} }
int window_handle_events(void) bool window_handle_events(void)
{ {
window *this; window *wnd;
XEvent e; XEvent e;
if (XPending(dpy)) { /* if there are no events, return to the drawing loop ASAP */
XNextEvent(dpy, &e); if (!XPending(x11_globals.dpy))
XFindContext(dpy, e.xany.window, xctx, (XPointer *)&this); goto out;
/* get a window event, and get the coresponding object pointer */
XNextEvent(x11_globals.dpy, &e);
XFindContext(x11_globals.dpy, e.xany.window,
x11_globals.xctx, (XPointer *)&wnd);
if (!wnd)
goto out;
/* process event */
switch (e.type) { switch (e.type) {
case ClientMessage: case ClientMessage:
if (e.xclient.data.l[0] == (long)atom_wm_delete) { if (e.xclient.data.l[0] == (long)x11_globals.atom_wm_delete) {
XUnmapWindow(dpy, e.xany.window); XUnmapWindow(x11_globals.dpy, e.xany.window);
--dpy_ref; wnd->visible = false;
x11_globals.vis_wnd_count -= 1;
} }
break; break;
} }
} out:
return x11_globals.vis_wnd_count > 0;
return (dpy_ref > 0);
} }