1 /*****************************************************************************
2 * directx.c: Windows DirectDraw video output
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
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 /*****************************************************************************
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 *****************************************************************************/
44 #include <vlc_common.h>
45 #include <vlc_plugin.h>
46 #include <vlc_vout_display.h>
47 #include <vlc_charset.h> /* FromT */
51 #include <commctrl.h> /* ListView_(Get|Set)* */
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 /*****************************************************************************
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 SYSMEM_TEXT N_("Use video buffers in system memory")
71 #define SYSMEM_LONGTEXT N_(\
72 "Create video buffers in system memory instead of video memory. This " \
73 "isn't recommended as usually using video memory allows benefiting from " \
74 "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
75 "This option doesn't have any effect when using overlays.")
77 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
78 #define TRIPLEBUF_LONGTEXT N_(\
79 "Try to use triple buffering when using YUV overlays. That results in " \
80 "much better video quality (no flickering).")
82 #define DEVICE_TEXT N_("Name of desired display device")
83 #define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
84 "specify the Windows device name of the display that you want the video " \
85 "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
86 "\"\\\\.\\DISPLAY2\".")
88 #define DX_HELP N_("Recommended video output for Windows XP. " \
89 "Incompatible with Vista's Aero interface" )
91 static int Open (vlc_object_t *);
92 static void Close(vlc_object_t *);
94 static int FindDevicesCallback(vlc_object_t *, const char *,
97 set_shortname("DirectX")
98 set_description(N_("DirectX (DirectDraw) video output"))
100 set_category(CAT_VIDEO)
101 set_subcategory(SUBCAT_VIDEO_VOUT)
102 add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
104 add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
106 add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
107 TRIPLEBUF_LONGTEXT, true)
108 add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
109 change_string_cb(FindDevicesCallback)
111 set_capability("vout display", 230)
112 add_shortcut("directx")
113 set_callbacks(Open, Close)
116 /*****************************************************************************
118 *****************************************************************************/
120 struct picture_sys_t {
121 LPDIRECTDRAWSURFACE2 surface;
122 LPDIRECTDRAWSURFACE2 front_surface;
126 /*****************************************************************************
128 * Defining them here allows us to get rid of the dxguid library during
130 *****************************************************************************/
131 #include <initguid.h>
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);
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 *);
143 static int WallpaperCallback(vlc_object_t *, char const *,
144 vlc_value_t, vlc_value_t, void *);
146 static int DirectXOpen(vout_display_t *, video_format_t *fmt);
147 static void DirectXClose(vout_display_t *);
149 static int DirectXLock(picture_t *);
150 static void DirectXUnlock(picture_t *);
152 static int DirectXUpdateOverlay(vout_display_t *, LPDIRECTDRAWSURFACE2 surface);
154 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper);
156 /** This function allocates and initialize the DirectX vout display.
158 static int Open(vlc_object_t *object)
160 vout_display_t *vd = (vout_display_t *)object;
161 vout_display_sys_t *sys;
163 /* Allocate structure */
164 vd->sys = sys = calloc(1, sizeof(*sys));
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");
177 sys->use_wallpaper = var_CreateGetBool(vd, "video-wallpaper");
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);
188 video_format_t fmt = vd->fmt;
190 if (DirectXOpen(vd, &fmt))
194 vout_display_info_t info = vd->info;
196 info.has_double_click = true;
197 info.has_hide_mouse = false;
198 info.has_pictures_invalid = true;
199 info.has_event_thread = true;
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;
208 val.psz_string = _("Wallpaper");
209 var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
210 var_AddCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
212 /* Setup vout_display now that everything is fine */
213 video_format_Clean(&vd->fmt);
214 video_format_Copy(&vd->fmt, &fmt);
219 vd->display = Display;
220 vd->control = Control;
228 FreeLibrary(sys->hddraw_dll);
233 /** Terminate a vout display created by Open.
235 static void Close(vlc_object_t *object)
237 vout_display_t *vd = (vout_display_t *)object;
238 vout_display_sys_t *sys = vd->sys;
240 var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
241 vlc_mutex_destroy(&sys->lock);
243 /* Make sure the wallpaper is restored */
244 WallpaperChange(vd, false);
251 FreeLibrary(sys->hddraw_dll);
255 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
258 return vd->sys->pool;
260 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
262 vout_display_sys_t *sys = vd->sys;
264 assert(sys->display);
266 /* Our surface can be lost so be sure to check this
267 * and restore it if need be */
268 if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
269 if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
270 if (sys->use_overlay)
271 DirectXUpdateOverlay(vd, NULL);
274 if (sys->restore_overlay)
275 DirectXUpdateOverlay(vd, NULL);
278 DirectXUnlock(picture);
280 if (sys->use_overlay) {
281 /* Flip the overlay buffers if we are using back buffers */
282 if (picture->p_sys->surface != picture->p_sys->front_surface) {
283 HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
286 msg_Warn(vd, "could not flip overlay (error %li)", hr);
289 /* Blit video surface to display with the NOTEARING option */
291 ZeroMemory(&ddbltfx, sizeof(ddbltfx));
292 ddbltfx.dwSize = sizeof(ddbltfx);
293 ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
295 HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
296 &sys->rect_dest_clipped,
297 picture->p_sys->surface,
298 &sys->rect_src_clipped,
299 DDBLT_ASYNC, &ddbltfx);
301 msg_Warn(vd, "could not blit surface (error %li)", hr);
303 DirectXLock(picture);
305 if (sys->is_first_display) {
306 IDirectDraw_WaitForVerticalBlank(sys->ddobject,
307 DDWAITVB_BLOCKBEGIN, NULL);
308 if (sys->use_overlay) {
309 HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
310 /* set the colorkey as the backgound brush for the video window */
311 SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
316 picture_Release(picture);
317 VLC_UNUSED(subpicture);
319 static int Control(vout_display_t *vd, int query, va_list args)
321 vout_display_sys_t *sys = vd->sys;
324 case VOUT_DISPLAY_RESET_PICTURES:
326 /* Make sure the wallpaper is restored */
327 if (sys->use_wallpaper) {
328 vlc_mutex_lock(&sys->lock);
329 if (!sys->ch_wallpaper) {
330 sys->ch_wallpaper = true;
331 sys->wallpaper_requested = true;
333 vlc_mutex_unlock(&sys->lock);
335 WallpaperChange(vd, false);
337 return DirectXOpen(vd, &vd->fmt);
339 return CommonControl(vd, query, args);
342 static void Manage(vout_display_t *vd)
344 vout_display_sys_t *sys = vd->sys;
348 if (sys->changes & DX_POSITION_CHANGE) {
350 if (sys->use_overlay)
351 DirectXUpdateOverlay(vd, NULL);
353 /* Check if we are still on the same monitor */
354 HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
355 if (sys->hmonitor != hmon) {
356 vout_display_SendEventPicturesInvalid(vd);
359 sys->changes &= ~DX_POSITION_CHANGE;
362 /* Wallpaper mode change */
363 vlc_mutex_lock(&sys->lock);
364 const bool ch_wallpaper = sys->ch_wallpaper;
365 const bool wallpaper_requested = sys->wallpaper_requested;
366 sys->ch_wallpaper = false;
367 vlc_mutex_unlock(&sys->lock);
370 WallpaperChange(vd, wallpaper_requested);
373 if (sys->restore_overlay)
374 DirectXUpdateOverlay(vd, NULL);
378 static int DirectXOpenDDraw(vout_display_t *);
379 static void DirectXCloseDDraw(vout_display_t *);
381 static int DirectXOpenDisplay(vout_display_t *vd);
382 static void DirectXCloseDisplay(vout_display_t *vd);
384 static int DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
385 static void DirectXDestroyPool(vout_display_t *);
387 static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
389 vout_display_sys_t *sys = vd->sys;
391 assert(!sys->ddobject);
392 assert(!sys->display);
393 assert(!sys->clipper);
395 /* Initialise DirectDraw */
396 if (DirectXOpenDDraw(vd)) {
397 msg_Err(vd, "cannot initialize DirectX DirectDraw");
401 /* Create the directx display */
402 if (DirectXOpenDisplay(vd)) {
403 msg_Err(vd, "cannot initialize DirectX DirectDraw");
406 UpdateRects(vd, NULL, NULL, true);
408 /* Create the picture pool */
409 if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
410 msg_Err(vd, "cannot create any DirectX surface");
415 if (sys->use_overlay)
416 DirectXUpdateOverlay(vd, NULL);
417 EventThreadUseOverlay(sys->event, sys->use_overlay);
419 /* Change the window title bar text */
420 const char *fallback;
421 if (sys->use_overlay)
422 fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
423 else if (vlc_fourcc_IsYUV(fmt->i_chroma))
424 fallback = VOUT_TITLE " (hardware YUV DirectX output)";
426 fallback = VOUT_TITLE " (software RGB DirectX output)";
427 EventThreadUpdateTitle(sys->event, fallback);
431 static void DirectXClose(vout_display_t *vd)
433 DirectXDestroyPool(vd);
434 DirectXCloseDisplay(vd);
435 DirectXCloseDDraw(vd);
439 static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPSTR desc,
440 LPSTR drivername, VOID *context,
443 vout_display_t *vd = context;
444 vout_display_sys_t *sys = vd->sys;
446 /* This callback function is called by DirectDraw once for each
447 * available DirectDraw device.
449 * Returning TRUE keeps enumerating.
454 char *psz_drivername = drivername;
455 char *psz_desc = desc;
457 msg_Dbg(vd, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername);
459 char *device = var_GetString(vd, "directx-device");
461 /* Check for forced device */
462 if (device && *device && !strcmp(psz_drivername, device)) {
463 MONITORINFO monitor_info;
464 monitor_info.cbSize = sizeof(MONITORINFO);
466 if (GetMonitorInfoA(hmon, &monitor_info)) {
469 /* Move window to the right screen */
470 GetWindowRect(sys->hwnd, &rect);
471 if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
472 rect.left = monitor_info.rcWork.left;
473 rect.top = monitor_info.rcWork.top;
474 msg_Dbg(vd, "DirectXEnumCallback: setting window "
475 "position to %ld,%ld", rect.left, rect.top);
476 SetWindowPos(sys->hwnd, NULL,
477 rect.left, rect.top, 0, 0,
478 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
481 sys->hmonitor = hmon;
485 if (hmon == sys->hmonitor) {
486 msg_Dbg(vd, "selecting %s, %s", psz_desc, psz_drivername);
488 free(sys->display_driver);
489 sys->display_driver = malloc(sizeof(*guid));
490 if (sys->display_driver)
491 *sys->display_driver = *guid;
497 * Probe the capabilities of the hardware
499 * It is nice to know which features are supported by the hardware so we can
500 * find ways to optimize our rendering.
502 static void DirectXGetDDrawCaps(vout_display_t *vd)
504 vout_display_sys_t *sys = vd->sys;
506 /* This is just an indication of whether or not we'll support overlay,
507 * but with this test we don't know if we support YUV overlay */
509 ZeroMemory(&ddcaps, sizeof(ddcaps));
510 ddcaps.dwSize = sizeof(ddcaps);
511 HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
513 msg_Warn(vd, "cannot get caps");
517 /* Determine if the hardware supports overlay surfaces */
518 const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
519 /* Determine if the hardware supports overlay surfaces */
520 const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
521 /* Determine if the hardware supports overlay deinterlacing */
522 const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
523 /* Determine if the hardware supports colorkeying */
524 const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
525 /* Determine if the hardware supports scaling of the overlay surface */
526 const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
527 /* Determine if the hardware supports color conversion during a blit */
528 sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
529 /* Determine overlay source boundary alignment */
530 const bool align_boundary_src = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
531 /* Determine overlay destination boundary alignment */
532 const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
533 /* Determine overlay destination size alignment */
534 const bool align_size_src = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
535 /* Determine overlay destination size alignment */
536 const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;
538 msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
539 "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
541 has_overlay, has_overlay_fourcc, can_deinterlace,
542 has_color_key, can_stretch, sys->can_blit_fourcc);
544 if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
545 if (align_boundary_src)
546 vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
547 if (align_boundary_dest)
548 vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
550 vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
552 vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;
555 "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
556 "align_size_src=%i,%i align_size_dest=%i,%i",
557 align_boundary_src, vd->sys->i_align_src_boundary,
558 align_boundary_dest, vd->sys->i_align_dest_boundary,
559 align_size_src, vd->sys->i_align_src_size,
560 align_size_dest, vd->sys->i_align_dest_size);
567 static int DirectXOpenDDraw(vout_display_t *vd)
569 vout_display_sys_t *sys = vd->sys;
573 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
574 OurDirectDrawCreate =
575 (void *)GetProcAddress(sys->hddraw_dll, "DirectDrawCreate");
576 if (!OurDirectDrawCreate) {
577 msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
582 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
583 OurDirectDrawEnumerateEx =
584 (void *)GetProcAddress(sys->hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
586 if (OurDirectDrawEnumerateEx) {
587 char *device = var_GetString(vd, "directx-device");
589 msg_Dbg(vd, "directx-device: %s", device);
593 sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
595 /* Enumerate displays */
596 OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
597 vd, DDENUM_ATTACHEDSECONDARYDEVICES);
600 /* Initialize DirectDraw now */
601 LPDIRECTDRAW ddobject;
602 hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
604 msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
608 /* Get the IDirectDraw2 interface */
610 hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
612 /* Release the unused interface */
613 IDirectDraw_Release(ddobject);
616 msg_Err(vd, "cannot get IDirectDraw2 interface");
617 sys->ddobject = NULL;
622 /* Set DirectDraw Cooperative level, ie what control we want over Windows
624 hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
626 msg_Err(vd, "cannot set direct draw cooperative level");
630 /* Get the size of the current display device */
632 MONITORINFO monitor_info;
633 monitor_info.cbSize = sizeof(MONITORINFO);
634 GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
635 sys->rect_display = monitor_info.rcMonitor;
637 sys->rect_display.left = 0;
638 sys->rect_display.top = 0;
639 sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
640 sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
643 msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
644 sys->rect_display.left,
645 sys->rect_display.top,
646 sys->rect_display.right,
647 sys->rect_display.bottom);
649 /* Probe the capabilities of the hardware */
650 DirectXGetDDrawCaps(vd);
655 static void DirectXCloseDDraw(vout_display_t *vd)
657 vout_display_sys_t *sys = vd->sys;
659 IDirectDraw2_Release(sys->ddobject);
661 sys->ddobject = NULL;
663 free(sys->display_driver);
664 sys->display_driver = NULL;
666 sys->hmonitor = NULL;
670 * Create a clipper that will be used when blitting the RGB surface to the main display.
672 * This clipper prevents us to modify by mistake anything on the screen
673 * which doesn't belong to our window. For example when a part of our video
674 * window is hidden by another window.
676 static void DirectXCreateClipper(vout_display_t *vd)
678 vout_display_sys_t *sys = vd->sys;
681 /* Create the clipper */
682 hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
684 msg_Warn(vd, "cannot create clipper (error %li)", hr);
688 /* Associate the clipper to the window */
689 hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
691 msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
695 /* associate the clipper with the surface */
696 hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
699 msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
707 IDirectDrawClipper_Release(sys->clipper);
712 * It finds out the 32bits RGB pixel value of the colorkey.
714 static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
716 vout_display_sys_t *sys = vd->sys;
721 ddsd.dwSize = sizeof(ddsd);
722 hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
726 uint32_t backup = *(uint32_t *)ddsd.lpSurface;
728 switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
730 *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
733 *(uint8_t *)ddsd.lpSurface = *color;
737 *(uint16_t *)ddsd.lpSurface = *color;
740 /* Seems to be problematic so we'll just put black as the colorkey */
743 *(uint32_t *)ddsd.lpSurface = *color;
746 IDirectDrawSurface2_Unlock(sys->display, NULL);
751 if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
752 rgb = GetPixel(hdc, 0, 0);
753 IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
758 /* Restore the pixel value */
759 ddsd.dwSize = sizeof(ddsd);
760 if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
761 *(uint32_t *)ddsd.lpSurface = backup;
762 IDirectDrawSurface2_Unlock(sys->display, NULL);
769 * Create and initialize display according to preferences specified in the vout
772 static int DirectXOpenDisplay(vout_display_t *vd)
774 vout_display_sys_t *sys = vd->sys;
777 /* Now get the primary surface. This surface is what you actually see
780 ZeroMemory(&ddsd, sizeof(ddsd));
781 ddsd.dwSize = sizeof(ddsd);
782 ddsd.dwFlags = DDSD_CAPS;
783 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
785 LPDIRECTDRAWSURFACE display;
786 hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
788 msg_Err(vd, "cannot get primary surface (error %li)", hr);
793 hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
795 /* Release the old interface */
796 IDirectDrawSurface_Release(display);
799 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
805 /* The clipper will be used only in non-overlay mode */
806 DirectXCreateClipper(vd);
808 /* Make sure the colorkey will be painted */
810 sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);
814 static void DirectXCloseDisplay(vout_display_t *vd)
816 vout_display_sys_t *sys = vd->sys;
818 if (sys->clipper != NULL)
819 IDirectDrawClipper_Release(sys->clipper);
821 if (sys->display != NULL)
822 IDirectDrawSurface2_Release(sys->display);
829 * Create an YUV overlay or RGB surface for the video.
831 * The best method of display is with an YUV overlay because the YUV->RGB
832 * conversion is done in hardware.
833 * You can also create a plain RGB surface.
834 * (Maybe we could also try an RGB overlay surface, which could have hardware
835 * scaling and which would also be faster in window mode because you don't
836 * need to do any blitting to the main display...)
838 static int DirectXCreateSurface(vout_display_t *vd,
839 LPDIRECTDRAWSURFACE2 *surface,
840 const video_format_t *fmt,
844 int backbuffer_count)
846 vout_display_sys_t *sys = vd->sys;
850 ZeroMemory(&ddsd, sizeof(ddsd));
851 ddsd.dwSize = sizeof(ddsd);
852 ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
853 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
854 ddsd.dwWidth = fmt->i_visible_width;
855 ddsd.dwHeight = fmt->i_visible_height;
857 ddsd.dwFlags |= DDSD_PIXELFORMAT;
858 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
859 ddsd.ddpfPixelFormat.dwFourCC = fourcc;
862 ddsd.dwFlags |= DDSD_CAPS;
863 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
864 if (backbuffer_count > 0)
865 ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
867 if (backbuffer_count > 0) {
868 ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
869 ddsd.dwBackBufferCount = backbuffer_count;
872 ddsd.dwFlags |= DDSD_CAPS;
873 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
875 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
877 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
880 /* Create the video surface */
881 LPDIRECTDRAWSURFACE surface_v1;
882 if (IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL) != DD_OK)
885 /* Now that the surface is created, try to get a newer DirectX interface */
886 HRESULT hr = IDirectDrawSurface_QueryInterface(surface_v1,
887 &IID_IDirectDrawSurface2,
889 IDirectDrawSurface_Release(surface_v1);
891 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
896 /* Check the overlay is useable as some graphics cards allow creating
897 * several overlays but only one can be used at one time. */
898 if (DirectXUpdateOverlay(vd, *surface)) {
899 IDirectDrawSurface2_Release(*surface);
900 msg_Err(vd, "overlay unuseable (might already be in use)");
908 static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
910 IDirectDrawSurface2_Release(surface);
913 * This function locks a surface and get the surface descriptor.
915 static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
916 LPDIRECTDRAWSURFACE2 surface,
921 DDSURFACEDESC ddsd_dummy;
925 ZeroMemory(ddsd, sizeof(*ddsd));
926 ddsd->dwSize = sizeof(*ddsd);
927 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
929 if (hr == DDERR_INVALIDPARAMS) {
930 /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
931 * in an invalid params error */
932 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
934 if (hr == DDERR_SURFACELOST) {
935 /* Your surface can be lost so be sure
936 * to check this and restore it if needed */
938 /* When using overlays with back-buffers, we need to restore
939 * the front buffer so the back-buffers get restored as well. */
940 if (front_surface != surface)
941 IDirectDrawSurface2_Restore(front_surface);
943 IDirectDrawSurface2_Restore(surface);
945 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
952 static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
953 LPDIRECTDRAWSURFACE2 surface)
955 VLC_UNUSED(front_surface);
956 IDirectDrawSurface2_Unlock(surface, NULL);
958 static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
959 LPDIRECTDRAWSURFACE2 surface)
961 if (DirectXLockSurface(front_surface, surface, NULL))
964 DirectXUnlockSurface(front_surface, surface);
975 static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
977 static const dx_format_t dx_formats[] = {
978 { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
979 { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
980 { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
981 { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
982 { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
983 { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
987 for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
988 if (dx_formats[i].codec == codec)
989 return dx_formats[i].fourcc;
994 static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
995 const video_format_t *fmt,
998 vout_display_sys_t *sys = vd->sys;
1000 bool allow_3buf = var_InheritBool(vd, "directx-3buffering");
1002 /* The overlay surface that we create won't be used to decode directly
1003 * into it because accessing video memory directly is way to slow (remember
1004 * that pictures are decoded macroblock per macroblock). Instead the video
1005 * will be decoded in picture buffers in system memory which will then be
1006 * memcpy() to the overlay surface. */
1007 LPDIRECTDRAWSURFACE2 front_surface;
1008 int ret = VLC_EGENERIC;
1010 /* Triple buffering rocks! it doesn't have any processing overhead
1011 * (you don't have to wait for the vsync) and provides for a very nice
1012 * video quality (no tearing). */
1013 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
1016 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
1018 return VLC_EGENERIC;
1019 msg_Dbg(vd, "YUV overlay surface created successfully");
1021 /* Get the back buffer */
1022 LPDIRECTDRAWSURFACE2 surface;
1024 ZeroMemory(&dds_caps, sizeof(dds_caps));
1025 dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1026 if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
1027 msg_Warn(vd, "Failed to get surface back buffer");
1028 /* front buffer is the same as back buffer */
1029 surface = front_surface;
1032 if (DirectXCheckLockingSurface(front_surface, surface)) {
1033 DirectXDestroySurface(front_surface);
1034 return VLC_EGENERIC;
1038 picture_sys_t *picsys = sys->picsys;
1039 picsys->front_surface = front_surface;
1040 picsys->surface = surface;
1041 picsys->fallback = NULL;
1044 static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
1045 const video_format_t *fmt,
1048 vout_display_sys_t *sys = vd->sys;
1050 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1052 /* As we can't have an overlay, we'll try to create a plain offscreen
1053 * surface. This surface will reside in video memory because there's a
1054 * better chance then that we'll be able to use some kind of hardware
1055 * acceleration like rescaling, blitting or YUV->RGB conversions.
1056 * We then only need to blit this surface onto the main display when we
1057 * want to display it */
1059 /* Check if the chroma is supported first. This is required
1060 * because a few buggy drivers don't mind creating the surface
1061 * even if they don't know about the chroma. */
1063 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
1064 return VLC_EGENERIC;
1066 DWORD *list = calloc(count, sizeof(*list));
1069 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
1071 return VLC_EGENERIC;
1074 for (index = 0; index < count; index++) {
1075 if (list[index] == fourcc)
1080 return VLC_EGENERIC;
1083 LPDIRECTDRAWSURFACE2 surface;
1084 if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
1085 return VLC_EGENERIC;
1086 msg_Dbg(vd, "YUV plain surface created successfully");
1088 if (DirectXCheckLockingSurface(surface, surface)) {
1089 DirectXDestroySurface(surface);
1090 return VLC_EGENERIC;
1094 picture_sys_t *picsys = sys->picsys;
1095 picsys->front_surface = surface;
1096 picsys->surface = surface;
1097 picsys->fallback = NULL;
1100 static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
1101 video_format_t *fmt)
1103 vout_display_sys_t *sys = vd->sys;
1104 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1106 /* Our last choice is to use a plain RGB surface */
1107 DDPIXELFORMAT ddpfPixelFormat;
1108 ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
1109 ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
1111 IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
1112 if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
1113 return VLC_EGENERIC;
1115 switch (ddpfPixelFormat.dwRGBBitCount) {
1117 fmt->i_chroma = VLC_CODEC_RGB8;
1120 fmt->i_chroma = VLC_CODEC_RGB15;
1123 fmt->i_chroma = VLC_CODEC_RGB16;
1126 fmt->i_chroma = VLC_CODEC_RGB24;
1129 fmt->i_chroma = VLC_CODEC_RGB32;
1132 msg_Err(vd, "unknown screen depth");
1133 return VLC_EGENERIC;
1135 fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
1136 fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
1137 fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
1140 LPDIRECTDRAWSURFACE2 surface;
1141 int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
1142 if (ret && !allow_sysmem)
1143 ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
1145 return VLC_EGENERIC;
1146 msg_Dbg(vd, "RGB plain surface created successfully");
1148 if (DirectXCheckLockingSurface(surface, surface)) {
1149 DirectXDestroySurface(surface);
1150 return VLC_EGENERIC;
1154 picture_sys_t *picsys = sys->picsys;
1155 picsys->front_surface = surface;
1156 picsys->surface = surface;
1157 picsys->fallback = NULL;
1161 static int DirectXCreatePictureResource(vout_display_t *vd,
1163 video_format_t *fmt)
1165 vout_display_sys_t *sys = vd->sys;
1168 picture_sys_t *picsys = calloc(1, sizeof(*picsys));
1169 if (unlikely(picsys == NULL))
1171 sys->picsys = picsys;
1174 bool allow_hw_yuv = sys->can_blit_fourcc &&
1175 vlc_fourcc_IsYUV(fmt->i_chroma) &&
1176 var_InheritBool(vd, "directx-hw-yuv");
1177 bool allow_overlay = var_InheritBool(vd, "overlay");
1179 /* Try to use an yuv surface */
1181 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1182 /* Try with overlay first */
1183 for (unsigned pass = allow_overlay ? 0 : 1; pass < 2; pass++) {
1184 for (unsigned i = 0; list[i] != 0; i++) {
1185 const DWORD fourcc = DirectXGetFourcc(list[i]);
1190 if (DirectXCreatePictureResourceYuvOverlay(vd, fmt, fourcc))
1193 if (DirectXCreatePictureResourceYuv(vd, fmt, fourcc))
1197 *use_overlay = pass == 0;
1198 fmt->i_chroma = list[i];
1205 return DirectXCreatePictureResourceRgb(vd, fmt);
1207 static void DirectXDestroyPictureResource(vout_display_t *vd)
1209 vout_display_sys_t *sys = vd->sys;
1211 if (sys->picsys->front_surface != sys->picsys->surface)
1212 DirectXDestroySurface(sys->picsys->surface);
1213 DirectXDestroySurface(sys->picsys->front_surface);
1214 if (sys->picsys->fallback)
1215 picture_Release(sys->picsys->fallback);
1218 static int DirectXLock(picture_t *picture)
1221 if (DirectXLockSurface(picture->p_sys->front_surface,
1222 picture->p_sys->surface, &ddsd))
1223 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
1225 CommonUpdatePicture(picture, NULL, ddsd.lpSurface, ddsd.lPitch);
1228 static void DirectXUnlock(picture_t *picture)
1230 DirectXUnlockSurface(picture->p_sys->front_surface,
1231 picture->p_sys->surface);
1234 static int DirectXCreatePool(vout_display_t *vd,
1235 bool *use_overlay, video_format_t *fmt)
1237 vout_display_sys_t *sys = vd->sys;
1242 if (DirectXCreatePictureResource(vd, use_overlay, fmt))
1243 return VLC_EGENERIC;
1245 /* Create the associated picture */
1246 picture_resource_t resource = { .p_sys = sys->picsys };
1247 picture_t *picture = picture_NewFromResource(fmt, &resource);
1249 DirectXDestroyPictureResource(vd);
1254 /* Wrap it into a picture pool */
1255 picture_pool_configuration_t cfg;
1256 memset(&cfg, 0, sizeof(cfg));
1257 cfg.picture_count = 1;
1258 cfg.picture = &picture;
1259 cfg.lock = DirectXLock;
1260 cfg.unlock = DirectXUnlock;
1262 sys->pool = picture_pool_NewExtended(&cfg);
1264 picture_Release(picture);
1265 DirectXDestroyPictureResource(vd);
1270 static void DirectXDestroyPool(vout_display_t *vd)
1272 vout_display_sys_t *sys = vd->sys;
1275 DirectXDestroyPictureResource(vd);
1276 picture_pool_Delete(sys->pool);
1282 * Move or resize overlay surface on video display.
1284 * This function is used to move or resize an overlay surface on the screen.
1285 * Ususally the overlay is moved by the user and thus, by a move or resize
1288 static int DirectXUpdateOverlay(vout_display_t *vd, LPDIRECTDRAWSURFACE2 surface)
1290 vout_display_sys_t *sys = vd->sys;
1292 RECT src = sys->rect_src_clipped;
1293 RECT dst = sys->rect_dest_clipped;
1295 if (sys->use_wallpaper) {
1296 src.left = vd->source.i_x_offset;
1297 src.top = vd->source.i_y_offset;
1298 src.right = vd->source.i_x_offset + vd->source.i_visible_width;
1299 src.bottom = vd->source.i_y_offset + vd->source.i_visible_height;
1300 AlignRect(&src, sys->i_align_src_boundary, sys->i_align_src_size);
1302 vout_display_cfg_t cfg = *vd->cfg;
1303 cfg.display.width = sys->rect_display.right;
1304 cfg.display.height = sys->rect_display.bottom;
1306 vout_display_place_t place;
1307 vout_display_PlacePicture(&place, &vd->source, &cfg, true);
1309 dst.left = sys->rect_display.left + place.x;
1310 dst.top = sys->rect_display.top + place.y;
1311 dst.right = dst.left + place.width;
1312 dst.bottom = dst.top + place.height;
1313 AlignRect(&dst, sys->i_align_dest_boundary, sys->i_align_dest_size);
1318 return VLC_EGENERIC;
1319 surface = sys->picsys->front_surface;
1322 /* The new window dimensions should already have been computed by the
1323 * caller of this function */
1325 /* Position and show the overlay */
1327 ZeroMemory(&ddofx, sizeof(ddofx));
1328 ddofx.dwSize = sizeof(ddofx);
1329 ddofx.dckDestColorkey.dwColorSpaceLowValue = sys->i_colorkey;
1330 ddofx.dckDestColorkey.dwColorSpaceHighValue = sys->i_colorkey;
1332 HRESULT hr = IDirectDrawSurface2_UpdateOverlay(surface,
1333 &src, sys->display, &dst,
1334 DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx);
1335 sys->restore_overlay = hr != DD_OK;
1338 msg_Warn(vd, "DirectDrawUpdateOverlay cannot move/resize overlay");
1339 return VLC_EGENERIC;
1345 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper)
1347 vout_display_sys_t *sys = vd->sys;
1349 if (!sys->use_wallpaper == !use_wallpaper)
1352 HWND hwnd = FindWindow(_T("Progman"), NULL);
1354 hwnd = FindWindowEx(hwnd, NULL, _T("SHELLDLL_DefView"), NULL);
1356 hwnd = FindWindowEx(hwnd, NULL, _T("SysListView32"), NULL);
1358 msg_Warn(vd, "couldn't find \"SysListView32\" window, "
1359 "wallpaper mode not supported");
1363 msg_Dbg(vd, "wallpaper mode %s", use_wallpaper ? "enabled" : "disabled");
1364 sys->use_wallpaper = use_wallpaper;
1366 if (sys->use_wallpaper) {
1367 sys->color_bkg = ListView_GetBkColor(hwnd);
1368 sys->color_bkgtxt = ListView_GetTextBkColor(hwnd);
1370 ListView_SetBkColor(hwnd, sys->i_rgb_colorkey);
1371 ListView_SetTextBkColor(hwnd, sys->i_rgb_colorkey);
1373 ListView_SetBkColor(hwnd, sys->color_bkg);
1374 ListView_SetTextBkColor(hwnd, sys->color_bkgtxt);
1377 /* Update desktop */
1378 InvalidateRect(hwnd, NULL, TRUE);
1381 if (sys->use_overlay)
1382 DirectXUpdateOverlay(vd, NULL);
1386 static int WallpaperCallback(vlc_object_t *object, char const *cmd,
1387 vlc_value_t oldval, vlc_value_t newval, void *data)
1389 vout_display_t *vd = (vout_display_t *)object;
1390 vout_display_sys_t *sys = vd->sys;
1391 VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
1393 vlc_mutex_lock(&sys->lock);
1394 const bool ch_wallpaper = !sys->wallpaper_requested != !newval.b_bool;
1395 sys->ch_wallpaper |= ch_wallpaper;
1396 sys->wallpaper_requested = newval.b_bool;
1397 vlc_mutex_unlock(&sys->lock);
1408 /*****************************************************************************
1409 * config variable callback
1410 *****************************************************************************/
1411 static BOOL WINAPI DirectXEnumCallback2(GUID *guid, LPSTR desc,
1412 LPSTR drivername, VOID *data,
1415 enum_context_t *ctx = data;
1417 VLC_UNUSED(guid); VLC_UNUSED(desc); VLC_UNUSED(hmon);
1419 char *psz_drivername = drivername;
1420 ctx->values = xrealloc(ctx->values, (ctx->count + 1) * sizeof(char *));
1421 ctx->descs = xrealloc(ctx->descs, (ctx->count + 1) * sizeof(char *));
1423 ctx->values[ctx->count] = strdup(psz_drivername);
1424 ctx->descs[ctx->count] = strdup(psz_drivername);
1427 return TRUE; /* Keep enumerating */
1430 static int FindDevicesCallback(vlc_object_t *object, const char *name,
1431 char ***values, char ***descs)
1435 ctx.values = xmalloc(sizeof(char *));
1436 ctx.descs = xmalloc(sizeof(char *));
1437 ctx.values[0] = strdup("");
1438 ctx.descs[0] = strdup(_("Default"));
1441 /* Load direct draw DLL */
1442 HINSTANCE hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1443 if (hddraw_dll != NULL)
1445 /* Enumerate displays */
1446 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA,
1448 (void *)GetProcAddress(hddraw_dll, DIRECTDRAWENUMERATEEX_NAME);
1449 if (OurDirectDrawEnumerateEx != NULL)
1450 OurDirectDrawEnumerateEx(DirectXEnumCallback2, &ctx,
1451 DDENUM_ATTACHEDSECONDARYDEVICES);
1452 FreeLibrary(hddraw_dll);
1458 *values = ctx.values;