• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 static struct _glapi_table *windows_api = NULL;
36 
37 static void *
windows_get_dl_handle(void)38 windows_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 
48 static void
windows_glapi_create_table(void)49 windows_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 
windows_glapi_set_dispatch(void)58 static void windows_glapi_set_dispatch(void)
59 {
60    windows_glapi_create_table();
61    _glapi_set_dispatch(windows_api);
62 }
63 
64 windowsContext *
windows_create_context(int pxfi,windowsContext * shared)65 windows_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 
125 windowsContext *
windows_create_context_attribs(int pxfi,windowsContext * shared,const int * attribList)126 windows_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 
186 void
windows_destroy_context(windowsContext * context)187 windows_destroy_context(windowsContext *context)
188 {
189    wglDeleteContext(context->ctx);
190    context->ctx = NULL;
191    free(context);
192 }
193 
windows_bind_context(windowsContext * context,windowsDrawable * draw,windowsDrawable * read)194 int 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 
windows_unbind_context(windowsContext * context)241 void windows_unbind_context(windowsContext * context)
242 {
243    wglMakeCurrent(NULL, NULL);
244 }
245 
246 /*
247  *
248  */
249 
250 void
windows_swap_buffers(windowsDrawable * draw)251 windows_swap_buffers(windowsDrawable *draw)
252 {
253    HDC drawDc = GetDC(draw->hWnd);
254    SwapBuffers(drawDc);
255    ReleaseDC(draw->hWnd, drawDc);
256 }
257 
258 
259 typedef void (__stdcall * PFNGLADDSWAPHINTRECTWIN) (GLint x, GLint y,
260                                                     GLsizei width,
261                                                     GLsizei height);
262 
263 static void
glAddSwapHintRectWIN(GLint x,GLint y,GLsizei width,GLsizei height)264 glAddSwapHintRectWIN(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 
272 void
windows_copy_subbuffer(windowsDrawable * draw,int x,int y,int width,int height)273 windows_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  */
283 static void
windows_call_with_context(void (* proc)(HDC,void *),void * args)284 windows_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 
335 static void
windows_check_render_test(HDC hdc,void * args)336 windows_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 
352 int
windows_check_renderer(void)353 windows_check_renderer(void)
354 {
355    int result;
356    windows_call_with_context(windows_check_render_test, &result);
357    return result;
358 }
359 
360 typedef struct {
361    char *gl_extensions;
362    char *wgl_extensions;
363 } windows_extensions_result;
364 
365 static void
windows_extensions_test(HDC hdc,void * args)366 windows_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 
379 void
windows_extensions(char ** gl_extensions,char ** wgl_extensions)380 windows_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