xref: /third_party/mesa3d/src/glx/windows/windowsgl.c (revision bf215546)
1/*
2 * Copyright © 2014 Jon Turney
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include "windowsgl.h"
25#include "windowsgl_internal.h"
26
27#include "glapi.h"
28#include "wgl.h"
29
30#include <dlfcn.h>
31#include <assert.h>
32#include <stdio.h>
33#include <strings.h>
34
35static struct _glapi_table *windows_api = NULL;
36
37static void *
38windows_get_dl_handle(void)
39{
40   static void *dl_handle = NULL;
41
42   if (!dl_handle)
43      dl_handle = dlopen("cygnativeGLthunk.dll", RTLD_NOW);
44
45   return dl_handle;
46}
47
48static void
49windows_glapi_create_table(void)
50{
51   if (windows_api)
52      return;
53
54   windows_api = _glapi_create_table_from_handle(windows_get_dl_handle(), "gl");
55   assert(windows_api);
56}
57
58static void windows_glapi_set_dispatch(void)
59{
60   windows_glapi_create_table();
61   _glapi_set_dispatch(windows_api);
62}
63
64windowsContext *
65windows_create_context(int pxfi, windowsContext *shared)
66{
67   windowsContext *gc;
68
69   gc = calloc(1, sizeof *gc);
70   if (gc == NULL)
71      return NULL;
72
73   // create a temporary, invisible window
74#define GL_TEMP_WINDOW_CLASS "glTempWndClass"
75   {
76      static wATOM glTempWndClass = 0;
77
78      if (glTempWndClass == 0) {
79         WNDCLASSEX wc;
80         wc.cbSize = sizeof(WNDCLASSEX);
81         wc.style = CS_HREDRAW | CS_VREDRAW;
82         wc.lpfnWndProc = DefWindowProc;
83         wc.cbClsExtra = 0;
84         wc.cbWndExtra = 0;
85         wc.hInstance = GetModuleHandle(NULL);
86         wc.hIcon = 0;
87         wc.hCursor = 0;
88         wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
89         wc.lpszMenuName = NULL;
90         wc.lpszClassName = GL_TEMP_WINDOW_CLASS;
91         wc.hIconSm = 0;
92         RegisterClassEx(&wc);
93      }
94   }
95
96   HWND hwnd = CreateWindowExA(0,
97                               GL_TEMP_WINDOW_CLASS,
98                               "glWindow",
99                               0,
100                               0, 0, 0, 0,
101                               NULL, NULL, GetModuleHandle(NULL), NULL);
102   HDC hdc = GetDC(hwnd);
103
104   // We must set the windows pixel format before we can create a WGL context
105   gc->pxfi = pxfi;
106   SetPixelFormat(hdc, gc->pxfi, NULL);
107
108   gc->ctx = wglCreateContext(hdc);
109
110   if (shared && gc->ctx)
111      wglShareLists(shared->ctx, gc->ctx);
112
113   ReleaseDC(hwnd, hdc);
114   DestroyWindow(hwnd);
115
116   if (!gc->ctx)
117   {
118     free(gc);
119     return NULL;
120   }
121
122   return gc;
123}
124
125windowsContext *
126windows_create_context_attribs(int pxfi, windowsContext *shared, const int *attribList)
127{
128   windowsContext *gc;
129
130   gc = calloc(1, sizeof *gc);
131   if (gc == NULL)
132      return NULL;
133
134   // create a temporary, invisible window
135#define GL_TEMP_WINDOW_CLASS "glTempWndClass"
136   {
137      static wATOM glTempWndClass = 0;
138
139      if (glTempWndClass == 0) {
140         WNDCLASSEX wc;
141         wc.cbSize = sizeof(WNDCLASSEX);
142         wc.style = CS_HREDRAW | CS_VREDRAW;
143         wc.lpfnWndProc = DefWindowProc;
144         wc.cbClsExtra = 0;
145         wc.cbWndExtra = 0;
146         wc.hInstance = GetModuleHandle(NULL);
147         wc.hIcon = 0;
148         wc.hCursor = 0;
149         wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
150         wc.lpszMenuName = NULL;
151         wc.lpszClassName = GL_TEMP_WINDOW_CLASS;
152         wc.hIconSm = 0;
153         RegisterClassEx(&wc);
154      }
155   }
156
157   HWND hwnd = CreateWindowExA(0,
158                               GL_TEMP_WINDOW_CLASS,
159                               "glWindow",
160                               0,
161                               0, 0, 0, 0,
162                               NULL, NULL, GetModuleHandle(NULL), NULL);
163   HDC hdc = GetDC(hwnd);
164   HGLRC shareContext = NULL;
165   if (shared)
166      shareContext = shared->ctx;
167
168   // We must set the windows pixel format before we can create a WGL context
169   gc->pxfi = pxfi;
170   SetPixelFormat(hdc, gc->pxfi, NULL);
171
172   gc->ctx = wglCreateContextAttribsARB(hdc, shareContext, attribList);
173
174   ReleaseDC(hwnd, hdc);
175   DestroyWindow(hwnd);
176
177   if (!gc->ctx)
178   {
179     free(gc);
180     return NULL;
181   }
182
183   return gc;
184}
185
186void
187windows_destroy_context(windowsContext *context)
188{
189   wglDeleteContext(context->ctx);
190   context->ctx = NULL;
191   free(context);
192}
193
194int windows_bind_context(windowsContext *context, windowsDrawable *draw, windowsDrawable *read)
195{
196   HDC drawDc = draw->callbacks->getdc(draw);
197
198   if (!draw->pxfi)
199   {
200      SetPixelFormat(drawDc, context->pxfi, NULL);
201      draw->pxfi = context->pxfi;
202   }
203
204   if ((read != NULL) &&  (read != draw))
205   {
206      /*
207        If there is a separate read drawable, create a separate read DC, and
208        use the wglMakeContextCurrent extension to make the context current
209        drawing to one DC and reading from the other
210
211        Should only occur when WGL_ARB_make_current_read extension is present
212      */
213      HDC readDc = read->callbacks->getdc(read);
214
215      BOOL ret = wglMakeContextCurrentARB(drawDc, readDc, context->ctx);
216
217      read->callbacks->releasedc(read, readDc);
218
219      if (!ret) {
220         printf("wglMakeContextCurrentARB error: %08x\n", (int)GetLastError());
221         return FALSE;
222      }
223   }
224   else
225   {
226      /* Otherwise, just use wglMakeCurrent */
227      BOOL ret = wglMakeCurrent(drawDc, context->ctx);
228      if (!ret) {
229         printf("wglMakeCurrent error: %08x\n", (int)GetLastError());
230         return FALSE;
231      }
232   }
233
234   draw->callbacks->releasedc(draw, drawDc);
235
236   windows_glapi_set_dispatch();
237
238   return TRUE;
239}
240
241void windows_unbind_context(windowsContext * context)
242{
243   wglMakeCurrent(NULL, NULL);
244}
245
246/*
247 *
248 */
249
250void
251windows_swap_buffers(windowsDrawable *draw)
252{
253   HDC drawDc = GetDC(draw->hWnd);
254   SwapBuffers(drawDc);
255   ReleaseDC(draw->hWnd, drawDc);
256}
257
258
259typedef void (__stdcall * PFNGLADDSWAPHINTRECTWIN) (GLint x, GLint y,
260                                                    GLsizei width,
261                                                    GLsizei height);
262
263static void
264glAddSwapHintRectWIN(GLint x, GLint y, GLsizei width,
265                            GLsizei height)
266{
267   PFNGLADDSWAPHINTRECTWIN proc = (PFNGLADDSWAPHINTRECTWIN)wglGetProcAddress("glAddSwapHintRectWIN");
268   if (proc)
269      proc(x, y, width, height);
270}
271
272void
273windows_copy_subbuffer(windowsDrawable *draw,
274                      int x, int y, int width, int height)
275{
276   glAddSwapHintRectWIN(x, y, width, height);
277   windows_swap_buffers(draw);
278}
279
280/*
281 * Helper function for calling a test function on an initial context
282 */
283static void
284windows_call_with_context(void (*proc)(HDC, void *), void *args)
285{
286   // create window class
287#define WIN_GL_TEST_WINDOW_CLASS "GLTest"
288   {
289      static wATOM glTestWndClass = 0;
290
291      if (glTestWndClass == 0) {
292         WNDCLASSEX wc;
293
294         wc.cbSize = sizeof(WNDCLASSEX);
295         wc.style = CS_HREDRAW | CS_VREDRAW;
296         wc.lpfnWndProc = DefWindowProc;
297         wc.cbClsExtra = 0;
298         wc.cbWndExtra = 0;
299         wc.hInstance = GetModuleHandle(NULL);
300         wc.hIcon = 0;
301         wc.hCursor = 0;
302         wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
303         wc.lpszMenuName = NULL;
304         wc.lpszClassName = WIN_GL_TEST_WINDOW_CLASS;
305         wc.hIconSm = 0;
306         glTestWndClass = RegisterClassEx(&wc);
307      }
308   }
309
310   // create an invisible window for a scratch DC
311   HWND hwnd = CreateWindowExA(0,
312                               WIN_GL_TEST_WINDOW_CLASS,
313                               "GL Renderer Capabilities Test Window",
314                               0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL),
315                               NULL);
316   if (hwnd) {
317      HDC hdc = GetDC(hwnd);
318
319      // we must set a pixel format before we can create a context, just use the first one...
320      SetPixelFormat(hdc, 1, NULL);
321      HGLRC hglrc = wglCreateContext(hdc);
322      wglMakeCurrent(hdc, hglrc);
323
324      // call the test function
325      proc(hdc, args);
326
327      // clean up
328      wglMakeCurrent(NULL, NULL);
329      wglDeleteContext(hglrc);
330      ReleaseDC(hwnd, hdc);
331      DestroyWindow(hwnd);
332   }
333}
334
335static void
336windows_check_render_test(HDC hdc, void *args)
337{
338   int *result = (int *)args;
339
340   /* Rather than play linkage games using stdcall to ensure we get
341      glGetString from opengl32.dll here, use dlsym */
342   void *dlhandle = windows_get_dl_handle();
343   const char *(*proc)(int) = dlsym(dlhandle, "glGetString");
344   const char *gl_renderer = (const char *)proc(GL_RENDERER);
345
346   if ((!gl_renderer) || (strcasecmp(gl_renderer, "GDI Generic") == 0))
347      *result = FALSE;
348   else
349      *result = TRUE;
350}
351
352int
353windows_check_renderer(void)
354{
355   int result;
356   windows_call_with_context(windows_check_render_test, &result);
357   return result;
358}
359
360typedef struct {
361   char *gl_extensions;
362   char *wgl_extensions;
363} windows_extensions_result;
364
365static void
366windows_extensions_test(HDC hdc, void *args)
367{
368   windows_extensions_result *r = (windows_extensions_result *)args;
369
370   void *dlhandle = windows_get_dl_handle();
371   const char *(*proc)(int) = dlsym(dlhandle, "glGetString");
372
373   r->gl_extensions = strdup(proc(GL_EXTENSIONS));
374
375   wglResolveExtensionProcs();
376   r->wgl_extensions = strdup(wglGetExtensionsStringARB(hdc));
377}
378
379void
380windows_extensions(char **gl_extensions, char **wgl_extensions)
381{
382   windows_extensions_result result;
383
384   *gl_extensions = "";
385   *wgl_extensions = "";
386
387   windows_call_with_context(windows_extensions_test, &result);
388
389   *gl_extensions = result.gl_extensions;
390   *wgl_extensions = result.wgl_extensions;
391}
392