Home | History | Annotate | Download | only in windows
      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