X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_output%2Fdirectx%2Fdirectx.c;h=61c604798f4896501c7a6626a1cc2bec667444db;hb=ba9826cacf6f17a741a88e9346a6cd674d7f0e29;hp=c34186cf624f58866530dcecaf0fc5c35ebdd880;hpb=1ecc320869fa09ca48e630eb913ff725c843fa59;p=vlc diff --git a/modules/video_output/directx/directx.c b/modules/video_output/directx/directx.c index c34186cf62..61c604798f 100644 --- a/modules/video_output/directx/directx.c +++ b/modules/video_output/directx/directx.c @@ -1,10 +1,10 @@ /***************************************************************************** * vout.c: Windows DirectX video output display method ***************************************************************************** - * Copyright (C) 2001 VideoLAN - * $Id: directx.c,v 1.23 2003/10/17 16:40:09 gbazin Exp $ + * Copyright (C) 2001-2004 VideoLAN + * $Id$ * - * Authors: Gildas Bazin + * Authors: Gildas Bazin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +44,16 @@ #include #include +#include + +#ifndef UNDER_CE +# include +#endif +#undef GetSystemMetrics + +#ifndef MONITOR_DEFAULTTONEAREST +# define MONITOR_DEFAULTTONEAREST 2 +#endif #include "vout.h" @@ -84,40 +94,77 @@ static void DirectXGetDDrawCaps ( vout_thread_t *p_vout ); static int DirectXLockSurface ( vout_thread_t *p_vout, picture_t *p_pic ); static int DirectXUnlockSurface ( vout_thread_t *p_vout, picture_t *p_pic ); +static DWORD DirectXFindColorkey( vout_thread_t *p_vout, uint32_t *i_color ); + +void SwitchWallpaperMode( vout_thread_t *, vlc_bool_t ); + /* Object variables callbacks */ -static int OnTopCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); +static int FindDevicesCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int WallpaperCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); /***************************************************************************** * Module descriptor *****************************************************************************/ -#define ON_TOP_TEXT N_("Always on top") -#define ON_TOP_LONGTEXT N_("Place the directx window on top of other windows") #define HW_YUV_TEXT N_("Use hardware YUV->RGB conversions") #define HW_YUV_LONGTEXT N_( \ "Try to use hardware acceleration for YUV->RGB conversions. " \ "This option doesn't have any effect when using overlays." ) + #define SYSMEM_TEXT N_("Use video buffers in system memory") #define SYSMEM_LONGTEXT N_( \ "Create video buffers in system memory instead of video memory. This " \ "isn't recommended as usually using video memory allows to benefit from " \ "more hardware acceleration (like rescaling or YUV->RGB conversions). " \ "This option doesn't have any effect when using overlays." ) + #define TRIPLEBUF_TEXT N_("Use triple buffering for overlays") #define TRIPLEBUF_LONGTEXT N_( \ - "Try to use triple bufferring when using YUV overlays. That results in " \ + "Try to use triple buffering when using YUV overlays. That results in " \ "much better video quality (no flickering)." ) +#define DEVICE_TEXT N_("Name of desired display device") +#define DEVICE_LONGTEXT N_("In a multiple monitor configuration, you can " \ + "specify the Windows device name of the display that you want the video " \ + "window to open on. For example, \"\\\\.\\DISPLAY1\" or " \ + "\"\\\\.\\DISPLAY2\"." ) + +#define WALLPAPER_TEXT N_("Enable wallpaper mode ") +#define WALLPAPER_LONGTEXT N_( \ + "The wallpaper mode allows you to display the video as the desktop " \ + "background. Note that this feature only works in overlay mode and " \ + "the desktop must not already have a wallpaper." ) + +static char *ppsz_dev[] = { "" }; +static char *ppsz_dev_text[] = { N_("Default") }; + vlc_module_begin(); - add_category_hint( N_("Video"), NULL, VLC_FALSE ); - add_bool( "directx-on-top", 0, NULL, ON_TOP_TEXT, ON_TOP_LONGTEXT, VLC_FALSE ); - add_bool( "directx-hw-yuv", 1, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT, VLC_TRUE ); - add_bool( "directx-use-sysmem", 0, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT, VLC_TRUE ); - add_bool( "directx-3buffering", 1, NULL, TRIPLEBUF_TEXT, TRIPLEBUF_LONGTEXT, VLC_TRUE ); + set_shortname( _("DirectX")) + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VOUT ); + add_bool( "directx-hw-yuv", 1, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT, + VLC_TRUE ); + add_bool( "directx-use-sysmem", 0, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT, + VLC_TRUE ); + add_bool( "directx-3buffering", 1, NULL, TRIPLEBUF_TEXT, + TRIPLEBUF_LONGTEXT, VLC_TRUE ); + + add_string( "directx-device", "", NULL, DEVICE_TEXT, DEVICE_LONGTEXT, + VLC_TRUE ); + change_string_list( ppsz_dev, ppsz_dev_text, FindDevicesCallback ); + change_action_add( FindDevicesCallback, N_("Refresh list") ); + + add_bool( "directx-wallpaper", 0, NULL, WALLPAPER_TEXT, WALLPAPER_LONGTEXT, + VLC_TRUE ); + set_description( _("DirectX video output") ); set_capability( "video output", 100 ); add_shortcut( "directx" ); set_callbacks( OpenVideo, CloseVideo ); + + /* FIXME: Hack to avoid unregistering our window class */ + linked_with_a_crap_library_which_uses_atexit( ); vlc_module_end(); #if 0 /* FIXME */ @@ -136,7 +183,8 @@ vlc_module_end(); static int OpenVideo( vlc_object_t *p_this ) { vout_thread_t * p_vout = (vout_thread_t *)p_this; - vlc_value_t val, text; + vlc_value_t val; + HMODULE huser32; /* Allocate structure */ p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); @@ -145,6 +193,7 @@ static int OpenVideo( vlc_object_t *p_this ) msg_Err( p_vout, "out of memory" ); return VLC_ENOMEM; } + memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); /* Initialisations */ p_vout->pf_init = Init; @@ -157,16 +206,37 @@ static int OpenVideo( vlc_object_t *p_this ) p_vout->p_sys->p_display = NULL; p_vout->p_sys->p_current_surface = NULL; p_vout->p_sys->p_clipper = NULL; - p_vout->p_sys->hbrush = NULL; - p_vout->p_sys->hwnd = NULL; - p_vout->p_sys->hparent = NULL; + p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL; + p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL; p_vout->p_sys->i_changes = 0; - p_vout->p_sys->b_caps_overlay_clipping = 0; + p_vout->p_sys->b_wallpaper = 0; + vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); SetRectEmpty( &p_vout->p_sys->rect_display ); - p_vout->p_sys->b_using_overlay = config_GetInt( p_vout, "overlay" ); - p_vout->p_sys->b_use_sysmem = config_GetInt( p_vout, "directx-use-sysmem"); - p_vout->p_sys->b_hw_yuv = config_GetInt( p_vout, "directx-hw-yuv" ); - p_vout->p_sys->b_3buf_overlay = config_GetInt( p_vout, "directx-3buffering" ); + SetRectEmpty( &p_vout->p_sys->rect_parent ); + + /* Multimonitor stuff */ + p_vout->p_sys->hmonitor = NULL; + p_vout->p_sys->p_display_driver = NULL; + p_vout->p_sys->MonitorFromWindow = NULL; + p_vout->p_sys->GetMonitorInfo = NULL; + if( (huser32 = GetModuleHandle( _T("USER32") ) ) ) + { + p_vout->p_sys->MonitorFromWindow = (HMONITOR (WINAPI *)( HWND, DWORD )) + GetProcAddress( huser32, _T("MonitorFromWindow") ); + p_vout->p_sys->GetMonitorInfo = +#ifndef UNICODE + GetProcAddress( huser32, "GetMonitorInfoA" ); +#else + GetProcAddress( huser32, _T("GetMonitorInfoW") ); +#endif + } + + var_Create( p_vout, "overlay", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "directx-use-sysmem", VLC_VAR_BOOL|VLC_VAR_DOINHERIT ); + var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "directx-3buffering", VLC_VAR_BOOL|VLC_VAR_DOINHERIT ); + var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); p_vout->p_sys->b_cursor_hidden = 0; p_vout->p_sys->i_lastmoved = mdate(); @@ -186,9 +256,8 @@ static int OpenVideo( vlc_object_t *p_this ) p_vout->p_sys->p_event = vlc_object_create( p_vout, sizeof(event_thread_t) ); p_vout->p_sys->p_event->p_vout = p_vout; - if( vlc_thread_create( p_vout->p_sys->p_event, - "DirectX Events Thread", DirectXEventThread, - 0, 1 ) ) + if( vlc_thread_create( p_vout->p_sys->p_event, "DirectX Events Thread", + E_(DirectXEventThread), 0, 1 ) ) { msg_Err( p_vout, "cannot create DirectXEventThread" ); vlc_object_destroy( p_vout->p_sys->p_event ); @@ -220,13 +289,19 @@ static int OpenVideo( vlc_object_t *p_this ) goto error; } - /* Add a variable to indicate if the window should be on top of others */ - var_Create( p_vout, "directx-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - text.psz_string = _("Always on top"); - var_Change( p_vout, "directx-on-top", VLC_VAR_SETTEXT, &text, NULL ); - var_Get( p_vout, "directx-on-top", &val ); - p_vout->p_sys->b_on_top_change = val.b_bool; - var_AddCallback( p_vout, "directx-on-top", OnTopCallback, NULL ); + /* Variable to indicate if the window should be on top of others */ + /* Trigger a callback right now */ + var_Get( p_vout, "video-on-top", &val ); + var_Set( p_vout, "video-on-top", val ); + + /* Variable to indicate if the window should be on top of others */ + /* Trigger a callback right now */ + var_Create( p_vout, "directx-wallpaper", VLC_VAR_BOOL|VLC_VAR_DOINHERIT ); + val.psz_string = _("Wallpaper"); + var_Change( p_vout, "directx-wallpaper", VLC_VAR_SETTEXT, &val, NULL ); + var_AddCallback( p_vout, "directx-wallpaper", WallpaperCallback, NULL ); + var_Get( p_vout, "directx-wallpaper", &val ); + var_Set( p_vout, "directx-wallpaper", val ); return VLC_SUCCESS; @@ -244,6 +319,35 @@ static int OpenVideo( vlc_object_t *p_this ) static int Init( vout_thread_t *p_vout ) { int i_chroma_backup; + vlc_value_t val; + + /* Get a few default parameters */ + var_Get( p_vout, "overlay", &val ); + p_vout->p_sys->b_using_overlay = val.b_bool; + var_Get( p_vout, "directx-use-sysmem", &val ); + p_vout->p_sys->b_use_sysmem = val.b_bool; + var_Get( p_vout, "directx-hw-yuv", &val ); + p_vout->p_sys->b_hw_yuv = val.b_bool; + var_Get( p_vout, "directx-3buffering", &val ); + p_vout->p_sys->b_3buf_overlay = val.b_bool; + + /* Initialise DirectDraw if not already done. + * We do this here because on multi-monitor systems we may have to + * re-create the directdraw surfaces. */ + if( !p_vout->p_sys->p_ddobject && + DirectXInitDDraw( p_vout ) != VLC_SUCCESS ) + { + msg_Err( p_vout, "cannot initialize DirectDraw" ); + return VLC_EGENERIC; + } + + /* Create the directx display */ + if( !p_vout->p_sys->p_display && + DirectXCreateDisplay( p_vout ) != VLC_SUCCESS ) + { + msg_Err( p_vout, "cannot initialize DirectDraw" ); + return VLC_EGENERIC; + } /* Initialize the output structure. * Since DirectDraw can do rescaling for us, stick to the default @@ -284,7 +388,7 @@ static int Init( vout_thread_t *p_vout ) if( !I_OUTPUTPICTURES ) { /* hmmm, it didn't work! Let's try commonly supported chromas */ - if( p_vout->output.i_chroma != VLC_FOURCC('Y','V','1','2') ) + if( p_vout->output.i_chroma != VLC_FOURCC('I','4','2','0') ) { p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2'); NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); @@ -306,16 +410,7 @@ static int Init( vout_thread_t *p_vout ) } /* Change the window title bar text */ - if( p_vout->p_sys->hparent ) - ; /* Do nothing */ - else if( p_vout->p_sys->b_using_overlay ) - SetWindowText( p_vout->p_sys->hwnd, - VOUT_TITLE " (hardware YUV overlay DirectX output)" ); - else if( p_vout->p_sys->b_hw_yuv ) - SetWindowText( p_vout->p_sys->hwnd, - VOUT_TITLE " (hardware YUV DirectX output)" ); - else SetWindowText( p_vout->p_sys->hwnd, - VOUT_TITLE " (software RGB DirectX output)" ); + PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 ); return VLC_SUCCESS; } @@ -329,6 +424,10 @@ static int Init( vout_thread_t *p_vout ) static void End( vout_thread_t *p_vout ) { FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES ); + + DirectXCloseDisplay( p_vout ); + DirectXCloseDDraw( p_vout ); + return; } @@ -343,11 +442,6 @@ static void CloseVideo( vlc_object_t *p_this ) msg_Dbg( p_vout, "CloseVideo" ); - var_Destroy( p_vout, "directx-on-top" ); - - DirectXCloseDisplay( p_vout ); - DirectXCloseDDraw( p_vout ); - if( p_vout->p_sys->p_event ) { vlc_object_detach( p_vout->p_sys->p_event ); @@ -366,6 +460,11 @@ static void CloseVideo( vlc_object_t *p_this ) vlc_object_destroy( p_vout->p_sys->p_event ); } + vlc_mutex_destroy( &p_vout->p_sys->lock ); + + /* Make sure the wallpaper is restored */ + SwitchWallpaperMode( p_vout, VLC_FALSE ); + if( p_vout->p_sys ) { free( p_vout->p_sys ); @@ -377,7 +476,7 @@ static void CloseVideo( vlc_object_t *p_this ) * Manage: handle Sys events ***************************************************************************** * This function should be called regularly by the video output thread. - * It returns a non null value if an error occured. + * It returns a non null value if an error occurred. *****************************************************************************/ static int Manage( vout_thread_t *p_vout ) { @@ -385,44 +484,67 @@ static int Manage( vout_thread_t *p_vout ) /* If we do not control our window, we check for geometry changes * ourselves because the parent might not send us its events. */ - if( p_vout->p_sys->hparent ) + vlc_mutex_lock( &p_vout->p_sys->lock ); + if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) { - DirectXUpdateRects( p_vout, VLC_FALSE ); - } + RECT rect_parent; + POINT point; - /* We used to call the Win32 PeekMessage function here to read the window - * messages. But since window can stay blocked into this function for a - * long time (for example when you move your window on the screen), I - * decided to isolate PeekMessage in another thread. */ + vlc_mutex_unlock( &p_vout->p_sys->lock ); - /* - * Scale Change - */ - if( p_vout->i_changes & VOUT_SCALE_CHANGE - || p_vout->p_sys->i_changes & VOUT_SCALE_CHANGE ) + GetClientRect( p_vout->p_sys->hparent, &rect_parent ); + point.x = point.y = 0; + ClientToScreen( p_vout->p_sys->hparent, &point ); + OffsetRect( &rect_parent, point.x, point.y ); + + if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) ) + { + p_vout->p_sys->rect_parent = rect_parent; + + /* This one is to force the update even if only + * the position has changed */ + SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1, + rect_parent.right - rect_parent.left, + rect_parent.bottom - rect_parent.top, 0 ); + + SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, + rect_parent.right - rect_parent.left, + rect_parent.bottom - rect_parent.top, 0 ); + } + } + else { - msg_Dbg( p_vout, "scale change" ); - if( !p_vout->p_sys->b_using_overlay ) - InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE ); - else - DirectXUpdateOverlay( p_vout ); - p_vout->i_changes &= ~VOUT_SCALE_CHANGE; - p_vout->p_sys->i_changes &= ~VOUT_SCALE_CHANGE; + vlc_mutex_unlock( &p_vout->p_sys->lock ); } /* - * Size Change + * Position Change */ - if( p_vout->i_changes & VOUT_SIZE_CHANGE - || p_vout->p_sys->i_changes & VOUT_SIZE_CHANGE ) + if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE ) { - msg_Dbg( p_vout, "size change" ); - if( !p_vout->p_sys->b_using_overlay ) - InvalidateRect( p_vout->p_sys->hwnd, NULL, TRUE ); - else - DirectXUpdateOverlay( p_vout ); - p_vout->i_changes &= ~VOUT_SIZE_CHANGE; - p_vout->p_sys->i_changes &= ~VOUT_SIZE_CHANGE; + p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE; + + /* Check if we are still on the same monitor */ + if( p_vout->p_sys->MonitorFromWindow && + p_vout->p_sys->hmonitor != + p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd, + MONITOR_DEFAULTTONEAREST ) ) + { + /* This will force the vout core to recreate the picture buffers */ + p_vout->i_changes |= VOUT_PICTURE_BUFFERS_CHANGE; + } + } + + /* We used to call the Win32 PeekMessage function here to read the window + * messages. But since window can stay blocked into this function for a + * long time (for example when you move your window on the screen), I + * decided to isolate PeekMessage in another thread. */ + + if( p_vout->p_sys->i_changes & DX_WALLPAPER_CHANGE ) + { + SwitchWallpaperMode( p_vout, !p_vout->p_sys->b_wallpaper ); + p_vout->p_sys->i_changes &= ~DX_WALLPAPER_CHANGE; + E_(DirectXUpdateOverlay)( p_vout ); } /* @@ -432,30 +554,79 @@ static int Manage( vout_thread_t *p_vout ) || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) { vlc_value_t val; + HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ? + p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd; p_vout->b_fullscreen = ! p_vout->b_fullscreen; /* We need to switch between Maximized and Normal sized window */ window_placement.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement ); + GetWindowPlacement( hwnd, &window_placement ); if( p_vout->b_fullscreen ) { - /* Maximized window */ - window_placement.showCmd = SW_SHOWMAXIMIZED; /* Change window style, no borders and no title bar */ - SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 ); + int i_style = WS_CLIPCHILDREN | WS_VISIBLE; + SetWindowLong( hwnd, GWL_STYLE, i_style ); + + if( p_vout->p_sys->hparent ) + { + /* Retrieve current window position so fullscreen will happen + * on the right screen */ + POINT point = {0,0}; + RECT rect; + ClientToScreen( p_vout->p_sys->hwnd, &point ); + GetClientRect( p_vout->p_sys->hwnd, &rect ); + SetWindowPos( hwnd, 0, point.x, point.y, + rect.right, rect.bottom, + SWP_NOZORDER|SWP_FRAMECHANGED ); + GetWindowPlacement( hwnd, &window_placement ); + } + + /* Maximize window */ + window_placement.showCmd = SW_SHOWMAXIMIZED; + SetWindowPlacement( hwnd, &window_placement ); + SetWindowPos( hwnd, 0, 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); + + if( p_vout->p_sys->hparent ) + { + RECT rect; + GetClientRect( hwnd, &rect ); + SetParent( p_vout->p_sys->hwnd, hwnd ); + SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, + rect.right, rect.bottom, + SWP_NOZORDER|SWP_FRAMECHANGED ); + } + SetForegroundWindow( hwnd ); } else { + /* Change window style, no borders and no title bar */ + SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style ); + /* Normal window */ window_placement.showCmd = SW_SHOWNORMAL; - /* Change window style, borders and title bar */ - SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, - WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE ); - } + SetWindowPlacement( hwnd, &window_placement ); + SetWindowPos( hwnd, 0, 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); + + if( p_vout->p_sys->hparent ) + { + RECT rect; + GetClientRect( p_vout->p_sys->hparent, &rect ); + SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent ); + SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, + rect.right, rect.bottom, + SWP_NOZORDER|SWP_FRAMECHANGED ); + + ShowWindow( hwnd, SW_HIDE ); + SetForegroundWindow( p_vout->p_sys->hparent ); + } - SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement ); + /* Make sure the mouse cursor is displayed */ + PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 ); + } /* Update the object variable and trigger callback */ val.b_bool = p_vout->b_fullscreen; @@ -468,15 +639,23 @@ static int Manage( vout_thread_t *p_vout ) /* * Pointer change */ - if( (!p_vout->p_sys->b_cursor_hidden) && - ( (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 ) ) + if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden && + (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 ) { - /* Hide the mouse automatically */ - if( p_vout->p_sys->hwnd != p_vout->p_sys->hparent ) + POINT point; + HWND hwnd; + + /* Hide the cursor only if it is inside our window */ + GetCursorPos( &point ); + hwnd = WindowFromPoint(point); + if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd ) { - p_vout->p_sys->b_cursor_hidden = VLC_TRUE; PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 ); } + else + { + p_vout->p_sys->i_lastmoved = mdate(); + } } /* @@ -487,8 +666,8 @@ static int Manage( vout_thread_t *p_vout ) vlc_value_t val; HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE ); - var_Get( p_vout, "directx-on-top", &val ); - + var_Get( p_vout, "video-on-top", &val ); + /* Set the window on top if necessary */ if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST ) ) @@ -533,7 +712,7 @@ static void Display( vout_thread_t *p_vout, picture_t *p_pic ) if( (p_vout->p_sys->p_display == NULL) ) { - msg_Warn( p_vout, "no display!!" ); + msg_Warn( p_vout, "no display!" ); return; } @@ -544,7 +723,7 @@ static void Display( vout_thread_t *p_vout, picture_t *p_pic ) { if( IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ) == DD_OK && p_vout->p_sys->b_using_overlay ) - DirectXUpdateOverlay( p_vout ); + E_(DirectXUpdateOverlay)( p_vout ); } if( !p_vout->p_sys->b_using_overlay ) @@ -598,9 +777,73 @@ static void Display( vout_thread_t *p_vout, picture_t *p_pic ) } } - /* following functions are local */ +/***************************************************************************** + * DirectXEnumCallback: Device enumeration + ***************************************************************************** + * This callback function is called by DirectDraw once for each + * available DirectDraw device. + *****************************************************************************/ +BOOL WINAPI DirectXEnumCallback( GUID* p_guid, LPTSTR psz_desc, + LPTSTR psz_drivername, VOID* p_context, + HMONITOR hmon ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_context; + vlc_value_t device; + + msg_Dbg( p_vout, "DirectXEnumCallback: %s, %s", psz_desc, psz_drivername ); + + if( hmon ) + { + var_Get( p_vout, "directx-device", &device ); + + if( ( !device.psz_string || !*device.psz_string ) && + hmon == p_vout->p_sys->hmonitor ) + { + if( device.psz_string ) free( device.psz_string ); + } + else if( strcmp( psz_drivername, device.psz_string ) == 0 ) + { + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof( MONITORINFO ); + + if( p_vout->p_sys->GetMonitorInfo( hmon, &monitor_info ) ) + { + RECT rect; + + /* Move window to the right screen */ + GetWindowRect( p_vout->p_sys->hwnd, &rect ); + if( !IntersectRect( &rect, &rect, &monitor_info.rcWork ) ) + { + rect.left = monitor_info.rcWork.left; + rect.top = monitor_info.rcWork.top; + msg_Dbg( p_vout, "DirectXEnumCallback: Setting window " + "position to %d,%d", rect.left, rect.top ); + SetWindowPos( p_vout->p_sys->hwnd, NULL, + rect.left, rect.top, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE ); + } + } + + p_vout->p_sys->hmonitor = hmon; + if( device.psz_string ) free( device.psz_string ); + } + else + { + if( device.psz_string ) free( device.psz_string ); + return TRUE; /* Keep enumerating */ + } + + msg_Dbg( p_vout, "selecting %s, %s", psz_desc, psz_drivername ); + p_vout->p_sys->p_display_driver = malloc( sizeof(GUID) ); + if( p_vout->p_sys->p_display_driver ) + memcpy( p_vout->p_sys->p_display_driver, p_guid, sizeof(GUID) ); + } + + return TRUE; /* Keep enumerating */ +} + /***************************************************************************** * DirectXInitDDraw: Takes care of all the DirectDraw initialisations ***************************************************************************** @@ -608,14 +851,16 @@ static void Display( vout_thread_t *p_vout, picture_t *p_pic ) *****************************************************************************/ static int DirectXInitDDraw( vout_thread_t *p_vout ) { - HRESULT dxresult; - HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *); - LPDIRECTDRAW p_ddobject; + HRESULT dxresult; + HRESULT (WINAPI *OurDirectDrawCreate)(GUID *,LPDIRECTDRAW *,IUnknown *); + HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID, + DWORD ); + LPDIRECTDRAW p_ddobject; msg_Dbg( p_vout, "DirectXInitDDraw" ); - /* load direct draw DLL */ - p_vout->p_sys->hddraw_dll = LoadLibrary("DDRAW.DLL"); + /* Load direct draw DLL */ + p_vout->p_sys->hddraw_dll = LoadLibrary(_T("DDRAW.DLL")); if( p_vout->p_sys->hddraw_dll == NULL ) { msg_Warn( p_vout, "DirectXInitDDraw failed loading ddraw.dll" ); @@ -623,15 +868,45 @@ static int DirectXInitDDraw( vout_thread_t *p_vout ) } OurDirectDrawCreate = - (void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate"); - if ( OurDirectDrawCreate == NULL ) + (void *)GetProcAddress( p_vout->p_sys->hddraw_dll, + _T("DirectDrawCreate") ); + if( OurDirectDrawCreate == NULL ) { msg_Err( p_vout, "DirectXInitDDraw failed GetProcAddress" ); goto error; } + OurDirectDrawEnumerateEx = + (void *)GetProcAddress( p_vout->p_sys->hddraw_dll, +#ifndef UNICODE + "DirectDrawEnumerateExA" ); +#else + _T("DirectDrawEnumerateExW") ); +#endif + + if( OurDirectDrawEnumerateEx && p_vout->p_sys->MonitorFromWindow ) + { + vlc_value_t device; + + var_Get( p_vout, "directx-device", &device ); + if( device.psz_string ) + { + msg_Dbg( p_vout, "directx-device: %s", device.psz_string ); + free( device.psz_string ); + } + + p_vout->p_sys->hmonitor = + p_vout->p_sys->MonitorFromWindow( p_vout->p_sys->hwnd, + MONITOR_DEFAULTTONEAREST ); + + /* Enumerate displays */ + OurDirectDrawEnumerateEx( DirectXEnumCallback, p_vout, + DDENUM_ATTACHEDSECONDARYDEVICES ); + } + /* Initialize DirectDraw now */ - dxresult = OurDirectDrawCreate( NULL, &p_ddobject, NULL ); + dxresult = OurDirectDrawCreate( p_vout->p_sys->p_display_driver, + &p_ddobject, NULL ); if( dxresult != DD_OK ) { msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" ); @@ -652,13 +927,36 @@ static int DirectXInitDDraw( vout_thread_t *p_vout ) /* Set DirectDraw Cooperative level, ie what control we want over Windows * display */ dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject, - p_vout->p_sys->hwnd, DDSCL_NORMAL ); + NULL, DDSCL_NORMAL ); if( dxresult != DD_OK ) { msg_Err( p_vout, "cannot set direct draw cooperative level" ); goto error; } + /* Get the size of the current display device */ + if( p_vout->p_sys->hmonitor && p_vout->p_sys->GetMonitorInfo ) + { + MONITORINFO monitor_info; + monitor_info.cbSize = sizeof( MONITORINFO ); + p_vout->p_sys->GetMonitorInfo( p_vout->p_sys->hmonitor, + &monitor_info ); + p_vout->p_sys->rect_display = monitor_info.rcMonitor; + } + else + { + p_vout->p_sys->rect_display.left = 0; + p_vout->p_sys->rect_display.top = 0; + p_vout->p_sys->rect_display.right = GetSystemMetrics(SM_CXSCREEN); + p_vout->p_sys->rect_display.bottom = GetSystemMetrics(SM_CYSCREEN); + } + + msg_Dbg( p_vout, "screen dimensions (%ix%i,%ix%i)", + p_vout->p_sys->rect_display.left, + p_vout->p_sys->rect_display.top, + p_vout->p_sys->rect_display.right, + p_vout->p_sys->rect_display.bottom ); + /* Probe the capabilities of the hardware */ DirectXGetDDrawCaps( p_vout ); @@ -686,7 +984,6 @@ static int DirectXCreateDisplay( vout_thread_t *p_vout ) HRESULT dxresult; DDSURFACEDESC ddsd; LPDIRECTDRAWSURFACE p_display; - DDPIXELFORMAT pixel_format; msg_Dbg( p_vout, "DirectXCreateDisplay" ); @@ -698,8 +995,7 @@ static int DirectXCreateDisplay( vout_thread_t *p_vout ) ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, - &ddsd, - &p_display, NULL ); + &ddsd, &p_display, NULL ); if( dxresult != DD_OK ) { msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult ); @@ -721,27 +1017,20 @@ static int DirectXCreateDisplay( vout_thread_t *p_vout ) /* The clipper will be used only in non-overlay mode */ DirectXCreateClipper( p_vout ); + /* Make sure the colorkey will be painted */ + p_vout->p_sys->i_colorkey = 1; + p_vout->p_sys->i_rgb_colorkey = + DirectXFindColorkey( p_vout, &p_vout->p_sys->i_colorkey ); -#if 1 - /* compute the colorkey pixel value from the RGB value we've got */ - memset( &pixel_format, 0, sizeof( DDPIXELFORMAT )); - pixel_format.dwSize = sizeof( DDPIXELFORMAT ); - dxresult = IDirectDrawSurface2_GetPixelFormat( p_vout->p_sys->p_display, - &pixel_format ); - if( dxresult != DD_OK ) - { - msg_Warn( p_vout, "DirectXUpdateOverlay GetPixelFormat failed " - "(error %li)", dxresult ); - } - p_vout->p_sys->i_colorkey = (DWORD)((( p_vout->p_sys->i_rgb_colorkey - * pixel_format.dwRBitMask) / 255) - & pixel_format.dwRBitMask ); -#endif + /* Create the actual brush */ + SetClassLong( p_vout->p_sys->hvideownd, GCL_HBRBACKGROUND, + (LONG)CreateSolidBrush( p_vout->p_sys->i_rgb_colorkey ) ); + InvalidateRect( p_vout->p_sys->hvideownd, NULL, TRUE ); + E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); return VLC_SUCCESS; } - /***************************************************************************** * DirectXCreateClipper: Create a clipper that will be used when blitting the * RGB surface to the main display. @@ -766,8 +1055,8 @@ static int DirectXCreateClipper( vout_thread_t *p_vout ) } /* Associate the clipper to the window */ - dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0, - p_vout->p_sys->hwnd); + dxresult = IDirectDrawClipper_SetHWnd( p_vout->p_sys->p_clipper, 0, + p_vout->p_sys->hvideownd ); if( dxresult != DD_OK ) { msg_Warn( p_vout, "cannot attach clipper to window (error %li)", @@ -831,13 +1120,9 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; ddsd.ddpfPixelFormat.dwFourCC = i_chroma; - ddsd.dwFlags = DDSD_CAPS | - DDSD_HEIGHT | - DDSD_WIDTH | - DDSD_PIXELFORMAT; + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; ddsd.dwFlags |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0); - ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | - DDSCAPS_VIDEOMEMORY; + ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; ddsd.ddsCaps.dwCaps |= (i_backbuffers ? DDSCAPS_COMPLEX | DDSCAPS_FLIP : 0 ); ddsd.dwHeight = p_vout->render.i_height; @@ -845,8 +1130,7 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, ddsd.dwBackBufferCount = i_backbuffers; dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, - &ddsd, - &p_surface, NULL ); + &ddsd, &p_surface, NULL ); if( dxresult != DD_OK ) { *pp_surface_final = NULL; @@ -866,9 +1150,7 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, memset( &ddsd, 0, sizeof( DDSURFACEDESC ) ); ddsd.dwSize = sizeof(DDSURFACEDESC); ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.dwFlags = DDSD_HEIGHT | - DDSD_WIDTH | - DDSD_CAPS; + ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; ddsd.dwHeight = p_vout->render.i_height; ddsd.dwWidth = p_vout->render.i_width; @@ -886,8 +1168,7 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, } dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, - &ddsd, - &p_surface, NULL ); + &ddsd, &p_surface, NULL ); if( dxresult != DD_OK ) { *pp_surface_final = NULL; @@ -913,7 +1194,7 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, /* Check the overlay is useable as some graphics cards allow creating * several overlays but only one can be used at one time. */ p_vout->p_sys->p_current_surface = *pp_surface_final; - if( DirectXUpdateOverlay( p_vout ) != VLC_SUCCESS ) + if( E_(DirectXUpdateOverlay)( p_vout ) != VLC_SUCCESS ) { IDirectDrawSurface2_Release( *pp_surface_final ); *pp_surface_final = NULL; @@ -932,15 +1213,40 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, * Ususally the overlay is moved by the user and thus, by a move or resize * event (in Manage). *****************************************************************************/ -int DirectXUpdateOverlay( vout_thread_t *p_vout ) +int E_(DirectXUpdateOverlay)( vout_thread_t *p_vout ) { DDOVERLAYFX ddofx; DWORD dwFlags; HRESULT dxresult; + RECT rect_src = p_vout->p_sys->rect_src_clipped; + RECT rect_dest = p_vout->p_sys->rect_dest_clipped; + + if( !p_vout->p_sys->b_using_overlay ) return VLC_EGENERIC; + + if( p_vout->p_sys->b_wallpaper ) + { + int i_x, i_y, i_width, i_height; - if( p_vout->p_sys->p_current_surface == NULL || - !p_vout->p_sys->b_using_overlay ) + rect_src.left = rect_src.top = 0; + rect_src.right = p_vout->render.i_width; + rect_src.bottom = p_vout->render.i_height; + + rect_dest = p_vout->p_sys->rect_display; + vout_PlacePicture( p_vout, rect_dest.right, rect_dest.bottom, + &i_x, &i_y, &i_width, &i_height ); + + rect_dest.left += i_x; + rect_dest.right = rect_dest.left + i_width; + rect_dest.top += i_y; + rect_dest.bottom = rect_dest.top + i_height; + } + + vlc_mutex_lock( &p_vout->p_sys->lock ); + if( p_vout->p_sys->p_current_surface == NULL ) + { + vlc_mutex_unlock( &p_vout->p_sys->lock ); return VLC_EGENERIC; + } /* The new window dimensions should already have been computed by the * caller of this function */ @@ -951,20 +1257,18 @@ int DirectXUpdateOverlay( vout_thread_t *p_vout ) ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey; ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey; - dwFlags = DDOVER_SHOW; - if( !p_vout->p_sys->b_caps_overlay_clipping ) - dwFlags |= DDOVER_KEYDESTOVERRIDE; + dwFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE; dxresult = IDirectDrawSurface2_UpdateOverlay( - p_vout->p_sys->p_current_surface, - &p_vout->p_sys->rect_src_clipped, - p_vout->p_sys->p_display, - &p_vout->p_sys->rect_dest_clipped, - dwFlags, &ddofx ); + p_vout->p_sys->p_current_surface, + &rect_src, p_vout->p_sys->p_display, &rect_dest, + dwFlags, &ddofx ); + + vlc_mutex_unlock( &p_vout->p_sys->lock ); + if(dxresult != DD_OK) { - msg_Warn( p_vout, - "DirectXUpdateOverlay cannot move or resize overlay" ); + msg_Warn( p_vout, "DirectXUpdateOverlay cannot move/resize overlay" ); return VLC_EGENERIC; } @@ -990,6 +1294,14 @@ static void DirectXCloseDDraw( vout_thread_t *p_vout ) FreeLibrary( p_vout->p_sys->hddraw_dll ); p_vout->p_sys->hddraw_dll = NULL; } + + if( p_vout->p_sys->p_display_driver != NULL ) + { + free( p_vout->p_sys->p_display_driver ); + p_vout->p_sys->p_display_driver = NULL; + } + + p_vout->p_sys->hmonitor = NULL; } /***************************************************************************** @@ -1114,7 +1426,7 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, { int i,j; for( i = 0; i < front_pic.i_planes; i++ ) - for( j = 0; j < front_pic.p[i].i_lines; j++) + for( j = 0; j < front_pic.p[i].i_visible_lines; j++) memset( front_pic.p[i].p_pixels + j * front_pic.p[i].i_pitch, 127, front_pic.p[i].i_visible_pitch ); @@ -1122,7 +1434,7 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, DirectXUnlockSurface( p_vout, &front_pic ); } - DirectXUpdateOverlay( p_vout ); + E_(DirectXUpdateOverlay)( p_vout ); I_OUTPUTPICTURES = 1; msg_Dbg( p_vout, "YUV overlay created successfully" ); } @@ -1146,11 +1458,11 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, * because a few buggy drivers don't mind creating the surface * even if they don't know about the chroma. */ if( IDirectDraw2_GetFourCCCodes( p_vout->p_sys->p_ddobject, - &i_codes, NULL ) ) + &i_codes, NULL ) == DD_OK ) { pi_codes = malloc( i_codes * sizeof(DWORD) ); if( pi_codes && IDirectDraw2_GetFourCCCodes( - p_vout->p_sys->p_ddobject, &i_codes, pi_codes ) ) + p_vout->p_sys->p_ddobject, &i_codes, pi_codes ) == DD_OK ) { for( i = 0; i < (int)i_codes; i++ ) { @@ -1255,6 +1567,7 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, { p_pic[i].i_status = DESTROYED_PICTURE; p_pic[i].i_type = DIRECT_PICTURE; + p_pic[i].b_slow = VLC_TRUE; p_pic[i].pf_lock = DirectXLockSurface; p_pic[i].pf_unlock = DirectXUnlockSurface; PP_OUTPUTPICTURE[i] = &p_pic[i]; @@ -1286,6 +1599,10 @@ static void FreePictureVec( vout_thread_t *p_vout, picture_t *p_pic, { int i; + vlc_mutex_lock( &p_vout->p_sys->lock ); + p_vout->p_sys->p_current_surface = 0; + vlc_mutex_unlock( &p_vout->p_sys->lock ); + for( i = 0; i < i_num_pics; i++ ) { DirectXCloseSurface( p_vout, p_pic[i].p_sys->p_front_surface ); @@ -1314,6 +1631,7 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, case VLC_FOURCC('R','V','3','2'): p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface; p_pic->p->i_lines = p_vout->output.i_height; + p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch; switch( p_vout->output.i_chroma ) { @@ -1339,9 +1657,15 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, break; case VLC_FOURCC('Y','V','1','2'): + case VLC_FOURCC('I','4','2','0'): + + /* U and V inverted compared to I420 + * Fixme: this should be handled by the vout core */ + p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0'); p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface; p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height; + p_pic->p[Y_PLANE].i_visible_lines = p_vout->output.i_height; p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch; p_pic->p[Y_PLANE].i_pixel_pitch = 1; p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width * @@ -1350,6 +1674,7 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->V_PIXELS = p_pic->Y_PIXELS + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch; p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2; + p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2; p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; p_pic->p[V_PLANE].i_pixel_pitch = 1; p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * @@ -1358,6 +1683,7 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->U_PIXELS = p_pic->V_PIXELS + p_pic->p[V_PLANE].i_lines * p_pic->p[V_PLANE].i_pitch; p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2; + p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2; p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; p_pic->p[U_PLANE].i_pixel_pitch = 1; p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * @@ -1370,6 +1696,7 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->Y_PIXELS = p_pic->p_sys->ddsd.lpSurface; p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height; + p_pic->p[Y_PLANE].i_visible_lines = p_vout->output.i_height; p_pic->p[Y_PLANE].i_pitch = p_pic->p_sys->ddsd.lPitch; p_pic->p[Y_PLANE].i_pixel_pitch = 1; p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width * @@ -1378,6 +1705,7 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->U_PIXELS = p_pic->Y_PIXELS + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch; p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2; + p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2; p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; p_pic->p[U_PLANE].i_pixel_pitch = 1; p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * @@ -1386,6 +1714,7 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->V_PIXELS = p_pic->U_PIXELS + p_pic->p[U_PLANE].i_lines * p_pic->p[U_PLANE].i_pitch; p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2; + p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2; p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; p_pic->p[V_PLANE].i_pixel_pitch = 1; p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * @@ -1394,10 +1723,12 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->i_planes = 3; break; + case VLC_FOURCC('U','Y','V','Y'): case VLC_FOURCC('Y','U','Y','2'): p_pic->p->p_pixels = p_pic->p_sys->ddsd.lpSurface; p_pic->p->i_lines = p_vout->output.i_height; + p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pitch = p_pic->p_sys->ddsd.lPitch; p_pic->p->i_pixel_pitch = 2; p_pic->p->i_visible_pitch = p_vout->output.i_width * @@ -1440,40 +1771,61 @@ static void DirectXGetDDrawCaps( vout_thread_t *p_vout ) } else { - BOOL bHasOverlay, bHasOverlayFourCC, bCanClipOverlay, - bHasColorKey, bCanStretch, bCanBltFourcc; + vlc_bool_t bHasOverlay, bHasOverlayFourCC, bCanDeinterlace, + bHasColorKey, bCanStretch, bCanBltFourcc, + bAlignBoundarySrc, bAlignBoundaryDest, + bAlignSizeSrc, bAlignSizeDest; /* Determine if the hardware supports overlay surfaces */ - bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) == - DDCAPS_OVERLAY) ? TRUE : FALSE; + bHasOverlay = (ddcaps.dwCaps & DDCAPS_OVERLAY) ? 1 : 0; /* Determine if the hardware supports overlay surfaces */ - bHasOverlayFourCC = ((ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC) == - DDCAPS_OVERLAYFOURCC) ? TRUE : FALSE; - /* Determine if the hardware supports overlay surfaces */ - bCanClipOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAYCANTCLIP) == - 0 ) ? TRUE : FALSE; + bHasOverlayFourCC = (ddcaps.dwCaps & DDCAPS_OVERLAYFOURCC) ? 1 : 0; + /* Determine if the hardware supports overlay deinterlacing */ + bCanDeinterlace = (ddcaps.dwCaps & DDCAPS2_CANFLIPODDEVEN) ? 1 : 0; /* Determine if the hardware supports colorkeying */ - bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) == - DDCAPS_COLORKEY) ? TRUE : FALSE; + bHasColorKey = (ddcaps.dwCaps & DDCAPS_COLORKEY) ? 1 : 0; /* Determine if the hardware supports scaling of the overlay surface */ - bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) == - DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE; + bCanStretch = (ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ? 1 : 0; /* Determine if the hardware supports color conversion during a blit */ - bCanBltFourcc = ((ddcaps.dwCaps & DDCAPS_BLTFOURCC ) == - DDCAPS_BLTFOURCC) ? TRUE : FALSE; - + bCanBltFourcc = (ddcaps.dwCaps & DDCAPS_BLTFOURCC) ? 1 : 0; + /* Determine overlay source boundary alignment */ + bAlignBoundarySrc = (ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) ? 1 : 0; + /* Determine overlay destination boundary alignment */ + bAlignBoundaryDest = (ddcaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) ? 1:0; + /* Determine overlay destination size alignment */ + bAlignSizeSrc = (ddcaps.dwCaps & DDCAPS_ALIGNSIZESRC) ? 1 : 0; + /* Determine overlay destination size alignment */ + bAlignSizeDest = (ddcaps.dwCaps & DDCAPS_ALIGNSIZEDEST) ? 1 : 0; + msg_Dbg( p_vout, "DirectDraw Capabilities: overlay=%i yuvoverlay=%i " - "can_clip_overlay=%i colorkey=%i stretch=%i " + "can_deinterlace_overlay=%i colorkey=%i stretch=%i " "bltfourcc=%i", - bHasOverlay, bHasOverlayFourCC, bCanClipOverlay, + bHasOverlay, bHasOverlayFourCC, bCanDeinterlace, bHasColorKey, bCanStretch, bCanBltFourcc ); - /* Overlay clipping support is interesting for us as it means we can - * get rid of the colorkey alltogether */ - p_vout->p_sys->b_caps_overlay_clipping = bCanClipOverlay; + if( bAlignBoundarySrc || bAlignBoundaryDest || + bAlignSizeSrc || bAlignSizeDest ) + { + if( bAlignBoundarySrc ) p_vout->p_sys->i_align_src_boundary = + ddcaps.dwAlignBoundarySrc; + if( bAlignBoundaryDest ) p_vout->p_sys->i_align_dest_boundary = + ddcaps.dwAlignBoundaryDest; + if( bAlignSizeDest ) p_vout->p_sys->i_align_src_size = + ddcaps.dwAlignSizeSrc; + if( bAlignSizeDest ) p_vout->p_sys->i_align_dest_size = + ddcaps.dwAlignSizeDest; + + msg_Dbg( p_vout, "align_boundary_src=%i,%i " + "align_boundary_dest=%i,%i " + "align_size_src=%i,%i align_size_dest=%i,%i", + bAlignBoundarySrc, p_vout->p_sys->i_align_src_boundary, + bAlignBoundaryDest, p_vout->p_sys->i_align_dest_boundary, + bAlignSizeSrc, p_vout->p_sys->i_align_src_size, + bAlignSizeDest, p_vout->p_sys->i_align_dest_size ); + } /* Don't ask for troubles */ - if( !bCanBltFourcc ) p_vout->p_sys->b_hw_yuv = FALSE; + if( !bCanBltFourcc ) p_vout->p_sys->b_hw_yuv = FALSE; } } @@ -1519,8 +1871,10 @@ static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL, &p_pic->p_sys->ddsd, DDLOCK_WAIT, NULL); +#if 0 if( dxresult == DDERR_SURFACELOST ) msg_Dbg( p_vout, "DirectXLockSurface: DDERR_SURFACELOST" ); +#endif } if( dxresult != DD_OK ) { @@ -1537,7 +1891,7 @@ static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) return VLC_EGENERIC; } else - return VLC_SUCCESS; + return VLC_SUCCESS; } /***************************************************************************** @@ -1553,13 +1907,210 @@ static int DirectXUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) } /***************************************************************************** - * object variables callbacks: a bunch of object variables are used by the - * interfaces to interact with the vout. + * DirectXFindColorkey: Finds out the 32bits RGB pixel value of the colorkey *****************************************************************************/ -static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +static DWORD DirectXFindColorkey( vout_thread_t *p_vout, uint32_t *pi_color ) +{ + DDSURFACEDESC ddsd; + HRESULT dxresult; + COLORREF i_rgb = 0; + uint32_t i_pixel_backup; + HDC hdc; + + ddsd.dwSize = sizeof(ddsd); + dxresult = IDirectDrawSurface2_Lock( p_vout->p_sys->p_display, NULL, + &ddsd, DDLOCK_WAIT, NULL ); + if( dxresult != DD_OK ) return 0; + + i_pixel_backup = *(uint32_t *)ddsd.lpSurface; + + switch( ddsd.ddpfPixelFormat.dwRGBBitCount ) + { + case 4: + *(uint8_t *)ddsd.lpSurface = *pi_color | (*pi_color << 4); + break; + case 8: + *(uint8_t *)ddsd.lpSurface = *pi_color; + break; + case 15: + case 16: + *(uint16_t *)ddsd.lpSurface = *pi_color; + break; + case 24: + /* Seems to be problematic so we'll just put black as the colorkey */ + *pi_color = 0; + default: + *(uint32_t *)ddsd.lpSurface = *pi_color; + break; + } + + IDirectDrawSurface2_Unlock( p_vout->p_sys->p_display, NULL ); + + if( IDirectDrawSurface2_GetDC( p_vout->p_sys->p_display, &hdc ) == DD_OK ) + { + i_rgb = GetPixel( hdc, 0, 0 ); + IDirectDrawSurface2_ReleaseDC( p_vout->p_sys->p_display, hdc ); + } + + ddsd.dwSize = sizeof(ddsd); + dxresult = IDirectDrawSurface2_Lock( p_vout->p_sys->p_display, NULL, + &ddsd, DDLOCK_WAIT, NULL ); + if( dxresult != DD_OK ) return i_rgb; + + *(uint32_t *)ddsd.lpSurface = i_pixel_backup; + + IDirectDrawSurface2_Unlock( p_vout->p_sys->p_display, NULL ); + + return i_rgb; +} + +/***************************************************************************** + * A few toolbox functions + *****************************************************************************/ +void SwitchWallpaperMode( vout_thread_t *p_vout, vlc_bool_t b_on ) +{ + HWND hwnd; + + if( p_vout->p_sys->b_wallpaper == b_on ) return; /* Nothing to do */ + + hwnd = FindWindow( _T("Progman"), NULL ); + if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SHELLDLL_DefView"), NULL ); + if( hwnd ) hwnd = FindWindowEx( hwnd, NULL, _T("SysListView32"), NULL ); + if( !hwnd ) + { + msg_Warn( p_vout, "couldn't find \"SysListView32\" window, " + "wallpaper mode not supported" ); + return; + } + + p_vout->p_sys->b_wallpaper = b_on; + + msg_Dbg( p_vout, "wallpaper mode %s", b_on ? "enabled" : "disabled" ); + + if( p_vout->p_sys->b_wallpaper ) + { + p_vout->p_sys->color_bkg = ListView_GetBkColor( hwnd ); + p_vout->p_sys->color_bkgtxt = ListView_GetTextBkColor( hwnd ); + + ListView_SetBkColor( hwnd, p_vout->p_sys->i_rgb_colorkey ); + ListView_SetTextBkColor( hwnd, p_vout->p_sys->i_rgb_colorkey ); + } + else if( hwnd ) + { + ListView_SetBkColor( hwnd, p_vout->p_sys->color_bkg ); + ListView_SetTextBkColor( hwnd, p_vout->p_sys->color_bkgtxt ); + } + + /* Update desktop */ + InvalidateRect( hwnd, NULL, TRUE ); + UpdateWindow( hwnd ); +} + +/***************************************************************************** + * config variable callback + *****************************************************************************/ +BOOL WINAPI DirectXEnumCallback2( GUID* p_guid, LPTSTR psz_desc, + LPTSTR psz_drivername, VOID* p_context, + HMONITOR hmon ) +{ + module_config_t *p_item = (module_config_t *)p_context; + + p_item->ppsz_list = + (char **)realloc( p_item->ppsz_list, + (p_item->i_list+2) * sizeof(char *) ); + p_item->ppsz_list_text = + (char **)realloc( p_item->ppsz_list_text, + (p_item->i_list+2) * sizeof(char *) ); + + p_item->ppsz_list[p_item->i_list] = strdup( psz_drivername ); + p_item->ppsz_list_text[p_item->i_list] = NULL; + p_item->i_list++; + p_item->ppsz_list[p_item->i_list] = NULL; + p_item->ppsz_list_text[p_item->i_list] = NULL; + + return TRUE; /* Keep enumerating */ +} + +static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, + vlc_value_t newval, vlc_value_t oldval, void *d) +{ + HRESULT (WINAPI *OurDirectDrawEnumerateEx)( LPDDENUMCALLBACKEXA, LPVOID, + DWORD ); + HINSTANCE hddraw_dll; + + module_config_t *p_item; + int i; + + p_item = config_FindConfig( p_this, psz_name ); + if( !p_item ) return VLC_SUCCESS; + + /* Clear-up the current list */ + if( p_item->i_list ) + { + /* Keep the first entry */ + for( i = 1; i < p_item->i_list; i++ ) + { + free( p_item->ppsz_list[i] ); + free( p_item->ppsz_list_text[i] ); + } + /* TODO: Remove when no more needed */ + p_item->ppsz_list[i] = NULL; + p_item->ppsz_list_text[i] = NULL; + } + p_item->i_list = 1; + + /* Load direct draw DLL */ + hddraw_dll = LoadLibrary(_T("DDRAW.DLL")); + if( hddraw_dll == NULL ) return VLC_SUCCESS; + + OurDirectDrawEnumerateEx = +#ifndef UNICODE + (void *)GetProcAddress( hddraw_dll, "DirectDrawEnumerateExA" ); +#else + (void *)GetProcAddress( hddraw_dll, _T("DirectDrawEnumerateExW") ); +#endif + + if( OurDirectDrawEnumerateEx ) + { + /* Enumerate displays */ + OurDirectDrawEnumerateEx( DirectXEnumCallback2, p_item, + DDENUM_ATTACHEDSECONDARYDEVICES ); + } + + FreeLibrary( hddraw_dll ); + + /* Signal change to the interface */ + p_item->b_dirty = VLC_TRUE; + + return VLC_SUCCESS; +} + +static int WallpaperCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) { vout_thread_t *p_vout = (vout_thread_t *)p_this; - p_vout->p_sys->b_on_top_change = VLC_TRUE; + + if( (newval.b_bool && !p_vout->p_sys->b_wallpaper) || + (!newval.b_bool && p_vout->p_sys->b_wallpaper) ) + { + playlist_t *p_playlist; + + p_playlist = + (playlist_t *)vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, + FIND_PARENT ); + if( p_playlist ) + { + /* Modify playlist as well because the vout might have to be + * restarted */ + var_Create( p_playlist, "directx-wallpaper", VLC_VAR_BOOL ); + var_Set( p_playlist, "directx-wallpaper", newval ); + + vlc_object_release( p_playlist ); + } + + p_vout->p_sys->i_changes |= DX_WALLPAPER_CHANGE; + } + return VLC_SUCCESS; }