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