"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / winsys / cogl-winsys-wgl.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2010,2011 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  *
23  * Authors:
24  *   Neil Roberts <neil@linux.intel.com>
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <windows.h>
32
33 #include "cogl.h"
34
35 #include "cogl-util.h"
36 #include "cogl-winsys-private.h"
37 #include "cogl-context-private.h"
38 #include "cogl-framebuffer.h"
39 #include "cogl-onscreen-private.h"
40 #include "cogl-swap-chain-private.h"
41 #include "cogl-renderer-private.h"
42 #include "cogl-display-private.h"
43 #include "cogl-onscreen-template-private.h"
44 #include "cogl-private.h"
45 #include "cogl-feature-private.h"
46 #include "cogl-win32-renderer.h"
47 #include "cogl-winsys-wgl-private.h"
48
49 typedef struct _CoglRendererWgl
50 {
51   GModule *gl_module;
52
53   /* Function pointers for GLX specific extensions */
54 #define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d, e, f)
55
56 #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \
57   ret (APIENTRY * pf_ ## name) args;
58
59 #define COGL_WINSYS_FEATURE_END()
60
61 #include "cogl-winsys-wgl-feature-functions.h"
62
63 #undef COGL_WINSYS_FEATURE_BEGIN
64 #undef COGL_WINSYS_FEATURE_FUNCTION
65 #undef COGL_WINSYS_FEATURE_END
66 } CoglRendererWgl;
67
68 typedef struct _CoglDisplayWgl
69 {
70   ATOM window_class;
71   HGLRC wgl_context;
72   HWND dummy_hwnd;
73   HDC dummy_dc;
74 } CoglDisplayWgl;
75
76 typedef struct _CoglOnscreenWin32
77 {
78   HWND hwnd;
79   gboolean is_foreign_hwnd;
80 } CoglOnscreenWin32;
81
82 typedef struct _CoglContextWgl
83 {
84   HDC current_dc;
85 } CoglContextWgl;
86
87 typedef struct _CoglOnscreenWgl
88 {
89   CoglOnscreenWin32 _parent;
90
91   HDC client_dc;
92
93 } CoglOnscreenWgl;
94
95 /* Define a set of arrays containing the functions required from GL
96    for each winsys feature */
97 #define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names,    \
98                                   feature_flags, feature_flags_private, \
99                                   winsys_feature)                       \
100   static const CoglFeatureFunction                                      \
101   cogl_wgl_feature_ ## name ## _funcs[] = {
102 #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)                   \
103   { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglRendererWgl, pf_ ## name) },
104 #define COGL_WINSYS_FEATURE_END()               \
105   { NULL, 0 },                                  \
106     };
107 #include "cogl-winsys-wgl-feature-functions.h"
108
109 /* Define an array of features */
110 #undef COGL_WINSYS_FEATURE_BEGIN
111 #define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names,    \
112                                   feature_flags, feature_flags_private, \
113                                   winsys_feature)                       \
114   { 255, 255, 0, namespaces, extension_names,                            \
115       feature_flags, feature_flags_private,                             \
116       winsys_feature,                                                   \
117       cogl_wgl_feature_ ## name ## _funcs },
118 #undef COGL_WINSYS_FEATURE_FUNCTION
119 #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args)
120 #undef COGL_WINSYS_FEATURE_END
121 #define COGL_WINSYS_FEATURE_END()
122
123 static const CoglFeatureData winsys_feature_data[] =
124   {
125 #include "cogl-winsys-wgl-feature-functions.h"
126   };
127
128 static CoglFuncPtr
129 _cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer,
130                                         const char *name)
131 {
132   CoglRendererWgl *wgl_renderer = renderer->winsys;
133   void *proc = wglGetProcAddress ((LPCSTR) name);
134
135   /* The documentation for wglGetProcAddress implies that it only
136      returns pointers to extension functions so if it fails we'll try
137      resolving the symbol directly from the the GL library */
138   if (proc == NULL)
139     {
140       if (wgl_renderer->gl_module == NULL)
141         wgl_renderer->gl_module = g_module_open ("opengl32", 0);
142
143       if (wgl_renderer->gl_module)
144         g_module_symbol (wgl_renderer->gl_module, name, &proc);
145     }
146
147   return proc;
148 }
149
150 static void
151 _cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
152 {
153   CoglRendererWgl *wgl_renderer = renderer->winsys;
154
155   if (wgl_renderer->gl_module)
156     g_module_close (wgl_renderer->gl_module);
157
158   g_slice_free (CoglRendererWgl, renderer->winsys);
159 }
160
161 static CoglOnscreen *
162 find_onscreen_for_hwnd (CoglContext *context, HWND hwnd)
163 {
164   CoglDisplayWgl *display_wgl = context->display->winsys;
165   GList *l;
166
167   /* If the hwnd has Cogl's window class then we can lookup the
168      onscreen pointer directly by reading the extra window data */
169   if (GetClassLongPtr (hwnd, GCW_ATOM) == display_wgl->window_class)
170     {
171       CoglOnscreen *onscreen = (CoglOnscreen *) GetWindowLongPtr (hwnd, 0);
172
173       if (onscreen)
174         return onscreen;
175     }
176
177   for (l = context->framebuffers; l; l = l->next)
178     {
179       CoglFramebuffer *framebuffer = l->data;
180
181       if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
182         {
183           CoglOnscreenWin32 *win32_onscreen =
184             COGL_ONSCREEN (framebuffer)->winsys;
185
186           if (win32_onscreen->hwnd == hwnd)
187             return COGL_ONSCREEN (framebuffer);
188         }
189     }
190
191   return NULL;
192 }
193
194 static CoglFilterReturn
195 win32_event_filter_cb (MSG *msg, void *data)
196 {
197   CoglContext *context = data;
198
199   if (msg->message == WM_SIZE)
200     {
201       CoglOnscreen *onscreen =
202         find_onscreen_for_hwnd (context, msg->hwnd);
203
204       if (onscreen)
205         {
206           CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
207
208           /* Ignore size changes resulting from the stage being
209              minimized - otherwise it will think the window has been
210              resized to 0,0 */
211           if (msg->wParam != SIZE_MINIMIZED)
212             {
213               WORD new_width = LOWORD (msg->lParam);
214               WORD new_height = HIWORD (msg->lParam);
215               _cogl_framebuffer_winsys_update_size (framebuffer,
216                                                     new_width,
217                                                     new_height);
218             }
219         }
220     }
221
222   return COGL_FILTER_CONTINUE;
223 }
224
225 static gboolean
226 _cogl_winsys_renderer_connect (CoglRenderer *renderer,
227                                GError **error)
228 {
229   renderer->winsys = g_slice_new0 (CoglRendererWgl);
230
231   return TRUE;
232 }
233
234 static LRESULT CALLBACK
235 window_proc (HWND hwnd, UINT umsg,
236              WPARAM wparam, LPARAM lparam)
237 {
238   gboolean message_handled = FALSE;
239   CoglOnscreen *onscreen;
240
241   /* It's not clear what the best thing to do with messages sent to
242      the window proc is. We want the application to forward on all
243      messages through Cogl so that it can have a chance to process
244      them which might mean that that in it's GetMessage loop it could
245      call cogl_win32_renderer_handle_event for every message. However
246      the message loop would usually call DispatchMessage as well which
247      mean this window proc would be invoked and Cogl would see the
248      message twice. However we can't just ignore messages in the
249      window proc because some messages are sent directly from windows
250      without going through the message queue. This function therefore
251      just forwards on all messages directly. This means that the
252      application is not expected to forward on messages if it has let
253      Cogl create the window itself because it will already see them
254      via the window proc. This limits the kinds of messages that Cogl
255      can handle to ones that are sent to the windows it creates, but I
256      think that is a reasonable restriction */
257
258   /* Convert the message to a MSG struct and pass it through the Cogl
259      message handling mechanism */
260
261   /* This window proc is only called for messages created with Cogl's
262      window class so we should be able to work out the corresponding
263      window class by looking in the extra window data. Windows will
264      send some extra messages before we get a chance to set this value
265      so we have to ignore these */
266   onscreen = (CoglOnscreen *) GetWindowLongPtr (hwnd, 0);
267
268   if (onscreen != NULL)
269     {
270       CoglRenderer *renderer;
271       DWORD message_pos;
272       MSG msg;
273
274       msg.hwnd = hwnd;
275       msg.message = umsg;
276       msg.wParam = wparam;
277       msg.lParam = lparam;
278       msg.time = GetMessageTime ();
279       /* Neither MAKE_POINTS nor GET_[XY]_LPARAM is defined in MinGW
280          headers so we need to convert to a signed type explicitly */
281       message_pos = GetMessagePos ();
282       msg.pt.x = (SHORT) LOWORD (message_pos);
283       msg.pt.y = (SHORT) HIWORD (message_pos);
284
285       renderer = COGL_FRAMEBUFFER (onscreen)->context->display->renderer;
286
287       message_handled =
288         cogl_win32_renderer_handle_event (renderer, &msg);
289     }
290
291   if (!message_handled)
292     return DefWindowProcW (hwnd, umsg, wparam, lparam);
293   else
294     return 0;
295 }
296
297 static gboolean
298 pixel_format_is_better (const PIXELFORMATDESCRIPTOR *pfa,
299                         const PIXELFORMATDESCRIPTOR *pfb)
300 {
301   /* Always prefer a format with a stencil buffer */
302   if (pfa->cStencilBits == 0)
303     {
304       if (pfb->cStencilBits > 0)
305         return TRUE;
306     }
307   else if (pfb->cStencilBits == 0)
308     return FALSE;
309
310   /* Prefer a bigger color buffer */
311   if (pfb->cColorBits > pfa->cColorBits)
312     return TRUE;
313   else if (pfb->cColorBits < pfa->cColorBits)
314     return FALSE;
315
316   /* Prefer a bigger depth buffer */
317   return pfb->cDepthBits > pfa->cDepthBits;
318 }
319
320 static int
321 choose_pixel_format (CoglFramebufferConfig *config,
322                      HDC dc, PIXELFORMATDESCRIPTOR *pfd)
323 {
324   int i, num_formats, best_pf = 0;
325   PIXELFORMATDESCRIPTOR best_pfd;
326
327   num_formats = DescribePixelFormat (dc, 0, sizeof (best_pfd), NULL);
328
329   /* XXX: currently we don't support multisampling on windows... */
330   if (config->samples_per_pixel)
331     return best_pf;
332
333   for (i = 1; i <= num_formats; i++)
334     {
335       memset (pfd, 0, sizeof (*pfd));
336
337       if (DescribePixelFormat (dc, i, sizeof (best_pfd), pfd) &&
338           /* Check whether this format is useable by Cogl */
339           ((pfd->dwFlags & (PFD_SUPPORT_OPENGL |
340                             PFD_DRAW_TO_WINDOW |
341                             PFD_DOUBLEBUFFER |
342                             PFD_GENERIC_FORMAT)) ==
343            (PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW)) &&
344           pfd->iPixelType == PFD_TYPE_RGBA &&
345           pfd->cColorBits >= 16 && pfd->cColorBits <= 32 &&
346           pfd->cDepthBits >= 16 && pfd->cDepthBits <= 32 &&
347           /* Check whether this is a better format than one we've
348              already found */
349           (best_pf == 0 || pixel_format_is_better (&best_pfd, pfd)))
350         {
351           if (config->swap_chain->has_alpha && pfd->cAlphaBits == 0)
352             continue;
353           if (config->need_stencil && pfd->cStencilBits == 0)
354             continue;
355
356           best_pf = i;
357           best_pfd = *pfd;
358         }
359     }
360
361   *pfd = best_pfd;
362
363   return best_pf;
364 }
365
366 static gboolean
367 create_window_class (CoglDisplay *display, GError **error)
368 {
369   CoglDisplayWgl *wgl_display = display->winsys;
370   char *class_name_ascii, *src;
371   WCHAR *class_name_wchar, *dst;
372   WNDCLASSW wndclass;
373
374   /* We create a window class per display so that we have an
375      opportunity to clean up the class when the display is
376      destroyed */
377
378   /* Generate a unique name containing the address of the display */
379   class_name_ascii = g_strdup_printf ("CoglWindow0x%0*" G_GINTPTR_MODIFIER "x",
380                                       sizeof (guintptr) * 2,
381                                       (guintptr) display);
382   /* Convert it to WCHARs */
383   class_name_wchar = g_malloc ((strlen (class_name_ascii) + 1) *
384                                sizeof (WCHAR));
385   for (src = class_name_ascii, dst = class_name_wchar;
386        *src;
387        src++, dst++)
388     *dst = *src;
389   *dst = L'\0';
390
391   memset (&wndclass, 0, sizeof (wndclass));
392   wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
393   wndclass.lpfnWndProc = window_proc;
394   /* We reserve extra space in the window data for a pointer back to
395      the CoglOnscreen */
396   wndclass.cbWndExtra = sizeof (LONG_PTR);
397   wndclass.hInstance = GetModuleHandleW (NULL);
398   wndclass.hIcon = LoadIconW (NULL, (LPWSTR) IDI_APPLICATION);
399   wndclass.hCursor = LoadCursorW (NULL, (LPWSTR) IDC_ARROW);
400   wndclass.hbrBackground = NULL;
401   wndclass.lpszMenuName = NULL;
402   wndclass.lpszClassName = class_name_wchar;
403   wgl_display->window_class = RegisterClassW (&wndclass);
404
405   g_free (class_name_wchar);
406   g_free (class_name_ascii);
407
408   if (wgl_display->window_class == 0)
409     {
410       g_set_error (error, COGL_WINSYS_ERROR,
411                    COGL_WINSYS_ERROR_CREATE_CONTEXT,
412                    "Unable to register window class");
413       return FALSE;
414     }
415
416   return TRUE;
417 }
418
419 static gboolean
420 create_context (CoglDisplay *display, GError **error)
421 {
422   CoglDisplayWgl *wgl_display = display->winsys;
423
424   _COGL_RETURN_VAL_IF_FAIL (wgl_display->wgl_context == NULL, FALSE);
425
426   /* Cogl assumes that there is always a GL context selected; in order
427    * to make sure that a WGL context exists and is made current, we
428    * use a small dummy window that never gets shown to which we can
429    * always fall back if no onscreen is available
430    */
431   if (wgl_display->dummy_hwnd == NULL)
432     {
433       wgl_display->dummy_hwnd =
434         CreateWindowW ((LPWSTR) MAKEINTATOM (wgl_display->window_class),
435                        L".",
436                        WS_OVERLAPPEDWINDOW,
437                        CW_USEDEFAULT,
438                        CW_USEDEFAULT,
439                        1, 1,
440                        NULL, NULL,
441                        GetModuleHandle (NULL),
442                        NULL);
443
444       if (wgl_display->dummy_hwnd == NULL)
445         {
446           g_set_error (error, COGL_WINSYS_ERROR,
447                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
448                        "Unable to create dummy window");
449           return FALSE;
450         }
451     }
452
453   if (wgl_display->dummy_dc == NULL)
454     {
455       PIXELFORMATDESCRIPTOR pfd;
456       int pf;
457
458       wgl_display->dummy_dc = GetDC (wgl_display->dummy_hwnd);
459
460       pf = choose_pixel_format (&display->onscreen_template->config,
461                                 wgl_display->dummy_dc, &pfd);
462
463       if (pf == 0 || !SetPixelFormat (wgl_display->dummy_dc, pf, &pfd))
464         {
465           g_set_error (error, COGL_WINSYS_ERROR,
466                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
467                        "Unable to find suitable GL pixel format");
468           ReleaseDC (wgl_display->dummy_hwnd, wgl_display->dummy_dc);
469           wgl_display->dummy_dc = NULL;
470           return FALSE;
471         }
472     }
473
474   if (wgl_display->wgl_context == NULL)
475     {
476       wgl_display->wgl_context = wglCreateContext (wgl_display->dummy_dc);
477
478       if (wgl_display->wgl_context == NULL)
479         {
480           g_set_error (error, COGL_WINSYS_ERROR,
481                        COGL_WINSYS_ERROR_CREATE_CONTEXT,
482                        "Unable to create suitable GL context");
483           return FALSE;
484         }
485     }
486
487   COGL_NOTE (WINSYS, "Selecting dummy 0x%x for the WGL context",
488              (unsigned int) wgl_display->dummy_hwnd);
489
490   wglMakeCurrent (wgl_display->dummy_dc, wgl_display->wgl_context);
491
492   return TRUE;
493 }
494
495 static void
496 _cogl_winsys_display_destroy (CoglDisplay *display)
497 {
498   CoglDisplayWgl *wgl_display = display->winsys;
499
500   _COGL_RETURN_IF_FAIL (wgl_display != NULL);
501
502   if (wgl_display->wgl_context)
503     {
504       wglMakeCurrent (NULL, NULL);
505       wglDeleteContext (wgl_display->wgl_context);
506     }
507
508   if (wgl_display->dummy_dc)
509     ReleaseDC (wgl_display->dummy_hwnd, wgl_display->dummy_dc);
510
511   if (wgl_display->dummy_hwnd)
512     DestroyWindow (wgl_display->dummy_hwnd);
513
514   if (wgl_display->window_class)
515     UnregisterClassW ((LPWSTR) MAKEINTATOM (wgl_display->window_class),
516                       GetModuleHandleW (NULL));
517
518   g_slice_free (CoglDisplayWgl, display->winsys);
519   display->winsys = NULL;
520 }
521
522 static gboolean
523 _cogl_winsys_display_setup (CoglDisplay *display,
524                             GError **error)
525 {
526   CoglDisplayWgl *wgl_display;
527
528   _COGL_RETURN_VAL_IF_FAIL (display->winsys == NULL, FALSE);
529
530   wgl_display = g_slice_new0 (CoglDisplayWgl);
531   display->winsys = wgl_display;
532
533   if (!create_window_class (display, error))
534     goto error;
535
536   if (!create_context (display, error))
537     goto error;
538
539   return TRUE;
540
541 error:
542   _cogl_winsys_display_destroy (display);
543   return FALSE;
544 }
545
546 static const char *
547 get_wgl_extensions_string (HDC dc)
548 {
549   const char * (APIENTRY *pf_wglGetExtensionsStringARB) (HDC);
550   const char * (APIENTRY *pf_wglGetExtensionsStringEXT) (void);
551
552   _COGL_GET_CONTEXT (ctx, NULL);
553
554   /* According to the docs for these two extensions, you are supposed
555      to use wglGetProcAddress to detect their availability so
556      presumably it will return NULL if they are not available */
557
558   pf_wglGetExtensionsStringARB =
559     (void *) wglGetProcAddress ("wglGetExtensionsStringARB");
560
561   if (pf_wglGetExtensionsStringARB)
562     return pf_wglGetExtensionsStringARB (dc);
563
564   pf_wglGetExtensionsStringEXT =
565     (void *) wglGetProcAddress ("wglGetExtensionsStringEXT");
566
567   if (pf_wglGetExtensionsStringEXT)
568     return pf_wglGetExtensionsStringEXT ();
569
570   /* The WGL_EXT_swap_control is also advertised as a GL extension as
571      GL_EXT_SWAP_CONTROL so if the extension to get the list of WGL
572      extensions isn't supported then we can at least fake it to
573      support the swap control extension */
574   if (_cogl_check_extension ("WGL_EXT_swap_control",
575                              (char *) ctx->glGetString (GL_EXTENSIONS)))
576     return "WGL_EXT_swap_control";
577
578   return NULL;
579 }
580
581 static gboolean
582 update_winsys_features (CoglContext *context, GError **error)
583 {
584   CoglDisplayWgl *wgl_display = context->display->winsys;
585   CoglRendererWgl *wgl_renderer = context->display->renderer->winsys;
586   const char *wgl_extensions;
587   int i;
588
589   _COGL_RETURN_VAL_IF_FAIL (wgl_display->wgl_context, FALSE);
590
591   if (!_cogl_context_update_features (context, error))
592     return FALSE;
593
594   memset (context->winsys_features, 0, sizeof (context->winsys_features));
595
596   context->feature_flags |= COGL_FEATURE_ONSCREEN_MULTIPLE;
597   COGL_FLAGS_SET (context->features,
598                   COGL_FEATURE_ID_ONSCREEN_MULTIPLE, TRUE);
599   COGL_FLAGS_SET (context->winsys_features,
600                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
601                   TRUE);
602
603   wgl_extensions = get_wgl_extensions_string (wgl_display->dummy_dc);
604
605   if (wgl_extensions)
606     {
607       COGL_NOTE (WINSYS, "  WGL Extensions: %s", wgl_extensions);
608
609       for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++)
610         if (_cogl_feature_check (context->display->renderer,
611                                  "WGL", winsys_feature_data + i, 0, 0,
612                                  COGL_DRIVER_GL,
613                                  wgl_extensions,
614                                  wgl_renderer))
615           {
616             context->feature_flags |= winsys_feature_data[i].feature_flags;
617             if (winsys_feature_data[i].winsys_feature)
618               COGL_FLAGS_SET (context->winsys_features,
619                               winsys_feature_data[i].winsys_feature,
620                               TRUE);
621           }
622     }
623
624   return TRUE;
625 }
626
627 static gboolean
628 _cogl_winsys_context_init (CoglContext *context, GError **error)
629 {
630   CoglContextWgl *wgl_context;
631
632   wgl_context = context->winsys = g_new0 (CoglContextWgl, 1);
633
634   cogl_win32_renderer_add_filter (context->display->renderer,
635                                   win32_event_filter_cb,
636                                   context);
637
638   return update_winsys_features (context, error);
639 }
640
641 static void
642 _cogl_winsys_context_deinit (CoglContext *context)
643 {
644   cogl_win32_renderer_remove_filter (context->display->renderer,
645                                      win32_event_filter_cb,
646                                      context);
647
648   g_free (context->winsys);
649 }
650
651 static void
652 _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
653 {
654   CoglFramebuffer *fb;
655   CoglContext *context;
656   CoglContextWgl *wgl_context;
657   CoglDisplayWgl *wgl_display;
658   CoglOnscreenWgl *wgl_onscreen;
659   CoglRendererWgl *wgl_renderer;
660
661   /* The glx backend tries to bind the dummy context if onscreen ==
662      NULL, but this isn't really going to work because before checking
663      whether onscreen == NULL it reads the pointer to get the
664      context */
665   _COGL_RETURN_IF_FAIL (onscreen != NULL);
666
667   fb = COGL_FRAMEBUFFER (onscreen);
668   context = fb->context;
669   wgl_context = context->winsys;
670   wgl_display = context->display->winsys;
671   wgl_onscreen = onscreen->winsys;
672   wgl_renderer = context->display->renderer->winsys;
673
674   if (wgl_context->current_dc == wgl_onscreen->client_dc)
675     return;
676
677   wglMakeCurrent (wgl_onscreen->client_dc, wgl_display->wgl_context);
678
679   /* According to the specs for WGL_EXT_swap_control SwapInterval()
680    * applies to the current window not the context so we apply it here
681    * to ensure its up-to-date even for new windows.
682    */
683   if (wgl_renderer->pf_wglSwapInterval)
684     {
685       if (fb->config.swap_throttled)
686         wgl_renderer->pf_wglSwapInterval (1);
687       else
688         wgl_renderer->pf_wglSwapInterval (0);
689     }
690
691   wgl_context->current_dc = wgl_onscreen->client_dc;
692 }
693
694 static void
695 _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
696 {
697   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
698   CoglContextWgl *wgl_context = context->winsys;
699   CoglOnscreenWin32 *win32_onscreen = onscreen->winsys;
700   CoglOnscreenWgl *wgl_onscreen = onscreen->winsys;
701
702   /* If we never successfully allocated then there's nothing to do */
703   if (wgl_onscreen == NULL)
704     return;
705
706   if (wgl_onscreen->client_dc)
707     {
708       if (wgl_context->current_dc == wgl_onscreen->client_dc)
709         _cogl_winsys_onscreen_bind (NULL);
710
711       ReleaseDC (win32_onscreen->hwnd, wgl_onscreen->client_dc);
712     }
713
714   if (!win32_onscreen->is_foreign_hwnd && win32_onscreen->hwnd)
715     {
716       /* Drop the pointer to the onscreen in the window so that any
717          further messages won't be processed */
718       SetWindowLongPtrW (win32_onscreen->hwnd, 0, (LONG_PTR) 0);
719       DestroyWindow (win32_onscreen->hwnd);
720     }
721
722   g_slice_free (CoglOnscreenWgl, onscreen->winsys);
723   onscreen->winsys = NULL;
724 }
725
726 static gboolean
727 _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
728                             GError **error)
729 {
730   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
731   CoglContext *context = framebuffer->context;
732   CoglDisplay *display = context->display;
733   CoglDisplayWgl *wgl_display = display->winsys;
734   CoglOnscreenWgl *wgl_onscreen;
735   CoglOnscreenWin32 *win32_onscreen;
736   PIXELFORMATDESCRIPTOR pfd;
737   int pf;
738   HWND hwnd;
739
740   _COGL_RETURN_VAL_IF_FAIL (wgl_display->wgl_context, FALSE);
741
742   /* XXX: Note we ignore the user's original width/height when given a
743    * foreign window. */
744   if (onscreen->foreign_hwnd)
745     {
746       RECT client_rect;
747
748       hwnd = onscreen->foreign_hwnd;
749
750       GetClientRect (hwnd, &client_rect);
751
752       _cogl_framebuffer_winsys_update_size (framebuffer,
753                                             client_rect.right,
754                                             client_rect.bottom);
755     }
756   else
757     {
758       int width, height;
759
760       width = COGL_FRAMEBUFFER (onscreen)->width;
761       height = COGL_FRAMEBUFFER (onscreen)->height;
762
763       /* The size of the window passed to CreateWindow for some reason
764          includes the window decorations so we need to compensate for
765          that */
766       width += GetSystemMetrics (SM_CXSIZEFRAME) * 2;
767       height += (GetSystemMetrics (SM_CYSIZEFRAME) * 2 +
768                  GetSystemMetrics (SM_CYCAPTION));
769
770       hwnd = CreateWindowW ((LPWSTR) MAKEINTATOM (wgl_display->window_class),
771                             L".",
772                             WS_OVERLAPPEDWINDOW,
773                             CW_USEDEFAULT, /* xpos */
774                             CW_USEDEFAULT, /* ypos */
775                             width,
776                             height,
777                             NULL, /* parent */
778                             NULL, /* menu */
779                             GetModuleHandle (NULL),
780                             NULL /* lparam for the WM_CREATE message */);
781
782       if (hwnd == NULL)
783         {
784           g_set_error (error, COGL_WINSYS_ERROR,
785                        COGL_WINSYS_ERROR_CREATE_ONSCREEN,
786                        "Unable to create window");
787           return FALSE;
788         }
789
790       /* Store a pointer back to the onscreen in the window extra data
791          so we can refer back to it quickly */
792       SetWindowLongPtrW (hwnd, 0, (LONG_PTR) onscreen);
793     }
794
795   onscreen->winsys = g_slice_new0 (CoglOnscreenWgl);
796   win32_onscreen = onscreen->winsys;
797   wgl_onscreen = onscreen->winsys;
798
799   win32_onscreen->hwnd = hwnd;
800
801   wgl_onscreen->client_dc = GetDC (hwnd);
802
803   /* Use the same pixel format as the dummy DC from the renderer */
804   pf = choose_pixel_format (&framebuffer->config,
805                             wgl_onscreen->client_dc, &pfd);
806
807   if (pf == 0 || !SetPixelFormat (wgl_onscreen->client_dc, pf, &pfd))
808     {
809       g_set_error (error, COGL_WINSYS_ERROR,
810                    COGL_WINSYS_ERROR_CREATE_ONSCREEN,
811                    "Error setting pixel format on the window");
812
813       _cogl_winsys_onscreen_deinit (onscreen);
814
815       return FALSE;
816     }
817
818   return TRUE;
819 }
820
821 static void
822 _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
823 {
824   CoglOnscreenWgl *wgl_onscreen = onscreen->winsys;
825
826   SwapBuffers (wgl_onscreen->client_dc);
827 }
828
829 static void
830 _cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen)
831 {
832   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
833   CoglContextWgl *wgl_context = context->winsys;
834   CoglOnscreenWgl *wgl_onscreen = onscreen->winsys;
835
836   if (wgl_context->current_dc != wgl_onscreen->client_dc)
837     return;
838
839   /* This will cause it to rebind the context and update the swap interval */
840   wgl_context->current_dc = NULL;
841   _cogl_winsys_onscreen_bind (onscreen);
842 }
843
844 static HWND
845 _cogl_winsys_onscreen_win32_get_window (CoglOnscreen *onscreen)
846 {
847   CoglOnscreenWin32 *win32_onscreen = onscreen->winsys;
848   return win32_onscreen->hwnd;
849 }
850
851 static void
852 _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
853                                       gboolean visibility)
854 {
855   CoglOnscreenWin32 *win32_onscreen = onscreen->winsys;
856
857   ShowWindow (win32_onscreen->hwnd, visibility ? SW_SHOW : SW_HIDE);
858 }
859
860 const CoglWinsysVtable *
861 _cogl_winsys_wgl_get_vtable (void)
862 {
863   static gboolean vtable_inited = FALSE;
864   static CoglWinsysVtable vtable;
865
866   /* It would be nice if we could use C99 struct initializers here
867      like the GLX backend does. However this code is more likely to be
868      compiled using Visual Studio which (still!) doesn't support them
869      so we initialize it in code instead */
870
871   if (!vtable_inited)
872     {
873       memset (&vtable, 0, sizeof (vtable));
874
875       vtable.id = COGL_WINSYS_ID_WGL;
876       vtable.name = "WGL";
877       vtable.renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address;
878       vtable.renderer_connect = _cogl_winsys_renderer_connect;
879       vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
880       vtable.display_setup = _cogl_winsys_display_setup;
881       vtable.display_destroy = _cogl_winsys_display_destroy;
882       vtable.context_init = _cogl_winsys_context_init;
883       vtable.context_deinit = _cogl_winsys_context_deinit;
884       vtable.onscreen_init = _cogl_winsys_onscreen_init;
885       vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit;
886       vtable.onscreen_bind = _cogl_winsys_onscreen_bind;
887       vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers;
888       vtable.onscreen_update_swap_throttled =
889         _cogl_winsys_onscreen_update_swap_throttled;
890       vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility;
891       vtable.onscreen_win32_get_window = _cogl_winsys_onscreen_win32_get_window;
892
893       vtable_inited = TRUE;
894     }
895
896   return &vtable;
897 }