/* * 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 #include #include #include 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; } void windows_setTexBuffer(windowsContext *context, int textureTarget, int textureFormat, windowsDrawable *drawable) { // not yet implemented } void windows_releaseTexBuffer(windowsContext *context, int textureTarget, windowsDrawable *drawable) { // not yet implemented }