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