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