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