core: remove the 360 video viewpoint zoom
[vlc.git] / modules / video_output / win32 / directdraw.c
blobc2f5eeaf78241a746a8775857aec0c1edf7de189
1 /*****************************************************************************
2 * directdraw.c: Windows DirectDraw video output
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble:
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.
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.
36 *****************************************************************************/
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
42 #include <assert.h>
44 #include <vlc_common.h>
45 #include <vlc_plugin.h>
46 #include <vlc_vout_display.h>
47 #include <vlc_charset.h> /* FromT */
49 #include <windows.h>
50 #include <ddraw.h>
51 #include <commctrl.h> /* ListView_(Get|Set)* */
53 #include "common.h"
55 /* Unicode function "DirectDrawEnumerateExW" has been desactivated
56 since in some cases this function fails and the callbacks are not
57 called. If the Unicode mode is restored, one should modify the
58 prototype of the callbacks and call the FromT conversion function.
60 #define DIRECTDRAWENUMERATEEX_NAME "DirectDrawEnumerateExA"
62 /*****************************************************************************
63 * Module descriptor
64 *****************************************************************************/
65 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
66 #define HW_YUV_LONGTEXT N_(\
67 "Try to use hardware acceleration for YUV->RGB conversions. " \
68 "This option doesn't have any effect when using overlays.")
70 #define OVERLAY_TEXT N_("Overlay video output")
71 #define OVERLAY_LONGTEXT N_(\
72 "Overlay is the hardware acceleration capability of your video card " \
73 "(ability to render video directly). VLC will try to use it by default.")
75 #define SYSMEM_TEXT N_("Use video buffers in system memory")
76 #define SYSMEM_LONGTEXT N_(\
77 "Create video buffers in system memory instead of video memory. This " \
78 "isn't recommended as usually using video memory allows benefiting from " \
79 "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
80 "This option doesn't have any effect when using overlays.")
82 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
83 #define TRIPLEBUF_LONGTEXT N_(\
84 "Try to use triple buffering when using YUV overlays. That results in " \
85 "much better video quality (no flickering).")
87 #define DEVICE_TEXT N_("Name of desired display device")
88 #define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
89 "specify the Windows device name of the display that you want the video " \
90 "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
91 "\"\\\\.\\DISPLAY2\".")
93 #define DX_HELP N_("Recommended video output for Windows XP. " \
94 "Incompatible with Vista's Aero interface" )
96 static int Open (vlc_object_t *);
97 static void Close(vlc_object_t *);
99 static int FindDevicesCallback(vlc_object_t *, const char *,
100 char ***, char ***);
101 vlc_module_begin()
102 set_shortname("DirectDraw")
103 set_description(N_("DirectX (DirectDraw) video output"))
104 set_help(DX_HELP)
105 set_category(CAT_VIDEO)
106 set_subcategory(SUBCAT_VIDEO_VOUT)
107 add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
108 true)
109 add_bool("directx-overlay", true, OVERLAY_TEXT, OVERLAY_LONGTEXT, false)
110 add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
111 true)
112 add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
113 TRIPLEBUF_LONGTEXT, true)
114 add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
115 change_string_cb(FindDevicesCallback)
117 set_capability("vout display", 230)
118 add_shortcut("directx", "directdraw")
119 set_callbacks(Open, Close)
120 vlc_module_end()
122 /*****************************************************************************
123 * Local prototypes.
124 *****************************************************************************/
126 struct picture_sys_t {
127 LPDIRECTDRAWSURFACE2 surface;
128 LPDIRECTDRAWSURFACE2 front_surface;
129 picture_t *fallback;
132 /*****************************************************************************
133 * DirectDraw GUIDs.
134 * Defining them here allows us to get rid of the dxguid library during
135 * the linking stage.
136 *****************************************************************************/
137 #include <initguid.h>
138 #undef GUID_EXT
139 #define GUID_EXT
140 DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56);
141 DEFINE_GUID(IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27);
143 static picture_pool_t *Pool (vout_display_t *, unsigned);
144 static void Display(vout_display_t *, picture_t *, subpicture_t *);
145 static int Control(vout_display_t *, int, va_list);
146 static void Manage (vout_display_t *);
148 /* */
149 static int WallpaperCallback(vlc_object_t *, char const *,
150 vlc_value_t, vlc_value_t, void *);
152 static int DirectXOpen(vout_display_t *, video_format_t *fmt);
153 static void DirectXClose(vout_display_t *);
155 static int DirectXLock(picture_t *);
156 static void DirectXUnlock(picture_t *);
158 static int DirectXUpdateOverlay(vout_display_t *, LPDIRECTDRAWSURFACE2 surface);
160 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper);
162 /** This function allocates and initialize the DirectX vout display.
164 static int Open(vlc_object_t *object)
166 vout_display_t *vd = (vout_display_t *)object;
167 vout_display_sys_t *sys;
169 /* Allocate structure */
170 vd->sys = sys = calloc(1, sizeof(*sys));
171 if (!sys)
172 return VLC_ENOMEM;
174 /* Load direct draw DLL */
175 sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
176 if (!sys->hddraw_dll) {
177 msg_Warn(vd, "DirectXInitDDraw failed loading ddraw.dll");
178 free(sys);
179 return VLC_EGENERIC;
182 /* */
183 sys->use_wallpaper = var_CreateGetBool(vd, "video-wallpaper");
184 /* FIXME */
185 sys->use_overlay = false;//var_CreateGetBool(vd, "overlay"); /* FIXME */
186 sys->restore_overlay = false;
187 var_Create(vd, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
189 /* Initialisation */
190 if (CommonInit(vd))
191 goto error;
193 /* */
194 video_format_t fmt = vd->fmt;
196 if (DirectXOpen(vd, &fmt))
197 goto error;
199 /* */
200 vout_display_info_t info = vd->info;
201 info.is_slow = true;
202 info.has_double_click = true;
203 info.has_hide_mouse = false;
204 info.has_pictures_invalid = true;
205 info.has_event_thread = true;
207 /* Interaction TODO support starting with wallpaper mode */
208 vlc_mutex_init(&sys->lock);
209 sys->ch_wallpaper = sys->use_wallpaper;
210 sys->wallpaper_requested = sys->use_wallpaper;
211 sys->use_wallpaper = false;
213 vlc_value_t val;
214 val.psz_string = _("Wallpaper");
215 var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
216 var_AddCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
218 /* Setup vout_display now that everything is fine */
219 video_format_Clean(&vd->fmt);
220 video_format_Copy(&vd->fmt, &fmt);
221 vd->info = info;
223 vd->pool = Pool;
224 vd->prepare = NULL;
225 vd->display = Display;
226 vd->control = Control;
227 vd->manage = Manage;
228 return VLC_SUCCESS;
230 error:
231 DirectXClose(vd);
232 CommonClean(vd);
233 if (sys->hddraw_dll)
234 FreeLibrary(sys->hddraw_dll);
235 free(sys);
236 return VLC_EGENERIC;
239 /** Terminate a vout display created by Open.
241 static void Close(vlc_object_t *object)
243 vout_display_t *vd = (vout_display_t *)object;
244 vout_display_sys_t *sys = vd->sys;
246 var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
247 vlc_mutex_destroy(&sys->lock);
249 /* Make sure the wallpaper is restored */
250 WallpaperChange(vd, false);
252 DirectXClose(vd);
254 CommonClean(vd);
256 if (sys->hddraw_dll)
257 FreeLibrary(sys->hddraw_dll);
258 free(sys);
261 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
263 VLC_UNUSED(count);
264 return vd->sys->pool;
266 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
268 vout_display_sys_t *sys = vd->sys;
270 assert(sys->display);
272 /* Our surface can be lost so be sure to check this
273 * and restore it if need be */
274 if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
275 if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
276 if (sys->use_overlay)
277 DirectXUpdateOverlay(vd, NULL);
280 if (sys->restore_overlay)
281 DirectXUpdateOverlay(vd, NULL);
283 /* */
284 DirectXUnlock(picture);
286 if (sys->use_overlay) {
287 /* Flip the overlay buffers if we are using back buffers */
288 if (picture->p_sys->surface != picture->p_sys->front_surface) {
289 HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
290 NULL, DDFLIP_WAIT);
291 if (hr != DD_OK)
292 msg_Warn(vd, "could not flip overlay (error %li)", hr);
294 } else {
295 /* Blit video surface to display with the NOTEARING option */
296 DDBLTFX ddbltfx;
297 ZeroMemory(&ddbltfx, sizeof(ddbltfx));
298 ddbltfx.dwSize = sizeof(ddbltfx);
299 ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
301 HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
302 &sys->rect_dest_clipped,
303 picture->p_sys->surface,
304 &sys->rect_src_clipped,
305 DDBLT_ASYNC, &ddbltfx);
306 if (hr != DD_OK)
307 msg_Warn(vd, "could not blit surface (error %li)", hr);
309 DirectXLock(picture);
311 if (sys->is_first_display) {
312 IDirectDraw_WaitForVerticalBlank(sys->ddobject,
313 DDWAITVB_BLOCKBEGIN, NULL);
314 if (sys->use_overlay) {
315 HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
316 /* set the colorkey as the backgound brush for the video window */
317 SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
320 CommonDisplay(vd);
322 picture_Release(picture);
323 VLC_UNUSED(subpicture);
325 static int Control(vout_display_t *vd, int query, va_list args)
327 vout_display_sys_t *sys = vd->sys;
329 switch (query) {
330 case VOUT_DISPLAY_RESET_PICTURES:
331 DirectXClose(vd);
332 /* Make sure the wallpaper is restored */
333 if (sys->use_wallpaper) {
334 vlc_mutex_lock(&sys->lock);
335 if (!sys->ch_wallpaper) {
336 sys->ch_wallpaper = true;
337 sys->wallpaper_requested = true;
339 vlc_mutex_unlock(&sys->lock);
341 WallpaperChange(vd, false);
343 return DirectXOpen(vd, &vd->fmt);
344 default:
345 return CommonControl(vd, query, args);
348 static void Manage(vout_display_t *vd)
350 vout_display_sys_t *sys = vd->sys;
352 CommonManage(vd);
354 if (sys->changes & DX_POSITION_CHANGE) {
355 /* Update overlay */
356 if (sys->use_overlay)
357 DirectXUpdateOverlay(vd, NULL);
359 /* Check if we are still on the same monitor */
360 HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
361 if (sys->hmonitor != hmon) {
362 vout_display_SendEventPicturesInvalid(vd);
364 /* */
365 sys->changes &= ~DX_POSITION_CHANGE;
368 /* Wallpaper mode change */
369 vlc_mutex_lock(&sys->lock);
370 const bool ch_wallpaper = sys->ch_wallpaper;
371 const bool wallpaper_requested = sys->wallpaper_requested;
372 sys->ch_wallpaper = false;
373 vlc_mutex_unlock(&sys->lock);
375 if (ch_wallpaper)
376 WallpaperChange(vd, wallpaper_requested);
378 /* */
379 if (sys->restore_overlay)
380 DirectXUpdateOverlay(vd, NULL);
383 /* */
384 static int DirectXOpenDDraw(vout_display_t *);
385 static void DirectXCloseDDraw(vout_display_t *);
387 static int DirectXOpenDisplay(vout_display_t *vd);
388 static void DirectXCloseDisplay(vout_display_t *vd);
390 static int DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
391 static void DirectXDestroyPool(vout_display_t *);
393 static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
395 vout_display_sys_t *sys = vd->sys;
397 assert(!sys->ddobject);
398 assert(!sys->display);
399 assert(!sys->clipper);
401 /* Initialise DirectDraw */
402 if (DirectXOpenDDraw(vd)) {
403 msg_Err(vd, "cannot initialize DirectX DirectDraw");
404 return VLC_EGENERIC;
407 /* Create the directx display */
408 if (DirectXOpenDisplay(vd)) {
409 msg_Err(vd, "cannot initialize DirectX DirectDraw");
410 return VLC_EGENERIC;
412 UpdateRects(vd, NULL, NULL, true);
414 /* Create the picture pool */
415 if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
416 msg_Err(vd, "cannot create any DirectX surface");
417 return VLC_EGENERIC;
420 /* */
421 if (sys->use_overlay)
422 DirectXUpdateOverlay(vd, NULL);
423 EventThreadUseOverlay(sys->event, sys->use_overlay);
425 /* Change the window title bar text */
426 const char *fallback;
427 if (sys->use_overlay)
428 fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
429 else if (vlc_fourcc_IsYUV(fmt->i_chroma))
430 fallback = VOUT_TITLE " (hardware YUV DirectX output)";
431 else
432 fallback = VOUT_TITLE " (software RGB DirectX output)";
433 EventThreadUpdateTitle(sys->event, fallback);
435 return VLC_SUCCESS;
437 static void DirectXClose(vout_display_t *vd)
439 DirectXDestroyPool(vd);
440 DirectXCloseDisplay(vd);
441 DirectXCloseDDraw(vd);
444 /* */
445 static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPSTR desc,
446 LPSTR drivername, VOID *context,
447 HMONITOR hmon)
449 vout_display_t *vd = context;
450 vout_display_sys_t *sys = vd->sys;
452 /* This callback function is called by DirectDraw once for each
453 * available DirectDraw device.
455 * Returning TRUE keeps enumerating.
457 if (!hmon)
458 return TRUE;
460 char *psz_drivername = drivername;
461 char *psz_desc = desc;
463 msg_Dbg(vd, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername);
465 char *device = var_GetString(vd, "directx-device");
467 /* Check for forced device */
468 if (device && *device && !strcmp(psz_drivername, device)) {
469 MONITORINFO monitor_info;
470 monitor_info.cbSize = sizeof(MONITORINFO);
472 if (GetMonitorInfoA(hmon, &monitor_info)) {
473 RECT rect;
475 /* Move window to the right screen */
476 GetWindowRect(sys->hwnd, &rect);
477 if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
478 rect.left = monitor_info.rcWork.left;
479 rect.top = monitor_info.rcWork.top;
480 msg_Dbg(vd, "DirectXEnumCallback: setting window "
481 "position to %ld,%ld", rect.left, rect.top);
482 SetWindowPos(sys->hwnd, NULL,
483 rect.left, rect.top, 0, 0,
484 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
487 sys->hmonitor = hmon;
489 free(device);
491 if (hmon == sys->hmonitor) {
492 msg_Dbg(vd, "selecting %s, %s", psz_desc, psz_drivername);
494 free(sys->display_driver);
495 sys->display_driver = malloc(sizeof(*guid));
496 if (sys->display_driver)
497 *sys->display_driver = *guid;
500 return TRUE;
503 * Probe the capabilities of the hardware
505 * It is nice to know which features are supported by the hardware so we can
506 * find ways to optimize our rendering.
508 static void DirectXGetDDrawCaps(vout_display_t *vd)
510 vout_display_sys_t *sys = vd->sys;
512 /* This is just an indication of whether or not we'll support overlay,
513 * but with this test we don't know if we support YUV overlay */
514 DDCAPS ddcaps;
515 ZeroMemory(&ddcaps, sizeof(ddcaps));
516 ddcaps.dwSize = sizeof(ddcaps);
517 HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
518 if (hr != DD_OK) {
519 msg_Warn(vd, "cannot get caps");
520 return;
523 /* Determine if the hardware supports overlay surfaces */
524 const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
525 /* Determine if the hardware supports overlay surfaces */
526 const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
527 /* Determine if the hardware supports overlay deinterlacing */
528 const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
529 /* Determine if the hardware supports colorkeying */
530 const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
531 /* Determine if the hardware supports scaling of the overlay surface */
532 const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
533 /* Determine if the hardware supports color conversion during a blit */
534 sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
535 /* Determine overlay source boundary alignment */
536 const bool align_boundary_src = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
537 /* Determine overlay destination boundary alignment */
538 const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
539 /* Determine overlay destination size alignment */
540 const bool align_size_src = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
541 /* Determine overlay destination size alignment */
542 const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;
544 msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
545 "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
546 "bltfourcc=%i",
547 has_overlay, has_overlay_fourcc, can_deinterlace,
548 has_color_key, can_stretch, sys->can_blit_fourcc);
550 if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
551 if (align_boundary_src)
552 vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
553 if (align_boundary_dest)
554 vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
555 if (align_size_src)
556 vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
557 if (align_size_dest)
558 vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;
560 msg_Dbg(vd,
561 "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
562 "align_size_src=%i,%i align_size_dest=%i,%i",
563 align_boundary_src, vd->sys->i_align_src_boundary,
564 align_boundary_dest, vd->sys->i_align_dest_boundary,
565 align_size_src, vd->sys->i_align_src_size,
566 align_size_dest, vd->sys->i_align_dest_size);
572 /* */
573 static int DirectXOpenDDraw(vout_display_t *vd)
575 vout_display_sys_t *sys = vd->sys;
576 HRESULT hr;
578 /* */
579 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
580 OurDirectDrawCreate =
581 (void *)GetProcAddress(sys->hddraw_dll, "DirectDrawCreate");
582 if (!OurDirectDrawCreate) {
583 msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
584 return VLC_EGENERIC;
587 /* */
588 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
589 OurDirectDrawEnumerateEx =
590 (void *)GetProcAddress(sys->hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
592 if (OurDirectDrawEnumerateEx) {
593 char *device = var_GetString(vd, "directx-device");
594 if (device) {
595 msg_Dbg(vd, "directx-device: %s", device);
596 free(device);
599 sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
601 /* Enumerate displays */
602 OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
603 vd, DDENUM_ATTACHEDSECONDARYDEVICES);
606 /* Initialize DirectDraw now */
607 LPDIRECTDRAW ddobject;
608 hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
609 if (hr != DD_OK) {
610 msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
611 return VLC_EGENERIC;
614 /* Get the IDirectDraw2 interface */
615 void *ptr;
616 hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
617 &ptr);
618 /* Release the unused interface */
619 IDirectDraw_Release(ddobject);
621 if (hr != DD_OK) {
622 msg_Err(vd, "cannot get IDirectDraw2 interface");
623 sys->ddobject = NULL;
624 return VLC_EGENERIC;
626 sys->ddobject = ptr;
628 /* Set DirectDraw Cooperative level, ie what control we want over Windows
629 * display */
630 hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
631 if (hr != DD_OK) {
632 msg_Err(vd, "cannot set direct draw cooperative level");
633 return VLC_EGENERIC;
636 /* Get the size of the current display device */
637 if (sys->hmonitor) {
638 MONITORINFO monitor_info;
639 monitor_info.cbSize = sizeof(MONITORINFO);
640 GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
641 sys->rect_display = monitor_info.rcMonitor;
642 } else {
643 sys->rect_display.left = 0;
644 sys->rect_display.top = 0;
645 sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
646 sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
649 msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
650 sys->rect_display.left,
651 sys->rect_display.top,
652 sys->rect_display.right,
653 sys->rect_display.bottom);
655 /* Probe the capabilities of the hardware */
656 DirectXGetDDrawCaps(vd);
658 return VLC_SUCCESS;
661 static void DirectXCloseDDraw(vout_display_t *vd)
663 vout_display_sys_t *sys = vd->sys;
664 if (sys->ddobject)
665 IDirectDraw2_Release(sys->ddobject);
667 sys->ddobject = NULL;
669 free(sys->display_driver);
670 sys->display_driver = NULL;
672 sys->hmonitor = NULL;
676 * Create a clipper that will be used when blitting the RGB surface to the main display.
678 * This clipper prevents us to modify by mistake anything on the screen
679 * which doesn't belong to our window. For example when a part of our video
680 * window is hidden by another window.
682 static void DirectXCreateClipper(vout_display_t *vd)
684 vout_display_sys_t *sys = vd->sys;
685 HRESULT hr;
687 /* Create the clipper */
688 hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
689 if (hr != DD_OK) {
690 msg_Warn(vd, "cannot create clipper (error %li)", hr);
691 goto error;
694 /* Associate the clipper to the window */
695 hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
696 if (hr != DD_OK) {
697 msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
698 goto error;
701 /* associate the clipper with the surface */
702 hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
703 if (hr != DD_OK)
705 msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
706 goto error;
709 return;
711 error:
712 if (sys->clipper)
713 IDirectDrawClipper_Release(sys->clipper);
714 sys->clipper = NULL;
718 * It finds out the 32bits RGB pixel value of the colorkey.
720 static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
722 vout_display_sys_t *sys = vd->sys;
723 HRESULT hr;
725 /* */
726 DDSURFACEDESC ddsd;
727 ddsd.dwSize = sizeof(ddsd);
728 hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
729 if (hr != DD_OK)
730 return 0;
732 uint32_t backup = *(uint32_t *)ddsd.lpSurface;
734 switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
735 case 4:
736 *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
737 break;
738 case 8:
739 *(uint8_t *)ddsd.lpSurface = *color;
740 break;
741 case 15:
742 case 16:
743 *(uint16_t *)ddsd.lpSurface = *color;
744 break;
745 case 24:
746 /* Seems to be problematic so we'll just put black as the colorkey */
747 *color = 0;
748 default:
749 *(uint32_t *)ddsd.lpSurface = *color;
750 break;
752 IDirectDrawSurface2_Unlock(sys->display, NULL);
754 /* */
755 HDC hdc;
756 COLORREF rgb;
757 if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
758 rgb = GetPixel(hdc, 0, 0);
759 IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
760 } else {
761 rgb = 0;
764 /* Restore the pixel value */
765 ddsd.dwSize = sizeof(ddsd);
766 if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
767 *(uint32_t *)ddsd.lpSurface = backup;
768 IDirectDrawSurface2_Unlock(sys->display, NULL);
771 return rgb;
775 * Create and initialize display according to preferences specified in the vout
776 * thread fields.
778 static int DirectXOpenDisplay(vout_display_t *vd)
780 vout_display_sys_t *sys = vd->sys;
781 HRESULT hr;
783 /* Now get the primary surface. This surface is what you actually see
784 * on your screen */
785 DDSURFACEDESC ddsd;
786 ZeroMemory(&ddsd, sizeof(ddsd));
787 ddsd.dwSize = sizeof(ddsd);
788 ddsd.dwFlags = DDSD_CAPS;
789 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
791 LPDIRECTDRAWSURFACE display;
792 hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
793 if (hr != DD_OK) {
794 msg_Err(vd, "cannot get primary surface (error %li)", hr);
795 return VLC_EGENERIC;
798 void *ptr;
799 hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
800 &ptr);
801 /* Release the old interface */
802 IDirectDrawSurface_Release(display);
804 if (hr != DD_OK) {
805 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
806 sys->display = NULL;
807 return VLC_EGENERIC;
809 sys->display = ptr;
811 /* The clipper will be used only in non-overlay mode */
812 DirectXCreateClipper(vd);
814 /* Make sure the colorkey will be painted */
815 sys->i_colorkey = 1;
816 sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);
818 return VLC_SUCCESS;
820 static void DirectXCloseDisplay(vout_display_t *vd)
822 vout_display_sys_t *sys = vd->sys;
824 if (sys->clipper != NULL)
825 IDirectDrawClipper_Release(sys->clipper);
827 if (sys->display != NULL)
828 IDirectDrawSurface2_Release(sys->display);
830 sys->clipper = NULL;
831 sys->display = NULL;
835 * Create an YUV overlay or RGB surface for the video.
837 * The best method of display is with an YUV overlay because the YUV->RGB
838 * conversion is done in hardware.
839 * You can also create a plain RGB surface.
840 * (Maybe we could also try an RGB overlay surface, which could have hardware
841 * scaling and which would also be faster in window mode because you don't
842 * need to do any blitting to the main display...)
844 static int DirectXCreateSurface(vout_display_t *vd,
845 LPDIRECTDRAWSURFACE2 *surface,
846 const video_format_t *fmt,
847 DWORD fourcc,
848 bool use_overlay,
849 bool use_sysmem,
850 int backbuffer_count)
852 vout_display_sys_t *sys = vd->sys;
854 DDSURFACEDESC ddsd;
856 ZeroMemory(&ddsd, sizeof(ddsd));
857 ddsd.dwSize = sizeof(ddsd);
858 ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
859 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
860 ddsd.dwWidth = fmt->i_width;
861 ddsd.dwHeight = fmt->i_height;
862 if (fourcc) {
863 ddsd.dwFlags |= DDSD_PIXELFORMAT;
864 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
865 ddsd.ddpfPixelFormat.dwFourCC = fourcc;
867 ddsd.dwFlags |= DDSD_CAPS;
868 if (use_overlay) {
869 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
870 if (backbuffer_count > 0) {
871 ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
872 ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
873 ddsd.dwBackBufferCount = backbuffer_count;
875 } else {
876 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
877 if (use_sysmem)
878 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
879 else
880 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
883 /* Create the video surface */
884 LPDIRECTDRAWSURFACE surface_v1;
885 HRESULT hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL);
886 if (hr == DDERR_INVALIDCAPS)
888 msg_Dbg(vd, "failed to create a DirectDrawSurface with invalid caps %lx", ddsd.ddsCaps.dwCaps);
889 return VLC_EGENERIC;
891 if (hr != DD_OK)
893 msg_Dbg(vd, "failed to create a DirectDrawSurface (error %li)", hr);
894 return VLC_EGENERIC;
897 /* Now that the surface is created, try to get a newer DirectX interface */
898 hr = IDirectDrawSurface_QueryInterface(surface_v1,
899 &IID_IDirectDrawSurface2,
900 (LPVOID *)surface);
901 IDirectDrawSurface_Release(surface_v1);
902 if (hr != DD_OK) {
903 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
904 return VLC_EGENERIC;
907 if (use_overlay) {
908 /* Check the overlay is useable as some graphics cards allow creating
909 * several overlays but only one can be used at one time. */
910 if (DirectXUpdateOverlay(vd, *surface)) {
911 IDirectDrawSurface2_Release(*surface);
912 msg_Err(vd, "overlay unuseable (might already be in use)");
913 return VLC_EGENERIC;
917 return VLC_SUCCESS;
920 static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
922 IDirectDrawSurface2_Release(surface);
925 * This function locks a surface and get the surface descriptor.
927 static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
928 LPDIRECTDRAWSURFACE2 surface,
929 DDSURFACEDESC *ddsd)
931 HRESULT hr;
933 DDSURFACEDESC ddsd_dummy;
934 if (!ddsd)
935 ddsd = &ddsd_dummy;
937 ZeroMemory(ddsd, sizeof(*ddsd));
938 ddsd->dwSize = sizeof(*ddsd);
939 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
940 if (hr != DD_OK) {
941 if (hr == DDERR_INVALIDPARAMS) {
942 /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
943 * in an invalid params error */
944 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
946 if (hr == DDERR_SURFACELOST) {
947 /* Your surface can be lost so be sure
948 * to check this and restore it if needed */
950 /* When using overlays with back-buffers, we need to restore
951 * the front buffer so the back-buffers get restored as well. */
952 if (front_surface != surface)
953 IDirectDrawSurface2_Restore(front_surface);
954 else
955 IDirectDrawSurface2_Restore(surface);
957 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
959 if (hr != DD_OK)
960 return VLC_EGENERIC;
962 return VLC_SUCCESS;
964 static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
965 LPDIRECTDRAWSURFACE2 surface)
967 VLC_UNUSED(front_surface);
968 IDirectDrawSurface2_Unlock(surface, NULL);
970 static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
971 LPDIRECTDRAWSURFACE2 surface)
973 if (DirectXLockSurface(front_surface, surface, NULL))
974 return VLC_EGENERIC;
976 DirectXUnlockSurface(front_surface, surface);
977 return VLC_SUCCESS;
982 typedef struct {
983 vlc_fourcc_t codec;
984 DWORD fourcc;
985 } dx_format_t;
987 static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
989 static const dx_format_t dx_formats[] = {
990 { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
991 { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
992 { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
993 { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
994 { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
995 { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
996 { 0, 0 }
999 for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
1000 if (dx_formats[i].codec == codec)
1001 return dx_formats[i].fourcc;
1003 return 0;
1006 static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
1007 const video_format_t *fmt,
1008 DWORD fourcc)
1010 vout_display_sys_t *sys = vd->sys;
1012 bool allow_3buf = var_InheritBool(vd, "directx-3buffering");
1014 /* The overlay surface that we create won't be used to decode directly
1015 * into it because accessing video memory directly is way to slow (remember
1016 * that pictures are decoded macroblock per macroblock). Instead the video
1017 * will be decoded in picture buffers in system memory which will then be
1018 * memcpy() to the overlay surface. */
1019 LPDIRECTDRAWSURFACE2 front_surface;
1020 int ret = VLC_EGENERIC;
1021 if (allow_3buf) {
1022 /* Triple buffering rocks! it doesn't have any processing overhead
1023 * (you don't have to wait for the vsync) and provides for a very nice
1024 * video quality (no tearing). */
1025 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
1027 if (ret)
1028 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
1029 if (ret)
1030 return VLC_EGENERIC;
1031 msg_Dbg(vd, "YUV overlay surface (%4.4s) created successfully", (const char *)&fourcc);
1033 /* Get the back buffer */
1034 LPDIRECTDRAWSURFACE2 surface;
1035 DDSCAPS dds_caps;
1036 ZeroMemory(&dds_caps, sizeof(dds_caps));
1037 dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1038 if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
1039 msg_Warn(vd, "Failed to get surface back buffer");
1040 /* front buffer is the same as back buffer */
1041 surface = front_surface;
1044 if (DirectXCheckLockingSurface(front_surface, surface)) {
1045 DirectXDestroySurface(front_surface);
1046 return VLC_EGENERIC;
1049 /* */
1050 picture_sys_t *picsys = sys->picsys;
1051 picsys->front_surface = front_surface;
1052 picsys->surface = surface;
1053 picsys->fallback = NULL;
1054 return VLC_SUCCESS;
1056 static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
1057 const video_format_t *fmt,
1058 DWORD fourcc)
1060 vout_display_sys_t *sys = vd->sys;
1062 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1064 /* As we can't have an overlay, we'll try to create a plain offscreen
1065 * surface. This surface will reside in video memory because there's a
1066 * better chance then that we'll be able to use some kind of hardware
1067 * acceleration like rescaling, blitting or YUV->RGB conversions.
1068 * We then only need to blit this surface onto the main display when we
1069 * want to display it */
1071 /* Check if the chroma is supported first. This is required
1072 * because a few buggy drivers don't mind creating the surface
1073 * even if they don't know about the chroma. */
1074 DWORD count;
1075 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
1076 return VLC_EGENERIC;
1078 DWORD *list = calloc(count, sizeof(*list));
1079 if (!list)
1080 return VLC_ENOMEM;
1081 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
1082 free(list);
1083 return VLC_EGENERIC;
1085 unsigned index;
1086 for (index = 0; index < count; index++) {
1087 if (list[index] == fourcc)
1088 break;
1090 free(list);
1091 if (index >= count)
1092 return VLC_EGENERIC;
1094 /* */
1095 LPDIRECTDRAWSURFACE2 surface;
1096 if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
1097 return VLC_EGENERIC;
1098 msg_Dbg(vd, "YUV plain surface (%4.4s) created successfully", (const char *)&fourcc);
1100 if (DirectXCheckLockingSurface(surface, surface)) {
1101 DirectXDestroySurface(surface);
1102 return VLC_EGENERIC;
1105 /* */
1106 picture_sys_t *picsys = sys->picsys;
1107 picsys->front_surface = surface;
1108 picsys->surface = surface;
1109 picsys->fallback = NULL;
1110 return VLC_SUCCESS;
1112 static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
1113 video_format_t *fmt)
1115 vout_display_sys_t *sys = vd->sys;
1116 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1118 /* Our last choice is to use a plain RGB surface */
1119 DDPIXELFORMAT ddpfPixelFormat;
1120 ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
1121 ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
1123 IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
1124 if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
1125 return VLC_EGENERIC;
1127 switch (ddpfPixelFormat.dwRGBBitCount) {
1128 case 8:
1129 fmt->i_chroma = VLC_CODEC_RGB8;
1130 break;
1131 case 15:
1132 fmt->i_chroma = VLC_CODEC_RGB15;
1133 break;
1134 case 16:
1135 fmt->i_chroma = VLC_CODEC_RGB16;
1136 break;
1137 case 24:
1138 fmt->i_chroma = VLC_CODEC_RGB24;
1139 break;
1140 case 32:
1141 fmt->i_chroma = VLC_CODEC_RGB32;
1142 break;
1143 default:
1144 msg_Err(vd, "unknown screen depth");
1145 return VLC_EGENERIC;
1147 fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
1148 fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
1149 fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
1151 /* */
1152 LPDIRECTDRAWSURFACE2 surface;
1153 int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
1154 if (ret && !allow_sysmem)
1155 ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
1156 if (ret)
1157 return VLC_EGENERIC;
1158 msg_Dbg(vd, "RGB plain surface (%4.4s) created successfully", (const char *)&fmt->i_chroma);
1160 if (DirectXCheckLockingSurface(surface, surface)) {
1161 DirectXDestroySurface(surface);
1162 return VLC_EGENERIC;
1165 /* */
1166 picture_sys_t *picsys = sys->picsys;
1167 picsys->front_surface = surface;
1168 picsys->surface = surface;
1169 picsys->fallback = NULL;
1170 return VLC_SUCCESS;
1173 static int DirectXCreatePictureResource(vout_display_t *vd,
1174 bool *use_overlay,
1175 video_format_t *fmt)
1177 vout_display_sys_t *sys = vd->sys;
1179 /* */
1180 picture_sys_t *picsys = calloc(1, sizeof(*picsys));
1181 if (unlikely(picsys == NULL))
1182 return VLC_ENOMEM;
1183 sys->picsys = picsys;
1185 /* */
1186 bool allow_hw_yuv = sys->can_blit_fourcc &&
1187 vlc_fourcc_IsYUV(fmt->i_chroma) &&
1188 var_InheritBool(vd, "directx-hw-yuv");
1189 bool allow_overlay = var_InheritBool(vd, "directx-overlay");
1191 /* Try to use an yuv surface */
1192 if (allow_hw_yuv) {
1193 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1194 /* Try with overlay first */
1195 for (unsigned pass = allow_overlay ? 0 : 1; pass < 2; pass++) {
1196 for (unsigned i = 0; list[i] != 0; i++) {
1197 const DWORD fourcc = DirectXGetFourcc(list[i]);
1198 if (!fourcc)
1199 continue;
1201 if (pass == 0) {
1202 if (DirectXCreatePictureResourceYuvOverlay(vd, fmt, fourcc))
1204 #ifndef NDEBUG
1205 msg_Dbg(vd, "Failed to create YUV overlay surface %4.4s", (const char*)&fourcc);
1206 #endif
1207 continue;
1209 } else {
1210 if (DirectXCreatePictureResourceYuv(vd, fmt, fourcc))
1212 #ifndef NDEBUG
1213 msg_Dbg(vd, "Failed to create YUV surface %4.4s", (const char*)&fourcc);
1214 #endif
1215 continue;
1218 /* */
1219 *use_overlay = pass == 0;
1220 fmt->i_chroma = list[i];
1221 return VLC_SUCCESS;
1226 /* Try plain RGB */
1227 return DirectXCreatePictureResourceRgb(vd, fmt);
1229 static void DirectXDestroyPictureResource(vout_display_t *vd)
1231 vout_display_sys_t *sys = vd->sys;
1233 if (sys->picsys->front_surface != sys->picsys->surface)
1234 DirectXDestroySurface(sys->picsys->surface);
1235 DirectXDestroySurface(sys->picsys->front_surface);
1236 if (sys->picsys->fallback)
1237 picture_Release(sys->picsys->fallback);
1240 static int DirectXLock(picture_t *picture)
1242 DDSURFACEDESC ddsd;
1243 if (DirectXLockSurface(picture->p_sys->front_surface,
1244 picture->p_sys->surface, &ddsd))
1245 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
1247 CommonUpdatePicture(picture, NULL, ddsd.lpSurface, ddsd.lPitch);
1248 return VLC_SUCCESS;
1250 static void DirectXUnlock(picture_t *picture)
1252 DirectXUnlockSurface(picture->p_sys->front_surface,
1253 picture->p_sys->surface);
1256 static int DirectXCreatePool(vout_display_t *vd,
1257 bool *use_overlay, video_format_t *fmt)
1259 vout_display_sys_t *sys = vd->sys;
1261 /* */
1262 *fmt = vd->source;
1264 if (DirectXCreatePictureResource(vd, use_overlay, fmt))
1265 return VLC_EGENERIC;
1267 /* Create the associated picture */
1268 picture_resource_t resource = { .p_sys = sys->picsys };
1269 picture_t *picture = picture_NewFromResource(fmt, &resource);
1270 if (!picture) {
1271 DirectXDestroyPictureResource(vd);
1272 free(sys->picsys);
1273 return VLC_ENOMEM;
1276 /* Wrap it into a picture pool */
1277 picture_pool_configuration_t cfg;
1278 memset(&cfg, 0, sizeof(cfg));
1279 cfg.picture_count = 1;
1280 cfg.picture = &picture;
1281 cfg.lock = DirectXLock;
1282 cfg.unlock = DirectXUnlock;
1284 sys->pool = picture_pool_NewExtended(&cfg);
1285 if (!sys->pool) {
1286 picture_Release(picture);
1287 DirectXDestroyPictureResource(vd);
1288 return VLC_ENOMEM;
1290 return VLC_SUCCESS;
1292 static void DirectXDestroyPool(vout_display_t *vd)
1294 vout_display_sys_t *sys = vd->sys;
1296 if (sys->pool) {
1297 DirectXDestroyPictureResource(vd);
1298 picture_pool_Release(sys->pool);
1300 sys->pool = NULL;
1304 * Move or resize overlay surface on video display.
1306 * This function is used to move or resize an overlay surface on the screen.
1307 * Ususally the overlay is moved by the user and thus, by a move or resize
1308 * event.
1310 static int DirectXUpdateOverlay(vout_display_t *vd, LPDIRECTDRAWSURFACE2 surface)
1312 vout_display_sys_t *sys = vd->sys;
1314 RECT src = sys->rect_src_clipped;
1315 RECT dst = sys->rect_dest_clipped;
1317 if (sys->use_wallpaper) {
1318 src.left = vd->source.i_x_offset;
1319 src.top = vd->source.i_y_offset;
1320 src.right = vd->source.i_x_offset + vd->source.i_visible_width;
1321 src.bottom = vd->source.i_y_offset + vd->source.i_visible_height;
1322 AlignRect(&src, sys->i_align_src_boundary, sys->i_align_src_size);
1324 vout_display_cfg_t cfg = *vd->cfg;
1325 cfg.display.width = sys->rect_display.right;
1326 cfg.display.height = sys->rect_display.bottom;
1328 vout_display_place_t place;
1329 vout_display_PlacePicture(&place, &vd->source, &cfg, true);
1331 dst.left = sys->rect_display.left + place.x;
1332 dst.top = sys->rect_display.top + place.y;
1333 dst.right = dst.left + place.width;
1334 dst.bottom = dst.top + place.height;
1335 AlignRect(&dst, sys->i_align_dest_boundary, sys->i_align_dest_size);
1338 if (!surface) {
1339 if (!sys->pool)
1340 return VLC_EGENERIC;
1341 surface = sys->picsys->front_surface;
1344 /* The new window dimensions should already have been computed by the
1345 * caller of this function */
1347 /* Position and show the overlay */
1348 DDOVERLAYFX ddofx;
1349 ZeroMemory(&ddofx, sizeof(ddofx));
1350 ddofx.dwSize = sizeof(ddofx);
1351 ddofx.dckDestColorkey.dwColorSpaceLowValue = sys->i_colorkey;
1352 ddofx.dckDestColorkey.dwColorSpaceHighValue = sys->i_colorkey;
1354 HRESULT hr = IDirectDrawSurface2_UpdateOverlay(surface,
1355 &src, sys->display, &dst,
1356 DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE | DDOVER_DDFX,
1357 &ddofx);
1358 sys->restore_overlay = hr != DD_OK;
1360 if (hr != DD_OK) {
1361 msg_Warn(vd, "DirectDrawUpdateOverlay cannot move/resize overlay. (hr=0x%lX)", hr);
1362 return VLC_EGENERIC;
1364 return VLC_SUCCESS;
1367 /* */
1368 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper)
1370 vout_display_sys_t *sys = vd->sys;
1372 if (!sys->use_wallpaper == !use_wallpaper)
1373 return;
1375 HWND hwnd = FindWindow(_T("Progman"), NULL);
1376 if (hwnd)
1377 hwnd = FindWindowEx(hwnd, NULL, _T("SHELLDLL_DefView"), NULL);
1378 if (hwnd)
1379 hwnd = FindWindowEx(hwnd, NULL, _T("SysListView32"), NULL);
1380 if (!hwnd) {
1381 msg_Warn(vd, "couldn't find \"SysListView32\" window, "
1382 "wallpaper mode not supported");
1383 return;
1386 msg_Dbg(vd, "wallpaper mode %s", use_wallpaper ? "enabled" : "disabled");
1387 sys->use_wallpaper = use_wallpaper;
1389 if (sys->use_wallpaper) {
1390 sys->color_bkg = ListView_GetBkColor(hwnd);
1391 sys->color_bkgtxt = ListView_GetTextBkColor(hwnd);
1393 ListView_SetBkColor(hwnd, sys->i_rgb_colorkey);
1394 ListView_SetTextBkColor(hwnd, sys->i_rgb_colorkey);
1395 } else {
1396 ListView_SetBkColor(hwnd, sys->color_bkg);
1397 ListView_SetTextBkColor(hwnd, sys->color_bkgtxt);
1400 /* Update desktop */
1401 InvalidateRect(hwnd, NULL, TRUE);
1402 UpdateWindow(hwnd);
1404 if (sys->use_overlay)
1405 DirectXUpdateOverlay(vd, NULL);
1408 /* */
1409 static int WallpaperCallback(vlc_object_t *object, char const *cmd,
1410 vlc_value_t oldval, vlc_value_t newval, void *data)
1412 vout_display_t *vd = (vout_display_t *)object;
1413 vout_display_sys_t *sys = vd->sys;
1414 VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
1416 vlc_mutex_lock(&sys->lock);
1417 const bool ch_wallpaper = !sys->wallpaper_requested != !newval.b_bool;
1418 sys->ch_wallpaper |= ch_wallpaper;
1419 sys->wallpaper_requested = newval.b_bool;
1420 vlc_mutex_unlock(&sys->lock);
1421 return VLC_SUCCESS;
1424 typedef struct
1426 char **values;
1427 char **descs;
1428 size_t count;
1429 } enum_context_t;
1431 /*****************************************************************************
1432 * config variable callback
1433 *****************************************************************************/
1434 static BOOL WINAPI DirectXEnumCallback2(GUID *guid, LPSTR desc,
1435 LPSTR drivername, VOID *data,
1436 HMONITOR hmon)
1438 enum_context_t *ctx = data;
1440 VLC_UNUSED(guid); VLC_UNUSED(desc); VLC_UNUSED(hmon);
1442 char *psz_drivername = drivername;
1443 ctx->values = xrealloc(ctx->values, (ctx->count + 1) * sizeof(char *));
1444 ctx->descs = xrealloc(ctx->descs, (ctx->count + 1) * sizeof(char *));
1446 ctx->values[ctx->count] = strdup(psz_drivername);
1447 ctx->descs[ctx->count] = strdup(psz_drivername);
1448 ctx->count++;
1450 return TRUE; /* Keep enumerating */
1453 static int FindDevicesCallback(vlc_object_t *object, const char *name,
1454 char ***values, char ***descs)
1456 enum_context_t ctx;
1458 ctx.values = xmalloc(sizeof(char *));
1459 ctx.descs = xmalloc(sizeof(char *));
1460 ctx.values[0] = strdup("");
1461 ctx.descs[0] = strdup(_("Default"));
1462 ctx.count = 1;
1464 /* Load direct draw DLL */
1465 HINSTANCE hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1466 if (hddraw_dll != NULL)
1468 /* Enumerate displays */
1469 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA,
1470 LPVOID, DWORD) =
1471 (void *)GetProcAddress(hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
1472 if (OurDirectDrawEnumerateEx != NULL)
1473 OurDirectDrawEnumerateEx(DirectXEnumCallback2, &ctx,
1474 DDENUM_ATTACHEDSECONDARYDEVICES);
1475 FreeLibrary(hddraw_dll);
1478 VLC_UNUSED(object);
1479 VLC_UNUSED(name);
1481 *values = ctx.values;
1482 *descs = ctx.descs;
1483 return ctx.count;