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