]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/glx/glcontext.cpp
Add support for using the latest available OpenGL version
[libs/gui.git] / source / graphics / glx / glcontext.cpp
1 #include <set>
2 #include <vector>
3 #include <GL/glx.h>
4 #include <GL/glxext.h>
5 #include <msp/strings/lexicalcast.h>
6 #include <msp/strings/utils.h>
7 #include "glcontext.h"
8 #include "display_private.h"
9 #include "window_private.h"
10
11 using namespace std;
12
13 namespace Msp {
14 namespace Graphics {
15
16 typedef GLXContext ContextHandle;
17
18 struct GLContext::Private
19 {
20         ContextHandle context;
21         // We need to create a window with the chosen visual
22         WindowHandle subwnd;
23         GLXWindow glxwnd;
24 };
25
26
27 void GLContext::platform_init(const GLOptions &opts)
28 {
29         DisplayHandle dpy = display.get_private().display;
30         int event_base;
31         int error_base;
32         if(!glXQueryExtension(dpy, &event_base, &error_base))
33                 throw runtime_error("glX not found");
34
35         int major, minor;
36         glXQueryVersion(dpy, &major, &minor);
37
38         set<string> extensions;
39         if(major>1 || (major==1 && minor>=1))
40         {
41                 if(const char *ext_str = glXQueryExtensionsString(dpy, DefaultScreen(dpy)))
42                 {
43                         vector<string> exts = split(ext_str);
44                         extensions.insert(exts.begin(), exts.end());
45                 }
46         }
47
48         if(major>1 || (major==1 && minor>=3))
49         {
50                 vector<int> fb_attribs;
51
52                 fb_attribs.push_back(GLX_DRAWABLE_TYPE);
53                 fb_attribs.push_back(GLX_WINDOW_BIT);
54
55                 fb_attribs.push_back(GLX_RENDER_TYPE);
56                 fb_attribs.push_back(GLX_RGBA_BIT);
57
58                 fb_attribs.push_back(GLX_DEPTH_SIZE);
59                 fb_attribs.push_back(1);
60
61                 if(opts.alpha)
62                 {
63                         fb_attribs.push_back(GLX_ALPHA_SIZE);
64                         fb_attribs.push_back(1);
65                 }
66
67                 if(opts.stencil)
68                 {
69                         fb_attribs.push_back(GLX_STENCIL_SIZE);
70                         fb_attribs.push_back(1);
71                 }
72
73                 fb_attribs.push_back(GLX_DOUBLEBUFFER);
74                 fb_attribs.push_back(opts.doublebuffer);
75
76                 if(opts.multisample>0)
77                 {
78                         fb_attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
79                         fb_attribs.push_back(1);
80                         fb_attribs.push_back(GLX_SAMPLES_ARB);
81                         fb_attribs.push_back(opts.multisample);
82                 }
83
84                 fb_attribs.push_back(0);
85
86                 int n_configs = 0;
87                 GLXFBConfig *fb_configs = glXChooseFBConfig(dpy, DefaultScreen(dpy), &fb_attribs[0], &n_configs);
88                 if(!fb_configs)
89                         throw unsupported_gl_mode(opts);
90
91                 XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, fb_configs[0]);
92
93                 XSetWindowAttributes attr;
94                 attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone);
95
96                 priv = new Private;
97                 priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr);
98                 XMapWindow(dpy, priv->subwnd);
99
100                 priv->glxwnd = glXCreateWindow(dpy, fb_configs[0], priv->subwnd, 0);
101
102                 if(opts.forward_compatible || opts.gl_version_major!=GLOptions::DEFAULT_VERSION)
103                 {
104                         if(!extensions.count("GLX_ARB_create_context") || !extensions.count("GLX_ARB_get_proc_address"))
105                                 throw unsupported_gl_mode(opts);
106
107                         unsigned gl_version_major = opts.gl_version_major;
108                         unsigned gl_version_minor = opts.gl_version_minor;
109                         if(opts.gl_version_major==GLOptions::LATEST_VERSION)
110                         {
111                                 ContextHandle probe_context = glXCreateNewContext(dpy, fb_configs[0], GLX_RGBA_TYPE, 0, true);
112                                 glXMakeContextCurrent(dpy, priv->glxwnd, priv->glxwnd, probe_context);
113
114                                 const char *gl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_VERSION));
115                                 if(!gl_ver_ptr)
116                                         throw unsupported_gl_mode(opts);
117
118                                 string gl_ver = gl_ver_ptr;
119                                 vector<string> parts = split(gl_ver.substr(0, gl_ver.find(' ')), '.');
120
121                                 gl_version_major = lexical_cast<unsigned>(parts[0]);
122                                 gl_version_minor = lexical_cast<unsigned>(parts[1]);
123
124                                 glXMakeContextCurrent(dpy, 0, 0, 0);
125                                 glXDestroyContext(dpy, probe_context);
126                         }
127
128                         vector<int> ctx_attribs;
129
130                         ctx_attribs.push_back(GLX_RENDER_TYPE);
131                         ctx_attribs.push_back(GLX_RGBA_TYPE);
132
133                         if(opts.forward_compatible)
134                         {
135                                 ctx_attribs.push_back(GLX_CONTEXT_FLAGS_ARB);
136                                 ctx_attribs.push_back(GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB);
137                         }
138
139                         if(opts.core_profile)
140                         {
141                                 ctx_attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
142                                 ctx_attribs.push_back(GLX_CONTEXT_CORE_PROFILE_BIT_ARB);
143                         }
144
145                         if(opts.gl_version_major)
146                         {
147                                 ctx_attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB);
148                                 ctx_attribs.push_back(gl_version_major);
149                                 ctx_attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB);
150                                 ctx_attribs.push_back(gl_version_minor);
151                         }
152
153                         ctx_attribs.push_back(0);
154
155                         const GLubyte *name = reinterpret_cast<const GLubyte *>("glXCreateContextAttribsARB");
156                         PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(glXGetProcAddressARB(name));
157                         priv->context = glXCreateContextAttribs(dpy, fb_configs[0], 0, true, &ctx_attribs[0]);
158                 }
159                 else
160                         priv->context = glXCreateNewContext(dpy, fb_configs[0], GLX_RGBA_TYPE, 0, true);
161
162                 XFree(vi);
163                 XFree(fb_configs);
164
165                 glXMakeContextCurrent(dpy, priv->glxwnd, priv->glxwnd, priv->context);
166         }
167         else if(opts.forward_compatible || opts.gl_version_major)
168                 throw unsupported_gl_mode(opts);
169         else
170         {
171                 vector<int> attribs;
172
173                 attribs.push_back(GLX_RGBA);
174                 attribs.push_back(GLX_DEPTH_SIZE);
175                 attribs.push_back(1);
176
177                 if(opts.alpha)
178                 {
179                         attribs.push_back(GLX_ALPHA_SIZE);
180                         attribs.push_back(1);
181                 }
182
183                 if(opts.stencil)
184                 {
185                         attribs.push_back(GLX_STENCIL_SIZE);
186                         attribs.push_back(1);
187                 }
188
189                 if(opts.doublebuffer)
190                         attribs.push_back(GLX_DOUBLEBUFFER);
191
192                 if(opts.multisample>0)
193                 {
194                         attribs.push_back(GLX_SAMPLE_BUFFERS_ARB);
195                         attribs.push_back(1);
196                         attribs.push_back(GLX_SAMPLES_ARB);
197                         attribs.push_back(opts.multisample);
198                 }
199
200                 attribs.push_back(0);
201
202                 XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), &attribs.front());
203                 if(!vi)
204                         throw unsupported_gl_mode(opts);
205
206                 priv = new Private;
207                 priv->glxwnd = 0;
208                 priv->context = glXCreateContext(dpy, vi, 0, true);
209
210                 XSetWindowAttributes attr;
211                 attr.colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vi->visual, AllocNone);
212
213                 priv->subwnd = XCreateWindow(dpy, window.get_private().window, 0, 0, window.get_width(), window.get_height(), 0, vi->depth, InputOutput, vi->visual, CWColormap, &attr);
214                 XMapWindow(dpy, priv->subwnd);
215
216                 XFree(vi);
217
218                 glXMakeCurrent(dpy, priv->subwnd, priv->context);
219         }
220 }
221
222 GLContext::~GLContext()
223 {
224         DisplayHandle dpy = display.get_private().display;
225
226         if(priv->glxwnd)
227         {
228                 glXMakeContextCurrent(dpy, 0, 0, 0);
229                 glXDestroyWindow(dpy, priv->glxwnd);
230         }
231         else
232                 glXMakeCurrent(dpy, 0, 0);
233         glXDestroyContext(dpy, priv->context);
234         XDestroyWindow(dpy, priv->subwnd);
235
236         delete priv;
237 }
238
239 void GLContext::set_swap_interval(unsigned i)
240 {
241         const GLubyte *name = reinterpret_cast<const GLubyte *>("glXSwapIntervalEXT");
242         PFNGLXSWAPINTERVALEXTPROC glXSwapInterval = reinterpret_cast<PFNGLXSWAPINTERVALEXTPROC>(glXGetProcAddress(name));
243         if(!glXSwapInterval)
244                 throw runtime_error("glXSwapIntervalEXT not found");
245         glXSwapInterval(display.get_private().display, (priv->glxwnd ? priv->glxwnd : priv->subwnd), i);
246 }
247
248 void GLContext::swap_buffers()
249 {
250         glXSwapBuffers(display.get_private().display, (priv->glxwnd ? priv->glxwnd : priv->subwnd));
251 }
252
253 void GLContext::window_resized(unsigned w, unsigned h)
254 {
255         XMoveResizeWindow(display.get_private().display, priv->subwnd, 0, 0, w, h);
256 }
257
258 } // namespace Graphics
259 } // namespace Msp