| /* |
| * Copyright © 2014 Jon Turney |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "windowsgl.h" |
| #include "windowsgl_internal.h" |
| |
| #include "glapi.h" |
| #include "wgl.h" |
| |
| #include <dlfcn.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <strings.h> |
| |
| static struct _glapi_table *windows_api = NULL; |
| |
| static void * |
| windows_get_dl_handle(void) |
| { |
| static void *dl_handle = NULL; |
| |
| if (!dl_handle) |
| dl_handle = dlopen("cygnativeGLthunk.dll", RTLD_NOW); |
| |
| return dl_handle; |
| } |
| |
| static void |
| windows_glapi_create_table(void) |
| { |
| if (windows_api) |
| return; |
| |
| windows_api = _glapi_create_table_from_handle(windows_get_dl_handle(), "gl"); |
| assert(windows_api); |
| } |
| |
| static void windows_glapi_set_dispatch(void) |
| { |
| windows_glapi_create_table(); |
| _glapi_set_dispatch(windows_api); |
| } |
| |
| windowsContext * |
| windows_create_context(int pxfi, windowsContext *shared) |
| { |
| windowsContext *gc; |
| |
| gc = calloc(1, sizeof *gc); |
| if (gc == NULL) |
| return NULL; |
| |
| // create a temporary, invisible window |
| #define GL_TEMP_WINDOW_CLASS "glTempWndClass" |
| { |
| static wATOM glTempWndClass = 0; |
| |
| if (glTempWndClass == 0) { |
| WNDCLASSEX wc; |
| wc.cbSize = sizeof(WNDCLASSEX); |
| wc.style = CS_HREDRAW | CS_VREDRAW; |
| wc.lpfnWndProc = DefWindowProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = GetModuleHandle(NULL); |
| wc.hIcon = 0; |
| wc.hCursor = 0; |
| wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); |
| wc.lpszMenuName = NULL; |
| wc.lpszClassName = GL_TEMP_WINDOW_CLASS; |
| wc.hIconSm = 0; |
| RegisterClassEx(&wc); |
| } |
| } |
| |
| HWND hwnd = CreateWindowExA(0, |
| GL_TEMP_WINDOW_CLASS, |
| "glWindow", |
| 0, |
| 0, 0, 0, 0, |
| NULL, NULL, GetModuleHandle(NULL), NULL); |
| HDC hdc = GetDC(hwnd); |
| |
| // We must set the windows pixel format before we can create a WGL context |
| gc->pxfi = pxfi; |
| SetPixelFormat(hdc, gc->pxfi, NULL); |
| |
| gc->ctx = wglCreateContext(hdc); |
| |
| if (shared && gc->ctx) |
| wglShareLists(shared->ctx, gc->ctx); |
| |
| ReleaseDC(hwnd, hdc); |
| DestroyWindow(hwnd); |
| |
| if (!gc->ctx) |
| { |
| free(gc); |
| return NULL; |
| } |
| |
| return gc; |
| } |
| |
| windowsContext * |
| windows_create_context_attribs(int pxfi, windowsContext *shared, const int *attribList) |
| { |
| windowsContext *gc; |
| |
| gc = calloc(1, sizeof *gc); |
| if (gc == NULL) |
| return NULL; |
| |
| // create a temporary, invisible window |
| #define GL_TEMP_WINDOW_CLASS "glTempWndClass" |
| { |
| static wATOM glTempWndClass = 0; |
| |
| if (glTempWndClass == 0) { |
| WNDCLASSEX wc; |
| wc.cbSize = sizeof(WNDCLASSEX); |
| wc.style = CS_HREDRAW | CS_VREDRAW; |
| wc.lpfnWndProc = DefWindowProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = GetModuleHandle(NULL); |
| wc.hIcon = 0; |
| wc.hCursor = 0; |
| wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); |
| wc.lpszMenuName = NULL; |
| wc.lpszClassName = GL_TEMP_WINDOW_CLASS; |
| wc.hIconSm = 0; |
| RegisterClassEx(&wc); |
| } |
| } |
| |
| HWND hwnd = CreateWindowExA(0, |
| GL_TEMP_WINDOW_CLASS, |
| "glWindow", |
| 0, |
| 0, 0, 0, 0, |
| NULL, NULL, GetModuleHandle(NULL), NULL); |
| HDC hdc = GetDC(hwnd); |
| HGLRC shareContext = NULL; |
| if (shared) |
| shareContext = shared->ctx; |
| |
| // We must set the windows pixel format before we can create a WGL context |
| gc->pxfi = pxfi; |
| SetPixelFormat(hdc, gc->pxfi, NULL); |
| |
| gc->ctx = wglCreateContextAttribsARB(hdc, shareContext, attribList); |
| |
| ReleaseDC(hwnd, hdc); |
| DestroyWindow(hwnd); |
| |
| if (!gc->ctx) |
| { |
| free(gc); |
| return NULL; |
| } |
| |
| return gc; |
| } |
| |
| void |
| windows_destroy_context(windowsContext *context) |
| { |
| wglDeleteContext(context->ctx); |
| context->ctx = NULL; |
| free(context); |
| } |
| |
| int windows_bind_context(windowsContext *context, windowsDrawable *draw, windowsDrawable *read) |
| { |
| HDC drawDc = draw->callbacks->getdc(draw); |
| |
| if (!draw->pxfi) |
| { |
| SetPixelFormat(drawDc, context->pxfi, NULL); |
| draw->pxfi = context->pxfi; |
| } |
| |
| if ((read != NULL) && (read != draw)) |
| { |
| /* |
| If there is a separate read drawable, create a separate read DC, and |
| use the wglMakeContextCurrent extension to make the context current |
| drawing to one DC and reading from the other |
| |
| Should only occur when WGL_ARB_make_current_read extension is present |
| */ |
| HDC readDc = read->callbacks->getdc(read); |
| |
| BOOL ret = wglMakeContextCurrentARB(drawDc, readDc, context->ctx); |
| |
| read->callbacks->releasedc(read, readDc); |
| |
| if (!ret) { |
| printf("wglMakeContextCurrentARB error: %08x\n", (int)GetLastError()); |
| return FALSE; |
| } |
| } |
| else |
| { |
| /* Otherwise, just use wglMakeCurrent */ |
| BOOL ret = wglMakeCurrent(drawDc, context->ctx); |
| if (!ret) { |
| printf("wglMakeCurrent error: %08x\n", (int)GetLastError()); |
| return FALSE; |
| } |
| } |
| |
| draw->callbacks->releasedc(draw, drawDc); |
| |
| windows_glapi_set_dispatch(); |
| |
| return TRUE; |
| } |
| |
| void windows_unbind_context(windowsContext * context) |
| { |
| wglMakeCurrent(NULL, NULL); |
| } |
| |
| /* |
| * |
| */ |
| |
| void |
| windows_swap_buffers(windowsDrawable *draw) |
| { |
| HDC drawDc = GetDC(draw->hWnd); |
| SwapBuffers(drawDc); |
| ReleaseDC(draw->hWnd, drawDc); |
| } |
| |
| |
| typedef void (__stdcall * PFNGLADDSWAPHINTRECTWIN) (GLint x, GLint y, |
| GLsizei width, |
| GLsizei height); |
| |
| static void |
| glAddSwapHintRectWIN(GLint x, GLint y, GLsizei width, |
| GLsizei height) |
| { |
| PFNGLADDSWAPHINTRECTWIN proc = (PFNGLADDSWAPHINTRECTWIN)wglGetProcAddress("glAddSwapHintRectWIN"); |
| if (proc) |
| proc(x, y, width, height); |
| } |
| |
| void |
| windows_copy_subbuffer(windowsDrawable *draw, |
| int x, int y, int width, int height) |
| { |
| glAddSwapHintRectWIN(x, y, width, height); |
| windows_swap_buffers(draw); |
| } |
| |
| /* |
| * Helper function for calling a test function on an initial context |
| */ |
| static void |
| windows_call_with_context(void (*proc)(HDC, void *), void *args) |
| { |
| // create window class |
| #define WIN_GL_TEST_WINDOW_CLASS "GLTest" |
| { |
| static wATOM glTestWndClass = 0; |
| |
| if (glTestWndClass == 0) { |
| WNDCLASSEX wc; |
| |
| wc.cbSize = sizeof(WNDCLASSEX); |
| wc.style = CS_HREDRAW | CS_VREDRAW; |
| wc.lpfnWndProc = DefWindowProc; |
| wc.cbClsExtra = 0; |
| wc.cbWndExtra = 0; |
| wc.hInstance = GetModuleHandle(NULL); |
| wc.hIcon = 0; |
| wc.hCursor = 0; |
| wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); |
| wc.lpszMenuName = NULL; |
| wc.lpszClassName = WIN_GL_TEST_WINDOW_CLASS; |
| wc.hIconSm = 0; |
| glTestWndClass = RegisterClassEx(&wc); |
| } |
| } |
| |
| // create an invisible window for a scratch DC |
| HWND hwnd = CreateWindowExA(0, |
| WIN_GL_TEST_WINDOW_CLASS, |
| "GL Renderer Capabilities Test Window", |
| 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), |
| NULL); |
| if (hwnd) { |
| HDC hdc = GetDC(hwnd); |
| |
| // we must set a pixel format before we can create a context, just use the first one... |
| SetPixelFormat(hdc, 1, NULL); |
| HGLRC hglrc = wglCreateContext(hdc); |
| wglMakeCurrent(hdc, hglrc); |
| |
| // call the test function |
| proc(hdc, args); |
| |
| // clean up |
| wglMakeCurrent(NULL, NULL); |
| wglDeleteContext(hglrc); |
| ReleaseDC(hwnd, hdc); |
| DestroyWindow(hwnd); |
| } |
| } |
| |
| static void |
| windows_check_render_test(HDC hdc, void *args) |
| { |
| int *result = (int *)args; |
| |
| /* Rather than play linkage games using stdcall to ensure we get |
| glGetString from opengl32.dll here, use dlsym */ |
| void *dlhandle = windows_get_dl_handle(); |
| const char *(*proc)(int) = dlsym(dlhandle, "glGetString"); |
| const char *gl_renderer = (const char *)proc(GL_RENDERER); |
| |
| if ((!gl_renderer) || (strcasecmp(gl_renderer, "GDI Generic") == 0)) |
| *result = FALSE; |
| else |
| *result = TRUE; |
| } |
| |
| int |
| windows_check_renderer(void) |
| { |
| int result; |
| windows_call_with_context(windows_check_render_test, &result); |
| return result; |
| } |
| |
| typedef struct { |
| char *gl_extensions; |
| char *wgl_extensions; |
| } windows_extensions_result; |
| |
| static void |
| windows_extensions_test(HDC hdc, void *args) |
| { |
| windows_extensions_result *r = (windows_extensions_result *)args; |
| |
| void *dlhandle = windows_get_dl_handle(); |
| const char *(*proc)(int) = dlsym(dlhandle, "glGetString"); |
| |
| r->gl_extensions = strdup(proc(GL_EXTENSIONS)); |
| |
| wglResolveExtensionProcs(); |
| r->wgl_extensions = strdup(wglGetExtensionsStringARB(hdc)); |
| } |
| |
| void |
| windows_extensions(char **gl_extensions, char **wgl_extensions) |
| { |
| windows_extensions_result result; |
| |
| *gl_extensions = ""; |
| *wgl_extensions = ""; |
| |
| windows_call_with_context(windows_extensions_test, &result); |
| |
| *gl_extensions = result.gl_extensions; |
| *wgl_extensions = result.wgl_extensions; |
| } |