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 * 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 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 58 static void windows_glapi_set_dispatch(void) 59 { 60 windows_glapi_create_table(); 61 _glapi_set_dispatch(windows_api); 62 } 63 64 windowsContext * 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 * 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 187 windows_destroy_context(windowsContext *context) 188 { 189 wglDeleteContext(context->ctx); 190 context->ctx = NULL; 191 free(context); 192 } 193 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", 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", 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 241 void windows_unbind_context(windowsContext * context) 242 { 243 wglMakeCurrent(NULL, NULL); 244 } 245 246 /* 247 * 248 */ 249 250 void 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 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 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 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 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 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 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 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 393 void windows_setTexBuffer(windowsContext *context, int textureTarget, 394 int textureFormat, windowsDrawable *drawable) 395 { 396 // not yet implemented 397 } 398 399 void windows_releaseTexBuffer(windowsContext *context, int textureTarget, 400 windowsDrawable *drawable) 401 { 402 // not yet implemented 403 } 404