[cogl/wip/cogl-winsys-wgl: 4/4] Add a WGL winsys



commit 1866fb137acc281e3bed0b688d6bb8b7973999df
Author: Neil Roberts <neil linux intel com>
Date:   Mon May 9 17:48:50 2011 +0100

    Add a WGL winsys
    
    This adds a full winsys to handle WGL and Win32.

 cogl/Makefile.am                                |    5 +
 cogl/cogl-framebuffer-private.h                 |    8 +
 cogl/cogl-framebuffer.c                         |   29 +
 cogl/cogl-framebuffer.h                         |   15 +
 cogl/cogl-renderer.c                            |    8 +-
 cogl/cogl.h                                     |    3 +
 cogl/winsys/cogl-winsys-private.h               |    5 +
 cogl/winsys/cogl-winsys-wgl-feature-functions.h |   38 +
 cogl/winsys/cogl-winsys-wgl.c                   |  838 +++++++++++++++++++++++
 configure.ac                                    |   26 +
 10 files changed, 974 insertions(+), 1 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index b3d7d3b..f8e4f57 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -319,6 +319,11 @@ cogl_sources_c += \
 	$(srcdir)/winsys/cogl-winsys-glx-feature-functions.h \
 	$(srcdir)/winsys/cogl-winsys-glx.c
 endif
+if SUPPORT_WGL
+cogl_sources_c += \
+	$(srcdir)/winsys/cogl-winsys-wgl.c \
+	$(srcdir)/winsys/cogl-winsys-wgl-feature-functions.h
+endif
 if SUPPORT_EGL_PLATFORM_POWERVR_X11
 cogl_sources_c += \
 	$(srcdir)/winsys/cogl-winsys-egl.c
diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h
index f48938f..42d7bab 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -38,6 +38,10 @@
 #include <GL/glxext.h>
 #endif
 
+#ifdef COGL_HAS_WIN32_SUPPORT
+#include <windows.h>
+#endif
+
 typedef enum _CoglFramebufferType {
   COGL_FRAMEBUFFER_TYPE_ONSCREEN,
   COGL_FRAMEBUFFER_TYPE_OFFSCREEN
@@ -122,6 +126,10 @@ struct _CoglOnscreen
   void *foreign_update_mask_data;
 #endif
 
+#ifdef COGL_HAS_WIN32_SUPPORT
+  HWND foreign_hwnd;
+#endif
+
   gboolean swap_throttled;
 
   void *winsys;
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 6afcd3f..73cce73 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -1679,6 +1679,35 @@ cogl_onscreen_x11_get_visual_xid (CoglOnscreen *onscreen)
 }
 #endif /* COGL_HAS_X11_SUPPORT */
 
