1 /*****************************************************************************
2 * directx.c: Windows DirectDraw video output
3 *****************************************************************************
4 * Copyright (C) 2001-2009 the VideoLAN team
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 /*****************************************************************************
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 */
50 #include <commctrl.h> /* ListView_(Get|Set)* */
55 # error "Unicode mode not supported"
58 /*****************************************************************************
60 *****************************************************************************/
61 #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions")
62 #define HW_YUV_LONGTEXT N_(\
63 "Try to use hardware acceleration for YUV->RGB conversions. " \
64 "This option doesn't have any effect when using overlays.")
66 #define SYSMEM_TEXT N_("Use video buffers in system memory")
67 #define SYSMEM_LONGTEXT N_(\
68 "Create video buffers in system memory instead of video memory. This " \
69 "isn't recommended as usually using video memory allows benefiting from " \
70 "more hardware acceleration (like rescaling or YUV->RGB conversions). " \
71 "This option doesn't have any effect when using overlays.")
73 #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays")
74 #define TRIPLEBUF_LONGTEXT N_(\
75 "Try to use triple buffering when using YUV overlays. That results in " \
76 "much better video quality (no flickering).")
78 #define DEVICE_TEXT N_("Name of desired display device")
79 #define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \
80 "specify the Windows device name of the display that you want the video " \
81 "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \
82 "\"\\\\.\\DISPLAY2\".")
84 #define DX_HELP N_("Recommended video output for Windows XP. " \
85 "Incompatible with Vista's Aero interface" )
87 static const char * const device[] = { "" };
88 static const char * const device_text[] = { N_("Default") };
90 static int Open (vlc_object_t *);
91 static void Close(vlc_object_t *);
93 static int FindDevicesCallback(vlc_object_t *, char const *,
94 vlc_value_t, vlc_value_t, void *);
96 set_shortname("DirectX")
97 set_description(N_("DirectX (DirectDraw) video output"))
99 set_category(CAT_VIDEO)
100 set_subcategory(SUBCAT_VIDEO_VOUT)
101 add_bool("directx-hw-yuv", true, HW_YUV_TEXT, HW_YUV_LONGTEXT,
103 add_bool("directx-use-sysmem", false, SYSMEM_TEXT, SYSMEM_LONGTEXT,
105 add_bool("directx-3buffering", true, TRIPLEBUF_TEXT,
106 TRIPLEBUF_LONGTEXT, true)
107 add_string("directx-device", "", DEVICE_TEXT, DEVICE_LONGTEXT, true)
108 change_string_list(device, device_text, FindDevicesCallback)
109 change_action_add(FindDevicesCallback, N_("Refresh list"))
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 */
218 vd->display = Display;
219 vd->control = Control;
227 FreeLibrary(sys->hddraw_dll);
232 /** Terminate a vout display created by Open.
234 static void Close(vlc_object_t *object)
236 vout_display_t *vd = (vout_display_t *)object;
237 vout_display_sys_t *sys = vd->sys;
239 var_DelCallback(vd, "video-wallpaper", WallpaperCallback, NULL);
240 vlc_mutex_destroy(&sys->lock);
242 /* Make sure the wallpaper is restored */
243 WallpaperChange(vd, false);
250 FreeLibrary(sys->hddraw_dll);
254 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
257 return vd->sys->pool;
259 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
261 vout_display_sys_t *sys = vd->sys;
263 assert(sys->display);
265 /* Our surface can be lost so be sure to check this
266 * and restore it if need be */
267 if (IDirectDrawSurface2_IsLost(sys->display) == DDERR_SURFACELOST) {
268 if (IDirectDrawSurface2_Restore(sys->display) == DD_OK) {
269 if (sys->use_overlay)
270 DirectXUpdateOverlay(vd, NULL);
273 if (sys->restore_overlay)
274 DirectXUpdateOverlay(vd, NULL);
277 DirectXUnlock(picture);
279 if (sys->use_overlay) {
280 /* Flip the overlay buffers if we are using back buffers */
281 if (picture->p_sys->surface != picture->p_sys->front_surface) {
282 HRESULT hr = IDirectDrawSurface2_Flip(picture->p_sys->front_surface,
285 msg_Warn(vd, "could not flip overlay (error %li)", hr);
288 /* Blit video surface to display with the NOTEARING option */
290 ZeroMemory(&ddbltfx, sizeof(ddbltfx));
291 ddbltfx.dwSize = sizeof(ddbltfx);
292 ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
294 HRESULT hr = IDirectDrawSurface2_Blt(sys->display,
295 &sys->rect_dest_clipped,
296 picture->p_sys->surface,
297 &sys->rect_src_clipped,
298 DDBLT_ASYNC, &ddbltfx);
300 msg_Warn(vd, "could not blit surface (error %li)", hr);
302 DirectXLock(picture);
304 if (sys->is_first_display) {
305 IDirectDraw_WaitForVerticalBlank(sys->ddobject,
306 DDWAITVB_BLOCKBEGIN, NULL);
307 if (sys->use_overlay) {
308 HBRUSH brush = CreateSolidBrush(sys->i_rgb_colorkey);
309 /* set the colorkey as the backgound brush for the video window */
310 SetClassLongPtr(sys->hvideownd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
315 picture_Release(picture);
316 VLC_UNUSED(subpicture);
318 static int Control(vout_display_t *vd, int query, va_list args)
320 vout_display_sys_t *sys = vd->sys;
323 case VOUT_DISPLAY_RESET_PICTURES:
325 /* Make sure the wallpaper is restored */
326 if (sys->use_wallpaper) {
327 vlc_mutex_lock(&sys->lock);
328 if (!sys->ch_wallpaper) {
329 sys->ch_wallpaper = true;
330 sys->wallpaper_requested = true;
332 vlc_mutex_unlock(&sys->lock);
334 WallpaperChange(vd, false);
336 return DirectXOpen(vd, &vd->fmt);
338 return CommonControl(vd, query, args);
341 static void Manage(vout_display_t *vd)
343 vout_display_sys_t *sys = vd->sys;
347 if (sys->changes & DX_POSITION_CHANGE) {
349 if (sys->use_overlay)
350 DirectXUpdateOverlay(vd, NULL);
352 /* Check if we are still on the same monitor */
353 HMONITOR hmon = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
354 if (sys->hmonitor != hmon) {
355 vout_display_SendEventPicturesInvalid(vd);
358 sys->changes &= ~DX_POSITION_CHANGE;
361 /* Wallpaper mode change */
362 vlc_mutex_lock(&sys->lock);
363 const bool ch_wallpaper = sys->ch_wallpaper;
364 const bool wallpaper_requested = sys->wallpaper_requested;
365 sys->ch_wallpaper = false;
366 vlc_mutex_unlock(&sys->lock);
369 WallpaperChange(vd, wallpaper_requested);
372 if (sys->restore_overlay)
373 DirectXUpdateOverlay(vd, NULL);
377 static int DirectXOpenDDraw(vout_display_t *);
378 static void DirectXCloseDDraw(vout_display_t *);
380 static int DirectXOpenDisplay(vout_display_t *vd);
381 static void DirectXCloseDisplay(vout_display_t *vd);
383 static int DirectXCreatePool(vout_display_t *, bool *, video_format_t *);
384 static void DirectXDestroyPool(vout_display_t *);
386 static int DirectXOpen(vout_display_t *vd, video_format_t *fmt)
388 vout_display_sys_t *sys = vd->sys;
390 assert(!sys->ddobject);
391 assert(!sys->display);
392 assert(!sys->clipper);
394 /* Initialise DirectDraw */
395 if (DirectXOpenDDraw(vd)) {
396 msg_Err(vd, "cannot initialize DirectX DirectDraw");
400 /* Create the directx display */
401 if (DirectXOpenDisplay(vd)) {
402 msg_Err(vd, "cannot initialize DirectX DirectDraw");
405 UpdateRects(vd, NULL, NULL, true);
407 /* Create the picture pool */
408 if (DirectXCreatePool(vd, &sys->use_overlay, fmt)) {
409 msg_Err(vd, "cannot create any DirectX surface");
414 if (sys->use_overlay)
415 DirectXUpdateOverlay(vd, NULL);
416 EventThreadUseOverlay(sys->event, sys->use_overlay);
418 /* Change the window title bar text */
419 const char *fallback;
420 if (sys->use_overlay)
421 fallback = VOUT_TITLE " (hardware YUV overlay DirectX output)";
422 else if (vlc_fourcc_IsYUV(fmt->i_chroma))
423 fallback = VOUT_TITLE " (hardware YUV DirectX output)";
425 fallback = VOUT_TITLE " (software RGB DirectX output)";
426 EventThreadUpdateTitle(sys->event, fallback);
430 static void DirectXClose(vout_display_t *vd)
432 DirectXDestroyPool(vd);
433 DirectXCloseDisplay(vd);
434 DirectXCloseDDraw(vd);
438 static BOOL WINAPI DirectXOpenDDrawCallback(GUID *guid, LPTSTR desc,
439 LPTSTR drivername, VOID *context,
442 vout_display_t *vd = context;
443 vout_display_sys_t *sys = vd->sys;
445 /* This callback function is called by DirectDraw once for each
446 * available DirectDraw device.
448 * Returning TRUE keeps enumerating.
453 msg_Dbg(vd, "DirectXEnumCallback: %s, %s", desc, drivername);
455 char *device = var_GetString(vd, "directx-device");
457 /* Check for forced device */
458 if (device && *device && !strcmp(drivername, device)) {
459 MONITORINFO monitor_info;
460 monitor_info.cbSize = sizeof(MONITORINFO);
462 if (GetMonitorInfoA(hmon, &monitor_info)) {
465 /* Move window to the right screen */
466 GetWindowRect(sys->hwnd, &rect);
467 if (!IntersectRect(&rect, &rect, &monitor_info.rcWork)) {
468 rect.left = monitor_info.rcWork.left;
469 rect.top = monitor_info.rcWork.top;
470 msg_Dbg(vd, "DirectXEnumCallback: setting window "
471 "position to %ld,%ld", rect.left, rect.top);
472 SetWindowPos(sys->hwnd, NULL,
473 rect.left, rect.top, 0, 0,
474 SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
477 sys->hmonitor = hmon;
481 if (hmon == sys->hmonitor) {
482 msg_Dbg(vd, "selecting %s, %s", desc, drivername);
484 free(sys->display_driver);
485 sys->display_driver = malloc(sizeof(*guid));
486 if (sys->display_driver)
487 *sys->display_driver = *guid;
493 * Probe the capabilities of the hardware
495 * It is nice to know which features are supported by the hardware so we can
496 * find ways to optimize our rendering.
498 static void DirectXGetDDrawCaps(vout_display_t *vd)
500 vout_display_sys_t *sys = vd->sys;
502 /* This is just an indication of whether or not we'll support overlay,
503 * but with this test we don't know if we support YUV overlay */
505 ZeroMemory(&ddcaps, sizeof(ddcaps));
506 ddcaps.dwSize = sizeof(ddcaps);
507 HRESULT hr = IDirectDraw2_GetCaps(sys->ddobject, &ddcaps, NULL);
509 msg_Warn(vd, "cannot get caps");
513 /* Determine if the hardware supports overlay surfaces */
514 const bool has_overlay = ddcaps.dwCaps & DDCAPS_OVERLAY;
515 /* Determine if the hardware supports overlay surfaces */
516 const bool has_overlay_fourcc = ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC;
517 /* Determine if the hardware supports overlay deinterlacing */
518 const bool can_deinterlace = ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN;
519 /* Determine if the hardware supports colorkeying */
520 const bool has_color_key = ddcaps.dwCaps & DDCAPS_COLORKEY;
521 /* Determine if the hardware supports scaling of the overlay surface */
522 const bool can_stretch = ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH;
523 /* Determine if the hardware supports color conversion during a blit */
524 sys->can_blit_fourcc = ddcaps.dwCaps & DDCAPS_BLTFOURCC;
525 /* Determine overlay source boundary alignment */
526 const bool align_boundary_src = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC;
527 /* Determine overlay destination boundary alignment */
528 const bool align_boundary_dest = ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST;
529 /* Determine overlay destination size alignment */
530 const bool align_size_src = ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC;
531 /* Determine overlay destination size alignment */
532 const bool align_size_dest = ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST;
534 msg_Dbg(vd, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i "
535 "can_deinterlace_overlay=%i colorkey=%i stretch=%i "
537 has_overlay, has_overlay_fourcc, can_deinterlace,
538 has_color_key, can_stretch, sys->can_blit_fourcc);
540 if (align_boundary_src || align_boundary_dest || align_size_src || align_size_dest) {
541 if (align_boundary_src)
542 vd->sys->i_align_src_boundary = ddcaps.dwAlignBoundarySrc;
543 if (align_boundary_dest)
544 vd->sys->i_align_dest_boundary = ddcaps.dwAlignBoundaryDest;
546 vd->sys->i_align_src_size = ddcaps.dwAlignSizeSrc;
548 vd->sys->i_align_dest_size = ddcaps.dwAlignSizeDest;
551 "align_boundary_src=%i,%i align_boundary_dest=%i,%i "
552 "align_size_src=%i,%i align_size_dest=%i,%i",
553 align_boundary_src, vd->sys->i_align_src_boundary,
554 align_boundary_dest, vd->sys->i_align_dest_boundary,
555 align_size_src, vd->sys->i_align_src_size,
556 align_size_dest, vd->sys->i_align_dest_size);
563 static int DirectXOpenDDraw(vout_display_t *vd)
565 vout_display_sys_t *sys = vd->sys;
569 HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *);
570 OurDirectDrawCreate =
571 (void *)GetProcAddress(sys->hddraw_dll, _T("DirectDrawCreate"));
572 if (!OurDirectDrawCreate) {
573 msg_Err(vd, "DirectXInitDDraw failed GetProcAddress");
578 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA, LPVOID, DWORD);
579 OurDirectDrawEnumerateEx =
580 (void *)GetProcAddress(sys->hddraw_dll, _T("DirectDrawEnumerateExA"));
582 if (OurDirectDrawEnumerateEx) {
583 char *device = var_GetString(vd, "directx-device");
585 msg_Dbg(vd, "directx-device: %s", device);
589 sys->hmonitor = MonitorFromWindow(sys->hwnd, MONITOR_DEFAULTTONEAREST);
591 /* Enumerate displays */
592 OurDirectDrawEnumerateEx(DirectXOpenDDrawCallback,
593 vd, DDENUM_ATTACHEDSECONDARYDEVICES);
596 /* Initialize DirectDraw now */
597 LPDIRECTDRAW ddobject;
598 hr = OurDirectDrawCreate(sys->display_driver, &ddobject, NULL);
600 msg_Err(vd, "DirectXInitDDraw cannot initialize DDraw");
604 /* Get the IDirectDraw2 interface */
606 hr = IDirectDraw_QueryInterface(ddobject, &IID_IDirectDraw2,
608 /* Release the unused interface */
609 IDirectDraw_Release(ddobject);
612 msg_Err(vd, "cannot get IDirectDraw2 interface");
613 sys->ddobject = NULL;
618 /* Set DirectDraw Cooperative level, ie what control we want over Windows
620 hr = IDirectDraw2_SetCooperativeLevel(sys->ddobject, NULL, DDSCL_NORMAL);
622 msg_Err(vd, "cannot set direct draw cooperative level");
626 /* Get the size of the current display device */
628 MONITORINFO monitor_info;
629 monitor_info.cbSize = sizeof(MONITORINFO);
630 GetMonitorInfoA(vd->sys->hmonitor, &monitor_info);
631 sys->rect_display = monitor_info.rcMonitor;
633 sys->rect_display.left = 0;
634 sys->rect_display.top = 0;
635 sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN);
636 sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN);
639 msg_Dbg(vd, "screen dimensions (%lix%li,%lix%li)",
640 sys->rect_display.left,
641 sys->rect_display.top,
642 sys->rect_display.right,
643 sys->rect_display.bottom);
645 /* Probe the capabilities of the hardware */
646 DirectXGetDDrawCaps(vd);
651 static void DirectXCloseDDraw(vout_display_t *vd)
653 vout_display_sys_t *sys = vd->sys;
655 IDirectDraw2_Release(sys->ddobject);
657 sys->ddobject = NULL;
659 free(sys->display_driver);
660 sys->display_driver = NULL;
662 sys->hmonitor = NULL;
666 * Create a clipper that will be used when blitting the RGB surface to the main display.
668 * This clipper prevents us to modify by mistake anything on the screen
669 * which doesn't belong to our window. For example when a part of our video
670 * window is hidden by another window.
672 static void DirectXCreateClipper(vout_display_t *vd)
674 vout_display_sys_t *sys = vd->sys;
677 /* Create the clipper */
678 hr = IDirectDraw2_CreateClipper(sys->ddobject, 0, &sys->clipper, NULL);
680 msg_Warn(vd, "cannot create clipper (error %li)", hr);
684 /* Associate the clipper to the window */
685 hr = IDirectDrawClipper_SetHWnd(sys->clipper, 0, sys->hvideownd);
687 msg_Warn(vd, "cannot attach clipper to window (error %li)", hr);
691 /* associate the clipper with the surface */
692 hr = IDirectDrawSurface_SetClipper(sys->display, sys->clipper);
695 msg_Warn(vd, "cannot attach clipper to surface (error %li)", hr);
703 IDirectDrawClipper_Release(sys->clipper);
708 * It finds out the 32bits RGB pixel value of the colorkey.
710 static uint32_t DirectXFindColorkey(vout_display_t *vd, uint32_t *color)
712 vout_display_sys_t *sys = vd->sys;
717 ddsd.dwSize = sizeof(ddsd);
718 hr = IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL);
722 uint32_t backup = *(uint32_t *)ddsd.lpSurface;
724 switch (ddsd.ddpfPixelFormat.dwRGBBitCount) {
726 *(uint8_t *)ddsd.lpSurface = *color | (*color << 4);
729 *(uint8_t *)ddsd.lpSurface = *color;
733 *(uint16_t *)ddsd.lpSurface = *color;
736 /* Seems to be problematic so we'll just put black as the colorkey */
739 *(uint32_t *)ddsd.lpSurface = *color;
742 IDirectDrawSurface2_Unlock(sys->display, NULL);
747 if (IDirectDrawSurface2_GetDC(sys->display, &hdc) == DD_OK) {
748 rgb = GetPixel(hdc, 0, 0);
749 IDirectDrawSurface2_ReleaseDC(sys->display, hdc);
754 /* Restore the pixel value */
755 ddsd.dwSize = sizeof(ddsd);
756 if (IDirectDrawSurface2_Lock(sys->display, NULL, &ddsd, DDLOCK_WAIT, NULL) == DD_OK) {
757 *(uint32_t *)ddsd.lpSurface = backup;
758 IDirectDrawSurface2_Unlock(sys->display, NULL);
765 * Create and initialize display according to preferences specified in the vout
768 static int DirectXOpenDisplay(vout_display_t *vd)
770 vout_display_sys_t *sys = vd->sys;
773 /* Now get the primary surface. This surface is what you actually see
776 ZeroMemory(&ddsd, sizeof(ddsd));
777 ddsd.dwSize = sizeof(ddsd);
778 ddsd.dwFlags = DDSD_CAPS;
779 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
781 LPDIRECTDRAWSURFACE display;
782 hr = IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &display, NULL);
784 msg_Err(vd, "cannot get primary surface (error %li)", hr);
789 hr = IDirectDrawSurface_QueryInterface(display, &IID_IDirectDrawSurface2,
791 /* Release the old interface */
792 IDirectDrawSurface_Release(display);
795 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
801 /* The clipper will be used only in non-overlay mode */
802 DirectXCreateClipper(vd);
804 /* Make sure the colorkey will be painted */
806 sys->i_rgb_colorkey = DirectXFindColorkey(vd, &sys->i_colorkey);
810 static void DirectXCloseDisplay(vout_display_t *vd)
812 vout_display_sys_t *sys = vd->sys;
814 if (sys->clipper != NULL)
815 IDirectDrawClipper_Release(sys->clipper);
817 if (sys->display != NULL)
818 IDirectDrawSurface2_Release(sys->display);
825 * Create an YUV overlay or RGB surface for the video.
827 * The best method of display is with an YUV overlay because the YUV->RGB
828 * conversion is done in hardware.
829 * You can also create a plain RGB surface.
830 * (Maybe we could also try an RGB overlay surface, which could have hardware
831 * scaling and which would also be faster in window mode because you don't
832 * need to do any blitting to the main display...)
834 static int DirectXCreateSurface(vout_display_t *vd,
835 LPDIRECTDRAWSURFACE2 *surface,
836 const video_format_t *fmt,
840 int backbuffer_count)
842 vout_display_sys_t *sys = vd->sys;
846 ZeroMemory(&ddsd, sizeof(ddsd));
847 ddsd.dwSize = sizeof(ddsd);
848 ddsd.ddpfPixelFormat.dwSize = sizeof(ddsd.ddpfPixelFormat);
849 ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
850 ddsd.dwWidth = fmt->i_width;
851 ddsd.dwHeight = fmt->i_height;
853 ddsd.dwFlags |= DDSD_PIXELFORMAT;
854 ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
855 ddsd.ddpfPixelFormat.dwFourCC = fourcc;
858 ddsd.dwFlags |= DDSD_CAPS;
859 ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
860 if (backbuffer_count > 0)
861 ddsd.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
863 if (backbuffer_count > 0) {
864 ddsd.dwFlags |= DDSD_BACKBUFFERCOUNT;
865 ddsd.dwBackBufferCount = backbuffer_count;
868 ddsd.dwFlags |= DDSD_CAPS;
869 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
871 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
873 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
876 /* Create the video surface */
877 LPDIRECTDRAWSURFACE surface_v1;
878 if (IDirectDraw2_CreateSurface(sys->ddobject, &ddsd, &surface_v1, NULL) != DD_OK)
881 /* Now that the surface is created, try to get a newer DirectX interface */
882 HRESULT hr = IDirectDrawSurface_QueryInterface(surface_v1,
883 &IID_IDirectDrawSurface2,
885 IDirectDrawSurface_Release(surface_v1);
887 msg_Err(vd, "cannot query IDirectDrawSurface2 interface (error %li)", hr);
892 /* Check the overlay is useable as some graphics cards allow creating
893 * several overlays but only one can be used at one time. */
894 if (DirectXUpdateOverlay(vd, *surface)) {
895 IDirectDrawSurface2_Release(*surface);
896 msg_Err(vd, "overlay unuseable (might already be in use)");
904 static void DirectXDestroySurface(LPDIRECTDRAWSURFACE2 surface)
906 IDirectDrawSurface2_Release(surface);
909 * This function locks a surface and get the surface descriptor.
911 static int DirectXLockSurface(LPDIRECTDRAWSURFACE2 front_surface,
912 LPDIRECTDRAWSURFACE2 surface,
917 DDSURFACEDESC ddsd_dummy;
921 ZeroMemory(ddsd, sizeof(*ddsd));
922 ddsd->dwSize = sizeof(*ddsd);
923 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL);
925 if (hr == DDERR_INVALIDPARAMS) {
926 /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting
927 * in an invalid params error */
928 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
930 if (hr == DDERR_SURFACELOST) {
931 /* Your surface can be lost so be sure
932 * to check this and restore it if needed */
934 /* When using overlays with back-buffers, we need to restore
935 * the front buffer so the back-buffers get restored as well. */
936 if (front_surface != surface)
937 IDirectDrawSurface2_Restore(front_surface);
939 IDirectDrawSurface2_Restore(surface);
941 hr = IDirectDrawSurface2_Lock(surface, NULL, ddsd, DDLOCK_WAIT, NULL);
948 static void DirectXUnlockSurface(LPDIRECTDRAWSURFACE2 front_surface,
949 LPDIRECTDRAWSURFACE2 surface)
951 VLC_UNUSED(front_surface);
952 IDirectDrawSurface2_Unlock(surface, NULL);
954 static int DirectXCheckLockingSurface(LPDIRECTDRAWSURFACE2 front_surface,
955 LPDIRECTDRAWSURFACE2 surface)
957 if (DirectXLockSurface(front_surface, surface, NULL))
960 DirectXUnlockSurface(front_surface, surface);
971 static DWORD DirectXGetFourcc(vlc_fourcc_t codec)
973 static const dx_format_t dx_formats[] = {
974 { VLC_CODEC_YUYV, MAKEFOURCC('Y','U','Y','2') },
975 { VLC_CODEC_UYVY, MAKEFOURCC('U','Y','V','Y') },
976 { VLC_CODEC_YVYU, MAKEFOURCC('Y','V','Y','U') },
977 { VLC_CODEC_YV12, MAKEFOURCC('Y','V','1','2') },
978 { VLC_CODEC_I420, MAKEFOURCC('Y','V','1','2') },
979 { VLC_CODEC_J420, MAKEFOURCC('Y','V','1','2') },
983 for (unsigned i = 0; dx_formats[i].codec != 0; i++) {
984 if (dx_formats[i].codec == codec)
985 return dx_formats[i].fourcc;
990 static int DirectXCreatePictureResourceYuvOverlay(vout_display_t *vd,
991 const video_format_t *fmt,
994 vout_display_sys_t *sys = vd->sys;
996 bool allow_3buf = var_InheritBool(vd, "directx-3buffering");
998 /* The overlay surface that we create won't be used to decode directly
999 * into it because accessing video memory directly is way to slow (remember
1000 * that pictures are decoded macroblock per macroblock). Instead the video
1001 * will be decoded in picture buffers in system memory which will then be
1002 * memcpy() to the overlay surface. */
1003 LPDIRECTDRAWSURFACE2 front_surface;
1004 int ret = VLC_EGENERIC;
1006 /* Triple buffering rocks! it doesn't have any processing overhead
1007 * (you don't have to wait for the vsync) and provides for a very nice
1008 * video quality (no tearing). */
1009 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 2);
1012 ret = DirectXCreateSurface(vd, &front_surface, fmt, fourcc, true, false, 0);
1014 return VLC_EGENERIC;
1015 msg_Dbg(vd, "YUV overlay surface created successfully");
1017 /* Get the back buffer */
1018 LPDIRECTDRAWSURFACE2 surface;
1020 ZeroMemory(&dds_caps, sizeof(dds_caps));
1021 dds_caps.dwCaps = DDSCAPS_BACKBUFFER;
1022 if (IDirectDrawSurface2_GetAttachedSurface(front_surface, &dds_caps, &surface) != DD_OK) {
1023 msg_Warn(vd, "Failed to get surface back buffer");
1024 /* front buffer is the same as back buffer */
1025 surface = front_surface;
1028 if (DirectXCheckLockingSurface(front_surface, surface)) {
1029 DirectXDestroySurface(front_surface);
1030 return VLC_EGENERIC;
1034 picture_resource_t *rsc = &sys->resource;
1035 rsc->p_sys->front_surface = front_surface;
1036 rsc->p_sys->surface = surface;
1037 rsc->p_sys->fallback = NULL;
1040 static int DirectXCreatePictureResourceYuv(vout_display_t *vd,
1041 const video_format_t *fmt,
1044 vout_display_sys_t *sys = vd->sys;
1046 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1048 /* As we can't have an overlay, we'll try to create a plain offscreen
1049 * surface. This surface will reside in video memory because there's a
1050 * better chance then that we'll be able to use some kind of hardware
1051 * acceleration like rescaling, blitting or YUV->RGB conversions.
1052 * We then only need to blit this surface onto the main display when we
1053 * want to display it */
1055 /* Check if the chroma is supported first. This is required
1056 * because a few buggy drivers don't mind creating the surface
1057 * even if they don't know about the chroma. */
1059 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, NULL) != DD_OK)
1060 return VLC_EGENERIC;
1062 DWORD *list = calloc(count, sizeof(*list));
1065 if (IDirectDraw2_GetFourCCCodes(sys->ddobject, &count, list) != DD_OK) {
1067 return VLC_EGENERIC;
1070 for (index = 0; index < count; index++) {
1071 if (list[index] == fourcc)
1076 return VLC_EGENERIC;
1079 LPDIRECTDRAWSURFACE2 surface;
1080 if (DirectXCreateSurface(vd, &surface, fmt, fourcc, false, allow_sysmem, 0))
1081 return VLC_EGENERIC;
1082 msg_Dbg(vd, "YUV plain surface created successfully");
1084 if (DirectXCheckLockingSurface(surface, surface)) {
1085 DirectXDestroySurface(surface);
1086 return VLC_EGENERIC;
1090 picture_resource_t *rsc = &sys->resource;
1091 rsc->p_sys->front_surface = surface;
1092 rsc->p_sys->surface = surface;
1093 rsc->p_sys->fallback = NULL;
1096 static int DirectXCreatePictureResourceRgb(vout_display_t *vd,
1097 video_format_t *fmt)
1099 vout_display_sys_t *sys = vd->sys;
1100 bool allow_sysmem = var_InheritBool(vd, "directx-use-sysmem");
1102 /* Our last choice is to use a plain RGB surface */
1103 DDPIXELFORMAT ddpfPixelFormat;
1104 ZeroMemory(&ddpfPixelFormat, sizeof(ddpfPixelFormat));
1105 ddpfPixelFormat.dwSize = sizeof(ddpfPixelFormat);
1107 IDirectDrawSurface2_GetPixelFormat(sys->display, &ddpfPixelFormat);
1108 if ((ddpfPixelFormat.dwFlags & DDPF_RGB) == 0)
1109 return VLC_EGENERIC;
1111 switch (ddpfPixelFormat.dwRGBBitCount) {
1113 fmt->i_chroma = VLC_CODEC_RGB8;
1116 fmt->i_chroma = VLC_CODEC_RGB15;
1119 fmt->i_chroma = VLC_CODEC_RGB16;
1122 fmt->i_chroma = VLC_CODEC_RGB24;
1125 fmt->i_chroma = VLC_CODEC_RGB32;
1128 msg_Err(vd, "unknown screen depth");
1129 return VLC_EGENERIC;
1131 fmt->i_rmask = ddpfPixelFormat.dwRBitMask;
1132 fmt->i_gmask = ddpfPixelFormat.dwGBitMask;
1133 fmt->i_bmask = ddpfPixelFormat.dwBBitMask;
1136 LPDIRECTDRAWSURFACE2 surface;
1137 int ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, allow_sysmem, 0);
1138 if (ret && !allow_sysmem)
1139 ret = DirectXCreateSurface(vd, &surface, fmt, 0, false, true, 0);
1141 return VLC_EGENERIC;
1142 msg_Dbg(vd, "RGB plain surface created successfully");
1144 if (DirectXCheckLockingSurface(surface, surface)) {
1145 DirectXDestroySurface(surface);
1146 return VLC_EGENERIC;
1150 picture_resource_t *rsc = &sys->resource;
1151 rsc->p_sys->front_surface = surface;
1152 rsc->p_sys->surface = surface;
1153 rsc->p_sys->fallback = NULL;
1157 static int DirectXCreatePictureResource(vout_display_t *vd,
1159 video_format_t *fmt)
1161 vout_display_sys_t *sys = vd->sys;
1164 picture_resource_t *rsc = &sys->resource;
1165 rsc->p_sys = calloc(1, sizeof(*rsc->p_sys));
1170 bool allow_hw_yuv = sys->can_blit_fourcc &&
1171 vlc_fourcc_IsYUV(fmt->i_chroma) &&
1172 var_InheritBool(vd, "directx-hw-yuv");
1173 bool allow_overlay = var_InheritBool(vd, "overlay");
1175 /* Try to use an yuv surface */
1177 const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
1178 /* Try with overlay first */
1179 for (unsigned pass = allow_overlay ? 0 : 1; pass < 2; pass++) {
1180 for (unsigned i = 0; list[i] != 0; i++) {
1181 const DWORD fourcc = DirectXGetFourcc(list[i]);
1186 if (DirectXCreatePictureResourceYuvOverlay(vd, fmt, fourcc))
1189 if (DirectXCreatePictureResourceYuv(vd, fmt, fourcc))
1193 *use_overlay = pass == 0;
1194 fmt->i_chroma = list[i];
1201 return DirectXCreatePictureResourceRgb(vd, fmt);
1203 static void DirectXDestroyPictureResource(vout_display_t *vd)
1205 vout_display_sys_t *sys = vd->sys;
1207 if (sys->resource.p_sys->front_surface != sys->resource.p_sys->surface)
1208 DirectXDestroySurface(sys->resource.p_sys->surface);
1209 DirectXDestroySurface(sys->resource.p_sys->front_surface);
1210 if (sys->resource.p_sys->fallback)
1211 picture_Release(sys->resource.p_sys->fallback);
1214 static int DirectXLock(picture_t *picture)
1217 if (DirectXLockSurface(picture->p_sys->front_surface,
1218 picture->p_sys->surface, &ddsd))
1219 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
1221 CommonUpdatePicture(picture, NULL, ddsd.lpSurface, ddsd.lPitch);
1224 static void DirectXUnlock(picture_t *picture)
1226 DirectXUnlockSurface(picture->p_sys->front_surface,
1227 picture->p_sys->surface);
1230 static int DirectXCreatePool(vout_display_t *vd,
1231 bool *use_overlay, video_format_t *fmt)
1233 vout_display_sys_t *sys = vd->sys;
1238 if (DirectXCreatePictureResource(vd, use_overlay, fmt))
1239 return VLC_EGENERIC;
1241 /* Create the associated picture */
1242 picture_resource_t *rsc = &sys->resource;
1243 for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
1244 rsc->p[i].p_pixels = NULL;
1245 rsc->p[i].i_pitch = 0;
1246 rsc->p[i].i_lines = 0;
1248 picture_t *picture = picture_NewFromResource(fmt, rsc);
1250 DirectXDestroyPictureResource(vd);
1255 /* Wrap it into a picture pool */
1256 picture_pool_configuration_t cfg;
1257 memset(&cfg, 0, sizeof(cfg));
1258 cfg.picture_count = 1;
1259 cfg.picture = &picture;
1260 cfg.lock = DirectXLock;
1261 cfg.unlock = DirectXUnlock;
1263 sys->pool = picture_pool_NewExtended(&cfg);
1265 picture_Release(picture);
1266 DirectXDestroyPictureResource(vd);
1271 static void DirectXDestroyPool(vout_display_t *vd)
1273 vout_display_sys_t *sys = vd->sys;
1276 DirectXDestroyPictureResource(vd);
1277 picture_pool_Delete(sys->pool);
1283 * Move or resize overlay surface on video display.
1285 * This function is used to move or resize an overlay surface on the screen.
1286 * Ususally the overlay is moved by the user and thus, by a move or resize
1289 static int DirectXUpdateOverlay(vout_display_t *vd, LPDIRECTDRAWSURFACE2 surface)
1291 vout_display_sys_t *sys = vd->sys;
1293 RECT src = sys->rect_src_clipped;
1294 RECT dst = sys->rect_dest_clipped;
1296 if (sys->use_wallpaper) {
1297 src.left = vd->source.i_x_offset;
1298 src.top = vd->source.i_y_offset;
1299 src.right = vd->source.i_x_offset + vd->source.i_visible_width;
1300 src.bottom = vd->source.i_y_offset + vd->source.i_visible_height;
1301 AlignRect(&src, sys->i_align_src_boundary, sys->i_align_src_size);
1303 vout_display_cfg_t cfg = *vd->cfg;
1304 cfg.display.width = sys->rect_display.right;
1305 cfg.display.height = sys->rect_display.bottom;
1307 vout_display_place_t place;
1308 vout_display_PlacePicture(&place, &vd->source, &cfg, true);
1310 dst.left = sys->rect_display.left + place.x;
1311 dst.top = sys->rect_display.top + place.y;
1312 dst.right = dst.left + place.width;
1313 dst.bottom = dst.top + place.height;
1314 AlignRect(&dst, sys->i_align_dest_boundary, sys->i_align_dest_size);
1319 return VLC_EGENERIC;
1320 surface = sys->resource.p_sys->front_surface;
1323 /* The new window dimensions should already have been computed by the
1324 * caller of this function */
1326 /* Position and show the overlay */
1328 ZeroMemory(&ddofx, sizeof(ddofx));
1329 ddofx.dwSize = sizeof(ddofx);
1330 ddofx.dckDestColorkey.dwColorSpaceLowValue = sys->i_colorkey;
1331 ddofx.dckDestColorkey.dwColorSpaceHighValue = sys->i_colorkey;
1333 HRESULT hr = IDirectDrawSurface2_UpdateOverlay(surface,
1334 &src, sys->display, &dst,
1335 DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx);
1336 sys->restore_overlay = hr != DD_OK;
1339 msg_Warn(vd, "DirectDrawUpdateOverlay cannot move/resize overlay");
1340 return VLC_EGENERIC;
1346 static void WallpaperChange(vout_display_t *vd, bool use_wallpaper)
1348 vout_display_sys_t *sys = vd->sys;
1350 if (!sys->use_wallpaper == !use_wallpaper)
1353 HWND hwnd = FindWindow(_T("Progman"), NULL);
1355 hwnd = FindWindowEx(hwnd, NULL, _T("SHELLDLL_DefView"), NULL);
1357 hwnd = FindWindowEx(hwnd, NULL, _T("SysListView32"), NULL);
1359 msg_Warn(vd, "couldn't find \"SysListView32\" window, "
1360 "wallpaper mode not supported");
1364 msg_Dbg(vd, "wallpaper mode %s", use_wallpaper ? "enabled" : "disabled");
1365 sys->use_wallpaper = use_wallpaper;
1367 if (sys->use_wallpaper) {
1368 sys->color_bkg = ListView_GetBkColor(hwnd);
1369 sys->color_bkgtxt = ListView_GetTextBkColor(hwnd);
1371 ListView_SetBkColor(hwnd, sys->i_rgb_colorkey);
1372 ListView_SetTextBkColor(hwnd, sys->i_rgb_colorkey);
1374 ListView_SetBkColor(hwnd, sys->color_bkg);
1375 ListView_SetTextBkColor(hwnd, sys->color_bkgtxt);
1378 /* Update desktop */
1379 InvalidateRect(hwnd, NULL, TRUE);
1382 if (sys->use_overlay)
1383 DirectXUpdateOverlay(vd, NULL);
1387 static int WallpaperCallback(vlc_object_t *object, char const *cmd,
1388 vlc_value_t oldval, vlc_value_t newval, void *data)
1390 vout_display_t *vd = (vout_display_t *)object;
1391 vout_display_sys_t *sys = vd->sys;
1392 VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(data);
1394 vlc_mutex_lock(&sys->lock);
1395 const bool ch_wallpaper = !sys->wallpaper_requested != !newval.b_bool;
1396 sys->ch_wallpaper |= ch_wallpaper;
1397 sys->wallpaper_requested = newval.b_bool;
1398 vlc_mutex_unlock(&sys->lock);
1400 /* FIXME we should have a way to export variable to be saved */
1402 playlist_t *p_playlist = pl_Get(vd);
1403 /* Modify playlist as well because the vout might have to be
1405 var_Create(p_playlist, "video-wallpaper", VLC_VAR_BOOL);
1406 var_SetBool(p_playlist, "video-wallpaper", newval.b_bool);
1411 /*****************************************************************************
1412 * config variable callback
1413 *****************************************************************************/
1414 static BOOL WINAPI DirectXEnumCallback2(GUID *guid, LPTSTR desc,
1415 LPTSTR drivername, VOID *context,
1418 VLC_UNUSED(guid); VLC_UNUSED(desc); VLC_UNUSED(hmon);
1420 module_config_t *item = context;
1422 item->ppsz_list = xrealloc(item->ppsz_list,
1423 (item->i_list+2) * sizeof(char *));
1424 item->ppsz_list_text = xrealloc(item->ppsz_list_text,
1425 (item->i_list+2) * sizeof(char *));
1427 item->ppsz_list[item->i_list] = strdup(drivername);
1428 item->ppsz_list_text[item->i_list] = NULL;
1430 item->ppsz_list[item->i_list] = NULL;
1431 item->ppsz_list_text[item->i_list] = NULL;
1433 return TRUE; /* Keep enumerating */
1436 static int FindDevicesCallback(vlc_object_t *object, char const *name,
1437 vlc_value_t newval, vlc_value_t oldval, void *data)
1439 VLC_UNUSED(newval); VLC_UNUSED(oldval); VLC_UNUSED(data);
1441 module_config_t *item = config_FindConfig(object, name);
1445 /* Clear-up the current list */
1446 if (item->i_list > 0) {
1448 /* Keep the first entry */
1449 for (i = 1; i < item->i_list; i++) {
1450 free(item->ppsz_list[i]);
1451 free(item->ppsz_list_text[i]);
1453 /* TODO: Remove when no more needed */
1454 item->ppsz_list[i] = NULL;
1455 item->ppsz_list_text[i] = NULL;
1459 /* Load direct draw DLL */
1460 HINSTANCE hddraw_dll = LoadLibrary(_T("DDRAW.DLL"));
1464 /* Enumerate displays */
1465 HRESULT (WINAPI *OurDirectDrawEnumerateEx)(LPDDENUMCALLBACKEXA,
1467 (void *)GetProcAddress(hddraw_dll, _T("DirectDrawEnumerateExA"));
1468 if (OurDirectDrawEnumerateEx)
1469 OurDirectDrawEnumerateEx(DirectXEnumCallback2, item,
1470 DDENUM_ATTACHEDSECONDARYDEVICES);
1472 FreeLibrary(hddraw_dll);
1474 /* Signal change to the interface */
1475 item->b_dirty = true;