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