diff --git a/shadermeh.h b/shadermeh.h index d568af4..66e1f40 100644 --- a/shadermeh.h +++ b/shadermeh.h @@ -22,6 +22,7 @@ typedef struct { Window wnd; GLXContext gl; + bool visible; } window; 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); -int window_handle_events(void); +bool window_handle_events(void); GLuint shader_program_load(const char *fsh); diff --git a/window.c b/window.c index b481a79..f43e5c4 100644 --- a/window.c +++ b/window.c @@ -18,13 +18,16 @@ typedef GLXContext (*CREATECONTEXTATTRIBSPROC)(Display *, GLXFBConfig, 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} }; -static Display *dpy; -static int dpy_ref = 0; -static Atom atom_wm_delete; -static XContext xctx; +static struct { + Display *dpy; + Atom atom_wm_delete; + XContext xctx; + size_t vis_wnd_count; + size_t refcount; +} x11_globals; static int xlib_swallow_errors(Display *display, XErrorEvent *event) { @@ -32,6 +35,36 @@ static int xlib_swallow_errors(Display *display, XErrorEvent *event) 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, @@ -46,38 +79,26 @@ window *window_create(unsigned int width, unsigned int height, XVisualInfo *vi; window *this; - this = calloc(1, sizeof(*this)); - if (!this) + if (xlib_grab_ref()) return NULL; - if (dpy) { - ++dpy_ref; - } else { - dpy = XOpenDisplay(0); - dpy_ref = 1; - - if (!dpy) { - free(this); - return NULL; - } - - XSetErrorHandler(xlib_swallow_errors); - atom_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", True); - - xctx = XUniqueContext(); + this = calloc(1, sizeof(*this)); + if (!this) { + xlib_drop_ref(); + return NULL; } + /* get a valid GLX framebuffer configuration; create an X11 visual */ attr[ 0] = GLX_BUFFER_SIZE; attr[ 1] = 32; attr[ 2] = GLX_RED_SIZE; attr[ 3] = 8; attr[ 4] = GLX_GREEN_SIZE; attr[ 5] = 8; attr[ 6] = GLX_BLUE_SIZE; attr[ 7] = 8; attr[ 8] = GLX_ALPHA_SIZE; attr[ 9] = 8; - attr[10] = GLX_DEPTH_SIZE; attr[11] = 24; - attr[12] = GLX_STENCIL_SIZE; attr[13] = 8; - attr[14] = GLX_DOUBLEBUFFER; attr[15] = True; - attr[16] = None; + attr[10] = GLX_DOUBLEBUFFER; attr[11] = True; + attr[12] = None; - fbl = glXChooseFBConfig(dpy, DefaultScreen(dpy), attr, &fbcount); + fbl = glXChooseFBConfig(x11_globals.dpy, DefaultScreen(x11_globals.dpy), + attr, &fbcount); if (!fbl || !fbcount) { window_destroy(this); @@ -85,7 +106,7 @@ window *window_create(unsigned int width, unsigned int height, } fbc = fbl[0]; - vi = glXGetVisualFromFBConfig(dpy, fbl[0]); + vi = glXGetVisualFromFBConfig(x11_globals.dpy, fbl[0]); XFree(fbl); if (!vi) { @@ -93,9 +114,10 @@ window *window_create(unsigned int width, unsigned int height, 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); - if (!swa.colormap) { XFree(vi); window_destroy(this); @@ -104,9 +126,11 @@ window *window_create(unsigned int width, unsigned int height, 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, - vi->visual, CWBorderPixel|CWColormap, &swa); + vi->visual, CWBorderPixel | CWColormap, &swa); XFree(vi); if (!this->wnd) { @@ -114,6 +138,7 @@ window *window_create(unsigned int width, unsigned int height, return NULL; } + /* try to create a GLX context, the new modern way */ CreateContextAttribs = (CREATECONTEXTATTRIBSPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); @@ -133,14 +158,15 @@ window *window_create(unsigned int width, unsigned int height, attr[1] = glversions[i][0]; attr[3] = glversions[i][1]; - this->gl = CreateContextAttribs(dpy, fbc, 0, + this->gl = CreateContextAttribs(x11_globals.dpy, fbc, 0, True, attr); } } + /* fallback: create a GLX context, the legacy way */ if (!this->gl) { - this->gl = glXCreateNewContext(dpy, fbc, GLX_RGBA_TYPE, - 0, GL_TRUE); + this->gl = glXCreateNewContext(x11_globals.dpy, fbc, + GLX_RGBA_TYPE, 0, GL_TRUE); } if (!this->gl) { @@ -148,38 +174,51 @@ window *window_create(unsigned int width, unsigned int height, return NULL; } + /* make the window non-resizable */ hints.flags = PSize | PMinSize | PMaxSize; hints.min_width = hints.max_width = hints.base_width = width; 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 | PointerMotionMask | PropertyChangeMask | ButtonPressMask | ButtonReleaseMask); - XSetWMProtocols(dpy, this->wnd, &atom_wm_delete, 1); - XFlush(dpy); + XSetWMProtocols(x11_globals.dpy, this->wnd, + &x11_globals.atom_wm_delete, 1); - XStoreName(dpy, this->wnd, caption); - XMapWindow(dpy, this->wnd); - XSaveContext(dpy, this->wnd, xctx, (XPointer)this); - XFlush(dpy); + /* store the object pointer with the window, + so we can get it back later */ + XSaveContext(x11_globals.dpy, this->wnd, + 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; } void window_make_current(window *this) { if (this) { - glXMakeContextCurrent(dpy, this->wnd, this->wnd, this->gl); + glXMakeContextCurrent(x11_globals.dpy, this->wnd, + this->wnd, this->gl); } else { - glXMakeContextCurrent(dpy, 0, 0, 0); + glXMakeContextCurrent(x11_globals.dpy, 0, 0, 0); } } void window_swap_buffers(window *this) { - glXSwapBuffers(dpy, this->wnd); + glXSwapBuffers(x11_globals.dpy, this->wnd); } void window_set_vsync(window *this, int enable) @@ -190,48 +229,55 @@ void window_set_vsync(window *this, int enable) glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT"); if (SwapIntervalEXT) - SwapIntervalEXT(dpy, this->wnd, enable ? 1 : 0); + SwapIntervalEXT(x11_globals.dpy, this->wnd, enable ? 1 : 0); } void window_destroy(window *this) { if (this->gl) { - glXMakeContextCurrent(dpy, 0, 0, 0); - glXDestroyContext(dpy, this->gl); + glXMakeContextCurrent(x11_globals.dpy, 0, 0, 0); + glXDestroyContext(x11_globals.dpy, this->gl); } if (this->wnd) { - XDeleteContext(dpy, this->wnd, xctx); - XDestroyWindow(dpy, this->wnd); + XDeleteContext(x11_globals.dpy, this->wnd, x11_globals.xctx); + XDestroyWindow(x11_globals.dpy, this->wnd); } - if ((--dpy_ref) <= 0) { - XCloseDisplay(dpy); - dpy_ref = 0; - dpy = NULL; - } + if (this->visible) + x11_globals.vis_wnd_count -= 1; + xlib_drop_ref(); free(this); } -int window_handle_events(void) +bool window_handle_events(void) { - window *this; + window *wnd; XEvent e; - if (XPending(dpy)) { - XNextEvent(dpy, &e); - XFindContext(dpy, e.xany.window, xctx, (XPointer *)&this); + /* if there are no events, return to the drawing loop ASAP */ + if (!XPending(x11_globals.dpy)) + goto out; - switch (e.type) { - case ClientMessage: - if (e.xclient.data.l[0] == (long)atom_wm_delete) { - XUnmapWindow(dpy, e.xany.window); - --dpy_ref; - } - break; + /* 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) { + case ClientMessage: + if (e.xclient.data.l[0] == (long)x11_globals.atom_wm_delete) { + XUnmapWindow(x11_globals.dpy, e.xany.window); + wnd->visible = false; + x11_globals.vis_wnd_count -= 1; } + break; } - - return (dpy_ref > 0); +out: + return x11_globals.vis_wnd_count > 0; }