#include "shadermeh.h" #ifndef GLX_ARB_create_context #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 #define GLX_CONTEXT_FLAGS_ARB 0x2094 #endif #ifndef GLX_ARB_create_context_profile #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #endif typedef GLXContext (*CREATECONTEXTATTRIBSPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *); typedef void (*SWAPINTERVALEXTPROC)(Display *, GLXDrawable, int); static int glversions[][2] = { {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 int xlib_swallow_errors(Display *display, XErrorEvent *event) { (void)display; (void)event; return 0; } /****************************************************************************/ window *window_create(unsigned int width, unsigned int height, const char *caption) { CREATECONTEXTATTRIBSPROC CreateContextAttribs; XSetWindowAttributes swa; int fbcount, attr[20]; GLXFBConfig fbc, *fbl; unsigned int i = 0; XSizeHints hints; XVisualInfo *vi; window *this; this = calloc(1, sizeof(*this)); if (!this) 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(); } 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; fbl = glXChooseFBConfig(dpy, DefaultScreen(dpy), attr, &fbcount); if (!fbl || !fbcount) { window_destroy(this); return NULL; } fbc = fbl[0]; vi = glXGetVisualFromFBConfig(dpy, fbl[0]); XFree(fbl); if (!vi) { window_destroy(this); return NULL; } swa.colormap = XCreateColormap(dpy, RootWindow(dpy,vi->screen), vi->visual, AllocNone); if (!swa.colormap) { XFree(vi); window_destroy(this); return NULL; } swa.border_pixel = 0; this->wnd = XCreateWindow(dpy, RootWindow(dpy,vi->screen), 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap, &swa); XFree(vi); if (!this->wnd) { window_destroy(this); return NULL; } CreateContextAttribs = (CREATECONTEXTATTRIBSPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); if (CreateContextAttribs) { attr[0] = GLX_CONTEXT_MAJOR_VERSION_ARB; attr[1] = 0; attr[2] = GLX_CONTEXT_MINOR_VERSION_ARB; attr[3] = 0; attr[4] = GLX_CONTEXT_PROFILE_MASK_ARB; attr[5] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; attr[6] = GLX_CONTEXT_FLAGS_ARB; attr[7] = GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; attr[8] = None; for (i = 0; !this->gl && i < sizeof(glversions) / sizeof(glversions[0]); ++i) { attr[1] = glversions[i][0]; attr[3] = glversions[i][1]; this->gl = CreateContextAttribs(dpy, fbc, 0, True, attr); } } if (!this->gl) { this->gl = glXCreateNewContext(dpy, fbc, GLX_RGBA_TYPE, 0, GL_TRUE); } if (!this->gl) { window_destroy(this); return NULL; } 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); XSelectInput(dpy, this->wnd, StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | PropertyChangeMask | ButtonPressMask | ButtonReleaseMask); XSetWMProtocols(dpy, this->wnd, &atom_wm_delete, 1); XFlush(dpy); XStoreName(dpy, this->wnd, caption); XMapWindow(dpy, this->wnd); XSaveContext(dpy, this->wnd, xctx, (XPointer)this); XFlush(dpy); return this; } void window_make_current(window *this) { if (this) { glXMakeContextCurrent(dpy, this->wnd, this->wnd, this->gl); } else { glXMakeContextCurrent(dpy, 0, 0, 0); } } void window_swap_buffers(window *this) { glXSwapBuffers(dpy, this->wnd); } void window_set_vsync(window *this, int enable) { SWAPINTERVALEXTPROC SwapIntervalEXT; SwapIntervalEXT = (SWAPINTERVALEXTPROC) glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT"); if (SwapIntervalEXT) SwapIntervalEXT(dpy, this->wnd, enable ? 1 : 0); } void window_destroy(window *this) { if (this->gl) { glXMakeContextCurrent(dpy, 0, 0, 0); glXDestroyContext(dpy, this->gl); } if (this->wnd) { XDeleteContext(dpy, this->wnd, xctx); XDestroyWindow(dpy, this->wnd); } if ((--dpy_ref) <= 0) { XCloseDisplay(dpy); dpy_ref = 0; dpy = NULL; } free(this); } int window_handle_events(void) { window *this; XEvent e; if (XPending(dpy)) { XNextEvent(dpy, &e); XFindContext(dpy, e.xany.window, xctx, (XPointer *)&this); switch (e.type) { case ClientMessage: if (e.xclient.data.l[0] == (long)atom_wm_delete) { XUnmapWindow(dpy, e.xany.window); --dpy_ref; } break; } } return (dpy_ref > 0); }