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