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