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