Add support for GTK 3
[grits] / src / gtkgl.c
1 /*
2  * Copyright (C) 2009-2011 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <gtk/gtk.h>
19
20 /***************************
21  * GtkGlExt implementation *
22  ***************************/
23 #if defined(SYS_GTKGLEXT)
24 #include <gtk/gtkgl.h>
25 void gtk_gl_enable(GtkWidget *widget)
26 {
27         GdkGLConfig *glconfig = gdk_gl_config_new_by_mode(
28                         GDK_GL_MODE_RGBA   | GDK_GL_MODE_DEPTH |
29                         GDK_GL_MODE_ALPHA  | GDK_GL_MODE_DOUBLE);
30         gtk_widget_set_gl_capability(widget,
31                         glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
32 }
33
34 void gtk_gl_begin(GtkWidget *widget)
35 {
36         GdkGLContext  *glcontext  = gtk_widget_get_gl_context(widget);
37         GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget);
38         gdk_gl_drawable_gl_begin(gldrawable, glcontext);
39 }
40
41 void gtk_gl_end(GtkWidget *widget)
42 {
43         GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(widget);
44         gdk_gl_drawable_swap_buffers(gldrawable);
45         gdk_gl_drawable_gl_end(gldrawable);
46 }
47
48 void gtk_gl_disable(GtkWidget *widget)
49 {
50 }
51
52
53 /**********************
54  * X11 implementation *
55  **********************/
56 #elif defined(SYS_X11)
57 #include <GL/glx.h>
58 #include <gdk/gdkx.h>
59 static gboolean gtk_gl_errors;
60 static int gtk_gl_handler(Display *xdisplay, XErrorEvent *xerror)
61 {
62         gtk_gl_errors = TRUE;
63         return 0;
64 }
65 static GLXContext gtk_gl_create_context(Display *xdisplay, XVisualInfo *xvinfo,
66                 GLXContext shared, Bool direct)
67 {
68         gtk_gl_errors = FALSE;
69         XSync(xdisplay, False);
70         void *handler = XSetErrorHandler(gtk_gl_handler);
71         GLXContext context = glXCreateContext(xdisplay, xvinfo, shared, direct);
72         XSync(xdisplay, False);
73         XSetErrorHandler(handler);
74
75         if (gtk_gl_errors)
76                 return 0;
77         return context;
78 }
79 void gtk_gl_enable(GtkWidget *widget)
80 {
81         g_debug("GtkGl: enable");
82         GdkScreen *screen   = gdk_screen_get_default();
83         Display   *xdisplay = GDK_SCREEN_XDISPLAY(screen);
84         gint       nscreen  = GDK_SCREEN_XNUMBER(screen);
85
86         /* Create context */
87         int attribs[] = {GLX_RGBA,
88                          GLX_RED_SIZE,    1,
89                          GLX_GREEN_SIZE,  1,
90                          GLX_BLUE_SIZE,   1,
91                          GLX_ALPHA_SIZE,  1,
92                          GLX_DOUBLEBUFFER,
93                          GLX_DEPTH_SIZE,  1,
94                          None};
95
96         XVisualInfo *xvinfo  = glXChooseVisual(xdisplay, nscreen, attribs);
97         if (!xvinfo)
98                 g_error("GtkGl: enable - unable to get valid OpenGL Visual");
99         GLXContext context = 0;
100         if (!context)
101                 context = gtk_gl_create_context(xdisplay, xvinfo, NULL, True);
102         if (!context)
103                 context = gtk_gl_create_context(xdisplay, xvinfo, NULL, False);
104         if (!context)
105                 g_error("Unable to create OpenGL context,"
106                         "possible graphics driver problem?");
107         g_debug("GtkGl: direct rendering = %d\n", glXIsDirect(xdisplay, context));
108
109         g_object_set_data(G_OBJECT(widget), "glcontext", context);
110
111         /* Fix up visual/colormap */
112 #if GTK_CHECK_VERSION(3,0,0)
113         GdkVisual *visual = gdk_x11_screen_lookup_visual(screen, xvinfo->visualid);
114         gtk_widget_set_visual(widget, visual);
115 #else
116         GdkVisual   *visual = gdk_x11_screen_lookup_visual(screen, xvinfo->visualid);
117         GdkColormap *cmap   = gdk_colormap_new(visual, FALSE);
118         gtk_widget_set_colormap(widget, cmap);
119         g_object_unref(cmap);
120 #endif
121         XFree(xvinfo);
122
123         /* Disable GTK double buffering */
124         gtk_widget_set_double_buffered(widget, FALSE);
125 }
126
127 void gtk_gl_begin(GtkWidget *widget)
128 {
129         g_debug("GtkGl: begin");
130         Display   *xdisplay = GDK_SCREEN_XDISPLAY(gtk_widget_get_screen(widget));
131         Window     xwindow  = GDK_WINDOW_XID(gtk_widget_get_window(widget));
132         GLXContext context  = g_object_get_data(G_OBJECT(widget), "glcontext");
133         glXMakeCurrent(xdisplay, xwindow, context);
134 }
135
136 void gtk_gl_end(GtkWidget *widget)
137 {
138         g_debug("GtkGl: end");
139         Display   *xdisplay = GDK_SCREEN_XDISPLAY(gtk_widget_get_screen(widget));
140         Window     xwindow  = GDK_WINDOW_XID(gtk_widget_get_window(widget));
141         glXSwapBuffers(xdisplay, xwindow);
142 }
143
144 void gtk_gl_disable(GtkWidget *widget)
145 {
146         g_debug("GtkGl: disable");
147         Display   *xdisplay = GDK_SCREEN_XDISPLAY(gtk_widget_get_screen(widget));
148         GLXContext context  = g_object_get_data(G_OBJECT(widget), "glcontext");
149         glXDestroyContext(xdisplay, context);
150 }
151
152
153 /************************
154  * Win32 implementation *
155  ************************/
156 #elif defined(SYS_WIN)
157 #include <windows.h>
158 #include <gdk/gdkwin32.h>
159 #include <GL/gl.h>
160
161 /* Windows doens't define OpenGL extensions */
162 static void APIENTRY (*glMultiTexCoord2dvPtr)(int target, const double *v);
163 static void APIENTRY (*glActiveTexturePtr)(int texture);
164
165 void APIENTRY glMultiTexCoord2dv(int target, const double *v)
166 {
167         glMultiTexCoord2dvPtr(target, v);
168 }
169
170 void APIENTRY glActiveTexture(int texture)
171 {
172         glActiveTexturePtr(texture);
173 }
174
175 static void init_extensions(void)
176 {
177         static gboolean init_done = FALSE;
178         if (init_done)
179                 return;
180         init_done = TRUE;
181
182         g_debug("GtkGl: init_extensions");
183         const guchar *exts = NULL;
184         if (!(exts = glGetString(GL_EXTENSIONS)))
185                 g_error("GtkGl: Unable to query extensions");
186         if (!(glMultiTexCoord2dvPtr = (void*)wglGetProcAddress("glMultiTexCoord2dvARB")))
187                 g_error("GtkGl: Unable to load glMultiTexCoord2dv extension:\n%s", exts);
188         if (!(glActiveTexturePtr    = (void*)wglGetProcAddress("glActiveTextureARB")))
189                 g_error("GtkGl: Unable to load glActiveTexture extension\n%s", exts);
190         g_debug("GtkGl: extensions - glMultiTexCoord2dvPtr=%p glActiveTexturePtr=%p",
191                         glMultiTexCoord2dvPtr, glActiveTexturePtr);
192 }
193
194 /* gtkgl implementation */
195 static void on_realize(GtkWidget *widget, gpointer _)
196 {
197         g_debug("GtkGl: on_realize");
198         gdk_window_ensure_native(gtk_widget_get_window(widget));
199         gtk_widget_set_double_buffered(widget, FALSE);
200
201         HWND  hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
202         HDC   hDC  = GetDC(hwnd);
203
204         PIXELFORMATDESCRIPTOR pfd = {
205                 .nSize       = sizeof(pfd),
206                 .nVersion    = 1,
207                 .dwFlags     = PFD_DRAW_TO_WINDOW
208                              | PFD_SUPPORT_OPENGL
209                              | PFD_DOUBLEBUFFER,
210                 //.dwFlags     = PFD_SUPPORT_OPENGL
211                 //             | PFD_DRAW_TO_WINDOW,
212                 .iPixelType  = PFD_TYPE_RGBA,
213                 .cColorBits  = 24,
214                 .cAlphaBits  = 8,
215                 .cDepthBits  = 32,
216                 .iLayerType  = PFD_MAIN_PLANE,
217         };
218         int pf = ChoosePixelFormat(hDC, &pfd);
219         if (pf == 0)
220                 g_error("GtkGl: ChoosePixelFormat failed");
221         if (!SetPixelFormat(hDC, pf, &pfd))
222                 g_error("GtkGl: SetPixelFormat failed");
223         HGLRC hRC = wglCreateContext(hDC);
224         if (hRC == NULL)
225                 g_error("GtkGl: wglCreateContext failed");
226         g_object_set_data(G_OBJECT(widget), "glcontext", hRC);
227 }
228
229 void gtk_gl_enable(GtkWidget *widget)
230 {
231         g_debug("GtkGl: enable");
232         g_signal_connect(widget, "realize", G_CALLBACK(on_realize), NULL);
233 }
234
235 void gtk_gl_begin(GtkWidget *widget)
236 {
237         g_debug("GtkGl: begin");
238         HWND  hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
239         HDC   hDC  = GetDC(hwnd);
240         HGLRC hRC  = g_object_get_data(G_OBJECT(widget), "glcontext");
241         if (!wglMakeCurrent(hDC, hRC))
242                 g_error("GtkGl: wglMakeCurrent failed");
243         init_extensions();
244 }
245
246 void gtk_gl_end(GtkWidget *widget)
247 {
248         g_debug("GtkGl: end");
249         HWND  hwnd = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
250         HDC   hDC  = GetDC(hwnd);
251         if (!SwapBuffers(hDC))
252                 g_error("GtkGl: SwapBuffers failed");
253 }
254
255 void gtk_gl_disable(GtkWidget *widget)
256 {
257         g_debug("GtkGl: disable");
258         HGLRC hRC = g_object_get_data(G_OBJECT(widget), "glcontext");
259         wglDeleteContext(hRC);
260 }
261
262
263 /**************************
264  * Mac OSX implementation *
265  **************************/
266 #elif defined(SYS_MAC)
267 #include <gdk/gdkquartz.h>
268 void gtk_gl_enable(GtkWidget *widget)
269 {
270         g_debug("GtkGl: enable");
271
272         /* Create context */
273         NSOpenGLPixelFormatAttribute attribs[] = {
274                 NSOpenGLPFAColorSize, 24,
275                 NSOpenGLPFAAlphaSize, 8,
276                 NSOpenGLPFADepthSize, 1,
277                 NSOpenGLPFADoubleBuffer,
278                 0
279         };
280         NSOpenGLPixelFormat *pix = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
281         NSOpenGLContext     *ctx = [[NSOpenGLContext     alloc] initWithFormat:pix shareContext:nil];
282
283         /* Attach to widget */
284         gtk_widget_set_double_buffered(widget, FALSE);
285
286         /* Save context */
287         g_object_set_data(G_OBJECT(widget), "glcontext", ctx);
288 }
289
290 void gtk_gl_begin(GtkWidget *widget)
291 {
292         g_debug("GtkGl: begin");
293         GtkAllocation alloc;
294         gdk_window_ensure_native(gtk_widget_get_window(widget));
295         gtk_widget_get_allocation(widget, &alloc);
296         gtk_widget_translate_coordinates(widget, gtk_widget_get_toplevel(widget),
297                 0, 0, &alloc.x, &alloc.y);
298
299         NSOpenGLContext *ctx  = g_object_get_data(G_OBJECT(widget), "glcontext");
300         GdkWindow       *win  = gtk_widget_get_window(widget);
301         NSView          *view = gdk_quartz_window_get_nsview(win);
302         NSRect           rect = NSMakeRect(alloc.x, alloc.y, alloc.width, alloc.height);
303
304         [ctx  setView:view];
305         [ctx  makeCurrentContext];
306         [ctx  update];
307         [view setFrame:rect];
308         [view setWantsBestResolutionOpenGLSurface:YES];
309 }
310
311 void gtk_gl_end(GtkWidget *widget)
312 {
313         g_debug("GtkGl: end");
314         NSOpenGLContext *ctx = g_object_get_data(G_OBJECT(widget), "glcontext");
315         [ctx flushBuffer];
316 }
317
318 void gtk_gl_disable(GtkWidget *widget)
319 {
320         g_debug("GtkGl: disable");
321 }
322
323
324 /****************************
325  * Undefined implementation *
326  ****************************/
327 #else
328 #warning "Unimplemented GtkGl"
329 void gtk_gl_enable(GtkWidget *widget) { }
330 void gtk_gl_begin(GtkWidget *widget) { }
331 void gtk_gl_end(GtkWidget *widget) { }
332 void gtk_gl_disable(GtkWidget *widget) { }
333 #endif