win32: Use a dummy window to support delayed stage creation
authorNeil Roberts <neil@linux.intel.com>
Wed, 20 Jan 2010 16:41:25 +0000 (16:41 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 3 Feb 2010 16:34:27 +0000 (16:34 +0000)
The Win32 backend now implements the create_context method which
creates a context and binds it to a 1x1 invisible window. That way
there will always be a context bound and the features can be retrieved
without creating the default stage. This reflects the changes in
1c6ffc8..b245d55 to the GLX backend.

clutter/win32/clutter-backend-win32.c
clutter/win32/clutter-backend-win32.h
clutter/win32/clutter-stage-win32.c

index ff45466..2ca7032 100644 (file)
@@ -156,10 +156,23 @@ clutter_backend_win32_dispose (GObject *gobject)
 
   if (backend_win32->gl_context)
     {
+      wglMakeCurrent (NULL, NULL);
       wglDeleteContext (backend_win32->gl_context);
       backend_win32->gl_context = NULL;
     }
 
+  if (backend_win32->dummy_dc)
+    {
+      ReleaseDC (backend_win32->dummy_hwnd, backend_win32->dummy_dc);
+      backend_win32->dummy_dc = NULL;
+    }
+
+  if (backend_win32->dummy_hwnd)
+    {
+      DestroyWindow (backend_win32->dummy_hwnd);
+      backend_win32->dummy_hwnd = NULL;
+    }
+
   G_OBJECT_CLASS (clutter_backend_win32_parent_class)->dispose (gobject);
 }
 
@@ -201,21 +214,20 @@ clutter_backend_win32_get_features (ClutterBackend *backend)
   SwapIntervalProc     swap_interval;
   ClutterBackendWin32 *backend_win32;
 
-  /* FIXME: we really need to check if gl context is set */
-
-  extensions = (const gchar *) glGetString (GL_EXTENSIONS);
-
   /* this will make sure that the GL context exists and is bound to a
      drawable */
   backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
   g_return_val_if_fail (backend_win32->gl_context != NULL, 0);
   g_return_val_if_fail (wglGetCurrentDC () != NULL, 0);
 
