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