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