1 /*
2 * GStreamer
3 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "gstglwindow_win32.h"
27
28 LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
29 LPARAM lParam);
30 LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
31 LPARAM lParam);
32
33 enum
34 {
35 PROP_0
36 };
37
38 struct _GstGLWindowWin32Private
39 {
40 gint preferred_width;
41 gint preferred_height;
42 GIOChannel *msg_io_channel;
43 };
44
45 #define GST_CAT_DEFAULT gst_gl_window_win32_debug
46 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
47
48 #define DEBUG_INIT \
49 GST_DEBUG_CATEGORY_GET (GST_CAT_DEFAULT, "glwindow");
50 #define gst_gl_window_win32_parent_class parent_class
51 G_DEFINE_TYPE_WITH_CODE (GstGLWindowWin32, gst_gl_window_win32,
52 GST_TYPE_GL_WINDOW, G_ADD_PRIVATE (GstGLWindowWin32) DEBUG_INIT);
53
54 static void gst_gl_window_win32_set_window_handle (GstGLWindow * window,
55 guintptr handle);
56 static guintptr gst_gl_window_win32_get_display (GstGLWindow * window);
57 static void gst_gl_window_win32_set_preferred_size (GstGLWindow * window,
58 gint width, gint height);
59 static void gst_gl_window_win32_show (GstGLWindow * window);
60 static void gst_gl_window_win32_draw (GstGLWindow * window);
61 gboolean gst_gl_window_win32_open (GstGLWindow * window, GError ** error);
62 void gst_gl_window_win32_close (GstGLWindow * window);
63 static void release_parent_win_id (GstGLWindowWin32 * window_win32);
64 static void gst_gl_window_win32_send_message (GstGLWindow * window,
65 GstGLWindowCB callback, gpointer data);
66
67 static void
gst_gl_window_win32_class_init(GstGLWindowWin32Class * klass)68 gst_gl_window_win32_class_init (GstGLWindowWin32Class * klass)
69 {
70 GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
71
72 window_class->set_window_handle =
73 GST_DEBUG_FUNCPTR (gst_gl_window_win32_set_window_handle);
74 window_class->draw = GST_DEBUG_FUNCPTR (gst_gl_window_win32_draw);
75 window_class->get_display =
76 GST_DEBUG_FUNCPTR (gst_gl_window_win32_get_display);
77 window_class->set_preferred_size =
78 GST_DEBUG_FUNCPTR (gst_gl_window_win32_set_preferred_size);
79 window_class->show = GST_DEBUG_FUNCPTR (gst_gl_window_win32_show);
80 window_class->open = GST_DEBUG_FUNCPTR (gst_gl_window_win32_open);
81 window_class->close = GST_DEBUG_FUNCPTR (gst_gl_window_win32_close);
82 window_class->send_message =
83 GST_DEBUG_FUNCPTR (gst_gl_window_win32_send_message);
84 }
85
86 static void
gst_gl_window_win32_init(GstGLWindowWin32 * window)87 gst_gl_window_win32_init (GstGLWindowWin32 * window)
88 {
89 window->priv = gst_gl_window_win32_get_instance_private (window);
90 }
91
92 GstGLWindowWin32 *
gst_gl_window_win32_new(GstGLDisplay * display)93 gst_gl_window_win32_new (GstGLDisplay * display)
94 {
95 GstGLWindowWin32 *window;
96
97 if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_WIN32) ==
98 0)
99 /* we require an win32 display to create win32 windows */
100 return NULL;
101
102 window = g_object_new (GST_TYPE_GL_WINDOW_WIN32, NULL);
103 gst_object_ref_sink (window);
104
105 return window;
106 }
107
108 static gboolean
msg_cb(GIOChannel * source,GIOCondition condition,gpointer data)109 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
110 {
111 MSG msg;
112
113 if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
114 return G_SOURCE_CONTINUE;
115
116 GST_TRACE ("handle message");
117 TranslateMessage (&msg);
118 DispatchMessage (&msg);
119
120 return G_SOURCE_CONTINUE;
121 }
122
123 gboolean
gst_gl_window_win32_open(GstGLWindow * window,GError ** error)124 gst_gl_window_win32_open (GstGLWindow * window, GError ** error)
125 {
126 GstGLWindowWin32 *window_win32 = GST_GL_WINDOW_WIN32 (window);
127
128 if (!GST_GL_WINDOW_CLASS (parent_class)->open (window, error))
129 return FALSE;
130
131 window_win32->priv->msg_io_channel = g_io_channel_win32_new_messages (0);
132 window_win32->msg_source =
133 g_io_create_watch (window_win32->priv->msg_io_channel, G_IO_IN);
134 g_source_set_callback (window_win32->msg_source, (GSourceFunc) msg_cb, NULL,
135 NULL);
136 g_source_attach (window_win32->msg_source, window->main_context);
137
138 return TRUE;
139 }
140
141 void
gst_gl_window_win32_close(GstGLWindow * window)142 gst_gl_window_win32_close (GstGLWindow * window)
143 {
144 GstGLWindowWin32 *window_win32 = GST_GL_WINDOW_WIN32 (window);
145
146 release_parent_win_id (window_win32);
147
148 if (window_win32->internal_win_id) {
149 RemoveProp (window_win32->internal_win_id, "gl_window");
150 ShowWindow (window_win32->internal_win_id, SW_HIDE);
151 SetParent (window_win32->internal_win_id, NULL);
152 if (!DestroyWindow (window_win32->internal_win_id))
153 GST_WARNING ("failed to destroy window %" G_GUINTPTR_FORMAT
154 ", 0x%x", (guintptr) window_win32->internal_win_id,
155 (unsigned int) GetLastError ());
156 }
157
158 g_source_destroy (window_win32->msg_source);
159 g_source_unref (window_win32->msg_source);
160 window_win32->msg_source = NULL;
161 g_io_channel_unref (window_win32->priv->msg_io_channel);
162 window_win32->priv->msg_io_channel = NULL;
163
164 GST_GL_WINDOW_CLASS (parent_class)->close (window);
165 }
166
167 static void
set_parent_win_id(GstGLWindowWin32 * window_win32)168 set_parent_win_id (GstGLWindowWin32 * window_win32)
169 {
170 WNDPROC window_parent_proc;
171 RECT rect;
172
173 if (!window_win32->parent_win_id) {
174 /* no parent so the internal window needs borders and system menu */
175 SetWindowLongPtr (window_win32->internal_win_id, GWL_STYLE,
176 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW);
177 SetParent (window_win32->internal_win_id, NULL);
178 return;
179 }
180
181 window_parent_proc =
182 (WNDPROC) GetWindowLongPtr (window_win32->parent_win_id, GWLP_WNDPROC);
183
184 GST_DEBUG ("set parent %" G_GUINTPTR_FORMAT,
185 (guintptr) window_win32->parent_win_id);
186
187 SetProp (window_win32->parent_win_id, "gl_window_id",
188 window_win32->internal_win_id);
189 SetProp (window_win32->parent_win_id, "gl_window_parent_proc",
190 (WNDPROC) window_parent_proc);
191 SetWindowLongPtr (window_win32->parent_win_id, GWLP_WNDPROC,
192 (LONG_PTR) sub_class_proc);
193
194 SetWindowLongPtr (window_win32->internal_win_id, GWL_STYLE,
195 WS_CHILD | WS_MAXIMIZE);
196 SetParent (window_win32->internal_win_id, window_win32->parent_win_id);
197
198 /* take changes into account: SWP_FRAMECHANGED */
199 GetClientRect (window_win32->parent_win_id, &rect);
200 SetWindowPos (window_win32->internal_win_id, HWND_TOP, rect.left, rect.top,
201 rect.right, rect.bottom,
202 SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
203 SWP_FRAMECHANGED | SWP_NOACTIVATE);
204 MoveWindow (window_win32->internal_win_id, rect.left, rect.top, rect.right,
205 rect.bottom, FALSE);
206 }
207
208 static void
release_parent_win_id(GstGLWindowWin32 * window_win32)209 release_parent_win_id (GstGLWindowWin32 * window_win32)
210 {
211 WNDPROC parent_proc;
212
213 if (!window_win32->parent_win_id)
214 return;
215
216 parent_proc = GetProp (window_win32->parent_win_id, "gl_window_parent_proc");
217 if (!parent_proc)
218 return;
219
220 GST_DEBUG ("release parent %" G_GUINTPTR_FORMAT,
221 (guintptr) window_win32->parent_win_id);
222
223 SetWindowLongPtr (window_win32->parent_win_id, GWLP_WNDPROC,
224 (LONG_PTR) parent_proc);
225
226 RemoveProp (window_win32->parent_win_id, "gl_window_parent_proc");
227 }
228
229 gboolean
gst_gl_window_win32_create_window(GstGLWindowWin32 * window_win32,GError ** error)230 gst_gl_window_win32_create_window (GstGLWindowWin32 * window_win32,
231 GError ** error)
232 {
233 // GstGLWindowWin32 *window_win32 = GST_GL_WINDOW_WIN32 (window);
234 WNDCLASSEX wc;
235 ATOM atom = 0;
236 HINSTANCE hinstance = GetModuleHandle (NULL);
237
238 static gint x = 0;
239 static gint y = 0;
240
241 GST_LOG ("Attempting to create a win32 window");
242
243 x += 20;
244 y += 20;
245
246 atom = GetClassInfoEx (hinstance, "GSTGL", &wc);
247
248 if (atom == 0) {
249 ZeroMemory (&wc, sizeof (WNDCLASSEX));
250
251 wc.cbSize = sizeof (WNDCLASSEX);
252 wc.lpfnWndProc = window_proc;
253 wc.cbClsExtra = 0;
254 wc.cbWndExtra = 0;
255 wc.hInstance = hinstance;
256 wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);
257 wc.hIconSm = NULL;
258 wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
259 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
260 wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
261 wc.lpszMenuName = NULL;
262 wc.lpszClassName = "GSTGL";
263
264 atom = RegisterClassEx (&wc);
265
266 if (atom == 0) {
267 g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
268 "Failed to register window class 0x%x\n",
269 (unsigned int) GetLastError ());
270 goto failure;
271 }
272 }
273
274 window_win32->internal_win_id = 0;
275 window_win32->device = 0;
276 window_win32->visible = FALSE;
277
278 window_win32->internal_win_id = CreateWindowEx (0,
279 "GSTGL",
280 "OpenGL renderer",
281 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
282 x, y, 0, 0, (HWND) NULL, (HMENU) NULL, hinstance, window_win32);
283
284 if (!window_win32->internal_win_id) {
285 g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
286 "failed to create gl window");
287 goto failure;
288 }
289
290 GST_DEBUG ("gl window created: %" G_GUINTPTR_FORMAT,
291 (guintptr) window_win32->internal_win_id);
292
293 //device is set in the window_proc
294 if (!window_win32->device) {
295 g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_FAILED,
296 "failed to create device");
297 goto failure;
298 }
299
300 ShowCursor (TRUE);
301
302 GST_LOG ("Created a win32 window");
303
304 /* The window has been created as if it had no parent, so there is nothing
305 * else to do in that case. Even if user has already set a window,
306 * parent_win_id could still be 0 at this point, and in that case calling
307 * set_parent_win_id() here would steal focus from the parent window. */
308 if (window_win32->parent_win_id)
309 set_parent_win_id (window_win32);
310
311 return TRUE;
312
313 failure:
314 return FALSE;
315 }
316
317 static guintptr
gst_gl_window_win32_get_display(GstGLWindow * window)318 gst_gl_window_win32_get_display (GstGLWindow * window)
319 {
320 GstGLWindowWin32 *window_win32;
321
322 window_win32 = GST_GL_WINDOW_WIN32 (window);
323
324 return (guintptr) window_win32->device;
325 }
326
327 static void
gst_gl_window_win32_set_window_handle(GstGLWindow * window,guintptr id)328 gst_gl_window_win32_set_window_handle (GstGLWindow * window, guintptr id)
329 {
330 GstGLWindowWin32 *window_win32;
331
332 window_win32 = GST_GL_WINDOW_WIN32 (window);
333
334 if (!window_win32->internal_win_id) {
335 window_win32->parent_win_id = (HWND) id;
336 return;
337 }
338
339 if (window_win32->visible) {
340 ShowWindow (window_win32->internal_win_id, SW_HIDE);
341 window_win32->visible = FALSE;
342 }
343
344 release_parent_win_id (window_win32);
345 window_win32->parent_win_id = (HWND) id;
346 set_parent_win_id (window_win32);
347 }
348
349 static void
gst_gl_window_win32_show(GstGLWindow * window)350 gst_gl_window_win32_show (GstGLWindow * window)
351 {
352 GstGLWindowWin32 *window_win32 = GST_GL_WINDOW_WIN32 (window);
353 gint width = window_win32->priv->preferred_width;
354 gint height = window_win32->priv->preferred_height;
355
356 if (!window_win32->visible) {
357 HWND parent_id = window_win32->parent_win_id;
358
359 /* if no parent the real size has to be set now because this has not been done
360 * when at window creation */
361 if (!parent_id) {
362 RECT rect;
363 GetClientRect (window_win32->internal_win_id, &rect);
364 width += 2 * GetSystemMetrics (SM_CXSIZEFRAME);
365 height +=
366 2 * GetSystemMetrics (SM_CYSIZEFRAME) +
367 GetSystemMetrics (SM_CYCAPTION);
368 MoveWindow (window_win32->internal_win_id, rect.left, rect.top, width,
369 height, FALSE);
370 }
371
372 ShowWindowAsync (window_win32->internal_win_id, SW_SHOW);
373 window_win32->visible = TRUE;
374 }
375 }
376
377 static void
gst_gl_window_win32_set_preferred_size(GstGLWindow * window,gint width,gint height)378 gst_gl_window_win32_set_preferred_size (GstGLWindow * window, gint width,
379 gint height)
380 {
381 GstGLWindowWin32 *window_win32 = GST_GL_WINDOW_WIN32 (window);
382
383 window_win32->priv->preferred_width = width;
384 window_win32->priv->preferred_height = height;
385 }
386
387 /* Thread safe */
388 static void
gst_gl_window_win32_draw(GstGLWindow * window)389 gst_gl_window_win32_draw (GstGLWindow * window)
390 {
391 GstGLWindowWin32 *window_win32 = GST_GL_WINDOW_WIN32 (window);
392
393 RedrawWindow (window_win32->internal_win_id, NULL, NULL,
394 RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE);
395 }
396
397 /* PRIVATE */
398
399 LRESULT CALLBACK
window_proc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)400 window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
401 {
402 GstGLWindowWin32 *window_win32;
403 LRESULT ret = 0;
404
405 if (uMsg == WM_CREATE) {
406 window_win32 =
407 GST_GL_WINDOW_WIN32 (((LPCREATESTRUCT) lParam)->lpCreateParams);
408
409 GST_TRACE ("WM_CREATE");
410
411 window_win32->device = GetDC (hWnd);
412 /* Do this, otherwise we hang on exit. We can still use it (due to the
413 * CS_OWNDC flag in the WindowClass) after we have Released.
414 */
415 ReleaseDC (hWnd, window_win32->device);
416
417 SetProp (hWnd, "gl_window", window_win32);
418 } else if (GetProp (hWnd, "gl_window")) {
419 GstGLWindow *window;
420 GstGLContext *context;
421
422 window_win32 = GST_GL_WINDOW_WIN32 (GetProp (hWnd, "gl_window"));
423 window = GST_GL_WINDOW (window_win32);
424 context = gst_gl_window_get_context (window);
425
426 g_assert (window_win32->internal_win_id == hWnd);
427
428 switch (uMsg) {
429 case WM_SIZE:
430 gst_gl_window_resize (window, LOWORD (lParam), HIWORD (lParam));
431 break;
432 case WM_PAINT:
433 {
434 if (window->queue_resize) {
435 guint width, height;
436
437 gst_gl_window_get_surface_dimensions (window, &width, &height);
438 gst_gl_window_resize (window, width, height);
439 }
440 if (window->draw) {
441 PAINTSTRUCT ps;
442 BeginPaint (hWnd, &ps);
443 window->draw (window->draw_data);
444 gst_gl_context_swap_buffers (context);
445 EndPaint (hWnd, &ps);
446 }
447 break;
448 }
449 case WM_CLOSE:
450 {
451 ShowWindowAsync (window_win32->internal_win_id, SW_HIDE);
452
453 GST_TRACE ("WM_CLOSE");
454
455 if (window->close)
456 window->close (window->close_data);
457 break;
458 }
459 case WM_CAPTURECHANGED:
460 {
461 GST_DEBUG ("WM_CAPTURECHANGED");
462 if (window->queue_resize) {
463 guint width, height;
464
465 gst_gl_window_get_surface_dimensions (window, &width, &height);
466 gst_gl_window_resize (window, width, height);
467 }
468 if (window->draw)
469 window->draw (window->draw_data);
470 break;
471 }
472 case WM_ERASEBKGND:
473 {
474 ret = TRUE;
475 break;
476 }
477 default:
478 {
479 /* transmit messages to the parrent (ex: mouse/keyboard input) */
480 HWND parent_id = window_win32->parent_win_id;
481 if (parent_id)
482 PostMessage (parent_id, uMsg, wParam, lParam);
483 ret = DefWindowProc (hWnd, uMsg, wParam, lParam);
484 }
485 }
486
487 gst_object_unref (context);
488 } else {
489 ret = DefWindowProc (hWnd, uMsg, wParam, lParam);
490 }
491
492 return ret;
493 }
494
495 LRESULT FAR PASCAL
sub_class_proc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)496 sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
497 {
498 WNDPROC window_parent_proc = GetProp (hWnd, "gl_window_parent_proc");
499
500 if (uMsg == WM_SIZE) {
501 HWND gl_window_id = GetProp (hWnd, "gl_window_id");
502 MoveWindow (gl_window_id, 0, 0, LOWORD (lParam), HIWORD (lParam), FALSE);
503 }
504
505 return CallWindowProc (window_parent_proc, hWnd, uMsg, wParam, lParam);
506 }
507
508 typedef struct _GstGLWin32SyncMessage
509 {
510 GstGLWindowCB callback;
511 gpointer data;
512
513 HANDLE *event;
514 } GstGLWin32SyncMessage;
515
516 static void
_run_message_sync(GstGLWin32SyncMessage * message)517 _run_message_sync (GstGLWin32SyncMessage * message)
518 {
519 if (message->callback)
520 message->callback (message->data);
521
522 SetEvent (message->event);
523 }
524
525 void
gst_gl_window_win32_send_message(GstGLWindow * window,GstGLWindowCB callback,gpointer data)526 gst_gl_window_win32_send_message (GstGLWindow * window,
527 GstGLWindowCB callback, gpointer data)
528 {
529 GstGLWin32SyncMessage message;
530
531 message.callback = callback;
532 message.data = data;
533 message.event = CreateEvent (NULL, FALSE, FALSE, NULL);
534
535 gst_gl_window_send_message_async (window, (GstGLWindowCB) _run_message_sync,
536 &message, NULL);
537
538 WaitForSingleObject (message.event, INFINITE);
539 CloseHandle (message.event);
540 }
541