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