+#ifdef COGL_HAS_WIN32_SUPPORT
+
+void
+cogl_onscreen_win32_set_foreign_window (CoglOnscreen *onscreen,
+                                        HWND hwnd)
+{
+  onscreen->foreign_hwnd = hwnd;
+}
+
+HWND
+cogl_onscreen_win32_get_window (CoglOnscreen *onscreen)
+{
+  if (onscreen->foreign_hwnd)
+    return onscreen->foreign_hwnd;
+  else
+    {
+      CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+      const CoglWinsysVtable *winsys =
+        _cogl_framebuffer_get_winsys (framebuffer);
+
+      /* This should only be called for win32 onscreens */
+      g_return_val_if_fail (winsys->onscreen_win32_get_window != NULL, 0);
+
+      return winsys->onscreen_win32_get_window (onscreen);
+    }
+}
+
+#endif /* COGL_HAS_WIN32_SUPPORT */
+
 unsigned int
 cogl_framebuffer_add_swap_buffers_callback (CoglFramebuffer *framebuffer,
                                             CoglSwapBuffersNotify callback,
diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h
index b685031..02188b7 100644
--- a/cogl/cogl-framebuffer.h
+++ b/cogl/cogl-framebuffer.h
@@ -30,6 +30,9 @@
 
 #include <glib.h>
 
+#ifdef COGL_HAS_WIN32
+#include <windows.h>
+#endif /* COGL_HAS_WIN32 */
 
 G_BEGIN_DECLS
 
@@ -157,6 +160,18 @@ guint32
 cogl_onscreen_x11_get_visual_xid (CoglOnscreen *onscreen);
 #endif /* COGL_HAS_X11 */
 
+#ifdef COGL_HAS_WIN32_SUPPORT
+#define cogl_onscreen_win32_set_foreign_window \
+  cogl_onscreen_win32_set_foreign_window_EXP
+void
+cogl_onscreen_win32_set_foreign_window (CoglOnscreen *onscreen,
+                                        HWND hwnd);
+
+#define cogl_onscreen_win32_get_window cogl_onscreen_win32_get_window_EXP
+HWND
+cogl_onscreen_win32_get_window (CoglOnscreen *onscreen);
+#endif /* COGL_HAS_WIN32_SUPPORT */
+
 #define cogl_onscreen_set_swap_throttled cogl_onscreen_set_swap_throttled_EXP
 void
 cogl_onscreen_set_swap_throttled (CoglOnscreen *onscreen,
diff --git a/cogl/cogl-renderer.c b/cogl/cogl-renderer.c
index 93e33a4..df05247 100644
--- a/cogl/cogl-renderer.c
+++ b/cogl/cogl-renderer.c
@@ -48,6 +48,9 @@ extern const CoglWinsysVtable *_cogl_winsys_glx_get_vtable (void);
 #ifdef COGL_HAS_EGL_SUPPORT
 extern const CoglWinsysVtable *_cogl_winsys_egl_get_vtable (void);
 #endif
+#ifdef COGL_HAS_WGL_SUPPORT
+extern const CoglWinsysVtable *_cogl_winsys_wgl_get_vtable (void);
+#endif
 
 typedef const CoglWinsysVtable *(*CoglWinsysVtableGetter) (void);
 
@@ -57,7 +60,10 @@ static CoglWinsysVtableGetter _cogl_winsys_vtable_getters[] =
   _cogl_winsys_glx_get_vtable,
 #endif
 #ifdef COGL_HAS_EGL_SUPPORT
-  _cogl_winsys_egl_get_vtable
+  _cogl_winsys_egl_get_vtable,
+#endif
+#ifdef COGL_HAS_WGL_SUPPORT
+  _cogl_winsys_wgl_get_vtable,
 #endif
 };
 
diff --git a/cogl/cogl.h b/cogl/cogl.h
index cbd2d0c..75c37de 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -88,6 +88,9 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
 #ifdef COGL_HAS_XLIB
 #include <cogl/cogl-xlib.h>
 #endif
+#ifdef COGL_HAS_WIN32
+#include <cogl/cogl-win32.h>
+#endif
 /* XXX: This will definitly go away once all the Clutter winsys
  * code has been migrated down into Cogl! */
 #include <cogl/cogl-clutter.h>
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index 3942b7c..2208a86 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -118,6 +118,11 @@ typedef struct _CoglWinsysVtable
   guint32
   (*onscreen_x11_get_window_xid) (CoglOnscreen *onscreen);
 
+#ifdef COGL_HAS_WIN32_SUPPORT
+  HWND
+  (*onscreen_win32_get_window) (CoglOnscreen *onscreen);
+#endif
+
   unsigned int
   (*onscreen_add_swap_buffers_callback) (CoglOnscreen *onscreen,
                                          CoglSwapBuffersNotify callback,
diff --git a/cogl/winsys/cogl-winsys-wgl-feature-functions.h b/cogl/winsys/cogl-winsys-wgl-feature-functions.h
new file mode 100644
index 0000000..d22d9c3
--- /dev/null
+++ b/cogl/winsys/cogl-winsys-wgl-feature-functions.h
@@ -0,0 +1,38 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Authors:
+ *   Neil Roberts <neil linux intel com>
+ */
+
+/* See cogl-winsys-glx-feature-functions.h for a description of how
+   this file is used */
+
+COGL_WINSYS_FEATURE_BEGIN (swap_control,
+                           "EXT\0",
+                           "swap_control\0",
+                           0,
+                           0,
+                           COGL_WINSYS_FEATURE_SWAP_THROTTLE)
+COGL_WINSYS_FEATURE_FUNCTION (int, wglSwapInterval,
+                              (int interval))
+COGL_WINSYS_FEATURE_END ()
diff --git a/cogl/winsys/cogl-winsys-wgl.c b/cogl/winsys/cogl-winsys-wgl.c
new file mode 100644
index 0000000..86a19c5
--- /dev/null
+++ b/cogl/winsys/cogl-winsys-wgl.c
@@ -0,0 +1,838 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2010,2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Authors:
+ *   Neil Roberts <neil linux intel com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <windows.h>
+
+#include "cogl.h"
+
+#include "cogl-winsys-private.h"
+#include "cogl-context-private.h"
+#include "cogl-framebuffer.h"
+#include "cogl-swap-chain-private.h"
+#include "cogl-renderer-private.h"
+#include "cogl-display-private.h"
+#include "cogl-onscreen-template-private.h"
+#include "cogl-private.h"
+#include "cogl-feature-private.h"
+
+typedef struct _CoglRendererWgl
+{
+  /* Function pointers for GLX specific extensions */
+#define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d, e, f)
+
+#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \
+  ret (APIENTRY * pf_ ## name) args;
+
+#define COGL_WINSYS_FEATURE_END()
+
+#include "cogl-winsys-wgl-feature-functions.h"
+
+#undef COGL_WINSYS_FEATURE_BEGIN
+#undef COGL_WINSYS_FEATURE_FUNCTION
+#undef COGL_WINSYS_FEATURE_END
+} CoglRendererWgl;
+
+typedef struct _CoglDisplayWgl
+{
+  ATOM window_class;
+  HGLRC wgl_context;
+  HWND dummy_hwnd;
+  HDC dummy_dc;
+} CoglDisplayWgl;
+
+typedef struct _CoglOnscreenWin32
+{
+  HWND hwnd;
+  gboolean is_foreign_hwnd;
+} CoglOnscreenWin32;
+
+typedef struct _CoglContextWgl
+{
+  HDC current_dc;
+} CoglContextWgl;
+
+typedef struct _CoglOnscreenWgl
+{
+  CoglOnscreenWin32 _parent;
+
+  HDC client_dc;
+
+  gboolean swap_throttled;
+} CoglOnscreenWgl;
+
+/* Define a set of arrays containing the functions required from GL
+   for each winsys feature */
+#define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names,    \
+                                  feature_flags, feature_flags_private, \
+                                  winsys_feature)                       \
+  static const CoglFeatureFunction                                      \
+  cogl_wgl_feature_ ## name ## _funcs[] = {
+#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)                   \
+  { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglRendererWgl, pf_ ## name) },
+#define COGL_WINSYS_FEATURE_END()               \
+  { NULL, 0 },                                  \
+    };
+#include "cogl-winsys-wgl-feature-functions.h"
+
+/* Define an array of features */
+#undef COGL_WINSYS_FEATURE_BEGIN
+#define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names,    \
+                                  feature_flags, feature_flags_private, \
+                                  winsys_feature)                       \
+  { 255, 255, namespaces, extension_names,                              \
+      feature_flags, feature_flags_private,                             \
+      winsys_feature,                                                   \
+      cogl_wgl_feature_ ## name ## _funcs },
+#undef COGL_WINSYS_FEATURE_FUNCTION
+#define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)
+#undef COGL_WINSYS_FEATURE_END
+#define COGL_WINSYS_FEATURE_END()
+
+static const CoglFeatureData winsys_feature_data[] =
+  {
+#include "cogl-winsys-wgl-feature-functions.h"
+  };
+
+static CoglFuncPtr
+_cogl_winsys_get_proc_address (const char *name)
+{
+  return (CoglFuncPtr) wglGetProcAddress ((LPCSTR) name);
+}
+
+static void
+_cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
+{
+  g_slice_free (CoglRendererWgl, renderer->winsys);
+}
+
+static CoglOnscreen *
+find_onscreen_for_hwnd (CoglContext *context, HWND hwnd)
+{
+  CoglDisplayWgl *display_wgl = context->display->winsys;
+  GList *l;
+
+  /* If the hwnd has Cogl's window class then we can lookup the
+     onscreen pointer directly by reading the extra window data */
+  if (GetClassLongPtr (hwnd, GCW_ATOM) == display_wgl->window_class)
+    {
+      CoglOnscreen *onscreen = (CoglOnscreen *) GetWindowLongPtr (hwnd, 0);
+
+      if (onscreen)
+        return onscreen;
+    }
+
+  for (l = context->framebuffers; l; l = l->next)
+    {
+      CoglFramebuffer *framebuffer = l->data;
+
+      if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+        {
+          CoglOnscreenWin32 *win32_onscreen =
+            COGL_ONSCREEN (framebuffer)->winsys;
+
+          if (win32_onscreen->hwnd == hwnd)
+            return COGL_ONSCREEN (framebuffer);
+        }
+    }
+
+  return NULL;
+}
+
+static CoglFilterReturn
+win32_event_filter_cb (void *native_event, void *data)
+{
+  MSG *msg = native_event;
+  CoglContext *context = data;
+
+  if (msg->message == WM_SIZE)
+    {
+      CoglOnscreen *onscreen =
+        find_onscreen_for_hwnd (context, msg->hwnd);
+
+      if (onscreen)
+        {
+          CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+
+          /* Ignore size changes resulting from the stage being
+             minimized - otherwise it will think the window has been
+             resized to 0,0 */
+          if (msg->wParam != SIZE_MINIMIZED)
+            {
+              WORD new_width = LOWORD (msg->lParam);
+              WORD new_height = HIWORD (msg->lParam);
+              _cogl_framebuffer_winsys_update_size (framebuffer,
+                                                    new_width,
+                                                    new_height);
+            }
+        }
+    }
+
+  return COGL_FILTER_CONTINUE;
+}
+
+static gboolean
+_cogl_winsys_renderer_connect (CoglRenderer *renderer,
+                               GError **error)
+{
+  renderer->winsys = g_slice_new0 (CoglRendererWgl);
+
+  return TRUE;
+}
+
+static LRESULT CALLBACK
+window_proc (HWND hwnd, UINT umsg,
+             WPARAM wparam, LPARAM lparam)
+{
+  gboolean message_handled = FALSE;
+  CoglOnscreen *onscreen;
+
+  /* It's not clear what the best thing to do with messages sent to
+     the window proc is. We want the application to forward on all
+     messages through Cogl so that it can have a chance to process
+     them which might mean that that in it's GetMessage loop it could
+     call cogl_renderer_handle_native_event for every message. However
+     the message loop would usually call DispatchMessage as well which
+     mean this window proc would be invoked and Cogl would see the
+     message twice. However we can't just ignore messages in the
+     window proc because some messages are sent directly from windows
+     without going through the message queue. This function therefore
+     just forwards on all messages directly. This means that the
+     application is not expected to forward on messages if it has let
+     Cogl create the window itself because it will already see them
+     via the window proc. This limits the kinds of messages that Cogl
+     can handle to ones that are sent to the windows it creates, but I
+     think that is a reasonable restriction */
+
+  /* Convert the message to a MSG struct and pass it through the Cogl
+     message handling mechanism */
+
+  /* This window proc is only called for messages created with Cogl's
+     window class so we should be able to work out the corresponding
+     window class by looking in the extra window data. Windows will
+     send some extra messages before we get a chance to set this value
+     so we have to ignore these */
+  onscreen = (CoglOnscreen *) GetWindowLongPtr (hwnd, 0);
+
+  if (onscreen != NULL)
+    {
+      CoglRenderer *renderer;
+      DWORD message_pos;
+      MSG msg;
+
+      msg.hwnd = hwnd;
+      msg.message = umsg;
+      msg.wParam = wparam;
+      msg.lParam = lparam;
+      msg.time = GetMessageTime ();
+      /* Neither MAKE_POINTS nor GET_[XY]_LPARAM is defined in MinGW
+         headers so we need to convert to a signed type explicitly */
+      message_pos = GetMessagePos ();
+      msg.pt.x = (SHORT) LOWORD (message_pos);
+      msg.pt.y = (SHORT) HIWORD (message_pos);
+
+      renderer = COGL_FRAMEBUFFER (onscreen)->context->display->renderer;
+
+      message_handled =
+        cogl_renderer_handle_native_event (renderer, &msg);
+    }
+
+  if (!message_handled)
+    return DefWindowProcW (hwnd, umsg, wparam, lparam);
+  else
+    return 0;
+}
+
+static gboolean
+pixel_format_is_better (const PIXELFORMATDESCRIPTOR *pfa,
+                        const PIXELFORMATDESCRIPTOR *pfb)
+{
+  /* Always prefer a format with a stencil buffer */
+  if (pfa->cStencilBits == 0)
+    {
+      if (pfb->cStencilBits > 0)
+        return TRUE;
+    }
+  else if (pfb->cStencilBits == 0)
+    return FALSE;
+
+  /* Prefer a bigger color buffer */
+  if (pfb->cColorBits > pfa->cColorBits)
+    return TRUE;
+  else if (pfb->cColorBits < pfa->cColorBits)
+    return FALSE;
+
+  /* Prefer a bigger depth buffer */
+  return pfb->cDepthBits > pfa->cDepthBits;
+}
+
+static int
+choose_pixel_format (HDC dc, PIXELFORMATDESCRIPTOR *pfd)
+{
+  int i, num_formats, best_pf = 0;
+  PIXELFORMATDESCRIPTOR best_pfd;
+
+  num_formats = DescribePixelFormat (dc, 0, sizeof (best_pfd), NULL);
+
+  for (i = 1; i <= num_formats; i++)
+    {
+      memset (pfd, 0, sizeof (*pfd));
+
+      if (DescribePixelFormat (dc, i, sizeof (best_pfd), pfd) &&
+          /* Check whether this format is useable by Cogl */
+          ((pfd->dwFlags & (PFD_SUPPORT_OPENGL |
+                            PFD_DRAW_TO_WINDOW |
+                            PFD_DOUBLEBUFFER |
+                            PFD_GENERIC_FORMAT)) ==
+           (PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW)) &&
+          pfd->iPixelType == PFD_TYPE_RGBA &&
+          pfd->cColorBits >= 16 && pfd->cColorBits <= 32 &&
+          pfd->cDepthBits >= 16 && pfd->cDepthBits <= 32 &&
+          /* Check whether this is a better format than one we've
+             already found */
+          (best_pf == 0 || pixel_format_is_better (&best_pfd, pfd)))
+        {
+          best_pf = i;
+          best_pfd = *pfd;
+        }
+    }
+
+  *pfd = best_pfd;
+
+  return best_pf;
+}
+
+static gboolean
+create_window_class (CoglDisplay *display, GError **error)
+{
+  CoglDisplayWgl *wgl_display = display->winsys;
+  char *class_name_ascii, *src;
+  WCHAR *class_name_wchar, *dst;
+  WNDCLASSW wndclass;
+
+  /* We create a window class per display so that we have an
+     opportunity to clean up the class when the display is
+     destroyed */
+
+  /* Generate a unique name containing the address of the display */
+  class_name_ascii = g_strdup_printf ("CoglWindow0x%0*" G_GINTPTR_MODIFIER "x",
+                                      sizeof (guintptr) * 2,
+                                      (guintptr) display);
+  /* Convert it to WCHARs */
+  class_name_wchar = g_malloc ((strlen (class_name_ascii) + 1) *
+                               sizeof (WCHAR));
+  for (src = class_name_ascii, dst = class_name_wchar;
+       *src;
+       src++, dst++)
+    *dst = *src;
+  *dst = L'\0';
+
+  memset (&wndclass, 0, sizeof (wndclass));
+  wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
+  wndclass.lpfnWndProc = window_proc;
+  /* We reserve extra space in the window data for a pointer back to
+     the CoglOnscreen */
+  wndclass.cbWndExtra = sizeof (LONG_PTR);
+  wndclass.hInstance = GetModuleHandleW (NULL);
+  wndclass.hIcon = LoadIconW (NULL, (LPWSTR) IDI_APPLICATION);
+  wndclass.hCursor = LoadCursorW (NULL, (LPWSTR) IDC_ARROW);
+  wndclass.hbrBackground = NULL;
+  wndclass.lpszMenuName = NULL;
+  wndclass.lpszClassName = class_name_wchar;
+  wgl_display->window_class = RegisterClassW (&wndclass);
+
+  g_free (class_name_wchar);
+  g_free (class_name_ascii);
+
+  if (wgl_display->window_class == 0)
+    {
+      g_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                   "Unable to register window class");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+create_context (CoglDisplay *display, GError **error)
+{
+  CoglDisplayWgl *wgl_display = display->winsys;
+
+  g_return_val_if_fail (wgl_display->wgl_context == NULL, FALSE);
+
+  /* COGL assumes that there is always a GL context selected; in order
+   * to make sure that a WGL context exists and is made current, we
+   * use a small dummy window that never gets shown to which we can
+   * always fall back if no stage is available
+   */
+  if (wgl_display->dummy_hwnd == NULL)
+    {
+      wgl_display->dummy_hwnd =
+        CreateWindowW ((LPWSTR) MAKEINTATOM (wgl_display->window_class),
+                       L".",
+                       WS_OVERLAPPEDWINDOW,
+                       CW_USEDEFAULT,
+                       CW_USEDEFAULT,
+                       1, 1,
+                       NULL, NULL,
+                       GetModuleHandle (NULL),
+                       NULL);
+
+      if (wgl_display->dummy_hwnd == NULL)
+        {
+          g_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                       "Unable to create dummy window");
+          return FALSE;
+        }
+    }
+
+  if (wgl_display->dummy_dc == NULL)
+    {
+      PIXELFORMATDESCRIPTOR pfd;
+      int pf;
+
+      wgl_display->dummy_dc = GetDC (wgl_display->dummy_hwnd);
+
+      pf = choose_pixel_format (wgl_display->dummy_dc, &pfd);
+
+      if (pf == 0 || !SetPixelFormat (wgl_display->dummy_dc, pf, &pfd))
+        {
+          g_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                       "Unable to find suitable GL pixel format");
+          ReleaseDC (wgl_display->dummy_hwnd, wgl_display->dummy_dc);
+          wgl_display->dummy_dc = NULL;
+          return FALSE;
+        }
+    }
+
+  if (wgl_display->wgl_context == NULL)
+    {
+      wgl_display->wgl_context = wglCreateContext (wgl_display->dummy_dc);
+
+      if (wgl_display->wgl_context == NULL)
+        {
+          g_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                       "Unable to create suitable GL context");
+          return FALSE;
+        }
+    }
+
+  COGL_NOTE (WINSYS, "Selecting dummy 0x%x for the WGL context",
+             (unsigned int) wgl_display->dummy_hwnd);
+
+  wglMakeCurrent (wgl_display->dummy_dc, wgl_display->wgl_context);
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_display_destroy (CoglDisplay *display)
+{
+  CoglDisplayWgl *wgl_display = display->winsys;
+
+  g_return_if_fail (wgl_display != NULL);
+
+  if (wgl_display->wgl_context)
+    {
+      wglMakeCurrent (NULL, NULL);
+      wglDeleteContext (wgl_display->wgl_context);
+    }
+
+  if (wgl_display->dummy_dc)
+    ReleaseDC (wgl_display->dummy_hwnd, wgl_display->dummy_dc);
+
+  if (wgl_display->dummy_hwnd)
+    DestroyWindow (wgl_display->dummy_hwnd);
+
+  if (wgl_display->window_class)
+    UnregisterClassW ((LPWSTR) MAKEINTATOM (wgl_display->window_class),
+                      GetModuleHandleW (NULL));
+
+  g_slice_free (CoglDisplayWgl, display->winsys);
+  display->winsys = NULL;
+}
+
+static gboolean
+_cogl_winsys_display_setup (CoglDisplay *display,
+                            GError **error)
+{
+  CoglDisplayWgl *wgl_display;
+
+  g_return_val_if_fail (display->winsys == NULL, FALSE);
+
+  wgl_display = g_slice_new0 (CoglDisplayWgl);
+  display->winsys = wgl_display;
+
+  if (!create_window_class (display, error))
+    goto error;
+
+  if (!create_context (display, error))
+    goto error;
+
+  return TRUE;
+
+error:
+  _cogl_winsys_display_destroy (display);
+  return FALSE;
+}
+
+static const char *
+get_wgl_extensions_string (HDC dc)
+{
+  const char * (APIENTRY *pf_wglGetExtensionsStringARB) (HDC);
+  const char * (APIENTRY *pf_wglGetExtensionsStringEXT) (void);
+
+  /* According to the docs for these two extensions, you are supposed
+     to use wglGetProcAddress to detect their availability so
+     presumably it will return NULL if they are not available */
+
+  pf_wglGetExtensionsStringARB =
+    (void *) wglGetProcAddress ("wglGetExtensionsStringARB");
+
+  if (pf_wglGetExtensionsStringARB)
+    return pf_wglGetExtensionsStringARB (dc);
+
+  pf_wglGetExtensionsStringEXT =
+    (void *) wglGetProcAddress ("wglGetExtensionsStringEXT");
+
+  if (pf_wglGetExtensionsStringEXT)
+    return pf_wglGetExtensionsStringEXT ();
+
+  /* The WGL_EXT_swap_control is also advertised as a GL extension as
+     GL_EXT_SWAP_CONTROL so if the extension to get the list of WGL
+     extensions isn't supported then we can at least fake it to
+     support the swap control extension */
+  if (_cogl_check_extension ("WGL_EXT_swap_control",
+                             (char *) glGetString (GL_EXTENSIONS)))
+    return "WGL_EXT_swap_control";
+
+  return NULL;
+}
+
+static void
+update_winsys_features (CoglContext *context)
+{
+  CoglDisplayWgl *wgl_display = context->display->winsys;
+  CoglRendererWgl *wgl_renderer = context->display->renderer->winsys;
+  const char *wgl_extensions;
+  int i;
+
+  g_return_if_fail (wgl_display->wgl_context);
+
+  _cogl_gl_update_features (context);
+
+  memset (context->winsys_features, 0, sizeof (context->winsys_features));
+
+  context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE;
+  COGL_FLAGS_SET (context->winsys_features,
+                  COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
+                  TRUE);
+
+  wgl_extensions = get_wgl_extensions_string (wgl_display->dummy_dc);
+
+  if (wgl_extensions)
+    {
+      COGL_NOTE (WINSYS, "  WGL Extensions: %s", wgl_extensions);
+
+      for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++)
+        if (_cogl_feature_check ("WGL", winsys_feature_data + i, 0, 0,
+                                 wgl_extensions,
+                                 wgl_renderer))
+          {
+            context->feature_flags |= winsys_feature_data[i].feature_flags;
+            if (winsys_feature_data[i].winsys_feature)
+              COGL_FLAGS_SET (context->winsys_features,
+                              winsys_feature_data[i].winsys_feature,
+                              TRUE);
+          }
+    }
+}
+
+static gboolean
+_cogl_winsys_context_init (CoglContext *context, GError **error)
+{
+  CoglContextWgl *wgl_context;
+
+  wgl_context = context->winsys = g_new0 (CoglContextWgl, 1);
+
+  cogl_renderer_add_native_filter (context->display->renderer,
+                                   win32_event_filter_cb,
+                                   context);
+
+  update_winsys_features (context);
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_context_deinit (CoglContext *context)
+{
+  cogl_renderer_remove_native_filter (context->display->renderer,
+                                      win32_event_filter_cb,
+                                      context);
+
+  g_free (context->winsys);
+}
+
+static gboolean
+_cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
+                            GError **error)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglDisplayWgl *wgl_display = display->winsys;
+  CoglOnscreenWgl *wgl_onscreen;
+  CoglOnscreenWin32 *win32_onscreen;
+  PIXELFORMATDESCRIPTOR pfd;
+  int pf;
+  HWND hwnd;
+
+  g_return_val_if_fail (wgl_display->wgl_context, FALSE);
+
+  /* XXX: Note we ignore the user's original width/height when given a
+   * foreign window. */
+  if (onscreen->foreign_hwnd)
+    {
+      RECT client_rect;
+
+      hwnd = onscreen->foreign_hwnd;
+
+      GetClientRect (hwnd, &client_rect);
+
+      _cogl_framebuffer_winsys_update_size (framebuffer,
+                                            client_rect.right,
+                                            client_rect.bottom);
+    }
+  else
+    {
+      int width, height;
+
+      width = COGL_FRAMEBUFFER (onscreen)->width;
+      height = COGL_FRAMEBUFFER (onscreen)->height;
+
+      /* The size of the window passed to CreateWindow for some reason
+         includes the window decorations so we need to compensate for
+         that */
+      width += GetSystemMetrics (SM_CXSIZEFRAME) * 2;
+      height += (GetSystemMetrics (SM_CYSIZEFRAME) * 2 +
+                 GetSystemMetrics (SM_CYCAPTION));
+
+      hwnd = CreateWindowW ((LPWSTR) MAKEINTATOM (wgl_display->window_class),
+                            L".",
+                            WS_OVERLAPPEDWINDOW,
+                            CW_USEDEFAULT, /* xpos */
+                            CW_USEDEFAULT, /* ypos */
+                            width,
+                            height,
+                            NULL, /* parent */
+                            NULL, /* menu */
+                            GetModuleHandle (NULL),
+                            NULL /* lparam for the WM_CREATE message */);
+
+      if (hwnd == NULL)
+        {
+          g_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                       "Unable to create window");
+          return FALSE;
+        }
+
+      /* Store a pointer back to the onscreen in the window extra data
+         so we can refer back to it quickly */
+      SetWindowLongPtrW (hwnd, 0, (LONG_PTR) onscreen);
+    }
+
+  onscreen->winsys = g_slice_new0 (CoglOnscreenWgl);
+  win32_onscreen = onscreen->winsys;
+  wgl_onscreen = onscreen->winsys;
+
+  win32_onscreen->hwnd = hwnd;
+
+  wgl_onscreen->client_dc = GetDC (hwnd);
+
+  /* Use the same pixel format as the dummy DC from the renderer */
+  pf = GetPixelFormat (wgl_display->dummy_dc);
+  DescribePixelFormat (wgl_display->dummy_dc, pf, sizeof (pfd), &pfd);
+  if (!SetPixelFormat (wgl_onscreen->client_dc, pf, &pfd))
+    {
+      g_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                   "Error setting pixel format on the window");
+      ReleaseDC (hwnd, wgl_onscreen->client_dc);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
+{
+  CoglContext *context;
+  CoglContextWgl *wgl_context;
+  CoglDisplayWgl *wgl_display;
+  CoglOnscreenWgl *wgl_onscreen;
+  CoglRendererWgl *wgl_renderer;
+
+  /* The glx backend tries to bind the dummy context if onscreen ==
+     NULL, but this isn't really going to work because before checking
+     whether onscreen == NULL it reads the pointer to get the
+     context */
+  g_return_if_fail (onscreen != NULL);
+
+  context = COGL_FRAMEBUFFER (onscreen)->context;
+  wgl_context = context->winsys;
+  wgl_display = context->display->winsys;
+  wgl_onscreen = onscreen->winsys;
+  wgl_renderer = context->display->renderer->winsys;
+
+  if (wgl_context->current_dc == wgl_onscreen->client_dc)
+    return;
+
+  wglMakeCurrent (wgl_onscreen->client_dc, wgl_display->wgl_context);
+
+  /* According to the specs for WGL_EXT_swap_control SwapInterval()
+   * applies to the current window not the context so we apply it here
+   * to ensure its up-to-date even for new windows.
+   */
+  if (wgl_renderer->pf_wglSwapInterval)
+    {
+      if (onscreen->swap_throttled)
+        wgl_renderer->pf_wglSwapInterval (1);
+      else
+        wgl_renderer->pf_wglSwapInterval (0);
+    }
+
+  wgl_context->current_dc = wgl_onscreen->client_dc;
+}
+
+static void
+_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
+{
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglContextWgl *wgl_context = context->winsys;
+  CoglOnscreenWin32 *win32_onscreen = onscreen->winsys;
+  CoglOnscreenWgl *wgl_onscreen = onscreen->winsys;
+
+  if (wgl_context->current_dc == wgl_onscreen->client_dc)
+    _cogl_winsys_onscreen_bind (NULL);
+
+  ReleaseDC (win32_onscreen->hwnd, wgl_onscreen->client_dc);
+
+  if (!win32_onscreen->is_foreign_hwnd)
+    {
+      /* Drop the pointer to the onscreen in the window so that any
+         further messages won't be processed */
+      SetWindowLongPtrW (win32_onscreen->hwnd, 0, (LONG_PTR) 0);
+      DestroyWindow (win32_onscreen->hwnd);
+    }
+}
+
+static void
+_cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
+{
+  CoglOnscreenWgl *wgl_onscreen = onscreen->winsys;
+
+  SwapBuffers (wgl_onscreen->client_dc);
+}
+
+static void
+_cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen)
+{
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglContextWgl *wgl_context = context->winsys;
+  CoglOnscreenWgl *wgl_onscreen = onscreen->winsys;
+
+  if (wgl_context->current_dc != wgl_onscreen->client_dc)
+    return;
+
+  /* This will cause it to rebind the context and update the swap interval */
+  wgl_context->current_dc = NULL;
+  _cogl_winsys_onscreen_bind (onscreen);
+}
+
+static HWND
+_cogl_winsys_onscreen_win32_get_window (CoglOnscreen *onscreen)
+{
+  CoglOnscreenWin32 *win32_onscreen = onscreen->winsys;
+  return win32_onscreen->hwnd;
+}
+
+static void
+_cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
+                                      gboolean visibility)
+{
+  CoglOnscreenWin32 *win32_onscreen = onscreen->winsys;
+
+  ShowWindow (win32_onscreen->hwnd, visibility ? SW_SHOW : SW_HIDE);
+}
+
+const CoglWinsysVtable *
+_cogl_winsys_wgl_get_vtable (void)
+{
+  static gboolean vtable_inited = FALSE;
+  static CoglWinsysVtable vtable;
+
+  /* It would be nice if we could use C99 struct initializers here
+     like the GLX backend does. However this code is more likely to be
+     compiled using Visual Studio which (still!) doesn't support them
+     so we initialize it in code instead */
+
+  if (!vtable_inited)
+    {
+      memset (&vtable, 0, sizeof (vtable));
+
+      vtable.name = "WGL";
+      vtable.get_proc_address = _cogl_winsys_get_proc_address;
+      vtable.renderer_connect = _cogl_winsys_renderer_connect;
+      vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
+      vtable.display_setup = _cogl_winsys_display_setup;
+      vtable.display_destroy = _cogl_winsys_display_destroy;
+      vtable.context_init = _cogl_winsys_context_init;
+      vtable.context_deinit = _cogl_winsys_context_deinit;
+      vtable.onscreen_init = _cogl_winsys_onscreen_init;
+      vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit;
+      vtable.onscreen_bind = _cogl_winsys_onscreen_bind;
+      vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers;
+      vtable.onscreen_update_swap_throttled =
+        _cogl_winsys_onscreen_update_swap_throttled;
+      vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility;
+      vtable.onscreen_win32_get_window = _cogl_winsys_onscreen_win32_get_window;
+
+      vtable_inited = TRUE;
+    }
+
+  return &vtable;
+}
diff --git a/configure.ac b/configure.ac
index 8f346e3..013b3a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -404,6 +404,7 @@ AS_IF([test "x$enable_gl" = "xyes"],
 	      [
 		COGL_EXTRA_LDFLAGS="$COGL_EXTRA_LDFLAGS -lopengl32 -lgdi32 -lwinmm"
 		COGL_EXTRA_CFLAGS="$COGL_EXTRA_CFLAGS -D_WIN32_WINNT=0x0500"
+                ALLOW_WGL=yes
 	      ],
 	      [
 		PKG_CHECK_EXISTS(
@@ -446,6 +447,7 @@ AS_IF([test "x$enable_stub_winsys" = "xyes"],
       [
         GL_WINSYS_APIS="$GL_WINSYS_APIS stub"
         ALLOW_GLX=no
+        ALLOW_WGL=no
       ])
 AM_CONDITIONAL(SUPPORT_STUB, [test "x$enable_stub_winsys" = "xyes"])
 
@@ -481,6 +483,30 @@ AS_IF([test "x$enable_glx" = "xyes"],
       ])
 AM_CONDITIONAL(SUPPORT_GLX, [test "x$SUPPORT_GLX" = "xyes"])
 
+AC_ARG_ENABLE(
+  [wgl],
+  [AC_HELP_STRING([--enable-wgl=@<:@no/yes@:>@], [Enable support for WGL @<:@default=auto@:>@])],
+  [],
+  [AS_IF([test "x$ALLOW_WGL" = "xyes"], [enable_wgl=yes], [enable_wgl=no])]
+)
+AS_IF([test "x$enable_wgl" = "xyes"],
+      [
+        AS_IF([test "x$enable_stub_winsys" = "xyes"],
+              [AC_MSG_ERROR([Stub winsys not currently compatible with others])])
+
+        AS_IF([test "x$ALLOW_WGL" != "xyes"],
+              [AC_MSG_ERROR([WGL not supported with $COGL_DRIVER driver])])
+
+        SUPPORT_WGL=yes
+        GL_WINSYS_APIS="$GL_WINSYS_APIS wgl"
+
+        AC_DEFINE([COGL_HAS_WGL_SUPPORT], [1], [Cogl supports OpenGL using the WGL API])
+        COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_WIN32_SUPPORT"
+
+        AC_DEFINE([COGL_HAS_FULL_WINSYS], [1],
+                  [Cogl can create its own OpenGL context])
+      ])
+AM_CONDITIONAL(SUPPORT_WGL, [test "x$SUPPORT_WGL" = "xyes"])
 
 EGL_PLATFORM_COUNT=0
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]