]> git.sesse.net Git - vlc/blob - modules/video_output/msw/directx.c
4832fd03bd11c87f8bb0d1abae031e1637345875
[vlc] / modules / video_output / msw / directx.c
1 /*****************************************************************************
2  * directx.c: Windows DirectDraw video output
3  *****************************************************************************
4  * Copyright (C) 2001-2009 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble:
26  *
27  * This plugin will use YUV overlay if supported, using overlay will result in
28  * the best video quality (hardware interpolation when rescaling the picture)
29  * and the fastest display as it requires less processing.
30  *
31  * If YUV overlay is not supported this plugin will use RGB offscreen video
32  * surfaces that will be blitted onto the primary surface (display) to
33  * effectively display the pictures. This fallback method also enables us to
34  * display video in window mode.
35  *
36  *****************************************************************************/
37
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
41
42 #include <assert.h>
43
44 #include <vlc_common.h>
45 #include <vlc_plugin.h>
46 #include <vlc_vout_display.h>
47 #include <vlc_charset.h>    /* FromT */
48
49 #include <windows.h>
50 #include <ddraw.h>
51 #include <commctrl.h>       /* ListView_(Get|Set)* */
52
53 #include "common.h"
54
55 /* Unicode function "DirectDrawEnumerateExW" has been desactivated
56    since in some cases this function fails and the callbacks are not
57    called. If the Unicode mode is restored, one should modify the
58    prototype of the callbacks and call the FromT conversion function.
59 */
60 #define DIRECTDRAWENUMERATEEX_NAME "DirectDrawEnumerateExA"
61
62 /*****************************************************************************
63  * Module descriptor
64  *****************************************************************************/
65 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
66 #define HW_YUV_LONGTEXT N_(\
67     "Try to use hardware acceleration for YUV->RGB conversions. " \
68     "This option doesn't have any effect when using overlays.")
69
70 #define SYSMEM_TEXT N_("Use video buffers in system memory")
71 #define SYSMEM_LONGTEXT N_(\
72     "Create video buffers in system memory instead of video memory. This " \
73     "isn't recommended as usually using video memory allows benefiting from " \
74     "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
75     "This option doesn't have any effect when using overlays.")
76
77 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
78 #define TRIPLEBUF_LONGTEXT N_(\
79     "Try to use triple buffering when using YUV overlays. That results in " \
80     "much better video quality (no flickering).")
81
82 #define DEVICE_TEXT N_("Name of desired display device")
83 #define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
84     "specify the Windows device name of the display that you want the video " \
85     "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
86     "\"\\\\.\\DISPLAY2\".")
87
88 #define DX_HELP N_("Recommended video output for Windows XP. " \
89     "Incompatible with Vista's Aero interface" )
90
91 static int  Open (vlc_object_t *);
92 static void Close(vlc_object_t *);
93
94 static int FindDevicesCallback(vlc_object_t *, const char *,
95                                char ***, char ***);
96 vlc_module_begin()
97     set_shortname("DirectX")
98     set_description(N_("DirectX (DirectDraw) video output"))
99     set_help(DX_HELP)
100     set_category(CAT_VIDEO)
101     set_subcategory(SUBCAT_VIDEO_VOUT)
102     add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
103               true)
104     add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
105               true)
106     add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
107               TRIPLEBUF_LONGTEXT, true)
108     add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
109         change_string_cb(FindDevicesCallback)
110
111     set_capability("vout display", 230)
112     add_shortcut("directx")
113     set_callbacks(Open, Close)
114 vlc_module_end()
115
116 /*****************************************************************************
117  * Local prototypes.
118  *****************************************************************************/
119
120 struct picture_sys_t {
121     LPDIRECTDRAWSURFACE2 surface;
122     LPDIRECTDRAWSURFACE2 front_surface;
123     picture_t            *fallback;
124 };
125
126 /*****************************************************************************
127  * DirectDraw GUIDs.
128  * Defining them here allows us to get rid of the dxguid library during
129  * the linking stage.
130  *****************************************************************************/
131 #include <initguid.h>
132 #undef GUID_EXT
133 #define GUID_EXT
134 DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56);
135 DEFINE_GUID(IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27);
136
137 static picture_pool_t *Pool  (vout_display_t *, unsigned);
138 static void           Display(vout_display_t *, picture_t *, subpicture_t *);
139 static int            Control(vout_display_t *, int, va_list);
140 static void           Manage (vout_display_t *);
141
142 /* */
143 static int WallpaperCallback(vlc_object_t *, char const *,
144                              vlc_value_t, vlc_value_t, void *);
145
146 static int  DirectXOpen(vout_display_t *, video_format_t *fmt);
147 static void DirectXClose(vout_display_t *);
148
149 static int  DirectXLock(picture_t *);
150 static void DirectXUnlock(picture_t *);
151
152 static int DirectXUpdateOverlay(vout_display_t *, LPDIRECTDRAWSURFACE2 surface);
153
154 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper);
155
156 /** This function allocates and initialize the DirectX vout display.
157  */
158 static int Open(vlc_object_t *object)
159 {
160     vout_display_t *vd = (vout_display_t *)object;
161     vout_display_sys_t *sys;
162
163     /* Allocate structure */
164     vd->sys = sys = calloc(1, sizeof(*sys));
165     if (!sys)
166         return VLC_ENOMEM;
167
168     /* Load direct draw DLL */
169     sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
170     if (!sys->hddraw_dll) {
171         msg_Warn(vd, "DirectXInitDDraw failed loading ddraw.dll");
172         free(sys);
173         return VLC_EGENERIC;
174     }
175
176     /* */
177     sys->use_wallpaper = var_CreateGetBool(vd, "video-wallpaper");
178     /* FIXME */
179     sys->use_overlay = false;//var_CreateGetBool(vd, "overlay"); /* FIXME */
180     sys->restore_overlay = false;
181     var_Create(vd, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
182
183     /* Initialisation */
184     if (CommonInit(vd))
185         goto error;
186
187     /* */
188     video_format_t fmt = vd->fmt;
189
190     if (DirectXOpen(vd, &fmt))
191         goto error;
192
193     /* */
194     vout_display_info_t info = vd->info;
195     info.is_slow = true;
196     info.has_double_click = true;
197     info.has_hide_mouse = false;
198     info.has_pictures_invalid = true;
199     info.has_event_thread = true;
200
201     /* Interaction TODO support starting with wallpaper mode */
202     vlc_mutex_init(&sys->lock);
203     sys->ch_wallpaper = sys->use_wallpaper;
204     sys->wallpaper_requested = sys->use_wallpaper;
205     sys->use_wallpaper = false;
206
207     vlc_value_t val;
208     val.psz_string = _("Wallpaper");
209     var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
210     var_AddCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
211
212     /* Setup vout_display now that everything is fine */
213     vd->fmt     = fmt;
214     vd->info    = info;
215
216     vd->pool    = Pool;
217     vd->prepare = NULL;
218     vd->display = Display;
219     vd->control = Control;
220     vd->manage  = Manage;
221     return VLC_SUCCESS;
222
223 error:
224     DirectXClose(vd);
225     CommonClean(vd);
226     if (sys->hddraw_dll)
227         FreeLibrary(sys->hddraw_dll);
228     free(sys);
229     return VLC_EGENERIC;
230 }
231
232 /** Terminate a vout display created by Open.
233  */
234 static void Close(vlc_object_t *object)
235 {
236     vout_display_t *vd = (vout_display_t *)object;
237     vout_display_sys_t *sys = vd->sys;
238
239     var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
240     vlc_mutex_destroy(&sys->lock);
241
242     /* Make sure the wallpaper is restored */
243     WallpaperChange(vd, false);
244
245     DirectXClose(vd);
246
247     CommonClean(vd);
248
249     if (sys->hddraw_dll)
250         FreeLibrary(sys->hddraw_dll);
251     free(sys);
252 }
253
254 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
255 {
256     VLC_UNUSED(count);
257     return vd->sys->pool;
258 }
259 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
260 {
261     vout_display_sys_t *sys = vd->sys;
262
263     assert(sys->display);
264
265     /* Our surface can be lost so be sure to check this
266      * and restore it if need be */
267     if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
268         if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
269             if (sys->use_overlay)
270                 DirectXUpdateOverlay(vd, NULL);
271         }
272     }
273     if (sys->restore_overlay)
274         DirectXUpdateOverlay(vd, NULL);
275
276     /* */
277     DirectXUnlock(picture);
278
279     if (sys->use_overlay) {
280         /* Flip the overlay buffers if we are using back buffers */
281         if (picture->p_sys->surface != picture->p_sys->front_surface) {
282             HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
283                                                   NULL, DDFLIP_WAIT);
284             if (hr != DD_OK)
285                 msg_Warn(vd, "could not flip overlay (error %li)", hr);
286         }
287     } else {
288         /* Blit video surface to display with the NOTEARING option */
289         DDBLTFX  ddbltfx;
290         ZeroMemory(&ddbltfx, sizeof(ddbltfx));
291         ddbltfx.dwSize = sizeof(ddbltfx);
292         ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
293
294         HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
295                                              &sys->rect_dest_clipped,
296                                              picture->p_sys->surface,
297                                              &sys->rect_src_clipped,
298                                              DDBLT_ASYNC, &ddbltfx);
299         if (hr != DD_OK)
300             msg_Warn(vd, "could not blit surface (error %li)", hr);
301     }
302     DirectXLock(picture);
303
304     if (sys->is_first_display) {
305         IDirectDraw_WaitForVerticalBlank(sys->ddobject,
306                                          DDWAITVB_BLOCKBEGIN, NULL);
307         if (sys->use_overlay) {
308             HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
309             /* set the colorkey as the backgound brush for the video window */
310             SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
311         }
312     }
313     CommonDisplay(vd);
314
315     picture_Release(picture);
316     VLC_UNUSED(subpicture);
317 }
318 static int Control(vout_display_t *vd, int query, va_list args)
319 {
320     vout_display_sys_t *sys = vd->sys;
321
322     switch (query) {
323     case VOUT_DISPLAY_RESET_PICTURES:
324         DirectXClose(vd);
325         /* Make sure the wallpaper is restored */
326         if (sys->use_wallpaper) {
327             vlc_mutex_lock(&sys->lock);
328             if (!sys->ch_wallpaper) {
329                 sys->ch_wallpaper = true;
330                 sys->wallpaper_requested = true;
331             }
332             vlc_mutex_unlock(&sys->lock);
333
334             WallpaperChange(vd, false);
335         }
336         return DirectXOpen(vd, &vd->fmt);
337     default:
338         return CommonControl(vd, query, args);
339     }
340 }
341 static void Manage(vout_display_t *vd)
342 {
343     vout_display_sys_t *sys = vd->sys;
344
345     CommonManage(vd);
346
347     if (sys->changes & DX_POSITION_CHANGE) {
348         /* Update overlay */
349         if (sys->use_overlay)
350             DirectXUpdateOverlay(vd, NULL);
351
352         /* Check if we are still on the same monitor */
353         HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
354         if (sys->hmonitor != hmon) {
355             vout_display_SendEventPicturesInvalid(vd);
356         }
357         /* */
358         sys->changes &= ~DX_POSITION_CHANGE;
359     }
360
361     /* Wallpaper mode change */
362     vlc_mutex_lock(&sys->lock);
363     const bool ch_wallpaper = sys->ch_wallpaper;
364     const bool wallpaper_requested = sys->wallpaper_requested;
365     sys->ch_wallpaper = false;
366     vlc_mutex_unlock(&sys->lock);
367
368     if (ch_wallpaper)
369         WallpaperChange(vd, wallpaper_requested);
370
371     /* */
372     if (sys->restore_overlay)
373         DirectXUpdateOverlay(vd, NULL);
374 }
375
376 /* */
377 static int  DirectXOpenDDraw(vout_display_t *);
378 static void DirectXCloseDDraw(vout_display_t *);
379
380 static int  DirectXOpenDisplay(vout_display_t *vd);
381 static void DirectXCloseDisplay(vout_display_t *vd);
382
383 static int  DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
384 static void DirectXDestroyPool(vout_display_t *);
385
386 static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
387 {
388     vout_display_sys_t *sys = vd->sys;
389
390     assert(!sys->ddobject);
391     assert(!sys->display);
392     assert(!sys->clipper);
393
394     /* Initialise DirectDraw */
395     if (DirectXOpenDDraw(vd)) {
396         msg_Err(vd, "cannot initialize DirectX DirectDraw");
397         return VLC_EGENERIC;
398     }
399
400     /* Create the directx display */
401     if (DirectXOpenDisplay(vd)) {
402         msg_Err(vd, "cannot initialize DirectX DirectDraw");
403         return VLC_EGENERIC;
404     }
405     UpdateRects(vd, NULL, NULL, true);
406
407     /* Create the picture pool */
408     if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
409         msg_Err(vd, "cannot create any DirectX surface");
410         return VLC_EGENERIC;
411     }
412
413     /* */
414     if (sys->use_overlay)
415         DirectXUpdateOverlay(vd, NULL);
416     EventThreadUseOverlay(sys->event, sys->use_overlay);
417
418     /* Change the window title bar text */
419     const char *fallback;
420     if (sys->use_overlay)
421         fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
422     else if (vlc_fourcc_IsYUV(fmt->i_chroma))
423         fallback = VOUT_TITLE " (hardware YUV DirectX output)";
424     else
425         fallback = VOUT_TITLE " (software RGB DirectX output)";
426     EventThreadUpdateTitle(sys->event, fallback);
427
428     return VLC_SUCCESS;
429 }
430 static void DirectXClose(vout_display_t *vd)
431 {
432     DirectXDestroyPool(vd);
433     DirectXCloseDisplay(vd);
434     DirectXCloseDDraw(vd);
435 }
436
437 /* */
438 static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPSTR desc,
439                                             LPSTR drivername, VOID *context,
440                                             HMONITOR hmon)
441 {
442     vout_display_t *vd = context;
443     vout_display_sys_t *sys = vd->sys;
444
445     /* This callback function is called by DirectDraw once for each
446      * available DirectDraw device.
447      *
448      * Returning TRUE keeps enumerating.
449      */
450     if (!hmon)
451         return TRUE;
452
453     char *psz_drivername = drivername;
454     char *psz_desc = desc;
455
456     msg_Dbg(vd, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername);
457
458     char *device = var_GetString(vd, "directx-device");
459
460     /* Check for forced device */
461     if (device && *device && !strcmp(psz_drivername, device)) {
462         MONITORINFO monitor_info;
463         monitor_info.cbSize = sizeof(MONITORINFO);
464
465         if (GetMonitorInfoA(hmon, &monitor_info)) {
466             RECT rect;
467
468             /* Move window to the right screen */
469             GetWindowRect(sys->hwnd, &rect);
470             if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
471                 rect.left = monitor_info.rcWork.left;
472                 rect.top = monitor_info.rcWork.top;
473                 msg_Dbg(vd, "DirectXEnumCallback: setting window "
474                             "position to %ld,%ld", rect.left, rect.top);
475                 SetWindowPos(sys->hwnd, NULL,
476                              rect.left, rect.top, 0, 0,
477                              SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
478             }
479         }
480         sys->hmonitor = hmon;
481     }
482     free(device);
483
484     if (hmon == sys->hmonitor) {
485         msg_Dbg(vd, "selecting %s, %s", psz_desc, psz_drivername);
486
487         free(sys->display_driver);
488         sys->display_driver = malloc(sizeof(*guid));
489         if (sys->display_driver)
490             *sys->display_driver = *guid;
491     }
492
493     return TRUE;
494 }
495 /**
496  * Probe the capabilities of the hardware
497  *
498  * It is nice to know which features are supported by the hardware so we can
499  * find ways to optimize our rendering.
500  */
501 static void DirectXGetDDrawCaps(vout_display_t *vd)
502 {
503     vout_display_sys_t *sys = vd->sys;
504
505     /* This is just an indication of whether or not we'll support overlay,
506      * but with this test we don't know if we support YUV overlay */
507     DDCAPS ddcaps;
508     ZeroMemory(&ddcaps, sizeof(ddcaps));
509     ddcaps.dwSize = sizeof(ddcaps);
510     HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
511     if (hr != DD_OK) {
512         msg_Warn(vd, "cannot get caps");
513         return;
514     }
515
516     /* Determine if the hardware supports overlay surfaces */
517     const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
518     /* Determine if the hardware supports overlay surfaces */
519     const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
520     /* Determine if the hardware supports overlay deinterlacing */
521     const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
522     /* Determine if the hardware supports colorkeying */
523     const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
524     /* Determine if the hardware supports scaling of the overlay surface */
525     const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
526     /* Determine if the hardware supports color conversion during a blit */
527     sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
528     /* Determine overlay source boundary alignment */
529     const bool align_boundary_src  = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
530     /* Determine overlay destination boundary alignment */
531     const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
532     /* Determine overlay destination size alignment */
533     const bool align_size_src  = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
534     /* Determine overlay destination size alignment */
535     const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;
536
537     msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
538                 "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
539                 "bltfourcc=%i",
540                 has_overlay, has_overlay_fourcc, can_deinterlace,
541                 has_color_key, can_stretch, sys->can_blit_fourcc);
542
543     if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
544         if (align_boundary_src)
545             vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
546         if (align_boundary_dest)
547             vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
548         if (align_size_src)
549             vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
550         if (align_size_dest)
551             vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;
552
553         msg_Dbg(vd,
554                 "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
555                 "align_size_src=%i,%i align_size_dest=%i,%i",
556                 align_boundary_src,  vd->sys->i_align_src_boundary,
557                 align_boundary_dest, vd->sys->i_align_dest_boundary,
558                 align_size_src,  vd->sys->i_align_src_size,
559                 align_size_dest, vd->sys->i_align_dest_size);
560     }
561 }
562
563
564
565 /* */
566 static int DirectXOpenDDraw(vout_display_t *vd)
567 {
568     vout_display_sys_t *sys = vd->sys;
569     HRESULT hr;
570
571     /* */
572     HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
573     OurDirectDrawCreate =
574         (void *)GetProcAddress(sys->hddraw_dll, "DirectDrawCreate");
575     if (!OurDirectDrawCreate) {
576         msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
577         return VLC_EGENERIC;
578     }
579
580     /* */
581     HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
582     OurDirectDrawEnumerateEx =
583       (void *)GetProcAddress(sys->hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
584
585     if (OurDirectDrawEnumerateEx) {
586         char *device = var_GetString(vd, "directx-device");
587         if (device) {
588             msg_Dbg(vd, "directx-device: %s", device);
589             free(device);
590         }
591
592         sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
593
594         /* Enumerate displays */
595         OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
596                                  vd, DDENUM_ATTACHEDSECONDARYDEVICES);
597     }
598
599     /* Initialize DirectDraw now */
600     LPDIRECTDRAW ddobject;
601     hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
602     if (hr != DD_OK) {
603         msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
604         return VLC_EGENERIC;
605     }
606
607     /* Get the IDirectDraw2 interface */
608     void *ptr;
609     hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
610                                     &ptr);
611     /* Release the unused interface */
612     IDirectDraw_Release(ddobject);
613
614     if (hr != DD_OK) {
615         msg_Err(vd, "cannot get IDirectDraw2 interface");
616         sys->ddobject = NULL;
617         return VLC_EGENERIC;
618     }
619     sys->ddobject = ptr;
620
621     /* Set DirectDraw Cooperative level, ie what control we want over Windows
622      * display */
623     hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
624     if (hr != DD_OK) {
625         msg_Err(vd, "cannot set direct draw cooperative level");
626         return VLC_EGENERIC;
627     }
628
629     /* Get the size of the current display device */
630     if (sys->hmonitor) {
631         MONITORINFO monitor_info;
632         monitor_info.cbSize = sizeof(MONITORINFO);
633         GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
634         sys->rect_display = monitor_info.rcMonitor;
635     } else {
636         sys->rect_display.left   = 0;
637         sys->rect_display.top    = 0;
638         sys->rect_display.right  = GetSystemMetrics(SM_CXSCREEN);
639         sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
640     }
641
642     msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
643             sys->rect_display.left,
644             sys->rect_display.top,
645             sys->rect_display.right,
646             sys->rect_display.bottom);
647
648     /* Probe the capabilities of the hardware */
649     DirectXGetDDrawCaps(vd);
650
651     return VLC_SUCCESS;
652 }
653
654 static void DirectXCloseDDraw(vout_display_t *vd)
655 {
656     vout_display_sys_t *sys = vd->sys;
657     if (sys->ddobject)
658         IDirectDraw2_Release(sys->ddobject);
659
660     sys->ddobject = NULL;
661
662     free(sys->display_driver);
663     sys->display_driver = NULL;
664
665     sys->hmonitor = NULL;
666 }
667
668 /**
669  * Create a clipper that will be used when blitting the RGB surface to the main display.
670  *
671  * This clipper prevents us to modify by mistake anything on the screen
672  * which doesn't belong to our window. For example when a part of our video
673  * window is hidden by another window.
674  */
675 static void DirectXCreateClipper(vout_display_t *vd)
676 {
677     vout_display_sys_t *sys = vd->sys;
678     HRESULT hr;
679
680     /* Create the clipper */
681     hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
682     if (hr != DD_OK) {
683         msg_Warn(vd, "cannot create clipper (error %li)", hr);
684         goto error;
685     }
686
687     /* Associate the clipper to the window */
688     hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
689     if (hr != DD_OK) {
690         msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
691         goto error;
692     }
693
694     /* associate the clipper with the surface */
695     hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
696     if (hr != DD_OK)
697     {
698         msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
699         goto error;
700     }
701
702     return;
703
704 error:
705     if (sys->clipper)
706         IDirectDrawClipper_Release(sys->clipper);
707     sys->clipper = NULL;
708 }
709
710 /**
711  * It finds out the 32bits RGB pixel value of the colorkey.
712  */
713 static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
714 {
715     vout_display_sys_t *sys = vd->sys;
716     HRESULT hr;
717
718     /* */
719     DDSURFACEDESC ddsd;
720     ddsd.dwSize = sizeof(ddsd);
721     hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
722     if (hr != DD_OK)
723         return 0;
724
725     uint32_t backup = *(uint32_t *)ddsd.lpSurface;
726
727     switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
728     case 4:
729         *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
730         break;
731     case 8:
732         *(uint8_t *)ddsd.lpSurface = *color;
733         break;
734     case 15:
735     case 16:
736         *(uint16_t *)ddsd.lpSurface = *color;
737         break;
738     case 24:
739         /* Seems to be problematic so we'll just put black as the colorkey */
740         *color = 0;
741     default:
742         *(uint32_t *)ddsd.lpSurface = *color;
743         break;
744     }
745     IDirectDrawSurface2_Unlock(sys->display, NULL);
746
747     /* */
748     HDC hdc;
749     COLORREF rgb;
750     if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
751         rgb = GetPixel(hdc, 0, 0);
752         IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
753     } else {
754         rgb = 0;
755     }
756
757     /* Restore the pixel value */
758     ddsd.dwSize = sizeof(ddsd);
759     if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
760         *(uint32_t *)ddsd.lpSurface = backup;
761         IDirectDrawSurface2_Unlock(sys->display, NULL);
762     }
763
764     return rgb;
765 }
766
767 /**
768  * Create and initialize display according to preferences specified in the vout
769  * thread fields.
770  */
771 static int DirectXOpenDisplay(vout_display_t *vd)
772 {
773     vout_display_sys_t *sys = vd->sys;
774     HRESULT hr;
775
776     /* Now get the primary surface. This surface is what you actually see
777      * on your screen */
778     DDSURFACEDESC ddsd;
779     ZeroMemory(&ddsd, sizeof(ddsd));
780     ddsd.dwSize = sizeof(ddsd);
781     ddsd.dwFlags = DDSD_CAPS;
782     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
783
784     LPDIRECTDRAWSURFACE display;
785     hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
786     if (hr != DD_OK) {
787         msg_Err(vd, "cannot get primary surface (error %li)", hr);
788         return VLC_EGENERIC;
789     }
790
791     void *ptr;
792     hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
793                                            &ptr);
794     /* Release the old interface */
795     IDirectDrawSurface_Release(display);
796
797     if (hr != DD_OK) {
798         msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
799         sys->display = NULL;
800         return VLC_EGENERIC;
801     }
802     sys->display = ptr;
803
804     /* The clipper will be used only in non-overlay mode */
805     DirectXCreateClipper(vd);
806
807     /* Make sure the colorkey will be painted */
808     sys->i_colorkey = 1;
809     sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);
810
811     return VLC_SUCCESS;
812 }
813 static void DirectXCloseDisplay(vout_display_t *vd)
814 {
815     vout_display_sys_t *sys = vd->sys;
816
817     if (sys->clipper != NULL)
818         IDirectDrawClipper_Release(sys->clipper);
819
820     if (sys->display != NULL)
821         IDirectDrawSurface2_Release(sys->display);
822
823     sys->clipper = NULL;
824     sys->display = NULL;
825 }
826
827 /**
828  * Create an YUV overlay or RGB surface for the video.
829  *
830  * The best method of display is with an YUV overlay because the YUV->RGB
831  * conversion is done in hardware.
832  * You can also create a plain RGB surface.
833  * (Maybe we could also try an RGB overlay surface, which could have hardware
834  * scaling and which would also be faster in window mode because you don't
835  * need to do any blitting to the main display...)
836  */
837 static int DirectXCreateSurface(vout_display_t *vd,
838                                 LPDIRECTDRAWSURFACE2 *surface,
839                                 const video_format_t *fmt,
840                                 DWORD fourcc,
841                                 bool use_overlay,
842                                 bool use_sysmem,
843                                 int backbuffer_count)
844 {
845     vout_display_sys_t *sys = vd->sys;
846
847     DDSURFACEDESC ddsd;
848
849     ZeroMemory(&ddsd, sizeof(ddsd));
850     ddsd.dwSize   = sizeof(ddsd);
851     ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
852     ddsd.dwFlags  = DDSD_HEIGHT | DDSD_WIDTH;
853     ddsd.dwWidth  = fmt->i_visible_width;
854     ddsd.dwHeight = fmt->i_visible_height;
855     if (fourcc) {
856         ddsd.dwFlags |= DDSD_PIXELFORMAT;
857         ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
858         ddsd.ddpfPixelFormat.dwFourCC = fourcc;
859     }
860     if (use_overlay) {
861         ddsd.dwFlags |= DDSD_CAPS;
862         ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
863         if (backbuffer_count > 0)
864             ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
865
866         if (backbuffer_count > 0) {
867             ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
868             ddsd.dwBackBufferCount = backbuffer_count;
869         }
870     } else {
871         ddsd.dwFlags |= DDSD_CAPS;
872         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
873         if (use_sysmem)
874             ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
875         else
876             ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
877     }
878
879     /* Create the video surface */
880     LPDIRECTDRAWSURFACE surface_v1;
881     if (IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL) != DD_OK)
882         return VLC_EGENERIC;
883
884     /* Now that the surface is created, try to get a newer DirectX interface */
885     HRESULT hr = IDirectDrawSurface_QueryInterface(surface_v1,
886                                                    &IID_IDirectDrawSurface2,
887                                                    (LPVOID *)surface);
888     IDirectDrawSurface_Release(surface_v1);
889     if (hr != DD_OK) {
890         msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
891         return VLC_EGENERIC;
892     }
893
894     if (use_overlay) {
895         /* Check the overlay is useable as some graphics cards allow creating
896          * several overlays but only one can be used at one time. */
897         if (DirectXUpdateOverlay(vd, *surface)) {
898             IDirectDrawSurface2_Release(*surface);
899             msg_Err(vd, "overlay unuseable (might already be in use)");
900             return VLC_EGENERIC;
901         }
902     }
903
904     return VLC_SUCCESS;
905 }
906
907 static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
908 {
909     IDirectDrawSurface2_Release(surface);
910 }
911 /**
912  * This function locks a surface and get the surface descriptor.
913  */
914 static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
915                               LPDIRECTDRAWSURFACE2 surface,
916                               DDSURFACEDESC *ddsd)
917 {
918     HRESULT hr;
919
920     DDSURFACEDESC ddsd_dummy;
921     if (!ddsd)
922         ddsd = &ddsd_dummy;
923
924     ZeroMemory(ddsd, sizeof(*ddsd));
925     ddsd->dwSize = sizeof(*ddsd);
926     hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
927     if (hr != DD_OK) {
928         if (hr == DDERR_INVALIDPARAMS) {
929             /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
930              * in an invalid params error */
931             hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
932         }
933         if (hr == DDERR_SURFACELOST) {
934             /* Your surface can be lost so be sure
935              * to check this and restore it if needed */
936
937             /* When using overlays with back-buffers, we need to restore
938              * the front buffer so the back-buffers get restored as well. */
939             if (front_surface != surface)
940                 IDirectDrawSurface2_Restore(front_surface);
941             else
942                 IDirectDrawSurface2_Restore(surface);
943
944             hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
945         }
946         if (hr != DD_OK)
947             return VLC_EGENERIC;
948     }
949     return VLC_SUCCESS;
950 }
951 static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
952                                  LPDIRECTDRAWSURFACE2 surface)
953 {
954     VLC_UNUSED(front_surface);
955     IDirectDrawSurface2_Unlock(surface, NULL);
956 }
957 static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
958                                       LPDIRECTDRAWSURFACE2 surface)
959 {
960     if (DirectXLockSurface(front_surface, surface, NULL))
961         return VLC_EGENERIC;
962
963     DirectXUnlockSurface(front_surface, surface);
964     return VLC_SUCCESS;
965 }
966
967
968
969 typedef struct {
970     vlc_fourcc_t codec;
971     DWORD        fourcc;
972 } dx_format_t;
973
974 static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
975 {
976     static const dx_format_t dx_formats[] = {
977         { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
978         { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
979         { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
980         { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
981         { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
982         { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
983         { 0, 0 }
984     };
985
986     for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
987         if (dx_formats[i].codec == codec)
988             return dx_formats[i].fourcc;
989     }
990     return 0;
991 }
992
993 static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
994                                                   const video_format_t *fmt,
995                                                   DWORD fourcc)
996 {
997     vout_display_sys_t *sys = vd->sys;
998
999     bool allow_3buf    = var_InheritBool(vd, "directx-3buffering");
1000
1001     /* The overlay surface that we create won't be used to decode directly
1002      * into it because accessing video memory directly is way to slow (remember
1003      * that pictures are decoded macroblock per macroblock). Instead the video
1004      * will be decoded in picture buffers in system memory which will then be
1005      * memcpy() to the overlay surface. */
1006     LPDIRECTDRAWSURFACE2 front_surface;
1007     int ret = VLC_EGENERIC;
1008     if (allow_3buf) {
1009         /* Triple buffering rocks! it doesn't have any processing overhead
1010          * (you don't have to wait for the vsync) and provides for a very nice
1011          * video quality (no tearing). */
1012         ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
1013     }
1014     if (ret)
1015         ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
1016     if (ret)
1017         return VLC_EGENERIC;
1018     msg_Dbg(vd, "YUV overlay surface created successfully");
1019
1020     /* Get the back buffer */
1021     LPDIRECTDRAWSURFACE2 surface;
1022     DDSCAPS dds_caps;
1023     ZeroMemory(&dds_caps, sizeof(dds_caps));
1024     dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1025     if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
1026         msg_Warn(vd, "Failed to get surface back buffer");
1027         /* front buffer is the same as back buffer */
1028         surface = front_surface;
1029     }
1030
1031     if (DirectXCheckLockingSurface(front_surface, surface)) {
1032         DirectXDestroySurface(front_surface);
1033         return VLC_EGENERIC;
1034     }
1035
1036     /* */
1037     picture_sys_t *picsys = sys->picsys;
1038     picsys->front_surface = front_surface;
1039     picsys->surface       = surface;
1040     picsys->fallback      = NULL;
1041     return VLC_SUCCESS;
1042 }
1043 static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
1044                                            const video_format_t *fmt,
1045                                            DWORD fourcc)
1046 {
1047     vout_display_sys_t *sys = vd->sys;
1048
1049     bool allow_sysmem  = var_InheritBool(vd, "directx-use-sysmem");
1050
1051     /* As we can't have an overlay, we'll try to create a plain offscreen
1052      * surface. This surface will reside in video memory because there's a
1053      * better chance then that we'll be able to use some kind of hardware
1054      * acceleration like rescaling, blitting or YUV->RGB conversions.
1055      * We then only need to blit this surface onto the main display when we
1056      * want to display it */
1057
1058     /* Check if the chroma is supported first. This is required
1059      * because a few buggy drivers don't mind creating the surface
1060      * even if they don't know about the chroma. */
1061     DWORD count;
1062     if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
1063         return VLC_EGENERIC;
1064
1065     DWORD *list = calloc(count, sizeof(*list));
1066     if (!list)
1067         return VLC_ENOMEM;
1068     if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
1069         free(list);
1070         return VLC_EGENERIC;
1071     }
1072     unsigned index;
1073     for (index = 0; index < count; index++) {
1074         if (list[index] == fourcc)
1075             break;
1076     }
1077     free(list);
1078     if (index >= count)
1079         return VLC_EGENERIC;
1080
1081     /* */
1082     LPDIRECTDRAWSURFACE2 surface;
1083     if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
1084         return VLC_EGENERIC;
1085     msg_Dbg(vd, "YUV plain surface created successfully");
1086
1087     if (DirectXCheckLockingSurface(surface, surface)) {
1088         DirectXDestroySurface(surface);
1089         return VLC_EGENERIC;
1090     }
1091
1092     /* */
1093     picture_sys_t *picsys = sys->picsys;
1094     picsys->front_surface = surface;
1095     picsys->surface       = surface;
1096     picsys->fallback      = NULL;
1097     return VLC_SUCCESS;
1098 }
1099 static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
1100                                            video_format_t *fmt)
1101 {
1102     vout_display_sys_t *sys = vd->sys;
1103     bool allow_sysmem  = var_InheritBool(vd, "directx-use-sysmem");
1104
1105     /* Our last choice is to use a plain RGB surface */
1106     DDPIXELFORMAT ddpfPixelFormat;
1107     ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
1108     ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
1109
1110     IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
1111     if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
1112         return VLC_EGENERIC;
1113
1114     switch (ddpfPixelFormat.dwRGBBitCount) {
1115     case 8:
1116         fmt->i_chroma = VLC_CODEC_RGB8;
1117         break;
1118     case 15:
1119         fmt->i_chroma = VLC_CODEC_RGB15;
1120         break;
1121     case 16:
1122         fmt->i_chroma = VLC_CODEC_RGB16;
1123         break;
1124     case 24:
1125         fmt->i_chroma = VLC_CODEC_RGB24;
1126         break;
1127     case 32:
1128         fmt->i_chroma = VLC_CODEC_RGB32;
1129         break;
1130     default:
1131         msg_Err(vd, "unknown screen depth");
1132         return VLC_EGENERIC;
1133     }
1134     fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
1135     fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
1136     fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
1137
1138     /* */
1139     LPDIRECTDRAWSURFACE2 surface;
1140     int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
1141     if (ret && !allow_sysmem)
1142         ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
1143     if (ret)
1144         return VLC_EGENERIC;
1145     msg_Dbg(vd, "RGB plain surface created successfully");
1146
1147     if (DirectXCheckLockingSurface(surface, surface)) {
1148         DirectXDestroySurface(surface);
1149         return VLC_EGENERIC;
1150     }
1151
1152     /* */
1153     picture_sys_t *picsys = sys->picsys;
1154     picsys->front_surface = surface;
1155     picsys->surface       = surface;
1156     picsys->fallback      = NULL;
1157     return VLC_SUCCESS;
1158 }
1159
1160 static int DirectXCreatePictureResource(vout_display_t *vd,
1161                                         bool *use_overlay,
1162                                         video_format_t *fmt)
1163 {
1164     vout_display_sys_t *sys = vd->sys;
1165
1166     /* */
1167     picture_sys_t *picsys = calloc(1, sizeof(*picsys));
1168     if (unlikely(picsys == NULL))
1169         return VLC_ENOMEM;
1170     sys->picsys = picsys;
1171
1172     /* */
1173     bool allow_hw_yuv  = sys->can_blit_fourcc &&
1174                          vlc_fourcc_IsYUV(fmt->i_chroma) &&
1175                          var_InheritBool(vd, "directx-hw-yuv");
1176     bool allow_overlay = var_InheritBool(vd, "overlay");
1177
1178     /* Try to use an yuv surface */
1179     if (allow_hw_yuv) {
1180         const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1181         /*  Try with overlay first */
1182         for (unsigned pass = allow_overlay ? 0 : 1; pass < 2; pass++) {
1183             for (unsigned i = 0; list[i] != 0; i++) {
1184                 const DWORD fourcc = DirectXGetFourcc(list[i]);
1185                 if (!fourcc)
1186                     continue;
1187
1188                 if (pass == 0) {
1189                     if (DirectXCreatePictureResourceYuvOverlay(vd, fmt, fourcc))
1190                         continue;
1191                 } else {
1192                     if (DirectXCreatePictureResourceYuv(vd, fmt, fourcc))
1193                         continue;
1194                 }
1195                 /* */
1196                 *use_overlay = pass == 0;
1197                 fmt->i_chroma = list[i];
1198                 return VLC_SUCCESS;
1199             }
1200         }
1201     }
1202
1203     /* Try plain RGB */
1204     return DirectXCreatePictureResourceRgb(vd, fmt);
1205 }
1206 static void DirectXDestroyPictureResource(vout_display_t *vd)
1207 {
1208     vout_display_sys_t *sys = vd->sys;
1209
1210     if (sys->picsys->front_surface != sys->picsys->surface)
1211         DirectXDestroySurface(sys->picsys->surface);
1212     DirectXDestroySurface(sys->picsys->front_surface);
1213     if (sys->picsys->fallback)
1214         picture_Release(sys->picsys->fallback);
1215 }
1216
1217 static int DirectXLock(picture_t *picture)
1218 {
1219     DDSURFACEDESC ddsd;
1220     if (DirectXLockSurface(picture->p_sys->front_surface,
1221                            picture->p_sys->surface, &ddsd))
1222         return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
1223
1224     CommonUpdatePicture(picture, NULL, ddsd.lpSurface, ddsd.lPitch);
1225     return VLC_SUCCESS;
1226 }
1227 static void DirectXUnlock(picture_t *picture)
1228 {
1229     DirectXUnlockSurface(picture->p_sys->front_surface,
1230                          picture->p_sys->surface);
1231 }
1232
1233 static int DirectXCreatePool(vout_display_t *vd,
1234                              bool *use_overlay, video_format_t *fmt)
1235 {
1236     vout_display_sys_t *sys = vd->sys;
1237
1238     /* */
1239     *fmt = vd->source;
1240
1241     if (DirectXCreatePictureResource(vd, use_overlay, fmt))
1242         return VLC_EGENERIC;
1243
1244     /* Create the associated picture */
1245     picture_resource_t resource = { .p_sys = sys->picsys };
1246     picture_t *picture = picture_NewFromResource(fmt, &resource);
1247     if (!picture) {
1248         DirectXDestroyPictureResource(vd);
1249         free(sys->picsys);
1250         return VLC_ENOMEM;
1251     }
1252
1253     /* Wrap it into a picture pool */
1254     picture_pool_configuration_t cfg;
1255     memset(&cfg, 0, sizeof(cfg));
1256     cfg.picture_count = 1;
1257     cfg.picture       = &picture;
1258     cfg.lock          = DirectXLock;
1259     cfg.unlock        = DirectXUnlock;
1260
1261     sys->pool = picture_pool_NewExtended(&cfg);
1262     if (!sys->pool) {
1263         picture_Release(picture);
1264         DirectXDestroyPictureResource(vd);
1265         return VLC_ENOMEM;
1266     }
1267     return VLC_SUCCESS;
1268 }
1269 static void DirectXDestroyPool(vout_display_t *vd)
1270 {
1271     vout_display_sys_t *sys = vd->sys;
1272
1273     if (sys->pool) {
1274         DirectXDestroyPictureResource(vd);
1275         picture_pool_Delete(sys->pool);
1276     }
1277     sys->pool = NULL;
1278 }
1279
1280 /**
1281  * Move or resize overlay surface on video display.
1282  *
1283  * This function is used to move or resize an overlay surface on the screen.
1284  * Ususally the overlay is moved by the user and thus, by a move or resize
1285  * event.
1286  */
1287 static int DirectXUpdateOverlay(vout_display_t *vd, LPDIRECTDRAWSURFACE2 surface)
1288 {
1289     vout_display_sys_t *sys = vd->sys;
1290
1291     RECT src = sys->rect_src_clipped;
1292     RECT dst = sys->rect_dest_clipped;
1293
1294     if (sys->use_wallpaper) {
1295         src.left   = vd->source.i_x_offset;
1296         src.top    = vd->source.i_y_offset;
1297         src.right  = vd->source.i_x_offset + vd->source.i_visible_width;
1298         src.bottom = vd->source.i_y_offset + vd->source.i_visible_height;
1299         AlignRect(&src, sys->i_align_src_boundary, sys->i_align_src_size);
1300
1301         vout_display_cfg_t cfg = *vd->cfg;
1302         cfg.display.width  = sys->rect_display.right;
1303         cfg.display.height = sys->rect_display.bottom;
1304
1305         vout_display_place_t place;
1306         vout_display_PlacePicture(&place, &vd->source, &cfg, true);
1307
1308         dst.left   = sys->rect_display.left + place.x;
1309         dst.top    = sys->rect_display.top  + place.y;
1310         dst.right  = dst.left + place.width;
1311         dst.bottom = dst.top  + place.height;
1312         AlignRect(&dst, sys->i_align_dest_boundary, sys->i_align_dest_size);
1313     }
1314
1315     if (!surface) {
1316         if (!sys->pool)
1317             return VLC_EGENERIC;
1318         surface = sys->picsys->front_surface;
1319     }
1320
1321     /* The new window dimensions should already have been computed by the
1322      * caller of this function */
1323
1324     /* Position and show the overlay */
1325     DDOVERLAYFX ddofx;
1326     ZeroMemory(&ddofx, sizeof(ddofx));
1327     ddofx.dwSize = sizeof(ddofx);
1328     ddofx.dckDestColorkey.dwColorSpaceLowValue = sys->i_colorkey;
1329     ddofx.dckDestColorkey.dwColorSpaceHighValue = sys->i_colorkey;
1330
1331     HRESULT hr = IDirectDrawSurface2_UpdateOverlay(surface,
1332                                                    &src, sys->display, &dst,
1333                                                    DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx);
1334     sys->restore_overlay = hr != DD_OK;
1335
1336     if (hr != DD_OK) {
1337         msg_Warn(vd, "DirectDrawUpdateOverlay cannot move/resize overlay");
1338         return VLC_EGENERIC;
1339     }
1340     return VLC_SUCCESS;
1341 }
1342
1343 /* */
1344 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper)
1345 {
1346     vout_display_sys_t *sys = vd->sys;
1347
1348     if (!sys->use_wallpaper == !use_wallpaper)
1349         return;
1350
1351     HWND hwnd = FindWindow(_T("Progman"), NULL);
1352     if (hwnd)
1353         hwnd = FindWindowEx(hwnd, NULL, _T("SHELLDLL_DefView"), NULL);
1354     if (hwnd)
1355         hwnd = FindWindowEx(hwnd, NULL, _T("SysListView32"), NULL);
1356     if (!hwnd) {
1357         msg_Warn(vd, "couldn't find \"SysListView32\" window, "
1358                      "wallpaper mode not supported");
1359         return;
1360     }
1361
1362     msg_Dbg(vd, "wallpaper mode %s", use_wallpaper ? "enabled" : "disabled");
1363     sys->use_wallpaper = use_wallpaper;
1364
1365     if (sys->use_wallpaper) {
1366         sys->color_bkg    = ListView_GetBkColor(hwnd);
1367         sys->color_bkgtxt = ListView_GetTextBkColor(hwnd);
1368
1369         ListView_SetBkColor(hwnd,     sys->i_rgb_colorkey);
1370         ListView_SetTextBkColor(hwnd, sys->i_rgb_colorkey);
1371     } else {
1372         ListView_SetBkColor(hwnd,     sys->color_bkg);
1373         ListView_SetTextBkColor(hwnd, sys->color_bkgtxt);
1374     }
1375
1376     /* Update desktop */
1377     InvalidateRect(hwnd, NULL, TRUE);
1378     UpdateWindow(hwnd);
1379
1380     if (sys->use_overlay)
1381       DirectXUpdateOverlay(vd, NULL);
1382 }
1383
1384 /* */
1385 static int WallpaperCallback(vlc_object_t *object, char const *cmd,
1386                              vlc_value_t oldval, vlc_value_t newval, void *data)
1387 {
1388     vout_display_t *vd = (vout_display_t *)object;
1389     vout_display_sys_t *sys = vd->sys;
1390     VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
1391
1392     vlc_mutex_lock(&sys->lock);
1393     const bool ch_wallpaper = !sys->wallpaper_requested != !newval.b_bool;
1394     sys->ch_wallpaper |= ch_wallpaper;
1395     sys->wallpaper_requested = newval.b_bool;
1396     vlc_mutex_unlock(&sys->lock);
1397     return VLC_SUCCESS;
1398 }
1399
1400 typedef struct
1401 {
1402     char **values;
1403     char **descs;
1404     size_t count;
1405 } enum_context_t;
1406
1407 /*****************************************************************************
1408  * config variable callback
1409  *****************************************************************************/
1410 static BOOL WINAPI DirectXEnumCallback2(GUID *guid, LPSTR desc,
1411                                         LPSTR drivername, VOID *data,
1412                                         HMONITOR hmon)
1413 {
1414     enum_context_t *ctx = data;
1415
1416     VLC_UNUSED(guid); VLC_UNUSED(desc); VLC_UNUSED(hmon);
1417
1418     char *psz_drivername = drivername;
1419     ctx->values = xrealloc(ctx->values, (ctx->count + 1) * sizeof(char *));
1420     ctx->descs = xrealloc(ctx->descs, (ctx->count + 1) * sizeof(char *));
1421
1422     ctx->values[ctx->count] = strdup(psz_drivername);
1423     ctx->descs[ctx->count] = strdup(psz_drivername);
1424     ctx->count++;
1425
1426     return TRUE; /* Keep enumerating */
1427 }
1428
1429 static int FindDevicesCallback(vlc_object_t *object, const char *name,
1430                                char ***values, char ***descs)
1431 {
1432     enum_context_t ctx;
1433
1434     ctx.values = xmalloc(sizeof(char *));
1435     ctx.descs = xmalloc(sizeof(char *));
1436     ctx.values[0] = strdup("");
1437     ctx.descs[0] = strdup(_("Default"));
1438     ctx.count = 1;
1439
1440     /* Load direct draw DLL */
1441     HINSTANCE hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1442     if (hddraw_dll != NULL)
1443     {
1444         /* Enumerate displays */
1445         HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA,
1446                                                    LPVOID, DWORD) =
1447               (void *)GetProcAddress(hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
1448         if (OurDirectDrawEnumerateEx != NULL)
1449             OurDirectDrawEnumerateEx(DirectXEnumCallback2, &ctx,
1450                                      DDENUM_ATTACHEDSECONDARYDEVICES);
1451         FreeLibrary(hddraw_dll);
1452     }
1453
1454     VLC_UNUSED(object);
1455     VLC_UNUSED(name);
1456
1457     *values = ctx.values;
1458     *descs = ctx.descs;
1459     return ctx.count;
1460 }
1461
1462