-  CLUTTER_NOTE (BACKEND, "Checking features\n"
-                "GL_VENDOR: %s\n"
-                "GL_RENDERER: %s\n"
-                "GL_VERSION: %s\n"
-                "GL_EXTENSIONS: %s\n",
+  extensions = (const gchar *) glGetString (GL_EXTENSIONS);
+
+  CLUTTER_NOTE (BACKEND,
+                "Checking features\n"
+                "  GL_VENDOR: %s\n"
+                "  GL_RENDERER: %s\n"
+                "  GL_VERSION: %s\n"
+                "  GL_EXTENSIONS: %s\n",
                 glGetString (GL_VENDOR),
                 glGetString (GL_RENDERER),
                 glGetString (GL_VERSION),
@@ -261,9 +273,166 @@ clutter_backend_win32_get_features (ClutterBackend *backend)
   else
     CLUTTER_NOTE (BACKEND, "no use-able vblank mechanism found");
 
+  CLUTTER_NOTE (BACKEND, "backend features checked");
+
   return flags;
 }
 
+static ATOM
+clutter_backend_win32_get_dummy_window_class ()
+{
+  static ATOM klass = 0;
+
+  if (klass == 0)
+    {
+      WNDCLASSW wndclass;
+      memset (&wndclass, 0, sizeof (wndclass));
+      wndclass.lpfnWndProc = DefWindowProc;
+      wndclass.hInstance = GetModuleHandleW (NULL);
+      wndclass.lpszClassName = L"ClutterBackendWin32DummyWindow";
+      klass = RegisterClassW (&wndclass);
+    }
+
+  return klass;
+}
+
+static gboolean
+clutter_backend_win32_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
+clutter_backend_win32_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 Clutter */
+          && ((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
+              || clutter_backend_win32_pixel_format_is_better (&best_pfd, pfd)))
+        {
+          best_pf = i;
+          best_pfd = *pfd;
+        }
+    }
+
+  *pfd = best_pfd;
+
+  return best_pf;
+}
+
+static gboolean
+clutter_backend_win32_create_context (ClutterBackend  *backend,
+                                      GError         **error)
+{
+  ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
+
+  /* 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 (backend_win32->dummy_hwnd == NULL)
+    {
+      ATOM window_class = clutter_backend_win32_get_dummy_window_class ();
+
+      if (window_class == 0)
+        {
+          g_critical ("Unable to register window class");
+          return FALSE;
+        }
+
+      backend_win32->dummy_hwnd =
+        CreateWindowW ((LPWSTR) MAKEINTATOM (window_class),
+                       L".",
+                       WS_OVERLAPPEDWINDOW,
+                       CW_USEDEFAULT,
+                       CW_USEDEFAULT,
+                       1, 1,
+                       NULL, NULL,
+                       GetModuleHandle (NULL),
+                       NULL);
+
+      if (backend_win32->dummy_hwnd == NULL)
+        {
+          g_critical ("Unable to create dummy window");
+          return FALSE;
+        }
+    }
+
+  if (backend_win32->dummy_dc == NULL)
+    {
+      PIXELFORMATDESCRIPTOR pfd;
+      int pf;
+
+      backend_win32->dummy_dc = GetDC (backend_win32->dummy_hwnd);
+
+      pf = clutter_backend_win32_choose_pixel_format (backend_win32->dummy_dc,
+                                                      &pfd);
+
+      if (pf == 0 || !SetPixelFormat (backend_win32->dummy_dc, pf, &pfd))
+        {
+          g_critical ("Unable to find suitable GL pixel format");
+          ReleaseDC (backend_win32->dummy_hwnd, backend_win32->dummy_dc);
+          backend_win32->dummy_dc = NULL;
+          return FALSE;
+        }
+    }
+
+  if (backend_win32->gl_context == NULL)
+    {
+      backend_win32->gl_context = wglCreateContext (backend_win32->dummy_dc);
+
+      if (backend_win32->gl_context == NULL)
+        {
+          g_critical ("Unable to create suitable GL context");
+          return FALSE;
+        }
+    }
+
+  CLUTTER_NOTE (BACKEND, "Selecting dummy 0x%x for the WGL context",
+                (unsigned int) backend_win32->dummy_hwnd);
+
+  wglMakeCurrent (backend_win32->dummy_dc, backend_win32->gl_context);
+
+  return TRUE;
+}
+
 static void
 clutter_backend_win32_ensure_context (ClutterBackend *backend, 
                                      ClutterStage   *stage)
@@ -304,7 +473,11 @@ clutter_backend_win32_ensure_context (ClutterBackend *backend,
           CLUTTER_NOTE (MULTISTAGE,
                         "Received a stale stage, clearing all context");
 
-          wglMakeCurrent (NULL, NULL);
+          if (backend_win32->dummy_dc != NULL)
+            wglMakeCurrent (backend_win32->dummy_dc,
+                            backend_win32->gl_context);
+          else
+            wglMakeCurrent (NULL, NULL);
         }
       else
         {
@@ -380,6 +553,7 @@ clutter_backend_win32_class_init (ClutterBackendWin32Class *klass)
   backend_class->add_options      = clutter_backend_win32_add_options;
   backend_class->get_features     = clutter_backend_win32_get_features;
   backend_class->redraw           = clutter_backend_win32_redraw;
+  backend_class->create_context   = clutter_backend_win32_create_context;
   backend_class->ensure_context   = clutter_backend_win32_ensure_context;
 }
 
index 0dfb74d..ce23d1a 100644 (file)
@@ -46,6 +46,8 @@ struct _ClutterBackendWin32
   ClutterBackend parent_instance;
 
   HGLRC          gl_context;
+  HWND           dummy_hwnd;
+  HDC            dummy_dc;
   gboolean       no_event_retrieval;
 
   HCURSOR        invisible_cursor;
index a11d94c..b0d8fd1 100644 (file)
@@ -384,66 +384,6 @@ clutter_stage_win32_get_window_class ()
 
   return klass;
 }
-static gboolean
-clutter_stage_win32_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
-clutter_stage_win32_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 Clutter */
-         && ((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
-             || clutter_stage_win32_pixel_format_is_better (&best_pfd, pfd)))
-        {
-          best_pf = i;
-          best_pfd = *pfd;
-        }
-    }
-
-  *pfd = best_pfd;
-
-  return best_pf;
-}
 
 static gboolean
 clutter_stage_win32_realize (ClutterStageWindow *stage_window)
@@ -452,6 +392,7 @@ clutter_stage_win32_realize (ClutterStageWindow *stage_window)
   ClutterBackendWin32 *backend_win32;
   PIXELFORMATDESCRIPTOR pfd;
   int pf;
+  GError *error = NULL;
 
   CLUTTER_NOTE (MISC, "Realizing main stage");
 
@@ -518,23 +459,22 @@ clutter_stage_win32_realize (ClutterStageWindow *stage_window)
 
   stage_win32->client_dc = GetDC (stage_win32->hwnd);
 
-  pf = clutter_stage_win32_choose_pixel_format (stage_win32->client_dc, &pfd);
-  
-  if (pf == 0 || !SetPixelFormat (stage_win32->client_dc, pf, &pfd))
+  /* Create a context. This will be a no-op if we already have one */
+  if (!_clutter_backend_create_context (CLUTTER_BACKEND (backend_win32),
+                                        &error))
     {
-      g_critical ("Unable to find suitable GL pixel format");
+      g_critical ("Unable to realize stage: %s", error->message);
+      g_error_free (error);
       goto fail;
     }
 
-  if (backend_win32->gl_context == NULL)
+  /* Use the same pixel format as the dummy DC */
+  pf = GetPixelFormat (backend_win32->dummy_dc);
+  DescribePixelFormat (backend_win32->dummy_dc, pf, sizeof (pfd), &pfd);
+  if (!SetPixelFormat (stage_win32->client_dc, pf, &pfd))
     {
-      backend_win32->gl_context = wglCreateContext (stage_win32->client_dc);
-      
-      if (backend_win32->gl_context == NULL)
-        {
-          g_critical ("Unable to create suitable GL context");
-          goto fail;
-        }
+      g_critical ("Unable to find suitable GL pixel format");
+      goto fail;
     }
 
   CLUTTER_NOTE (BACKEND, "Successfully realized stage");