direct-to-native-GL for GLX clients on Cygwin ("Windows-DRI")
[mesa.git] / src / glx / windows / windowsgl.c
diff --git a/src/glx/windows/windowsgl.c b/src/glx/windows/windowsgl.c
new file mode 100644 (file)
index 0000000..56849da
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * 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", GetLastError());
+         return FALSE;
+      }
+   }
+   else
+   {
+      /* Otherwise, just use wglMakeCurrent */
+      BOOL ret = wglMakeCurrent(drawDc, context->ctx);
+      if (!ret) {
+         printf("wglMakeCurrent error: %08x\n", 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
+}