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