1 /*
2 * GStreamer
3 * Copyright (C) 2008 Julien Isorce <julien.isorce@gmail.com>
4 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
5 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "gstd3d11window_win32.h"
28 #include <wrl.h>
29
30 /* *INDENT-OFF* */
31 using namespace Microsoft::WRL;
32 /* *INDENT-ON* */
33
34 GST_DEBUG_CATEGORY_EXTERN (gst_d3d11_window_debug);
35 #define GST_CAT_DEFAULT gst_d3d11_window_debug
36
37 G_LOCK_DEFINE_STATIC (create_lock);
38
39 #define EXTERNAL_PROC_PROP_NAME "d3d11_window_external_proc"
40 #define D3D11_WINDOW_PROP_NAME "gst_d3d11_window_win32_object"
41
42 #define WM_GST_D3D11_FULLSCREEN (WM_USER + 1)
43 #define WM_GST_D3D11_CONSTRUCT_INTERNAL_WINDOW (WM_USER + 2)
44 #define WM_GST_D3D11_DESTROY_INTERNAL_WINDOW (WM_USER + 3)
45 #define WM_GST_D3D11_MOVE_WINDOW (WM_USER + 4)
46
47 static LRESULT CALLBACK window_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
48 LPARAM lParam);
49 static LRESULT FAR PASCAL sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam,
50 LPARAM lParam);
51
52 typedef enum
53 {
54 GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE = 0,
55 GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED,
56 GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED,
57 } GstD3D11WindowWin32OverlayState;
58
59 struct _GstD3D11WindowWin32
60 {
61 GstD3D11Window parent;
62
63 GMutex lock;
64 GCond cond;
65
66 GMainContext *main_context;
67 GMainLoop *loop;
68
69 gboolean visible;
70
71 GSource *msg_source;
72 GIOChannel *msg_io_channel;
73
74 GThread *thread;
75
76 GThread *internal_hwnd_thread;
77
78 HWND internal_hwnd;
79 HWND external_hwnd;
80 GstD3D11WindowWin32OverlayState overlay_state;
81
82 HDC device_handle;
83 gboolean first_present;
84 gboolean have_swapchain1;
85
86 /* atomic */
87 gint pending_fullscreen_count;
88 gint pending_move_window;
89
90 /* fullscreen related */
91 RECT restore_rect;
92 LONG restore_style;
93
94 /* Handle set_render_rectangle */
95 GstVideoRectangle render_rect;
96 };
97
98 #define gst_d3d11_window_win32_parent_class parent_class
99 G_DEFINE_TYPE (GstD3D11WindowWin32, gst_d3d11_window_win32,
100 GST_TYPE_D3D11_WINDOW);
101
102 static void gst_d3d11_window_win32_constructed (GObject * object);
103 static void gst_d3d11_window_win32_dispose (GObject * object);
104 static void gst_d3d11_window_win32_finalize (GObject * object);
105
106 static void gst_d3d11_window_win32_show (GstD3D11Window * window);
107 static void gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window);
108 static void
109 gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window);
110 static gboolean
111 gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window,
112 DXGI_FORMAT format, guint width, guint height,
113 guint swapchain_flags, IDXGISwapChain ** swap_chain);
114 static GstFlowReturn gst_d3d11_window_win32_present (GstD3D11Window * window,
115 guint present_flags);
116
117 static gpointer gst_d3d11_window_win32_thread_func (gpointer data);
118 static gboolean
119 gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self);
120 static void gst_d3d11_window_win32_destroy_internal_window (HWND hwnd);
121 static void gst_d3d11_window_win32_release_external_handle (HWND hwnd);
122 static void
123 gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self,
124 guintptr handle);
125 static void
126 gst_d3d11_window_win32_on_resize (GstD3D11Window * window,
127 guint width, guint height);
128 static void gst_d3d11_window_win32_unprepare (GstD3D11Window * window);
129 static void
130 gst_d3d11_window_win32_set_render_rectangle (GstD3D11Window * window,
131 const GstVideoRectangle * rect);
132 static void gst_d3d11_window_win32_set_title (GstD3D11Window * window,
133 const gchar * title);
134
135 static void
gst_d3d11_window_win32_class_init(GstD3D11WindowWin32Class * klass)136 gst_d3d11_window_win32_class_init (GstD3D11WindowWin32Class * klass)
137 {
138 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
139 GstD3D11WindowClass *window_class = GST_D3D11_WINDOW_CLASS (klass);
140
141 gobject_class->constructed = gst_d3d11_window_win32_constructed;
142 gobject_class->dispose = gst_d3d11_window_win32_dispose;
143 gobject_class->finalize = gst_d3d11_window_win32_finalize;
144
145 window_class->show = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_show);
146 window_class->update_swap_chain =
147 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_update_swap_chain);
148 window_class->change_fullscreen_mode =
149 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_change_fullscreen_mode);
150 window_class->create_swap_chain =
151 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_create_swap_chain);
152 window_class->present = GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_present);
153 window_class->on_resize =
154 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_on_resize);
155 window_class->unprepare =
156 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_unprepare);
157 window_class->set_render_rectangle =
158 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_set_render_rectangle);
159 window_class->set_title =
160 GST_DEBUG_FUNCPTR (gst_d3d11_window_win32_set_title);
161 }
162
163 static void
gst_d3d11_window_win32_init(GstD3D11WindowWin32 * self)164 gst_d3d11_window_win32_init (GstD3D11WindowWin32 * self)
165 {
166 g_mutex_init (&self->lock);
167 g_cond_init (&self->cond);
168
169 self->main_context = g_main_context_new ();
170 }
171
172 static void
gst_d3d11_window_win32_constructed(GObject * object)173 gst_d3d11_window_win32_constructed (GObject * object)
174 {
175 GstD3D11Window *window = GST_D3D11_WINDOW (object);
176 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
177
178 if (window->external_handle) {
179 gst_d3d11_window_win32_set_window_handle (self, window->external_handle);
180 goto done;
181 }
182
183 g_mutex_lock (&self->lock);
184 self->loop = g_main_loop_new (self->main_context, FALSE);
185 self->thread = g_thread_new ("GstD3D11WindowWin32",
186 (GThreadFunc) gst_d3d11_window_win32_thread_func, self);
187 while (!g_main_loop_is_running (self->loop))
188 g_cond_wait (&self->cond, &self->lock);
189 g_mutex_unlock (&self->lock);
190
191 done:
192 G_OBJECT_CLASS (parent_class)->constructed (object);
193 }
194
195 static void
gst_d3d11_window_win32_dispose(GObject * object)196 gst_d3d11_window_win32_dispose (GObject * object)
197 {
198 gst_d3d11_window_win32_unprepare (GST_D3D11_WINDOW (object));
199
200 G_OBJECT_CLASS (parent_class)->dispose (object);
201 }
202
203 static void
gst_d3d11_window_win32_unprepare(GstD3D11Window * window)204 gst_d3d11_window_win32_unprepare (GstD3D11Window * window)
205 {
206 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
207
208 if (self->external_hwnd) {
209 gst_d3d11_window_win32_release_external_handle (self->external_hwnd);
210 RemovePropA (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
211
212 if (self->internal_hwnd_thread == g_thread_self ()) {
213 /* State changing thread is identical to internal window thread.
214 * window can be closed here */
215
216 GST_INFO_OBJECT (self, "Closing internal window immediately");
217 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
218 } else {
219 /* We cannot destroy internal window from non-window thread.
220 * and we cannot use synchronously SendMessage() method at this point
221 * since window thread might be wait for current thread and SendMessage()
222 * will be blocked until it's called from window thread.
223 * Instead, posts message so that it can be closed from window thread
224 * asynchronously */
225 GST_INFO_OBJECT (self, "Posting custom destory message");
226 PostMessageA (self->internal_hwnd, WM_GST_D3D11_DESTROY_INTERNAL_WINDOW,
227 0, 0);
228 }
229
230 self->external_hwnd = NULL;
231 self->internal_hwnd = NULL;
232 self->internal_hwnd_thread = NULL;
233 }
234
235 if (self->loop) {
236 g_main_loop_quit (self->loop);
237 }
238
239 if (self->thread) {
240 g_thread_join (self->thread);
241 self->thread = NULL;
242 }
243
244 if (self->loop) {
245 g_main_loop_unref (self->loop);
246 self->loop = NULL;
247 }
248
249 if (self->main_context) {
250 g_main_context_unref (self->main_context);
251 self->main_context = NULL;
252 }
253 }
254
255 static void
gst_d3d11_window_win32_set_render_rectangle(GstD3D11Window * window,const GstVideoRectangle * rect)256 gst_d3d11_window_win32_set_render_rectangle (GstD3D11Window * window,
257 const GstVideoRectangle * rect)
258 {
259 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
260
261 if (self->external_hwnd && self->internal_hwnd) {
262 g_atomic_int_add (&self->pending_move_window, 1);
263 self->render_rect = *rect;
264
265 if (self->internal_hwnd_thread == g_thread_self ()) {
266 /* We are on message pumping thread already, handle this synchroniously */
267 SendMessageA (self->internal_hwnd, WM_GST_D3D11_MOVE_WINDOW, 0, 0);
268 } else {
269 /* Post message to message pumping thread. Handling HWND specific message
270 * on message pumping thread is not a worst idea in generall */
271 PostMessageA (self->internal_hwnd, WM_GST_D3D11_MOVE_WINDOW, 0, 0);
272 }
273 } else {
274 /* XXX: Not sure what's expected behavior if we are drawing on internal
275 * HWND but user wants to specify rectangle.
276 *
277 * - Should we move window to corresponding desktop coordinates ?
278 * - Or should crop correspondingly by modifying viewport of
279 * render target view of swapchian's backbuffer or so ?
280 * - Or should we ignore set_render_rectangle if we are drawing on
281 * internal HWND without external HWND ?
282 */
283 }
284 }
285
286 static void
gst_d3d11_window_win32_set_title(GstD3D11Window * window,const gchar * title)287 gst_d3d11_window_win32_set_title (GstD3D11Window * window, const gchar * title)
288 {
289 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
290
291 /* Do this only when we are rendring on our own HWND */
292 if (!self->external_hwnd && self->internal_hwnd) {
293 gunichar2 *str = g_utf8_to_utf16 (title, -1, nullptr, nullptr, nullptr);
294
295 if (str) {
296 SetWindowTextW (self->internal_hwnd, (LPCWSTR) str);
297 g_free (str);
298 }
299 }
300 }
301
302 static void
gst_d3d11_window_win32_finalize(GObject * object)303 gst_d3d11_window_win32_finalize (GObject * object)
304 {
305 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (object);
306
307 g_mutex_clear (&self->lock);
308 g_cond_clear (&self->cond);
309
310 G_OBJECT_CLASS (parent_class)->finalize (object);
311 }
312
313 static gboolean
running_cb(gpointer user_data)314 running_cb (gpointer user_data)
315 {
316 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (user_data);
317
318 GST_TRACE_OBJECT (self, "Main loop running now");
319
320 g_mutex_lock (&self->lock);
321 g_cond_signal (&self->cond);
322 g_mutex_unlock (&self->lock);
323
324 return G_SOURCE_REMOVE;
325 }
326
327 static gboolean
msg_cb(GIOChannel * source,GIOCondition condition,gpointer data)328 msg_cb (GIOChannel * source, GIOCondition condition, gpointer data)
329 {
330 MSG msg;
331
332 if (!PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
333 return G_SOURCE_CONTINUE;
334
335 TranslateMessage (&msg);
336 DispatchMessage (&msg);
337
338 return G_SOURCE_CONTINUE;
339 }
340
341 static gpointer
gst_d3d11_window_win32_thread_func(gpointer data)342 gst_d3d11_window_win32_thread_func (gpointer data)
343 {
344 GstD3D11Window *window = GST_D3D11_WINDOW (data);
345 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (data);
346 GSource *source;
347
348 GST_DEBUG_OBJECT (self, "Enter loop");
349 g_main_context_push_thread_default (self->main_context);
350
351 window->initialized = gst_d3d11_window_win32_create_internal_window (self);
352
353 self->msg_io_channel =
354 g_io_channel_win32_new_messages ((guintptr) self->internal_hwnd);
355 self->msg_source = g_io_create_watch (self->msg_io_channel, G_IO_IN);
356 g_source_set_callback (self->msg_source, (GSourceFunc) msg_cb, self, NULL);
357 g_source_attach (self->msg_source, self->main_context);
358
359 source = g_idle_source_new ();
360 g_source_set_callback (source, (GSourceFunc) running_cb, self, NULL);
361 g_source_attach (source, self->main_context);
362 g_source_unref (source);
363
364 g_main_loop_run (self->loop);
365
366 RemovePropA (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
367 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
368 self->internal_hwnd = NULL;
369 self->internal_hwnd_thread = NULL;
370
371 if (self->msg_source) {
372 g_source_destroy (self->msg_source);
373 g_source_unref (self->msg_source);
374 self->msg_source = NULL;
375 }
376
377 if (self->msg_io_channel) {
378 g_io_channel_unref (self->msg_io_channel);
379 self->msg_io_channel = NULL;
380 }
381
382 g_main_context_pop_thread_default (self->main_context);
383
384 GST_DEBUG_OBJECT (self, "Exit loop");
385
386 return NULL;
387 }
388
389 static void
gst_d3d11_window_win32_destroy_internal_window(HWND hwnd)390 gst_d3d11_window_win32_destroy_internal_window (HWND hwnd)
391 {
392 if (!hwnd)
393 return;
394
395 SetParent (hwnd, NULL);
396
397 GST_INFO ("Destroying internal window %" G_GUINTPTR_FORMAT, (guintptr) hwnd);
398
399 if (!DestroyWindow (hwnd))
400 GST_WARNING ("failed to destroy window %" G_GUINTPTR_FORMAT
401 ", 0x%x", (guintptr) hwnd, (guint) GetLastError ());
402 }
403
404 static void
gst_d3d11_window_win32_set_external_handle(GstD3D11WindowWin32 * self)405 gst_d3d11_window_win32_set_external_handle (GstD3D11WindowWin32 * self)
406 {
407 WNDPROC external_window_proc;
408
409 external_window_proc =
410 (WNDPROC) GetWindowLongPtrA (self->external_hwnd, GWLP_WNDPROC);
411
412 GST_DEBUG_OBJECT (self, "set external window %" G_GUINTPTR_FORMAT
413 ", original window procedure %p", (guintptr) self->external_hwnd,
414 external_window_proc);
415
416 SetPropA (self->external_hwnd, EXTERNAL_PROC_PROP_NAME,
417 (HANDLE) external_window_proc);
418 SetPropA (self->external_hwnd, D3D11_WINDOW_PROP_NAME, self);
419 SetWindowLongPtrA (self->external_hwnd, GWLP_WNDPROC,
420 (LONG_PTR) sub_class_proc);
421
422 /* Will create our internal window on parent window's thread */
423 SendMessageA (self->external_hwnd, WM_GST_D3D11_CONSTRUCT_INTERNAL_WINDOW,
424 0, 0);
425 }
426
427 static void
gst_d3d11_window_win32_release_external_handle(HWND hwnd)428 gst_d3d11_window_win32_release_external_handle (HWND hwnd)
429 {
430 WNDPROC external_proc;
431
432 if (!hwnd)
433 return;
434
435 external_proc = (WNDPROC) GetPropA (hwnd, EXTERNAL_PROC_PROP_NAME);
436 if (!external_proc) {
437 GST_WARNING ("Failed to get original window procedure");
438 return;
439 }
440
441 GST_DEBUG ("release external window %" G_GUINTPTR_FORMAT
442 ", original window procedure %p", (guintptr) hwnd, external_proc);
443
444 RemovePropA (hwnd, EXTERNAL_PROC_PROP_NAME);
445 RemovePropA (hwnd, D3D11_WINDOW_PROP_NAME);
446
447 if (!SetWindowLongPtrA (hwnd, GWLP_WNDPROC, (LONG_PTR) external_proc))
448 GST_WARNING ("Couldn't restore original window procedure");
449 }
450
451 static gboolean
gst_d3d11_window_win32_create_internal_window(GstD3D11WindowWin32 * self)452 gst_d3d11_window_win32_create_internal_window (GstD3D11WindowWin32 * self)
453 {
454 WNDCLASSEXA wc;
455 ATOM atom = 0;
456 HINSTANCE hinstance = GetModuleHandleA (NULL);
457
458 GST_LOG_OBJECT (self, "Attempting to create a win32 window");
459
460 G_LOCK (create_lock);
461 atom = GetClassInfoExA (hinstance, "GSTD3D11", &wc);
462 if (atom == 0) {
463 GST_LOG_OBJECT (self, "Register internal window class");
464 ZeroMemory (&wc, sizeof (WNDCLASSEXA));
465
466 wc.cbSize = sizeof (WNDCLASSEXA);
467 wc.lpfnWndProc = window_proc;
468 wc.hInstance = hinstance;
469 wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);
470 wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
471 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
472 wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
473 wc.lpszClassName = "GSTD3D11";
474
475 atom = RegisterClassExA (&wc);
476
477 if (atom == 0) {
478 G_UNLOCK (create_lock);
479 GST_ERROR_OBJECT (self, "Failed to register window class 0x%x",
480 (unsigned int) GetLastError ());
481 return FALSE;
482 }
483 } else {
484 GST_LOG_OBJECT (self, "window class was already registered");
485 }
486
487 self->device_handle = 0;
488 self->internal_hwnd = 0;
489 self->visible = FALSE;
490
491 self->internal_hwnd = CreateWindowExA (0,
492 "GSTD3D11",
493 "Direct3D11 renderer",
494 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW,
495 CW_USEDEFAULT, CW_USEDEFAULT,
496 0, 0, (HWND) NULL, (HMENU) NULL, hinstance, self);
497
498 G_UNLOCK (create_lock);
499
500 if (!self->internal_hwnd) {
501 GST_ERROR_OBJECT (self, "Failed to create d3d11 window");
502 return FALSE;
503 }
504
505 GST_DEBUG_OBJECT (self, "d3d11 window created: %" G_GUINTPTR_FORMAT,
506 (guintptr) self->internal_hwnd);
507
508 /* device_handle is set in the window_proc */
509 if (!self->device_handle) {
510 GST_ERROR_OBJECT (self, "device handle is not available");
511 return FALSE;
512 }
513
514 GST_LOG_OBJECT (self,
515 "Created a internal d3d11 window %p", self->internal_hwnd);
516
517 self->internal_hwnd_thread = g_thread_self ();
518
519 return TRUE;
520 }
521
522 /* always called from window thread */
523 static void
gst_d3d11_window_win32_change_fullscreen_mode_internal(GstD3D11WindowWin32 * self)524 gst_d3d11_window_win32_change_fullscreen_mode_internal (GstD3D11WindowWin32 *
525 self)
526 {
527 GstD3D11Window *window = GST_D3D11_WINDOW (self);
528 HWND hwnd = self->external_hwnd ? self->external_hwnd : self->internal_hwnd;
529
530 if (!window->swap_chain)
531 return;
532
533 if (window->requested_fullscreen == window->fullscreen)
534 return;
535
536 GST_DEBUG_OBJECT (self, "Change mode to %s",
537 window->requested_fullscreen ? "fullscreen" : "windowed");
538
539 window->fullscreen = !window->fullscreen;
540
541 if (!window->fullscreen) {
542 /* Restore the window's attributes and size */
543 SetWindowLongA (hwnd, GWL_STYLE, self->restore_style);
544
545 SetWindowPos (hwnd, HWND_NOTOPMOST,
546 self->restore_rect.left,
547 self->restore_rect.top,
548 self->restore_rect.right - self->restore_rect.left,
549 self->restore_rect.bottom - self->restore_rect.top,
550 SWP_FRAMECHANGED | SWP_NOACTIVATE);
551
552 ShowWindow (hwnd, SW_NORMAL);
553 } else {
554 ComPtr < IDXGIOutput > output;
555 DXGI_OUTPUT_DESC output_desc;
556 IDXGISwapChain *swap_chain = window->swap_chain;
557
558 /* show window before change style */
559 ShowWindow (hwnd, SW_SHOW);
560
561 /* Save the old window rect so we can restore it when exiting
562 * fullscreen mode */
563 GetWindowRect (hwnd, &self->restore_rect);
564 self->restore_style = GetWindowLong (hwnd, GWL_STYLE);
565
566 /* Make the window borderless so that the client area can fill the screen */
567 SetWindowLongA (hwnd, GWL_STYLE,
568 self->restore_style &
569 ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU |
570 WS_THICKFRAME));
571
572 swap_chain->GetContainingOutput (&output);
573 output->GetDesc (&output_desc);
574
575 SetWindowPos (hwnd, HWND_TOPMOST,
576 output_desc.DesktopCoordinates.left,
577 output_desc.DesktopCoordinates.top,
578 output_desc.DesktopCoordinates.right,
579 output_desc.DesktopCoordinates.bottom,
580 SWP_FRAMECHANGED | SWP_NOACTIVATE);
581
582 ShowWindow (hwnd, SW_MAXIMIZE);
583 }
584
585 GST_DEBUG_OBJECT (self, "Fullscreen mode change done");
586 }
587
588 static void
gst_d3d11_window_win32_on_key_event(GstD3D11WindowWin32 * self,HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)589 gst_d3d11_window_win32_on_key_event (GstD3D11WindowWin32 * self,
590 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
591 {
592 GstD3D11Window *window = GST_D3D11_WINDOW (self);
593 gunichar2 wcrep[128];
594 const gchar *event;
595
596 if (!window->enable_navigation_events)
597 return;
598
599 if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) {
600 gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
601 if (utfrep) {
602 if (uMsg == WM_KEYDOWN)
603 event = "key-press";
604 else
605 event = "key-release";
606
607 gst_d3d11_window_on_key_event (window, event, utfrep);
608 g_free (utfrep);
609 }
610 }
611 }
612
613 static void
gst_d3d11_window_win32_on_mouse_event(GstD3D11WindowWin32 * self,HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)614 gst_d3d11_window_win32_on_mouse_event (GstD3D11WindowWin32 * self,
615 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
616 {
617 GstD3D11Window *window = GST_D3D11_WINDOW (self);
618 gint button;
619 const gchar *event = NULL;
620
621 if (!window->enable_navigation_events)
622 return;
623
624 /* FIXME: convert to render coordinate */
625 switch (uMsg) {
626 case WM_MOUSEMOVE:
627 button = 0;
628 event = "mouse-move";
629 break;
630 case WM_LBUTTONDOWN:
631 button = 1;
632 event = "mouse-button-press";
633 break;
634 case WM_LBUTTONUP:
635 button = 1;
636 event = "mouse-button-release";
637 break;
638 case WM_RBUTTONDOWN:
639 button = 2;
640 event = "mouse-button-press";
641 break;
642 case WM_RBUTTONUP:
643 button = 2;
644 event = "mouse-button-release";
645 break;
646 case WM_MBUTTONDOWN:
647 button = 3;
648 event = "mouse-button-press";
649 break;
650 case WM_MBUTTONUP:
651 button = 3;
652 event = "mouse-button-release";
653 break;
654 default:
655 break;
656 }
657
658 if (event)
659 gst_d3d11_window_on_mouse_event (window,
660 event, button, (gdouble) LOWORD (lParam), (gdouble) HIWORD (lParam));
661 }
662
663 static void
gst_d3d11_window_win32_handle_window_proc(GstD3D11WindowWin32 * self,HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)664 gst_d3d11_window_win32_handle_window_proc (GstD3D11WindowWin32 * self,
665 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
666 {
667 GstD3D11Window *window = GST_D3D11_WINDOW (self);
668
669 switch (uMsg) {
670 case WM_SIZE:
671 gst_d3d11_window_win32_on_resize (window, 0, 0);
672 break;
673 case WM_CLOSE:
674 if (self->internal_hwnd) {
675 RemovePropA (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
676 ShowWindow (self->internal_hwnd, SW_HIDE);
677 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
678 self->internal_hwnd = NULL;
679 self->internal_hwnd_thread = NULL;
680 }
681 break;
682 case WM_KEYDOWN:
683 case WM_KEYUP:
684 gst_d3d11_window_win32_on_key_event (self, hWnd, uMsg, wParam, lParam);
685 break;
686 case WM_LBUTTONDOWN:
687 case WM_LBUTTONUP:
688 case WM_RBUTTONDOWN:
689 case WM_RBUTTONUP:
690 case WM_MBUTTONDOWN:
691 case WM_MBUTTONUP:
692 case WM_MOUSEMOVE:
693 gst_d3d11_window_win32_on_mouse_event (self, hWnd, uMsg, wParam, lParam);
694 break;
695 case WM_SYSKEYDOWN:
696 if ((window->fullscreen_toggle_mode &
697 GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER)
698 == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_ALT_ENTER) {
699 WORD state = GetKeyState (VK_RETURN);
700 BYTE high = HIBYTE (state);
701
702 if (high & 0x1) {
703 window->requested_fullscreen = !window->fullscreen;
704 gst_d3d11_window_win32_change_fullscreen_mode_internal (self);
705 }
706 }
707 break;
708 case WM_GST_D3D11_FULLSCREEN:
709 if (g_atomic_int_get (&self->pending_fullscreen_count)) {
710 g_atomic_int_dec_and_test (&self->pending_fullscreen_count);
711 if ((window->fullscreen_toggle_mode &
712 GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY)
713 == GST_D3D11_WINDOW_FULLSCREEN_TOGGLE_MODE_PROPERTY)
714 gst_d3d11_window_win32_change_fullscreen_mode_internal (self);
715 }
716 break;
717 case WM_GST_D3D11_MOVE_WINDOW:
718 if (g_atomic_int_get (&self->pending_move_window)) {
719 g_atomic_int_set (&self->pending_move_window, 0);
720
721 if (self->internal_hwnd && self->external_hwnd) {
722 if (self->render_rect.w < 0 || self->render_rect.h < 0) {
723 RECT rect;
724
725 /* Reset render rect and back to full-size window */
726 if (GetClientRect (self->external_hwnd, &rect)) {
727 MoveWindow (self->internal_hwnd, 0, 0,
728 rect.right - rect.left, rect.bottom - rect.top, FALSE);
729 }
730 } else {
731 MoveWindow (self->internal_hwnd, self->render_rect.x,
732 self->render_rect.y, self->render_rect.w, self->render_rect.h,
733 FALSE);
734 }
735 }
736 }
737 break;
738 default:
739 break;
740 }
741
742 return;
743 }
744
745 static LRESULT CALLBACK
window_proc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)746 window_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
747 {
748 GstD3D11WindowWin32 *self;
749
750 if (uMsg == WM_CREATE) {
751 self = GST_D3D11_WINDOW_WIN32 (((LPCREATESTRUCT) lParam)->lpCreateParams);
752
753 GST_LOG_OBJECT (self, "WM_CREATE");
754
755 self->device_handle = GetDC (hWnd);
756 /* Do this, otherwise we hang on exit. We can still use it (due to the
757 * CS_OWNDC flag in the WindowClass) after we have Released.
758 */
759 ReleaseDC (hWnd, self->device_handle);
760
761 SetPropA (hWnd, D3D11_WINDOW_PROP_NAME, self);
762 } else if (GetPropA (hWnd, D3D11_WINDOW_PROP_NAME)) {
763 HANDLE handle = GetPropA (hWnd, D3D11_WINDOW_PROP_NAME);
764
765 if (!GST_IS_D3D11_WINDOW_WIN32 (handle)) {
766 GST_WARNING ("%p is not d3d11window object", handle);
767 return DefWindowProcA (hWnd, uMsg, wParam, lParam);
768 }
769
770 self = GST_D3D11_WINDOW_WIN32 (handle);
771
772 g_assert (self->internal_hwnd == hWnd);
773
774 gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam,
775 lParam);
776
777 switch (uMsg) {
778 case WM_SIZE:
779 /* We handled this event already */
780 return 0;
781 case WM_NCHITTEST:
782 /* To passthrough mouse event if external window is used.
783 * Only hit-test succeeded window can receive/handle some mouse events
784 * and we want such events to be handled by parent (application) window
785 */
786 if (self->external_hwnd)
787 return (LRESULT) HTTRANSPARENT;
788 break;
789 default:
790 break;
791 }
792 } else if (uMsg == WM_GST_D3D11_DESTROY_INTERNAL_WINDOW) {
793 GST_INFO ("Handle destroy window message");
794 gst_d3d11_window_win32_destroy_internal_window (hWnd);
795
796 return 0;
797 }
798
799 return DefWindowProcA (hWnd, uMsg, wParam, lParam);
800 }
801
802 static LRESULT FAR PASCAL
sub_class_proc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)803 sub_class_proc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
804 {
805 WNDPROC external_window_proc =
806 (WNDPROC) GetPropA (hWnd, EXTERNAL_PROC_PROP_NAME);
807 GstD3D11WindowWin32 *self =
808 (GstD3D11WindowWin32 *) GetPropA (hWnd, D3D11_WINDOW_PROP_NAME);
809
810 if (uMsg == WM_GST_D3D11_CONSTRUCT_INTERNAL_WINDOW) {
811 GstD3D11Window *window = GST_D3D11_WINDOW (self);
812 RECT rect;
813
814 GST_DEBUG_OBJECT (self, "Create internal window");
815
816 window->initialized = gst_d3d11_window_win32_create_internal_window (self);
817
818 SetWindowLongPtrA (self->internal_hwnd, GWL_STYLE, WS_CHILD | WS_MAXIMIZE);
819 SetParent (self->internal_hwnd, self->external_hwnd);
820
821 /* take changes into account: SWP_FRAMECHANGED */
822 GetClientRect (self->external_hwnd, &rect);
823 SetWindowPos (self->internal_hwnd, HWND_TOP, rect.left, rect.top,
824 rect.right, rect.bottom,
825 SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
826 SWP_FRAMECHANGED | SWP_NOACTIVATE);
827 MoveWindow (self->internal_hwnd, rect.left, rect.top, rect.right,
828 rect.bottom, FALSE);
829
830 /* don't need to be chained up to parent window procedure,
831 * as this is our custom message */
832 return 0;
833 } else if (self) {
834 if (uMsg == WM_SIZE) {
835 MoveWindow (self->internal_hwnd, 0, 0, LOWORD (lParam), HIWORD (lParam),
836 FALSE);
837 } else if (uMsg == WM_CLOSE || uMsg == WM_DESTROY) {
838 g_mutex_lock (&self->lock);
839 GST_WARNING_OBJECT (self, "external window is closing");
840 gst_d3d11_window_win32_release_external_handle (self->external_hwnd);
841 self->external_hwnd = NULL;
842
843 RemovePropA (self->internal_hwnd, D3D11_WINDOW_PROP_NAME);
844 ShowWindow (self->internal_hwnd, SW_HIDE);
845 gst_d3d11_window_win32_destroy_internal_window (self->internal_hwnd);
846 self->internal_hwnd = NULL;
847 self->internal_hwnd_thread = NULL;
848
849 self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED;
850 g_mutex_unlock (&self->lock);
851 } else {
852 gst_d3d11_window_win32_handle_window_proc (self, hWnd, uMsg, wParam,
853 lParam);
854 }
855 }
856
857 return CallWindowProcA (external_window_proc, hWnd, uMsg, wParam, lParam);
858 }
859
860 static void
gst_d3d11_window_win32_disable_alt_enter(GstD3D11WindowWin32 * self,GstD3D11Device * device,IDXGISwapChain * swap_chain,HWND hwnd)861 gst_d3d11_window_win32_disable_alt_enter (GstD3D11WindowWin32 * self,
862 GstD3D11Device * device, IDXGISwapChain * swap_chain, HWND hwnd)
863 {
864 ComPtr < IDXGIFactory1 > factory;
865 HRESULT hr;
866
867 hr = swap_chain->GetParent (IID_PPV_ARGS (&factory));
868 if (!gst_d3d11_result (hr, device) || !factory) {
869 GST_WARNING_OBJECT (self,
870 "Cannot get parent dxgi factory for swapchain %p, hr: 0x%x",
871 swap_chain, (guint) hr);
872 return;
873 }
874
875 hr = factory->MakeWindowAssociation (hwnd, DXGI_MWA_NO_ALT_ENTER);
876 if (!gst_d3d11_result (hr, device)) {
877 GST_WARNING_OBJECT (self,
878 "MakeWindowAssociation failure, hr: 0x%x", (guint) hr);
879 }
880 }
881
882 static IDXGISwapChain *
create_swap_chain(GstD3D11WindowWin32 * self,GstD3D11Device * device,DXGI_SWAP_CHAIN_DESC * desc)883 create_swap_chain (GstD3D11WindowWin32 * self, GstD3D11Device * device,
884 DXGI_SWAP_CHAIN_DESC * desc)
885 {
886 HRESULT hr;
887 IDXGISwapChain *swap_chain = NULL;
888 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
889 IDXGIFactory1 *factory = gst_d3d11_device_get_dxgi_factory_handle (device);
890
891 gst_d3d11_device_lock (device);
892 hr = factory->CreateSwapChain (device_handle, desc, &swap_chain);
893 gst_d3d11_device_unlock (device);
894
895 if (!gst_d3d11_result (hr, device)) {
896 GST_WARNING_OBJECT (self, "Cannot create SwapChain Object: 0x%x",
897 (guint) hr);
898 swap_chain = NULL;
899 }
900
901 return swap_chain;
902 }
903
904 #if (GST_D3D11_DXGI_HEADER_VERSION >= 2)
905 static IDXGISwapChain1 *
create_swap_chain_for_hwnd(GstD3D11WindowWin32 * self,GstD3D11Device * device,HWND hwnd,DXGI_SWAP_CHAIN_DESC1 * desc,DXGI_SWAP_CHAIN_FULLSCREEN_DESC * fullscreen_desc,IDXGIOutput * output)906 create_swap_chain_for_hwnd (GstD3D11WindowWin32 * self, GstD3D11Device * device,
907 HWND hwnd, DXGI_SWAP_CHAIN_DESC1 * desc,
908 DXGI_SWAP_CHAIN_FULLSCREEN_DESC * fullscreen_desc, IDXGIOutput * output)
909 {
910 HRESULT hr;
911 IDXGISwapChain1 *swap_chain = NULL;
912 ID3D11Device *device_handle = gst_d3d11_device_get_device_handle (device);
913 IDXGIFactory1 *factory = gst_d3d11_device_get_dxgi_factory_handle (device);
914 ComPtr < IDXGIFactory2 > factory2;
915
916 hr = factory->QueryInterface (IID_PPV_ARGS (&factory2));
917 if (!gst_d3d11_result (hr, device)) {
918 GST_WARNING_OBJECT (self, "IDXGIFactory2 interface is unavailable");
919 return NULL;
920 }
921
922 gst_d3d11_device_lock (device);
923 hr = factory2->CreateSwapChainForHwnd (device_handle, hwnd, desc,
924 fullscreen_desc, output, &swap_chain);
925 gst_d3d11_device_unlock (device);
926
927 if (!gst_d3d11_result (hr, device)) {
928 GST_WARNING_OBJECT (self, "Cannot create SwapChain Object: 0x%x",
929 (guint) hr);
930 swap_chain = NULL;
931 }
932
933 return swap_chain;
934 }
935 #endif
936
937 static gboolean
gst_d3d11_window_win32_create_swap_chain(GstD3D11Window * window,DXGI_FORMAT format,guint width,guint height,guint swapchain_flags,IDXGISwapChain ** swap_chain)938 gst_d3d11_window_win32_create_swap_chain (GstD3D11Window * window,
939 DXGI_FORMAT format, guint width, guint height,
940 guint swapchain_flags, IDXGISwapChain ** swap_chain)
941 {
942 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
943 DXGI_SWAP_CHAIN_DESC desc = { 0, };
944 IDXGISwapChain *new_swapchain = NULL;
945 GstD3D11Device *device = window->device;
946
947 self->have_swapchain1 = FALSE;
948
949 #if (GST_D3D11_DXGI_HEADER_VERSION >= 2)
950 {
951 DXGI_SWAP_CHAIN_DESC1 desc1 = { 0, };
952 desc1.Width = 0;
953 desc1.Height = 0;
954 desc1.Format = format;
955 /* FIXME: add support stereo */
956 desc1.Stereo = FALSE;
957 desc1.SampleDesc.Count = 1;
958 desc1.SampleDesc.Quality = 0;
959 desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
960 desc1.BufferCount = 2;
961 desc1.Scaling = DXGI_SCALING_STRETCH;
962
963 /* scaling-stretch would break aspect-ratio so we prefer to use scaling-none,
964 * but Windows7 does not support this method */
965 if (gst_d3d11_is_windows_8_or_greater ())
966 desc1.Scaling = DXGI_SCALING_NONE;
967 desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
968 desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
969 desc1.Flags = swapchain_flags;
970
971 new_swapchain = create_swap_chain_for_hwnd (self, device,
972 self->internal_hwnd, &desc1, NULL, NULL);
973
974 if (!new_swapchain) {
975 GST_WARNING_OBJECT (self, "Failed to create swapchain1");
976 } else {
977 self->have_swapchain1 = TRUE;
978 }
979 }
980 #endif
981
982 if (!new_swapchain) {
983 DXGI_SWAP_EFFECT swap_effect = DXGI_SWAP_EFFECT_DISCARD;
984
985 if (gst_d3d11_is_windows_8_or_greater ())
986 swap_effect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
987
988 /* we will get client area at on_resize */
989 desc.BufferDesc.Width = 0;
990 desc.BufferDesc.Height = 0;
991 /* don't care refresh rate */
992 desc.BufferDesc.RefreshRate.Numerator = 0;
993 desc.BufferDesc.RefreshRate.Denominator = 1;
994 desc.BufferDesc.Format = format;
995 desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
996 desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
997 desc.SampleDesc.Count = 1;
998 desc.SampleDesc.Quality = 0;
999 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1000 desc.BufferCount = 2;
1001 desc.SwapEffect = swap_effect;
1002 desc.OutputWindow = self->internal_hwnd;
1003 desc.Windowed = TRUE;
1004 desc.Flags = swapchain_flags;
1005
1006 new_swapchain = create_swap_chain (self, device, &desc);
1007 }
1008
1009 if (!new_swapchain) {
1010 GST_ERROR_OBJECT (self, "Cannot create swapchain");
1011 return FALSE;
1012 }
1013
1014 /* disable alt+enter here. It should be manually handled */
1015 gst_d3d11_device_lock (device);
1016 gst_d3d11_window_win32_disable_alt_enter (self,
1017 device, new_swapchain, desc.OutputWindow);
1018 gst_d3d11_device_unlock (device);
1019
1020 *swap_chain = new_swapchain;
1021
1022 return TRUE;
1023 }
1024
1025 static void
gst_d3d11_window_win32_set_window_handle(GstD3D11WindowWin32 * self,guintptr handle)1026 gst_d3d11_window_win32_set_window_handle (GstD3D11WindowWin32 * self,
1027 guintptr handle)
1028 {
1029 self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_NONE;
1030
1031 self->external_hwnd = (HWND) handle;
1032 gst_d3d11_window_win32_set_external_handle (self);
1033
1034 self->overlay_state = GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_OPENED;
1035 }
1036
1037 static void
gst_d3d11_window_win32_show(GstD3D11Window * window)1038 gst_d3d11_window_win32_show (GstD3D11Window * window)
1039 {
1040 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1041 gint width, height;
1042
1043 width = GST_VIDEO_INFO_WIDTH (&window->render_info);
1044 height = GST_VIDEO_INFO_HEIGHT (&window->render_info);
1045
1046 if (!self->visible) {
1047 /* if no parent the real size has to be set now because this has not been done
1048 * when at window creation */
1049 if (!self->external_hwnd) {
1050 RECT rect;
1051 GetClientRect (self->internal_hwnd, &rect);
1052 width += 2 * GetSystemMetrics (SM_CXSIZEFRAME);
1053 height +=
1054 2 * GetSystemMetrics (SM_CYSIZEFRAME) +
1055 GetSystemMetrics (SM_CYCAPTION);
1056 MoveWindow (self->internal_hwnd, rect.left, rect.top, width,
1057 height, FALSE);
1058 }
1059
1060 ShowWindow (self->internal_hwnd, SW_SHOW);
1061 self->visible = TRUE;
1062 }
1063 }
1064
1065 static GstFlowReturn
gst_d3d11_window_win32_present(GstD3D11Window * window,guint present_flags)1066 gst_d3d11_window_win32_present (GstD3D11Window * window, guint present_flags)
1067 {
1068 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1069 HRESULT hr;
1070
1071 if ((!self->external_hwnd &&
1072 self->overlay_state == GST_D3D11_WINDOW_WIN32_OVERLAY_STATE_CLOSED)
1073 || !self->internal_hwnd) {
1074 GST_ERROR_OBJECT (self, "Output window was closed");
1075
1076 return GST_D3D11_WINDOW_FLOW_CLOSED;
1077 }
1078 #if (GST_D3D11_DXGI_HEADER_VERSION >= 2)
1079 if (self->have_swapchain1) {
1080 IDXGISwapChain1 *swap_chain1 = (IDXGISwapChain1 *) window->swap_chain;
1081 DXGI_PRESENT_PARAMETERS present_params = { 0, };
1082
1083 /* the first present should not specify dirty-rect */
1084 if (!window->first_present) {
1085 present_params.DirtyRectsCount = 1;
1086 present_params.pDirtyRects = &window->render_rect;
1087 }
1088
1089 hr = swap_chain1->Present1 (0, present_flags, &present_params);
1090 } else
1091 #endif
1092 {
1093 hr = window->swap_chain->Present (0, present_flags);
1094 }
1095
1096 if (!gst_d3d11_result (hr, window->device)) {
1097 GST_WARNING_OBJECT (self, "Direct3D cannot present texture, hr: 0x%x",
1098 (guint) hr);
1099 }
1100
1101 return GST_FLOW_OK;
1102 }
1103
1104 static void
gst_d3d11_window_win32_on_resize(GstD3D11Window * window,guint width,guint height)1105 gst_d3d11_window_win32_on_resize (GstD3D11Window * window,
1106 guint width, guint height)
1107 {
1108 /* Set zero width and height here. dxgi will decide client area by itself */
1109 GST_D3D11_WINDOW_CLASS (parent_class)->on_resize (window, 0, 0);
1110 }
1111
1112 static void
gst_d3d11_window_win32_update_swap_chain(GstD3D11Window * window)1113 gst_d3d11_window_win32_update_swap_chain (GstD3D11Window * window)
1114 {
1115 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1116
1117 if (self->internal_hwnd)
1118 PostMessageA (self->internal_hwnd, WM_SIZE, 0, 0);
1119
1120 return;
1121 }
1122
1123 static void
gst_d3d11_window_win32_change_fullscreen_mode(GstD3D11Window * window)1124 gst_d3d11_window_win32_change_fullscreen_mode (GstD3D11Window * window)
1125 {
1126 GstD3D11WindowWin32 *self = GST_D3D11_WINDOW_WIN32 (window);
1127
1128 if (self->internal_hwnd) {
1129 g_atomic_int_add (&self->pending_fullscreen_count, 1);
1130 PostMessageA (self->internal_hwnd, WM_GST_D3D11_FULLSCREEN, 0, 0);
1131 }
1132 }
1133
1134 GstD3D11Window *
gst_d3d11_window_win32_new(GstD3D11Device * device,guintptr handle)1135 gst_d3d11_window_win32_new (GstD3D11Device * device, guintptr handle)
1136 {
1137 GstD3D11Window *window;
1138
1139 g_return_val_if_fail (GST_IS_D3D11_DEVICE (device), NULL);
1140
1141 window = (GstD3D11Window *) g_object_new (GST_TYPE_D3D11_WINDOW_WIN32,
1142 "d3d11device", device, "window-handle", handle, NULL);
1143 if (!window->initialized) {
1144 gst_object_unref (window);
1145 return NULL;
1146 }
1147
1148 g_object_ref_sink (window);
1149
1150 return window;
1151 }
1152