Cleanup window handling a little bit
Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
parent
75baf054e5
commit
319b283868
2 changed files with 118 additions and 71 deletions
|
@ -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);
|
||||||
|
|
186
window.c
186
window.c
|
@ -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;
|
||||||
|
|
||||||
this = calloc(1, sizeof(*this));
|
if (xlib_grab_ref())
|
||||||
if (!this)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (dpy) {
|
this = calloc(1, sizeof(*this));
|
||||||
++dpy_ref;
|
if (!this) {
|
||||||
} else {
|
xlib_drop_ref();
|
||||||
dpy = XOpenDisplay(0);
|
return NULL;
|
||||||
dpy_ref = 1;
|
|
||||||
|
|
||||||
if (!dpy) {
|
|
||||||
free(this);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
XSetErrorHandler(xlib_swallow_errors);
|
|
||||||
atom_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
|
|
||||||
|
|
||||||
xctx = XUniqueContext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get a valid GLX framebuffer configuration; create an X11 visual */
|
||||||
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;
|
||||||
|
|
||||||
switch (e.type) {
|
/* get a window event, and get the coresponding object pointer */
|
||||||
case ClientMessage:
|
XNextEvent(x11_globals.dpy, &e);
|
||||||
if (e.xclient.data.l[0] == (long)atom_wm_delete) {
|
XFindContext(x11_globals.dpy, e.xany.window,
|
||||||
XUnmapWindow(dpy, e.xany.window);
|
x11_globals.xctx, (XPointer *)&wnd);
|
||||||
--dpy_ref;
|
|
||||||
}
|
if (!wnd)
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
return (dpy_ref > 0);
|
return x11_globals.vis_wnd_count > 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue