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