+#include <set>
#include <vector>
-#include <GL/gl.h>
#include <GL/glx.h>
+#include <GL/glxext.h>
+#include <msp/strings/utils.h>
#include "glcontext.h"
#include "display_private.h"
#include "window_private.h"
ContextHandle context;
// We need to create a window with the chosen visual
WindowHandle subwnd;
+ GLXWindow glxwnd;
};
void GLContext::platform_init(const GLOptions &opts)
{
- vector<int> attribs;
+ DisplayHandle dpy = display.get_private().display;
+ int event_base;
+ int error_base;
+ if(!glXQueryExtension(dpy, &event_base, &error_base))
+ throw runtime_error("glX not found");
- attribs.push_back(GLX_RGBA);
- attribs.push_back(GLX_DEPTH_SIZE);
- attribs.push_back(1);
+ int major, minor;
+ glXQueryVersion(dpy, &major, &minor);
- if(opts.alpha)
+ set<string> extensions;
+ if(major>1 || (major==1 && minor>=1))
{
- attribs.push_back(GLX_ALPHA_SIZE);
- attribs.push_back(1);
+ if(const char *ext_str = glXQueryExtensionsString(dpy, DefaultScreen(dpy)))
+ {
+ vector<string> exts = split(ext_str);
+ extensions.insert(exts.begin(), exts.end());
+ }
}
- if(opts.stencil)
+ if(major>1 || (major==1 && minor>=3))
{
- attribs.push_back(GLX_STENCIL_SIZE);
- attribs.push_back(1);
- }
+ vector<int> fb_attribs;
+
+ fb_attribs.push_back(GLX_DRAWABLE_TYPE);
+ fb_attribs.push_back(GLX_WINDOW_BIT);
+
+ fb_attribs.push_back(GLX_RENDER_TYPE);
+ fb_attribs.push_back(GLX_RGBA_BIT);
+
+ fb_attribs.push_back(GLX_DEPTH_SIZE);
+ fb_attribs.push_back(1);
+
+ if(opts.alpha)
+ {
+ fb_attribs.push_back(GLX_ALPHA_SIZE);
+ fb_attribs.push_back(1);
+ }
+
+ if(opts.stencil)
+ {
+ fb_attribs.push_back(GLX_STENCIL_SIZE);
+ fb_attribs.push_back(1);
+ }
+
+ fb_attribs.push_back(GLX_DOUBLEBUFFER);
+ fb_attribs.push_back(opts.doublebuffer);
+
+ if(opts.multisample>0)
+ {
+ fb_attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
+ fb_attribs.push_back(1);
+ fb_attribs.push_back(GLX_SAMPLES_ARB);
+ fb_attribs.push_back(opts.multisample);
+ }
+
+ fb_attribs.push_back(0);
+
+ int n_configs = 0;
+ GLXFBConfig *fb_configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), &fb_attribs[0], &n_configs);
+ if(!fb_configs)
+ throw unsupported_gl_mode(opts);
+
+ XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, fb_configs[0]);
+
+ XSetWindowAttributes attr;
+ attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone);
+
+ priv = new Private;
+ priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr);
+ XMapWindow(dpy, priv->subwnd);
+
+ priv->glxwnd = glXCreateWindow(dpy, fb_configs[0], priv->subwnd, 0);
+
+ if(opts.forward_compatible || opts.gl_version_major)
+ {
+ if(!extensions.count("GLX_ARB_create_context") || !extensions.count("GLX_ARB_get_proc_address"))
+ throw unsupported_gl_mode(opts);
- if(opts.doublebuffer)
- attribs.push_back(GLX_DOUBLEBUFFER);
+ vector<int> ctx_attribs;
- if(opts.multisample>0)
+ ctx_attribs.push_back(GLX_RENDER_TYPE);
+ ctx_attribs.push_back(GLX_RGBA_TYPE);
+
+ if(opts.forward_compatible)
+ {
+ ctx_attribs.push_back(GLX_CONTEXT_FLAGS_ARB);
+ ctx_attribs.push_back(GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB);
+ }
+
+ if(opts.gl_version_major)
+ {
+ ctx_attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB);
+ ctx_attribs.push_back(opts.gl_version_major);
+ ctx_attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB);
+ ctx_attribs.push_back(opts.gl_version_minor);
+ }
+
+ ctx_attribs.push_back(0);
+
+ const GLubyte *name = reinterpret_cast<const GLubyte *>("glXCreateContextAttribsARB");
+ PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddressARB(name));
+ priv->context = glXCreateContextAttribs(dpy, fb_configs[0], 0, true, &ctx_attribs[0]);
+ }
+ else
+ priv->context = glXCreateNewContext(dpy, fb_configs[0], GLX_RGBA_TYPE, 0, true);
+
+ XFree(vi);
+ XFree(fb_configs);
+
+ glXMakeContextCurrent(dpy, priv->glxwnd, priv->glxwnd, priv->context);
+ }
+ else if(opts.forward_compatible || opts.gl_version_major)
+ throw unsupported_gl_mode(opts);
+ else
{
- attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
+ vector<int> attribs;
+
+ attribs.push_back(GLX_RGBA);
+ attribs.push_back(GLX_DEPTH_SIZE);
attribs.push_back(1);
- attribs.push_back(GLX_SAMPLES_ARB);
- attribs.push_back(opts.multisample);
- }
- attribs.push_back(0);
+ if(opts.alpha)
+ {
+ attribs.push_back(GLX_ALPHA_SIZE);
+ attribs.push_back(1);
+ }
- DisplayHandle dpy = display.get_private().display;
+ if(opts.stencil)
+ {
+ attribs.push_back(GLX_STENCIL_SIZE);
+ attribs.push_back(1);
+ }
- XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front());
- if(!vi)
- throw unsupported_gl_mode(opts);
+ if(opts.doublebuffer)
+ attribs.push_back(GLX_DOUBLEBUFFER);
+
+ if(opts.multisample>0)
+ {
+ attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
+ attribs.push_back(1);
+ attribs.push_back(GLX_SAMPLES_ARB);
+ attribs.push_back(opts.multisample);
+ }
+
+ attribs.push_back(0);
- priv = new Private;
- priv->context = glXCreateContext(dpy, vi, 0, true);
+ XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front());
+ if(!vi)
+ throw unsupported_gl_mode(opts);
- XSetWindowAttributes attr;
- attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone);
+ priv = new Private;
+ priv->glxwnd = 0;
+ priv->context = glXCreateContext(dpy, vi, 0, true);
- priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr);
- XMapWindow(dpy, priv->subwnd);
+ XSetWindowAttributes attr;
+ attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone);
- XFree(vi);
+ priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr);
+ XMapWindow(dpy, priv->subwnd);
- glXMakeCurrent(dpy, priv->subwnd, priv->context);
+ XFree(vi);
+
+ glXMakeCurrent(dpy, priv->subwnd, priv->context);
+ }
}
GLContext::~GLContext()
{
DisplayHandle dpy = display.get_private().display;
- glXMakeCurrent(dpy, 0, 0);
+ if(priv->glxwnd)
+ {
+ glXMakeContextCurrent(dpy, 0, 0, 0);
+ glXDestroyWindow(dpy, priv->glxwnd);
+ }
+ else
+ glXMakeCurrent(dpy, 0, 0);
glXDestroyContext(dpy, priv->context);
XDestroyWindow(dpy, priv->subwnd);
PFNGLXSWAPINTERVALEXTPROC func = reinterpret_cast<PFNGLXSWAPINTERVALEXTPROC>(glXGetProcAddress(name));
if(!func)
throw runtime_error("glXSwapIntervalEXT not found");
- func(display.get_private().display, priv->subwnd, i);
+ func(display.get_private().display, (priv->glxwnd ? priv->glxwnd : priv->subwnd), i);
}
void GLContext::swap_buffers()
{
- glXSwapBuffers(display.get_private().display, priv->subwnd);
+ glXSwapBuffers(display.get_private().display, (priv->glxwnd ? priv->glxwnd : priv->subwnd));
}
void GLContext::window_resized(unsigned w, unsigned h)