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 *****************************************************************************/
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 #include <vlc_charset.h>
51 #include <commctrl.h> /* ListView_(Get|Set)* */
56 # error "Unicode mode not supported"
59 /*****************************************************************************
61 *****************************************************************************/
62 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
63 #define HW_YUV_LONGTEXT N_(\
64 "Try to use hardware acceleration for YUV->RGB conversions. " \
65 "This option doesn't have any effect when using overlays.")
67 #define SYSMEM_TEXT N_("Use video buffers in system memory")
68 #define SYSMEM_LONGTEXT N_(\
69 "Create video buffers in system memory instead of video memory. This " \
70 "isn't recommended as usually using video memory allows benefiting from " \
71 "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
72 "This option doesn't have any effect when using overlays.")
74 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
75 #define TRIPLEBUF_LONGTEXT N_(\
76 "Try to use triple buffering when using YUV overlays. That results in " \
77 "much better video quality (no flickering).")
79 #define DEVICE_TEXT N_("Name of desired display device")
80 #define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
81 "specify the Windows device name of the display that you want the video " \
82 "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
83 "\"\\\\.\\DISPLAY2\".")
85 #define DX_HELP N_("Recommended video output for Windows XP. " \
86 "Incompatible with Vista's Aero interface" )
88 static int Open (vlc_object_t *);
89 static void Close(vlc_object_t *);
91 static int FindDevicesCallback(vlc_object_t *, const char *,
94 set_shortname("DirectX")
95 set_description(N_("DirectX (DirectDraw) video output"))
97 set_category(CAT_VIDEO)
98 set_subcategory(SUBCAT_VIDEO_VOUT)
99 add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
101 add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
103 add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
104 TRIPLEBUF_LONGTEXT, true)
105 add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
106 change_string_cb(FindDevicesCallback)
108 set_capability("vout display", 230)
109 add_shortcut("directx")
110 set_callbacks(Open, Close)
113 /*****************************************************************************
115 *****************************************************************************/
117 struct picture_sys_t {
118 LPDIRECTDRAWSURFACE2 surface;
119 LPDIRECTDRAWSURFACE2 front_surface;
123 /*****************************************************************************
125 * Defining them here allows us to get rid of the dxguid library during
127 *****************************************************************************/
128 #include <initguid.h>
131 DEFINE_GUID(IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56);
132 DEFINE_GUID(IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27);
134 static picture_pool_t *Pool (vout_display_t *, unsigned);
135 static void Display(vout_display_t *, picture_t *, subpicture_t *);
136 static int Control(vout_display_t *, int, va_list);
137 static void Manage (vout_display_t *);
140 static int WallpaperCallback(vlc_object_t *, char const *,
141 vlc_value_t, vlc_value_t, void *);
143 static int DirectXOpen(vout_display_t *, video_format_t *fmt);
144 static void DirectXClose(vout_display_t *);
146 static int DirectXLock(picture_t *);
147 static void DirectXUnlock(picture_t *);
149 static int DirectXUpdateOverlay(vout_display_t *, LPDIRECTDRAWSURFACE2 surface);
151 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper);
153 /** This function allocates and initialize the DirectX vout display.
155 static int Open(vlc_object_t *object)
157 vout_display_t *vd = (vout_display_t *)object;
158 vout_display_sys_t *sys;
160 /* Allocate structure */
161 vd->sys = sys = calloc(1, sizeof(*sys));
165 /* Load direct draw DLL */
166 sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
167 if (!sys->hddraw_dll) {
168 msg_Warn(vd, "DirectXInitDDraw failed loading ddraw.dll");
174 sys->use_wallpaper = var_CreateGetBool(vd, "video-wallpaper");
176 sys->use_overlay = false;//var_CreateGetBool(vd, "overlay"); /* FIXME */
177 sys->restore_overlay = false;
178 var_Create(vd, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
185 video_format_t fmt = vd->fmt;
187 if (DirectXOpen(vd, &fmt))
191 vout_display_info_t info = vd->info;
193 info.has_double_click = true;
194 info.has_hide_mouse = false;
195 info.has_pictures_invalid = true;
196 info.has_event_thread = true;
198 /* Interaction TODO support starting with wallpaper mode */
199 vlc_mutex_init(&sys->lock);
200 sys->ch_wallpaper = sys->use_wallpaper;
201 sys->wallpaper_requested = sys->use_wallpaper;
202 sys->use_wallpaper = false;
205 val.psz_string = _("Wallpaper");
206 var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
207 var_AddCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
209 /* Setup vout_display now that everything is fine */
215 vd->display = Display;
216 vd->control = Control;
224 FreeLibrary(sys->hddraw_dll);
229 /** Terminate a vout display created by Open.
231 static void Close(vlc_object_t *object)
233 vout_display_t *vd = (vout_display_t *)object;
234 vout_display_sys_t *sys = vd->sys;
236 var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
237 vlc_mutex_destroy(&sys->lock);
239 /* Make sure the wallpaper is restored */
240 WallpaperChange(vd, false);
247 FreeLibrary(sys->hddraw_dll);
251 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
254 return vd->sys->pool;
256 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
258 vout_display_sys_t *sys = vd->sys;
260 assert(sys->display);
262 /* Our surface can be lost so be sure to check this
263 * and restore it if need be */
264 if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
265 if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
266 if (sys->use_overlay)
267 DirectXUpdateOverlay(vd, NULL);
270 if (sys->restore_overlay)
271 DirectXUpdateOverlay(vd, NULL);
274 DirectXUnlock(picture);
276 if (sys->use_overlay) {
277 /* Flip the overlay buffers if we are using back buffers */
278 if (picture->p_sys->surface != picture->p_sys->front_surface) {
279 HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
282 msg_Warn(vd, "could not flip overlay (error %li)", hr);
285 /* Blit video surface to display with the NOTEARING option */
287 ZeroMemory(&ddbltfx, sizeof(ddbltfx));
288 ddbltfx.dwSize = sizeof(ddbltfx);
289 ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
291 HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
292 &sys->rect_dest_clipped,
293 picture->p_sys->surface,
294 &sys->rect_src_clipped,
295 DDBLT_ASYNC, &ddbltfx);
297 msg_Warn(vd, "could not blit surface (error %li)", hr);
299 DirectXLock(picture);
301 if (sys->is_first_display) {
302 IDirectDraw_WaitForVerticalBlank(sys->ddobject,
303 DDWAITVB_BLOCKBEGIN, NULL);
304 if (sys->use_overlay) {
305 HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
306 /* set the colorkey as the backgound brush for the video window */
307 SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
312 picture_Release(picture);
313 VLC_UNUSED(subpicture);
315 static int Control(vout_display_t *vd, int query, va_list args)
317 vout_display_sys_t *sys = vd->sys;
320 case VOUT_DISPLAY_RESET_PICTURES:
322 /* Make sure the wallpaper is restored */
323 if (sys->use_wallpaper) {
324 vlc_mutex_lock(&sys->lock);
325 if (!sys->ch_wallpaper) {
326 sys->ch_wallpaper = true;
327 sys->wallpaper_requested = true;
329 vlc_mutex_unlock(&sys->lock);
331 WallpaperChange(vd, false);
333 return DirectXOpen(vd, &vd->fmt);
335 return CommonControl(vd, query, args);
338 static void Manage(vout_display_t *vd)
340 vout_display_sys_t *sys = vd->sys;
344 if (sys->changes & DX_POSITION_CHANGE) {
346 if (sys->use_overlay)
347 DirectXUpdateOverlay(vd, NULL);
349 /* Check if we are still on the same monitor */
350 HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
351 if (sys->hmonitor != hmon) {
352 vout_display_SendEventPicturesInvalid(vd);
355 sys->changes &= ~DX_POSITION_CHANGE;
358 /* Wallpaper mode change */
359 vlc_mutex_lock(&sys->lock);
360 const bool ch_wallpaper = sys->ch_wallpaper;
361 const bool wallpaper_requested = sys->wallpaper_requested;
362 sys->ch_wallpaper = false;
363 vlc_mutex_unlock(&sys->lock);
366 WallpaperChange(vd, wallpaper_requested);
369 if (sys->restore_overlay)
370 DirectXUpdateOverlay(vd, NULL);
374 static int DirectXOpenDDraw(vout_display_t *);
375 static void DirectXCloseDDraw(vout_display_t *);
377 static int DirectXOpenDisplay(vout_display_t *vd);
378 static void DirectXCloseDisplay(vout_display_t *vd);
380 static int DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
381 static void DirectXDestroyPool(vout_display_t *);
383 static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
385 vout_display_sys_t *sys = vd->sys;
387 assert(!sys->ddobject);
388 assert(!sys->display);
389 assert(!sys->clipper);
391 /* Initialise DirectDraw */
392 if (DirectXOpenDDraw(vd)) {
393 msg_Err(vd, "cannot initialize DirectX DirectDraw");
397 /* Create the directx display */
398 if (DirectXOpenDisplay(vd)) {
399 msg_Err(vd, "cannot initialize DirectX DirectDraw");
402 UpdateRects(vd, NULL, NULL, true);
404 /* Create the picture pool */
405 if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
406 msg_Err(vd, "cannot create any DirectX surface");
411 if (sys->use_overlay)
412 DirectXUpdateOverlay(vd, NULL);
413 EventThreadUseOverlay(sys->event, sys->use_overlay);
415 /* Change the window title bar text */
416 const char *fallback;
417 if (sys->use_overlay)
418 fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
419 else if (vlc_fourcc_IsYUV(fmt->i_chroma))
420 fallback = VOUT_TITLE " (hardware YUV DirectX output)";
422 fallback = VOUT_TITLE " (software RGB DirectX output)";
423 EventThreadUpdateTitle(sys->event, fallback);
427 static void DirectXClose(vout_display_t *vd)
429 DirectXDestroyPool(vd);
430 DirectXCloseDisplay(vd);
431 DirectXCloseDDraw(vd);
435 static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPTSTR desc,
436 LPTSTR drivername, VOID *context,
439 vout_display_t *vd = context;
440 vout_display_sys_t *sys = vd->sys;
442 /* This callback function is called by DirectDraw once for each
443 * available DirectDraw device.
445 * Returning TRUE keeps enumerating.
450 msg_Dbg(vd, "DirectXEnumCallback: %s, %s", desc, drivername);
452 char *device = var_GetString(vd, "directx-device");
454 /* Check for forced device */
455 if (device && *device && !strcmp(drivername, device)) {
456 MONITORINFO monitor_info;
457 monitor_info.cbSize = sizeof(MONITORINFO);
459 if (GetMonitorInfoA(hmon, &monitor_info)) {
462 /* Move window to the right screen */
463 GetWindowRect(sys->hwnd, &rect);
464 if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
465 rect.left = monitor_info.rcWork.left;
466 rect.top = monitor_info.rcWork.top;
467 msg_Dbg(vd, "DirectXEnumCallback: setting window "
468 "position to %ld,%ld", rect.left, rect.top);
469 SetWindowPos(sys->hwnd, NULL,
470 rect.left, rect.top, 0, 0,
471 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
474 sys->hmonitor = hmon;
478 if (hmon == sys->hmonitor) {
479 msg_Dbg(vd, "selecting %s, %s", desc, drivername);
481 free(sys->display_driver);
482 sys->display_driver = malloc(sizeof(*guid));
483 if (sys->display_driver)
484 *sys->display_driver = *guid;
490 * Probe the capabilities of the hardware
492 * It is nice to know which features are supported by the hardware so we can
493 * find ways to optimize our rendering.
495 static void DirectXGetDDrawCaps(vout_display_t *vd)
497 vout_display_sys_t *sys = vd->sys;
499 /* This is just an indication of whether or not we'll support overlay,
500 * but with this test we don't know if we support YUV overlay */
502 ZeroMemory(&ddcaps, sizeof(ddcaps));
503 ddcaps.dwSize = sizeof(ddcaps);
504 HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
506 msg_Warn(vd, "cannot get caps");
510 /* Determine if the hardware supports overlay surfaces */
511 const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
512 /* Determine if the hardware supports overlay surfaces */
513 const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
514 /* Determine if the hardware supports overlay deinterlacing */
515 const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
516 /* Determine if the hardware supports colorkeying */
517 const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
518 /* Determine if the hardware supports scaling of the overlay surface */
519 const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
520 /* Determine if the hardware supports color conversion during a blit */
521 sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
522 /* Determine overlay source boundary alignment */
523 const bool align_boundary_src = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
524 /* Determine overlay destination boundary alignment */
525 const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
526 /* Determine overlay destination size alignment */
527 const bool align_size_src = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
528 /* Determine overlay destination size alignment */
529 const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;
531 msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
532 "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
534 has_overlay, has_overlay_fourcc, can_deinterlace,
535 has_color_key, can_stretch, sys->can_blit_fourcc);
537 if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
538 if (align_boundary_src)
539 vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
540 if (align_boundary_dest)
541 vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
543 vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
545 vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;
548 "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
549 "align_size_src=%i,%i align_size_dest=%i,%i",
550 align_boundary_src, vd->sys->i_align_src_boundary,
551 align_boundary_dest, vd->sys->i_align_dest_boundary,
552 align_size_src, vd->sys->i_align_src_size,
553 align_size_dest, vd->sys->i_align_dest_size);
560 static int DirectXOpenDDraw(vout_display_t *vd)
562 vout_display_sys_t *sys = vd->sys;
566 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
567 OurDirectDrawCreate =
568 (void *)GetProcAddress(sys->hddraw_dll, _T("DirectDrawCreate"));
569 if (!OurDirectDrawCreate) {
570 msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
575 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
576 OurDirectDrawEnumerateEx =
577 (void *)GetProcAddress(sys->hddraw_dll, _T("DirectDrawEnumerateExA"));
579 if (OurDirectDrawEnumerateEx) {
580 char *device = var_GetString(vd, "directx-device");
582 msg_Dbg(vd, "directx-device: %s", device);
586 sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
588 /* Enumerate displays */
589 OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
590 vd, DDENUM_ATTACHEDSECONDARYDEVICES);
593 /* Initialize DirectDraw now */
594 LPDIRECTDRAW ddobject;
595 hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
597 msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
601 /* Get the IDirectDraw2 interface */
603 hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
605 /* Release the unused interface */
606 IDirectDraw_Release(ddobject);
609 msg_Err(vd, "cannot get IDirectDraw2 interface");
610 sys->ddobject = NULL;
615 /* Set DirectDraw Cooperative level, ie what control we want over Windows
617 hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
619 msg_Err(vd, "cannot set direct draw cooperative level");
623 /* Get the size of the current display device */
625 MONITORINFO monitor_info;
626 monitor_info.cbSize = sizeof(MONITORINFO);
627 GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
628 sys->rect_display = monitor_info.rcMonitor;
630 sys->rect_display.left = 0;
631 sys->rect_display.top = 0;
632 sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
633 sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
636 msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
637 sys->rect_display.left,
638 sys->rect_display.top,
639 sys->rect_display.right,
640 sys->rect_display.bottom);
642 /* Probe the capabilities of the hardware */
643 DirectXGetDDrawCaps(vd);
648 static void DirectXCloseDDraw(vout_display_t *vd)
650 vout_display_sys_t *sys = vd->sys;
652 IDirectDraw2_Release(sys->ddobject);
654 sys->ddobject = NULL;
656 free(sys->display_driver);
657 sys->display_driver = NULL;
659 sys->hmonitor = NULL;
663 * Create a clipper that will be used when blitting the RGB surface to the main display.
665 * This clipper prevents us to modify by mistake anything on the screen
666 * which doesn't belong to our window. For example when a part of our video
667 * window is hidden by another window.
669 static void DirectXCreateClipper(vout_display_t *vd)
671 vout_display_sys_t *sys = vd->sys;
674 /* Create the clipper */
675 hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
677 msg_Warn(vd, "cannot create clipper (error %li)", hr);
681 /* Associate the clipper to the window */
682 hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
684 msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
688 /* associate the clipper with the surface */
689 hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
692 msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
700 IDirectDrawClipper_Release(sys->clipper);
705 * It finds out the 32bits RGB pixel value of the colorkey.
707 static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
709 vout_display_sys_t *sys = vd->sys;
714 ddsd.dwSize = sizeof(ddsd);
715 hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
719 uint32_t backup = *(uint32_t *)ddsd.lpSurface;
721 switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
723 *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
726 *(uint8_t *)ddsd.lpSurface = *color;
730 *(uint16_t *)ddsd.lpSurface = *color;
733 /* Seems to be problematic so we'll just put black as the colorkey */
736 *(uint32_t *)ddsd.lpSurface = *color;
739 IDirectDrawSurface2_Unlock(sys->display, NULL);
744 if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
745 rgb = GetPixel(hdc, 0, 0);
746 IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
751 /* Restore the pixel value */
752 ddsd.dwSize = sizeof(ddsd);
753 if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
754 *(uint32_t *)ddsd.lpSurface = backup;
755 IDirectDrawSurface2_Unlock(sys->display, NULL);
762 * Create and initialize display according to preferences specified in the vout
765 static int DirectXOpenDisplay(vout_display_t *vd)
767 vout_display_sys_t *sys = vd->sys;
770 /* Now get the primary surface. This surface is what you actually see
773 ZeroMemory(&ddsd, sizeof(ddsd));
774 ddsd.dwSize = sizeof(ddsd);
775 ddsd.dwFlags = DDSD_CAPS;
776 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
778 LPDIRECTDRAWSURFACE display;
779 hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
781 msg_Err(vd, "cannot get primary surface (error %li)", hr);
786 hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
788 /* Release the old interface */
789 IDirectDrawSurface_Release(display);
792 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
798 /* The clipper will be used only in non-overlay mode */
799 DirectXCreateClipper(vd);
801 /* Make sure the colorkey will be painted */
803 sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);
807 static void DirectXCloseDisplay(vout_display_t *vd)
809 vout_display_sys_t *sys = vd->sys;
811 if (sys->clipper != NULL)
812 IDirectDrawClipper_Release(sys->clipper);
814 if (sys->display != NULL)
815 IDirectDrawSurface2_Release(sys->display);
822 * Create an YUV overlay or RGB surface for the video.
824 * The best method of display is with an YUV overlay because the YUV->RGB
825 * conversion is done in hardware.
826 * You can also create a plain RGB surface.
827 * (Maybe we could also try an RGB overlay surface, which could have hardware
828 * scaling and which would also be faster in window mode because you don't
829 * need to do any blitting to the main display...)
831 static int DirectXCreateSurface(vout_display_t *vd,
832 LPDIRECTDRAWSURFACE2 *surface,
833 const video_format_t *fmt,
837 int backbuffer_count)
839 vout_display_sys_t *sys = vd->sys;
843 ZeroMemory(&ddsd, sizeof(ddsd));
844 ddsd.dwSize = sizeof(ddsd);
845 ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
846 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
847 ddsd.dwWidth = fmt->i_width;
848 ddsd.dwHeight = fmt->i_height;
850 ddsd.dwFlags |= DDSD_PIXELFORMAT;
851 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
852 ddsd.ddpfPixelFormat.dwFourCC = fourcc;
855 ddsd.dwFlags |= DDSD_CAPS;
856 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
857 if (backbuffer_count > 0)
858 ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
860 if (backbuffer_count > 0) {
861 ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
862 ddsd.dwBackBufferCount = backbuffer_count;
865 ddsd.dwFlags |= DDSD_CAPS;
866 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
868 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
870 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
873 /* Create the video surface */
874 LPDIRECTDRAWSURFACE surface_v1;
875 if (IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL) != DD_OK)
878 /* Now that the surface is created, try to get a newer DirectX interface */
879 HRESULT hr = IDirectDrawSurface_QueryInterface(surface_v1,
880 &IID_IDirectDrawSurface2,
882 IDirectDrawSurface_Release(surface_v1);
884 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
889 /* Check the overlay is useable as some graphics cards allow creating
890 * several overlays but only one can be used at one time. */
891 if (DirectXUpdateOverlay(vd, *surface)) {
892 IDirectDrawSurface2_Release(*surface);
893 msg_Err(vd, "overlay unuseable (might already be in use)");
901 static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
903 IDirectDrawSurface2_Release(surface);
906 * This function locks a surface and get the surface descriptor.
908 static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
909 LPDIRECTDRAWSURFACE2 surface,
914 DDSURFACEDESC ddsd_dummy;
918 ZeroMemory(ddsd, sizeof(*ddsd));
919 ddsd->dwSize = sizeof(*ddsd);
920 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
922 if (hr == DDERR_INVALIDPARAMS) {
923 /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
924 * in an invalid params error */
925 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
927 if (hr == DDERR_SURFACELOST) {
928 /* Your surface can be lost so be sure
929 * to check this and restore it if needed */
931 /* When using overlays with back-buffers, we need to restore
932 * the front buffer so the back-buffers get restored as well. */
933 if (front_surface != surface)
934 IDirectDrawSurface2_Restore(front_surface);
936 IDirectDrawSurface2_Restore(surface);
938 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
945 static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
946 LPDIRECTDRAWSURFACE2 surface)
948 VLC_UNUSED(front_surface);
949 IDirectDrawSurface2_Unlock(surface, NULL);
951 static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
952 LPDIRECTDRAWSURFACE2 surface)
954 if (DirectXLockSurface(front_surface, surface, NULL))
957 DirectXUnlockSurface(front_surface, surface);
968 static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
970 static const dx_format_t dx_formats[] = {
971 { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
972 { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
973 { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
974 { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
975 { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
976 { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
980 for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
981 if (dx_formats[i].codec == codec)
982 return dx_formats[i].fourcc;
987 static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
988 const video_format_t *fmt,
991 vout_display_sys_t *sys = vd->sys;
993 bool allow_3buf = var_InheritBool(vd, "directx-3buffering");
995 /* The overlay surface that we create won't be used to decode directly
996 * into it because accessing video memory directly is way to slow (remember
997 * that pictures are decoded macroblock per macroblock). Instead the video
998 * will be decoded in picture buffers in system memory which will then be
999 * memcpy() to the overlay surface. */
1000 LPDIRECTDRAWSURFACE2 front_surface;
1001 int ret = VLC_EGENERIC;
1003 /* Triple buffering rocks! it doesn't have any processing overhead
1004 * (you don't have to wait for the vsync) and provides for a very nice
1005 * video quality (no tearing). */
1006 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
1009 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
1011 return VLC_EGENERIC;
1012 msg_Dbg(vd, "YUV overlay surface created successfully");
1014 /* Get the back buffer */
1015 LPDIRECTDRAWSURFACE2 surface;
1017 ZeroMemory(&dds_caps, sizeof(dds_caps));
1018 dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1019 if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
1020 msg_Warn(vd, "Failed to get surface back buffer");
1021 /* front buffer is the same as back buffer */
1022 surface = front_surface;
1025 if (DirectXCheckLockingSurface(front_surface, surface)) {
1026 DirectXDestroySurface(front_surface);
1027 return VLC_EGENERIC;
1031 picture_resource_t *rsc = &sys->resource;
1032 rsc->p_sys->front_surface = front_surface;
1033 rsc->p_sys->surface = surface;
1034 rsc->p_sys->fallback = NULL;
1037 static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
1038 const video_format_t *fmt,
1041 vout_display_sys_t *sys = vd->sys;
1043 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1045 /* As we can't have an overlay, we'll try to create a plain offscreen
1046 * surface. This surface will reside in video memory because there's a
1047 * better chance then that we'll be able to use some kind of hardware
1048 * acceleration like rescaling, blitting or YUV->RGB conversions.
1049 * We then only need to blit this surface onto the main display when we
1050 * want to display it */
1052 /* Check if the chroma is supported first. This is required
1053 * because a few buggy drivers don't mind creating the surface
1054 * even if they don't know about the chroma. */
1056 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
1057 return VLC_EGENERIC;
1059 DWORD *list = calloc(count, sizeof(*list));
1062 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
1064 return VLC_EGENERIC;
1067 for (index = 0; index < count; index++) {
1068 if (list[index] == fourcc)
1073 return VLC_EGENERIC;
1076 LPDIRECTDRAWSURFACE2 surface;
1077 if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
1078 return VLC_EGENERIC;
1079 msg_Dbg(vd, "YUV plain surface created successfully");
1081 if (DirectXCheckLockingSurface(surface, surface)) {
1082 DirectXDestroySurface(surface);
1083 return VLC_EGENERIC;
1087 picture_resource_t *rsc = &sys->resource;
1088 rsc->p_sys->front_surface = surface;
1089 rsc->p_sys->surface = surface;
1090 rsc->p_sys->fallback = NULL;
1093 static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
1094 video_format_t *fmt)
1096 vout_display_sys_t *sys = vd->sys;
1097 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1099 /* Our last choice is to use a plain RGB surface */
1100 DDPIXELFORMAT ddpfPixelFormat;
1101 ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
1102 ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
1104 IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
1105 if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
1106 return VLC_EGENERIC;
1108 switch (ddpfPixelFormat.dwRGBBitCount) {
1110 fmt->i_chroma = VLC_CODEC_RGB8;
1113 fmt->i_chroma = VLC_CODEC_RGB15;
1116 fmt->i_chroma = VLC_CODEC_RGB16;
1119 fmt->i_chroma = VLC_CODEC_RGB24;
1122 fmt->i_chroma = VLC_CODEC_RGB32;
1125 msg_Err(vd, "unknown screen depth");
1126 return VLC_EGENERIC;
1128 fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
1129 fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
1130 fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
1133 LPDIRECTDRAWSURFACE2 surface;
1134 int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
1135 if (ret && !allow_sysmem)
1136 ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
1138 return VLC_EGENERIC;
1139 msg_Dbg(vd, "RGB plain surface created successfully");
1141 if (DirectXCheckLockingSurface(surface, surface)) {
1142 DirectXDestroySurface(surface);
1143 return VLC_EGENERIC;
1147 picture_resource_t *rsc = &sys->resource;
1148 rsc->p_sys->front_surface = surface;
1149 rsc->p_sys->surface = surface;
1150 rsc->p_sys->fallback = NULL;
1154 static int DirectXCreatePictureResource(vout_display_t *vd,
1156 video_format_t *fmt)
1158 vout_display_sys_t *sys = vd->sys;
1161 picture_resource_t *rsc = &sys->resource;
1162 rsc->p_sys = calloc(1, sizeof(*rsc->p_sys));
1167 bool allow_hw_yuv = sys->can_blit_fourcc &&
1168 vlc_fourcc_IsYUV(fmt->i_chroma) &&
1169 var_InheritBool(vd, "directx-hw-yuv");
1170 bool allow_overlay = var_InheritBool(vd, "overlay");
1172 /* Try to use an yuv surface */
1174 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1175 /* Try with overlay first */
1176 for (unsigned pass = allow_overlay ? 0 : 1; pass < 2; pass++) {
1177 for (unsigned i = 0; list[i] != 0; i++) {
1178 const DWORD fourcc = DirectXGetFourcc(list[i]);
1183 if (DirectXCreatePictureResourceYuvOverlay(vd, fmt, fourcc))
1186 if (DirectXCreatePictureResourceYuv(vd, fmt, fourcc))
1190 *use_overlay = pass == 0;
1191 fmt->i_chroma = list[i];
1198 return DirectXCreatePictureResourceRgb(vd, fmt);
1200 static void DirectXDestroyPictureResource(vout_display_t *vd)
1202 vout_display_sys_t *sys = vd->sys;
1204 if (sys->resource.p_sys->front_surface != sys->resource.p_sys->surface)
1205 DirectXDestroySurface(sys->resource.p_sys->surface);
1206 DirectXDestroySurface(sys->resource.p_sys->front_surface);
1207 if (sys->resource.p_sys->fallback)
1208 picture_Release(sys->resource.p_sys->fallback);
1211 static int DirectXLock(picture_t *picture)
1214 if (DirectXLockSurface(picture->p_sys->front_surface,
1215 picture->p_sys->surface, &ddsd))
1216 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
1218 CommonUpdatePicture(picture, NULL, ddsd.lpSurface, ddsd.lPitch);
1221 static void DirectXUnlock(picture_t *picture)
1223 DirectXUnlockSurface(picture->p_sys->front_surface,
1224 picture->p_sys->surface);
1227 static int DirectXCreatePool(vout_display_t *vd,
1228 bool *use_overlay, video_format_t *fmt)
1230 vout_display_sys_t *sys = vd->sys;
1235 if (DirectXCreatePictureResource(vd, use_overlay, fmt))
1236 return VLC_EGENERIC;
1238 /* Create the associated picture */
1239 picture_resource_t *rsc = &sys->resource;
1240 for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
1241 rsc->p[i].p_pixels = NULL;
1242 rsc->p[i].i_pitch = 0;
1243 rsc->p[i].i_lines = 0;
1245 picture_t *picture = picture_NewFromResource(fmt, rsc);
1247 DirectXDestroyPictureResource(vd);
1252 /* Wrap it into a picture pool */
1253 picture_pool_configuration_t cfg;
1254 memset(&cfg, 0, sizeof(cfg));
1255 cfg.picture_count = 1;
1256 cfg.picture = &picture;
1257 cfg.lock = DirectXLock;
1258 cfg.unlock = DirectXUnlock;
1260 sys->pool = picture_pool_NewExtended(&cfg);
1262 picture_Release(picture);
1263 DirectXDestroyPictureResource(vd);
1268 static void DirectXDestroyPool(vout_display_t *vd)
1270 vout_display_sys_t *sys = vd->sys;
1273 DirectXDestroyPictureResource(vd);
1274 picture_pool_Delete(sys->pool);
1280 * Move or resize overlay surface on video display.
1282 * This function is used to move or resize an overlay surface on the screen.
1283 * Ususally the overlay is moved by the user and thus, by a move or resize
1286 static int DirectXUpdateOverlay(vout_display_t *vd, LPDIRECTDRAWSURFACE2 surface)
1288 vout_display_sys_t *sys = vd->sys;
1290 RECT src = sys->rect_src_clipped;
1291 RECT dst = sys->rect_dest_clipped;
1293 if (sys->use_wallpaper) {
1294 src.left = vd->source.i_x_offset;
1295 src.top = vd->source.i_y_offset;
1296 src.right = vd->source.i_x_offset + vd->source.i_visible_width;
1297 src.bottom = vd->source.i_y_offset + vd->source.i_visible_height;
1298 AlignRect(&src, sys->i_align_src_boundary, sys->i_align_src_size);
1300 vout_display_cfg_t cfg = *vd->cfg;
1301 cfg.display.width = sys->rect_display.right;
1302 cfg.display.height = sys->rect_display.bottom;
1304 vout_display_place_t place;
1305 vout_display_PlacePicture(&place, &vd->source, &cfg, true);
1307 dst.left = sys->rect_display.left + place.x;
1308 dst.top = sys->rect_display.top + place.y;
1309 dst.right = dst.left + place.width;
1310 dst.bottom = dst.top + place.height;
1311 AlignRect(&dst, sys->i_align_dest_boundary, sys->i_align_dest_size);
1316 return VLC_EGENERIC;
1317 surface = sys->resource.p_sys->front_surface;
1320 /* The new window dimensions should already have been computed by the
1321 * caller of this function */
1323 /* Position and show the overlay */
1325 ZeroMemory(&ddofx, sizeof(ddofx));
1326 ddofx.dwSize = sizeof(ddofx);
1327 ddofx.dckDestColorkey.dwColorSpaceLowValue = sys->i_colorkey;
1328 ddofx.dckDestColorkey.dwColorSpaceHighValue = sys->i_colorkey;
1330 HRESULT hr = IDirectDrawSurface2_UpdateOverlay(surface,
1331 &src, sys->display, &dst,
1332 DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx);
1333 sys->restore_overlay = hr != DD_OK;
1336 msg_Warn(vd, "DirectDrawUpdateOverlay cannot move/resize overlay");
1337 return VLC_EGENERIC;
1343 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper)
1345 vout_display_sys_t *sys = vd->sys;
1347 if (!sys->use_wallpaper == !use_wallpaper)
1350 HWND hwnd = FindWindow(_T("Progman"), NULL);
1352 hwnd = FindWindowEx(hwnd, NULL, _T("SHELLDLL_DefView"), NULL);
1354 hwnd = FindWindowEx(hwnd, NULL, _T("SysListView32"), NULL);
1356 msg_Warn(vd, "couldn't find \"SysListView32\" window, "
1357 "wallpaper mode not supported");
1361 msg_Dbg(vd, "wallpaper mode %s", use_wallpaper ? "enabled" : "disabled");
1362 sys->use_wallpaper = use_wallpaper;
1364 if (sys->use_wallpaper) {
1365 sys->color_bkg = ListView_GetBkColor(hwnd);
1366 sys->color_bkgtxt = ListView_GetTextBkColor(hwnd);
1368 ListView_SetBkColor(hwnd, sys->i_rgb_colorkey);
1369 ListView_SetTextBkColor(hwnd, sys->i_rgb_colorkey);
1371 ListView_SetBkColor(hwnd, sys->color_bkg);
1372 ListView_SetTextBkColor(hwnd, sys->color_bkgtxt);
1375 /* Update desktop */
1376 InvalidateRect(hwnd, NULL, TRUE);
1379 if (sys->use_overlay)
1380 DirectXUpdateOverlay(vd, NULL);
1384 static int WallpaperCallback(vlc_object_t *object, char const *cmd,
1385 vlc_value_t oldval, vlc_value_t newval, void *data)
1387 vout_display_t *vd = (vout_display_t *)object;
1388 vout_display_sys_t *sys = vd->sys;
1389 VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
1391 vlc_mutex_lock(&sys->lock);
1392 const bool ch_wallpaper = !sys->wallpaper_requested != !newval.b_bool;
1393 sys->ch_wallpaper |= ch_wallpaper;
1394 sys->wallpaper_requested = newval.b_bool;
1395 vlc_mutex_unlock(&sys->lock);
1397 /* FIXME we should have a way to export variable to be saved */
1399 playlist_t *p_playlist = pl_Get(vd);
1400 /* Modify playlist as well because the vout might have to be
1402 var_Create(p_playlist, "video-wallpaper", VLC_VAR_BOOL);
1403 var_SetBool(p_playlist, "video-wallpaper", newval.b_bool);
1415 /*****************************************************************************
1416 * config variable callback
1417 *****************************************************************************/
1418 static BOOL WINAPI DirectXEnumCallback2(GUID *guid, LPTSTR desc,
1419 LPTSTR drivername, VOID *data,
1422 enum_context_t *ctx = data;
1424 VLC_UNUSED(guid); VLC_UNUSED(desc); VLC_UNUSED(hmon);
1426 ctx->values = xrealloc(ctx->values, (ctx->count + 1) * sizeof(char *));
1427 ctx->descs = xrealloc(ctx->descs, (ctx->count + 1) * sizeof(char *));
1429 /* TODO? Unicode APIs */
1430 ctx->values[ctx->count] = FromANSI(drivername);
1431 ctx->descs[ctx->count] = FromANSI(drivername);
1434 return TRUE; /* Keep enumerating */
1437 static int FindDevicesCallback(vlc_object_t *object, const char *name,
1438 char ***values, char ***descs)
1442 ctx.values = xmalloc(sizeof(char *));
1443 ctx.descs = xmalloc(sizeof(char *));
1444 ctx.values[0] = strdup("");
1445 ctx.descs[0] = strdup(_("Default"));
1448 /* Load direct draw DLL */
1449 HINSTANCE hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1450 if (hddraw_dll != NULL)
1452 /* Enumerate displays */
1453 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA,
1455 (void *)GetProcAddress(hddraw_dll, _T("DirectDrawEnumerateExA"));
1456 if (OurDirectDrawEnumerateEx != NULL)
1457 OurDirectDrawEnumerateEx(DirectXEnumCallback2, &ctx,
1458 DDENUM_ATTACHEDSECONDARYDEVICES);
1459 FreeLibrary(hddraw_dll);
1465 *values = ctx.values;