101e04c3fSmrg/* 201e04c3fSmrg * Copyright © 2014 Jon Turney 301e04c3fSmrg * 401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 501e04c3fSmrg * copy of this software and associated documentation files (the "Software"), 601e04c3fSmrg * to deal in the Software without restriction, including without limitation 701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the 901e04c3fSmrg * Software is furnished to do so, subject to the following conditions: 1001e04c3fSmrg * 1101e04c3fSmrg * The above copyright notice and this permission notice (including the next 1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the 1301e04c3fSmrg * Software. 1401e04c3fSmrg * 1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2101e04c3fSmrg * IN THE SOFTWARE. 2201e04c3fSmrg */ 2301e04c3fSmrg 2401e04c3fSmrg#include "windowsgl.h" 2501e04c3fSmrg#include "windowsgl_internal.h" 2601e04c3fSmrg 2701e04c3fSmrg#include "glapi.h" 2801e04c3fSmrg#include "wgl.h" 2901e04c3fSmrg 3001e04c3fSmrg#include <dlfcn.h> 3101e04c3fSmrg#include <assert.h> 3201e04c3fSmrg#include <stdio.h> 3301e04c3fSmrg#include <strings.h> 3401e04c3fSmrg 3501e04c3fSmrgstatic struct _glapi_table *windows_api = NULL; 3601e04c3fSmrg 3701e04c3fSmrgstatic void * 3801e04c3fSmrgwindows_get_dl_handle(void) 3901e04c3fSmrg{ 4001e04c3fSmrg static void *dl_handle = NULL; 4101e04c3fSmrg 4201e04c3fSmrg if (!dl_handle) 4301e04c3fSmrg dl_handle = dlopen("cygnativeGLthunk.dll", RTLD_NOW); 4401e04c3fSmrg 4501e04c3fSmrg return dl_handle; 4601e04c3fSmrg} 4701e04c3fSmrg 4801e04c3fSmrgstatic void 4901e04c3fSmrgwindows_glapi_create_table(void) 5001e04c3fSmrg{ 5101e04c3fSmrg if (windows_api) 5201e04c3fSmrg return; 5301e04c3fSmrg 5401e04c3fSmrg windows_api = _glapi_create_table_from_handle(windows_get_dl_handle(), "gl"); 5501e04c3fSmrg assert(windows_api); 5601e04c3fSmrg} 5701e04c3fSmrg 5801e04c3fSmrgstatic void windows_glapi_set_dispatch(void) 5901e04c3fSmrg{ 6001e04c3fSmrg windows_glapi_create_table(); 6101e04c3fSmrg _glapi_set_dispatch(windows_api); 6201e04c3fSmrg} 6301e04c3fSmrg 6401e04c3fSmrgwindowsContext * 6501e04c3fSmrgwindows_create_context(int pxfi, windowsContext *shared) 6601e04c3fSmrg{ 6701e04c3fSmrg windowsContext *gc; 6801e04c3fSmrg 6901e04c3fSmrg gc = calloc(1, sizeof *gc); 7001e04c3fSmrg if (gc == NULL) 7101e04c3fSmrg return NULL; 7201e04c3fSmrg 7301e04c3fSmrg // create a temporary, invisible window 7401e04c3fSmrg#define GL_TEMP_WINDOW_CLASS "glTempWndClass" 7501e04c3fSmrg { 7601e04c3fSmrg static wATOM glTempWndClass = 0; 7701e04c3fSmrg 7801e04c3fSmrg if (glTempWndClass == 0) { 7901e04c3fSmrg WNDCLASSEX wc; 8001e04c3fSmrg wc.cbSize = sizeof(WNDCLASSEX); 8101e04c3fSmrg wc.style = CS_HREDRAW | CS_VREDRAW; 8201e04c3fSmrg wc.lpfnWndProc = DefWindowProc; 8301e04c3fSmrg wc.cbClsExtra = 0; 8401e04c3fSmrg wc.cbWndExtra = 0; 8501e04c3fSmrg wc.hInstance = GetModuleHandle(NULL); 8601e04c3fSmrg wc.hIcon = 0; 8701e04c3fSmrg wc.hCursor = 0; 8801e04c3fSmrg wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 8901e04c3fSmrg wc.lpszMenuName = NULL; 9001e04c3fSmrg wc.lpszClassName = GL_TEMP_WINDOW_CLASS; 9101e04c3fSmrg wc.hIconSm = 0; 9201e04c3fSmrg RegisterClassEx(&wc); 9301e04c3fSmrg } 9401e04c3fSmrg } 9501e04c3fSmrg 9601e04c3fSmrg HWND hwnd = CreateWindowExA(0, 9701e04c3fSmrg GL_TEMP_WINDOW_CLASS, 9801e04c3fSmrg "glWindow", 9901e04c3fSmrg 0, 10001e04c3fSmrg 0, 0, 0, 0, 10101e04c3fSmrg NULL, NULL, GetModuleHandle(NULL), NULL); 10201e04c3fSmrg HDC hdc = GetDC(hwnd); 10301e04c3fSmrg 10401e04c3fSmrg // We must set the windows pixel format before we can create a WGL context 10501e04c3fSmrg gc->pxfi = pxfi; 10601e04c3fSmrg SetPixelFormat(hdc, gc->pxfi, NULL); 10701e04c3fSmrg 10801e04c3fSmrg gc->ctx = wglCreateContext(hdc); 10901e04c3fSmrg 11001e04c3fSmrg if (shared && gc->ctx) 11101e04c3fSmrg wglShareLists(shared->ctx, gc->ctx); 11201e04c3fSmrg 11301e04c3fSmrg ReleaseDC(hwnd, hdc); 11401e04c3fSmrg DestroyWindow(hwnd); 11501e04c3fSmrg 11601e04c3fSmrg if (!gc->ctx) 11701e04c3fSmrg { 11801e04c3fSmrg free(gc); 11901e04c3fSmrg return NULL; 12001e04c3fSmrg } 12101e04c3fSmrg 12201e04c3fSmrg return gc; 12301e04c3fSmrg} 12401e04c3fSmrg 12501e04c3fSmrgwindowsContext * 12601e04c3fSmrgwindows_create_context_attribs(int pxfi, windowsContext *shared, const int *attribList) 12701e04c3fSmrg{ 12801e04c3fSmrg windowsContext *gc; 12901e04c3fSmrg 13001e04c3fSmrg gc = calloc(1, sizeof *gc); 13101e04c3fSmrg if (gc == NULL) 13201e04c3fSmrg return NULL; 13301e04c3fSmrg 13401e04c3fSmrg // create a temporary, invisible window 13501e04c3fSmrg#define GL_TEMP_WINDOW_CLASS "glTempWndClass" 13601e04c3fSmrg { 13701e04c3fSmrg static wATOM glTempWndClass = 0; 13801e04c3fSmrg 13901e04c3fSmrg if (glTempWndClass == 0) { 14001e04c3fSmrg WNDCLASSEX wc; 14101e04c3fSmrg wc.cbSize = sizeof(WNDCLASSEX); 14201e04c3fSmrg wc.style = CS_HREDRAW | CS_VREDRAW; 14301e04c3fSmrg wc.lpfnWndProc = DefWindowProc; 14401e04c3fSmrg wc.cbClsExtra = 0; 14501e04c3fSmrg wc.cbWndExtra = 0; 14601e04c3fSmrg wc.hInstance = GetModuleHandle(NULL); 14701e04c3fSmrg wc.hIcon = 0; 14801e04c3fSmrg wc.hCursor = 0; 14901e04c3fSmrg wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 15001e04c3fSmrg wc.lpszMenuName = NULL; 15101e04c3fSmrg wc.lpszClassName = GL_TEMP_WINDOW_CLASS; 15201e04c3fSmrg wc.hIconSm = 0; 15301e04c3fSmrg RegisterClassEx(&wc); 15401e04c3fSmrg } 15501e04c3fSmrg } 15601e04c3fSmrg 15701e04c3fSmrg HWND hwnd = CreateWindowExA(0, 15801e04c3fSmrg GL_TEMP_WINDOW_CLASS, 15901e04c3fSmrg "glWindow", 16001e04c3fSmrg 0, 16101e04c3fSmrg 0, 0, 0, 0, 16201e04c3fSmrg NULL, NULL, GetModuleHandle(NULL), NULL); 16301e04c3fSmrg HDC hdc = GetDC(hwnd); 16401e04c3fSmrg HGLRC shareContext = NULL; 16501e04c3fSmrg if (shared) 16601e04c3fSmrg shareContext = shared->ctx; 16701e04c3fSmrg 16801e04c3fSmrg // We must set the windows pixel format before we can create a WGL context 16901e04c3fSmrg gc->pxfi = pxfi; 17001e04c3fSmrg SetPixelFormat(hdc, gc->pxfi, NULL); 17101e04c3fSmrg 17201e04c3fSmrg gc->ctx = wglCreateContextAttribsARB(hdc, shareContext, attribList); 17301e04c3fSmrg 17401e04c3fSmrg ReleaseDC(hwnd, hdc); 17501e04c3fSmrg DestroyWindow(hwnd); 17601e04c3fSmrg 17701e04c3fSmrg if (!gc->ctx) 17801e04c3fSmrg { 17901e04c3fSmrg free(gc); 18001e04c3fSmrg return NULL; 18101e04c3fSmrg } 18201e04c3fSmrg 18301e04c3fSmrg return gc; 18401e04c3fSmrg} 18501e04c3fSmrg 18601e04c3fSmrgvoid 18701e04c3fSmrgwindows_destroy_context(windowsContext *context) 18801e04c3fSmrg{ 18901e04c3fSmrg wglDeleteContext(context->ctx); 19001e04c3fSmrg context->ctx = NULL; 19101e04c3fSmrg free(context); 19201e04c3fSmrg} 19301e04c3fSmrg 19401e04c3fSmrgint windows_bind_context(windowsContext *context, windowsDrawable *draw, windowsDrawable *read) 19501e04c3fSmrg{ 19601e04c3fSmrg HDC drawDc = draw->callbacks->getdc(draw); 19701e04c3fSmrg 19801e04c3fSmrg if (!draw->pxfi) 19901e04c3fSmrg { 20001e04c3fSmrg SetPixelFormat(drawDc, context->pxfi, NULL); 20101e04c3fSmrg draw->pxfi = context->pxfi; 20201e04c3fSmrg } 20301e04c3fSmrg 20401e04c3fSmrg if ((read != NULL) && (read != draw)) 20501e04c3fSmrg { 20601e04c3fSmrg /* 20701e04c3fSmrg If there is a separate read drawable, create a separate read DC, and 20801e04c3fSmrg use the wglMakeContextCurrent extension to make the context current 20901e04c3fSmrg drawing to one DC and reading from the other 21001e04c3fSmrg 21101e04c3fSmrg Should only occur when WGL_ARB_make_current_read extension is present 21201e04c3fSmrg */ 21301e04c3fSmrg HDC readDc = read->callbacks->getdc(read); 21401e04c3fSmrg 21501e04c3fSmrg BOOL ret = wglMakeContextCurrentARB(drawDc, readDc, context->ctx); 21601e04c3fSmrg 21701e04c3fSmrg read->callbacks->releasedc(read, readDc); 21801e04c3fSmrg 21901e04c3fSmrg if (!ret) { 2207ec681f3Smrg printf("wglMakeContextCurrentARB error: %08x\n", (int)GetLastError()); 22101e04c3fSmrg return FALSE; 22201e04c3fSmrg } 22301e04c3fSmrg } 22401e04c3fSmrg else 22501e04c3fSmrg { 22601e04c3fSmrg /* Otherwise, just use wglMakeCurrent */ 22701e04c3fSmrg BOOL ret = wglMakeCurrent(drawDc, context->ctx); 22801e04c3fSmrg if (!ret) { 2297ec681f3Smrg printf("wglMakeCurrent error: %08x\n", (int)GetLastError()); 23001e04c3fSmrg return FALSE; 23101e04c3fSmrg } 23201e04c3fSmrg } 23301e04c3fSmrg 23401e04c3fSmrg draw->callbacks->releasedc(draw, drawDc); 23501e04c3fSmrg 23601e04c3fSmrg windows_glapi_set_dispatch(); 23701e04c3fSmrg 23801e04c3fSmrg return TRUE; 23901e04c3fSmrg} 24001e04c3fSmrg 24101e04c3fSmrgvoid windows_unbind_context(windowsContext * context) 24201e04c3fSmrg{ 24301e04c3fSmrg wglMakeCurrent(NULL, NULL); 24401e04c3fSmrg} 24501e04c3fSmrg 24601e04c3fSmrg/* 24701e04c3fSmrg * 24801e04c3fSmrg */ 24901e04c3fSmrg 25001e04c3fSmrgvoid 25101e04c3fSmrgwindows_swap_buffers(windowsDrawable *draw) 25201e04c3fSmrg{ 25301e04c3fSmrg HDC drawDc = GetDC(draw->hWnd); 25401e04c3fSmrg SwapBuffers(drawDc); 25501e04c3fSmrg ReleaseDC(draw->hWnd, drawDc); 25601e04c3fSmrg} 25701e04c3fSmrg 25801e04c3fSmrg 25901e04c3fSmrgtypedef void (__stdcall * PFNGLADDSWAPHINTRECTWIN) (GLint x, GLint y, 26001e04c3fSmrg GLsizei width, 26101e04c3fSmrg GLsizei height); 26201e04c3fSmrg 26301e04c3fSmrgstatic void 26401e04c3fSmrgglAddSwapHintRectWIN(GLint x, GLint y, GLsizei width, 26501e04c3fSmrg GLsizei height) 26601e04c3fSmrg{ 26701e04c3fSmrg PFNGLADDSWAPHINTRECTWIN proc = (PFNGLADDSWAPHINTRECTWIN)wglGetProcAddress("glAddSwapHintRectWIN"); 26801e04c3fSmrg if (proc) 26901e04c3fSmrg proc(x, y, width, height); 27001e04c3fSmrg} 27101e04c3fSmrg 27201e04c3fSmrgvoid 27301e04c3fSmrgwindows_copy_subbuffer(windowsDrawable *draw, 27401e04c3fSmrg int x, int y, int width, int height) 27501e04c3fSmrg{ 27601e04c3fSmrg glAddSwapHintRectWIN(x, y, width, height); 27701e04c3fSmrg windows_swap_buffers(draw); 27801e04c3fSmrg} 27901e04c3fSmrg 28001e04c3fSmrg/* 28101e04c3fSmrg * Helper function for calling a test function on an initial context 28201e04c3fSmrg */ 28301e04c3fSmrgstatic void 28401e04c3fSmrgwindows_call_with_context(void (*proc)(HDC, void *), void *args) 28501e04c3fSmrg{ 28601e04c3fSmrg // create window class 28701e04c3fSmrg#define WIN_GL_TEST_WINDOW_CLASS "GLTest" 28801e04c3fSmrg { 28901e04c3fSmrg static wATOM glTestWndClass = 0; 29001e04c3fSmrg 29101e04c3fSmrg if (glTestWndClass == 0) { 29201e04c3fSmrg WNDCLASSEX wc; 29301e04c3fSmrg 29401e04c3fSmrg wc.cbSize = sizeof(WNDCLASSEX); 29501e04c3fSmrg wc.style = CS_HREDRAW | CS_VREDRAW; 29601e04c3fSmrg wc.lpfnWndProc = DefWindowProc; 29701e04c3fSmrg wc.cbClsExtra = 0; 29801e04c3fSmrg wc.cbWndExtra = 0; 29901e04c3fSmrg wc.hInstance = GetModuleHandle(NULL); 30001e04c3fSmrg wc.hIcon = 0; 30101e04c3fSmrg wc.hCursor = 0; 30201e04c3fSmrg wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 30301e04c3fSmrg wc.lpszMenuName = NULL; 30401e04c3fSmrg wc.lpszClassName = WIN_GL_TEST_WINDOW_CLASS; 30501e04c3fSmrg wc.hIconSm = 0; 30601e04c3fSmrg glTestWndClass = RegisterClassEx(&wc); 30701e04c3fSmrg } 30801e04c3fSmrg } 30901e04c3fSmrg 31001e04c3fSmrg // create an invisible window for a scratch DC 31101e04c3fSmrg HWND hwnd = CreateWindowExA(0, 31201e04c3fSmrg WIN_GL_TEST_WINDOW_CLASS, 31301e04c3fSmrg "GL Renderer Capabilities Test Window", 31401e04c3fSmrg 0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), 31501e04c3fSmrg NULL); 31601e04c3fSmrg if (hwnd) { 31701e04c3fSmrg HDC hdc = GetDC(hwnd); 31801e04c3fSmrg 31901e04c3fSmrg // we must set a pixel format before we can create a context, just use the first one... 32001e04c3fSmrg SetPixelFormat(hdc, 1, NULL); 32101e04c3fSmrg HGLRC hglrc = wglCreateContext(hdc); 32201e04c3fSmrg wglMakeCurrent(hdc, hglrc); 32301e04c3fSmrg 32401e04c3fSmrg // call the test function 32501e04c3fSmrg proc(hdc, args); 32601e04c3fSmrg 32701e04c3fSmrg // clean up 32801e04c3fSmrg wglMakeCurrent(NULL, NULL); 32901e04c3fSmrg wglDeleteContext(hglrc); 33001e04c3fSmrg ReleaseDC(hwnd, hdc); 33101e04c3fSmrg DestroyWindow(hwnd); 33201e04c3fSmrg } 33301e04c3fSmrg} 33401e04c3fSmrg 33501e04c3fSmrgstatic void 33601e04c3fSmrgwindows_check_render_test(HDC hdc, void *args) 33701e04c3fSmrg{ 33801e04c3fSmrg int *result = (int *)args; 33901e04c3fSmrg 34001e04c3fSmrg /* Rather than play linkage games using stdcall to ensure we get 34101e04c3fSmrg glGetString from opengl32.dll here, use dlsym */ 34201e04c3fSmrg void *dlhandle = windows_get_dl_handle(); 34301e04c3fSmrg const char *(*proc)(int) = dlsym(dlhandle, "glGetString"); 34401e04c3fSmrg const char *gl_renderer = (const char *)proc(GL_RENDERER); 34501e04c3fSmrg 34601e04c3fSmrg if ((!gl_renderer) || (strcasecmp(gl_renderer, "GDI Generic") == 0)) 34701e04c3fSmrg *result = FALSE; 34801e04c3fSmrg else 34901e04c3fSmrg *result = TRUE; 35001e04c3fSmrg} 35101e04c3fSmrg 35201e04c3fSmrgint 35301e04c3fSmrgwindows_check_renderer(void) 35401e04c3fSmrg{ 35501e04c3fSmrg int result; 35601e04c3fSmrg windows_call_with_context(windows_check_render_test, &result); 35701e04c3fSmrg return result; 35801e04c3fSmrg} 35901e04c3fSmrg 36001e04c3fSmrgtypedef struct { 36101e04c3fSmrg char *gl_extensions; 36201e04c3fSmrg char *wgl_extensions; 36301e04c3fSmrg} windows_extensions_result; 36401e04c3fSmrg 36501e04c3fSmrgstatic void 36601e04c3fSmrgwindows_extensions_test(HDC hdc, void *args) 36701e04c3fSmrg{ 36801e04c3fSmrg windows_extensions_result *r = (windows_extensions_result *)args; 36901e04c3fSmrg 37001e04c3fSmrg void *dlhandle = windows_get_dl_handle(); 37101e04c3fSmrg const char *(*proc)(int) = dlsym(dlhandle, "glGetString"); 37201e04c3fSmrg 37301e04c3fSmrg r->gl_extensions = strdup(proc(GL_EXTENSIONS)); 37401e04c3fSmrg 37501e04c3fSmrg wglResolveExtensionProcs(); 37601e04c3fSmrg r->wgl_extensions = strdup(wglGetExtensionsStringARB(hdc)); 37701e04c3fSmrg} 37801e04c3fSmrg 37901e04c3fSmrgvoid 38001e04c3fSmrgwindows_extensions(char **gl_extensions, char **wgl_extensions) 38101e04c3fSmrg{ 38201e04c3fSmrg windows_extensions_result result; 38301e04c3fSmrg 38401e04c3fSmrg *gl_extensions = ""; 38501e04c3fSmrg *wgl_extensions = ""; 38601e04c3fSmrg 38701e04c3fSmrg windows_call_with_context(windows_extensions_test, &result); 38801e04c3fSmrg 38901e04c3fSmrg *gl_extensions = result.gl_extensions; 39001e04c3fSmrg *wgl_extensions = result.wgl_extensions; 39101e04c3fSmrg} 392