X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_output%2Fdirectx%2Fdirectx.c;h=5c345c579d73b5f67380ada45020577073fafad7;hb=b7e5d7c50f89f9c354627729901bd3b0630f6f47;hp=80bf6f169f1d42a3c727d9f6f53c6793c82d6dc6;hpb=eddf123620abac25909f3e7b669f5ed34cd62405;p=vlc diff --git a/modules/video_output/directx/directx.c b/modules/video_output/directx/directx.c index 80bf6f169f..5c345c579d 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.3 2002/10/06 19:28:28 gbazin Exp $ + * Copyright (C) 2001-2004 the VideoLAN team + * $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 @@ -32,7 +32,7 @@ * surfaces that will be blitted onto the primary surface (display) to * effectively display the pictures. This fallback method also enables us to * display video in window mode. - * + * *****************************************************************************/ #include /* ENOMEM */ #include /* free() */ @@ -44,8 +44,16 @@ #include #include +#include + +#ifndef UNDER_CE +# include +#endif +#undef GetSystemMetrics -#include "netutils.h" +#ifndef MONITOR_DEFAULTTONEAREST +# define MONITOR_DEFAULTTONEAREST 2 +#endif #include "vout.h" @@ -68,6 +76,7 @@ static int Init ( vout_thread_t * ); static void End ( vout_thread_t * ); static int Manage ( vout_thread_t * ); static void Display ( vout_thread_t *, picture_t * ); +static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * ); static int NewPictureVec ( vout_thread_t *, picture_t *, int ); static void FreePictureVec ( vout_thread_t *, picture_t *, int ); @@ -83,29 +92,80 @@ static void DirectXCloseSurface ( vout_thread_t *p_vout, LPDIRECTDRAWSURFACE2 ); static int DirectXCreateClipper ( vout_thread_t *p_vout ); static void DirectXGetDDrawCaps ( vout_thread_t *p_vout ); -static int DirectXGetSurfaceDesc ( picture_t *p_pic ); +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 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 HW_YUV_TEXT N_("use hardware YUV->RGB conversions") +#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_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 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 ); - add_bool( "directx-hw-yuv", 1, NULL, HW_YUV_TEXT, HW_YUV_LONGTEXT ); - add_bool( "directx-use-sysmem", 0, NULL, SYSMEM_TEXT, SYSMEM_LONGTEXT ); - set_description( _("DirectX video module") ); + 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 */ @@ -124,14 +184,17 @@ 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; + HMODULE huser32; /* Allocate structure */ p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); if( p_vout->p_sys == NULL ) { msg_Err( p_vout, "out of memory" ); - return 1; + return VLC_ENOMEM; } + memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); /* Initialisations */ p_vout->pf_init = Init; @@ -144,14 +207,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->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" ); + 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(); @@ -171,9 +257,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, - VLC_THREAD_PRIORITY_LOW, 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 ); @@ -205,12 +290,25 @@ static int OpenVideo( vlc_object_t *p_this ) goto error; } - return 0; + /* 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; error: CloseVideo( VLC_OBJECT(p_vout) ); - return 1; - + return VLC_EGENERIC; } /***************************************************************************** @@ -222,6 +320,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 @@ -229,6 +356,8 @@ static int Init( vout_thread_t *p_vout ) p_vout->output.i_width = p_vout->render.i_width; p_vout->output.i_height = p_vout->render.i_height; p_vout->output.i_aspect = p_vout->render.i_aspect; + p_vout->fmt_out = p_vout->fmt_in; + E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); #define MAX_DIRECTBUFFERS 1 /* Right now we use only 1 directbuffer because we don't want the @@ -262,11 +391,14 @@ static int Init( vout_thread_t *p_vout ) if( !I_OUTPUTPICTURES ) { /* hmmm, it didn't work! Let's try commonly supported chromas */ - p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2'); - NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); + 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 ); + } if( !I_OUTPUTPICTURES ) { - /* hmmm, it didn't work! Let's try commonly supported chromas */ + /* hmmm, it still didn't work! Let's try another one */ p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2'); NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); } @@ -281,16 +413,10 @@ static int Init( vout_thread_t *p_vout ) } /* Change the window title bar text */ - 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)" ); - - return 0; + PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 ); + + p_vout->fmt_out.i_chroma = p_vout->output.i_chroma; + return VLC_SUCCESS; } /***************************************************************************** @@ -302,6 +428,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; } @@ -311,30 +441,34 @@ static void End( vout_thread_t *p_vout ) * Terminate an output method created by Create *****************************************************************************/ static void CloseVideo( vlc_object_t *p_this ) -{ +{ vout_thread_t * p_vout = (vout_thread_t *)p_this; - - msg_Dbg( p_vout, "CloseVideo" ); - DirectXCloseDisplay( p_vout ); - DirectXCloseDDraw( p_vout ); + msg_Dbg( p_vout, "CloseVideo" ); if( p_vout->p_sys->p_event ) { vlc_object_detach( p_vout->p_sys->p_event ); /* Kill DirectXEventThread */ - p_vout->p_sys->p_event->b_die = 1; + p_vout->p_sys->p_event->b_die = VLC_TRUE; /* we need to be sure DirectXEventThread won't stay stuck in * GetMessage, so we send a fake message */ if( p_vout->p_sys->hwnd ) + { PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0); + } vlc_thread_join( p_vout->p_sys->p_event ); 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 ); @@ -346,45 +480,93 @@ 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 ) { WINDOWPLACEMENT window_placement; - /* 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 we do not control our window, we check for geometry changes + * ourselves because the parent might not send us its events. */ + vlc_mutex_lock( &p_vout->p_sys->lock ); + if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) + { + RECT rect_parent; + POINT point; - /* - * Scale Change - */ - if( p_vout->i_changes & VOUT_SCALE_CHANGE - || p_vout->p_sys->i_changes & VOUT_SCALE_CHANGE) + vlc_mutex_unlock( &p_vout->p_sys->lock ); + + 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; + } + } + + /* Check for cropping / aspect changes */ + if( p_vout->i_changes & VOUT_CROP_CHANGE || + p_vout->i_changes & VOUT_ASPECT_CHANGE ) + { + p_vout->i_changes &= ~VOUT_CROP_CHANGE; + p_vout->i_changes &= ~VOUT_ASPECT_CHANGE; + + p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset; + p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset; + p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width; + p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height; + p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect; + p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num; + p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den; + p_vout->output.i_aspect = p_vout->fmt_in.i_aspect; + E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); + } + + /* 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 ); } /* @@ -393,29 +575,84 @@ static int Manage( vout_thread_t *p_vout ) if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE || 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 ); + } + + /* Make sure the mouse cursor is displayed */ + PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 ); } - SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement ); + /* Update the object variable and trigger callback */ + val.b_bool = p_vout->b_fullscreen; + var_Set( p_vout, "fullscreen", val ); p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; @@ -424,19 +661,65 @@ 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 ) + { + 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 ) + { + PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 ); + } + else + { + p_vout->p_sys->i_lastmoved = mdate(); + } + } + + /* + * "Always on top" status change + */ + if( p_vout->p_sys->b_on_top_change ) { - /* Hide the mouse automatically */ - p_vout->p_sys->b_cursor_hidden = 1; - PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 ); + vlc_value_t val; + HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE ); + + 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 ) ) + { + CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP, + MF_BYCOMMAND | MFS_CHECKED ); + SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE ); + } + else + /* The window shouldn't be on top */ + if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE ) + & WS_EX_TOPMOST ) ) + { + CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP, + MF_BYCOMMAND | MFS_UNCHECKED ); + SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE ); + } + + p_vout->p_sys->b_on_top_change = VLC_FALSE; } /* Check if the event thread is still running */ if( p_vout->p_sys->p_event->b_die ) - return 1; /* exit */ + { + return VLC_EGENERIC; /* exit */ + } - return 0; + return VLC_SUCCESS; } /***************************************************************************** @@ -451,10 +734,20 @@ 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; } + /* Our surface can be lost so be sure to check this + * and restore it if need be */ + if( IDirectDrawSurface2_IsLost( p_vout->p_sys->p_display ) + == DDERR_SURFACELOST ) + { + if( IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ) == DD_OK && + p_vout->p_sys->b_using_overlay ) + E_(DirectXUpdateOverlay)( p_vout ); + } + if( !p_vout->p_sys->b_using_overlay ) { DDBLTFX ddbltfx; @@ -465,79 +758,113 @@ static void Display( vout_thread_t *p_vout, picture_t *p_pic ) ddbltfx.dwDDFX = DDBLTFX_NOTEARING; /* Blit video surface to display */ - dxresult = IDirectDrawSurface2_Blt(p_vout->p_sys->p_display, - &p_vout->p_sys->rect_dest_clipped, - p_pic->p_sys->p_surface, - &p_vout->p_sys->rect_src_clipped, - DDBLT_ASYNC, &ddbltfx ); - if ( dxresult == DDERR_SURFACELOST ) - { - /* Our surface can be lost so be sure - * to check this and restore it if needed */ - IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ); - - /* Now that the surface has been restored try to display again */ - dxresult = IDirectDrawSurface2_Blt(p_vout->p_sys->p_display, - &p_vout->p_sys->rect_dest_clipped, - p_pic->p_sys->p_surface, - &p_vout->p_sys->rect_src_clipped, - DDBLT_ASYNC, &ddbltfx ); - } - + dxresult = IDirectDrawSurface2_Blt( p_vout->p_sys->p_display, + &p_vout->p_sys->rect_dest_clipped, + p_pic->p_sys->p_surface, + &p_vout->p_sys->rect_src_clipped, + DDBLT_ASYNC, &ddbltfx ); if( dxresult != DD_OK ) { - msg_Warn( p_vout, "could not Blit the surface" ); + msg_Warn( p_vout, "could not blit surface (error %li)", dxresult ); return; } } else /* using overlay */ { - /* Flip the overlay buffers if we are using back buffers */ if( p_pic->p_sys->p_front_surface == p_pic->p_sys->p_surface ) + { return; + } dxresult = IDirectDrawSurface2_Flip( p_pic->p_sys->p_front_surface, NULL, DDFLIP_WAIT ); - if ( dxresult == DDERR_SURFACELOST ) + if( dxresult != DD_OK ) { - /* Our surface can be lost so be sure - * to check this and restore it if needed */ - IDirectDrawSurface2_Restore( p_vout->p_sys->p_display ); - IDirectDrawSurface2_Restore( p_pic->p_sys->p_front_surface ); - - /* Now that the surface has been restored try to display again */ - dxresult = IDirectDrawSurface2_Flip( p_pic->p_sys->p_front_surface, - NULL, DDFLIP_WAIT ); - DirectXUpdateOverlay( p_vout ); + msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult ); } - if( dxresult != DD_OK ) - msg_Warn( p_vout, "could not flip overlay surface" ); + /* set currently displayed pic */ + p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface; - if( !DirectXGetSurfaceDesc( p_pic ) ) + /* Lock surface to get all the required info */ + if( DirectXLockSurface( p_vout, p_pic ) ) { /* AAARRGG */ - msg_Err( p_vout, "cannot get surface desc" ); + msg_Warn( p_vout, "cannot lock surface" ); return; } + DirectXUnlockSurface( p_vout, 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( !UpdatePictureStruct( p_vout, p_pic, p_vout->output.i_chroma ) ) + if( ( !device.psz_string || !*device.psz_string ) && + hmon == p_vout->p_sys->hmonitor ) { - /* AAARRGG */ - msg_Err( p_vout, "invalid pic chroma" ); - return; + 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 ); - /* set currently displayed pic */ - p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface; - } + 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 */ + } -/* following functions are local */ + 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 @@ -546,30 +873,62 @@ 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" ); goto error; } - - OurDirectDrawCreate = - (void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate"); - if ( OurDirectDrawCreate == NULL ) + + OurDirectDrawCreate = + (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" ); @@ -590,18 +949,41 @@ 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 ); msg_Dbg( p_vout, "End DirectXInitDDraw" ); - return 0; + return VLC_SUCCESS; error: if( p_vout->p_sys->p_ddobject ) @@ -610,7 +992,7 @@ static int DirectXInitDDraw( vout_thread_t *p_vout ) FreeLibrary( p_vout->p_sys->hddraw_dll ); p_vout->p_sys->hddraw_dll = NULL; p_vout->p_sys->p_ddobject = NULL; - return 1; + return VLC_EGENERIC; } /***************************************************************************** @@ -624,7 +1006,6 @@ static int DirectXCreateDisplay( vout_thread_t *p_vout ) HRESULT dxresult; DDSURFACEDESC ddsd; LPDIRECTDRAWSURFACE p_display; - DDPIXELFORMAT pixel_format; msg_Dbg( p_vout, "DirectXCreateDisplay" ); @@ -636,12 +1017,11 @@ 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 direct draw primary surface" ); - return 1; + msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult ); + return VLC_EGENERIC; } dxresult = IDirectDrawSurface_QueryInterface( p_display, @@ -651,31 +1031,28 @@ static int DirectXCreateDisplay( vout_thread_t *p_vout ) IDirectDrawSurface_Release( p_display ); if ( dxresult != DD_OK ) { - msg_Err( p_vout, "cannot get IDirectDrawSurface2 interface" ); - return 1; + msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface " + "(error %li)", dxresult ); + return VLC_EGENERIC; } /* 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" ); - 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 0; + return VLC_SUCCESS; } - /***************************************************************************** * DirectXCreateClipper: Create a clipper that will be used when blitting the * RGB surface to the main display. @@ -695,17 +1072,17 @@ static int DirectXCreateClipper( vout_thread_t *p_vout ) &p_vout->p_sys->p_clipper, NULL ); if( dxresult != DD_OK ) { - msg_Warn( p_vout, "DirectXCreateClipper cannot create clipper" ); + msg_Warn( p_vout, "cannot create clipper (error %li)", dxresult ); goto error; } - /* associate the clipper to the window */ - dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0, - p_vout->p_sys->hwnd); + /* Associate the clipper to the window */ + dxresult = IDirectDrawClipper_SetHWnd( p_vout->p_sys->p_clipper, 0, + p_vout->p_sys->hvideownd ); if( dxresult != DD_OK ) { - msg_Warn( p_vout, - "DirectXCreateClipper cannot attach clipper to window" ); + msg_Warn( p_vout, "cannot attach clipper to window (error %li)", + dxresult ); goto error; } @@ -714,19 +1091,20 @@ static int DirectXCreateClipper( vout_thread_t *p_vout ) p_vout->p_sys->p_clipper); if( dxresult != DD_OK ) { - msg_Warn( p_vout, - "DirectXCreateClipper cannot attach clipper to surface" ); + msg_Warn( p_vout, "cannot attach clipper to surface (error %li)", + dxresult ); goto error; - } + } - return 0; + return VLC_SUCCESS; error: if( p_vout->p_sys->p_clipper ) + { IDirectDrawClipper_Release( p_vout->p_sys->p_clipper ); + } p_vout->p_sys->p_clipper = NULL; - return 1; - + return VLC_EGENERIC; } /***************************************************************************** @@ -764,13 +1142,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; @@ -778,12 +1152,11 @@ 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; - return 0; + return VLC_EGENERIC; } } @@ -799,9 +1172,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; @@ -819,12 +1190,11 @@ 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; - return 0; + return VLC_EGENERIC; } } @@ -835,12 +1205,27 @@ static int DirectXCreateSurface( vout_thread_t *p_vout, IDirectDrawSurface_Release( p_surface ); /* Release the old interface */ if ( dxresult != DD_OK ) { - msg_Err( p_vout, "cannot get IDirectDrawSurface2 interface" ); + msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface " + "(error %li)", dxresult ); *pp_surface_final = NULL; - return 0; + return VLC_EGENERIC; } - return 1; + if( b_overlay ) + { + /* 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( E_(DirectXUpdateOverlay)( p_vout ) != VLC_SUCCESS ) + { + IDirectDrawSurface2_Release( *pp_surface_final ); + *pp_surface_final = NULL; + msg_Err( p_vout, "overlay unuseable (might already be in use)" ); + return VLC_EGENERIC; + } + } + + return VLC_SUCCESS; } /***************************************************************************** @@ -850,15 +1235,41 @@ 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). *****************************************************************************/ -void 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->p_current_surface == NULL || - !p_vout->p_sys->b_using_overlay ) - return; + 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; + + rect_src.left = p_vout->fmt_out.i_x_offset; + rect_src.top = p_vout->fmt_out.i_y_offset; + rect_src.right = rect_src.left + p_vout->fmt_out.i_visible_width; + rect_src.bottom = rect_src.top + p_vout->fmt_out.i_visible_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 */ @@ -869,23 +1280,22 @@ void 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; } + return VLC_SUCCESS; } /***************************************************************************** @@ -907,6 +1317,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; } /***************************************************************************** @@ -957,10 +1375,12 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, int i_num_pics ) { int i; - vlc_bool_t b_result_ok; + int i_ret = VLC_SUCCESS; LPDIRECTDRAWSURFACE2 p_surface; - msg_Dbg( p_vout, "NewPictureVec" ); + msg_Dbg( p_vout, "NewPictureVec overlay:%s chroma:%.4s", + p_vout->p_sys->b_using_overlay ? "yes" : "no", + (char *)&p_vout->output.i_chroma ); I_OUTPUTPICTURES = 0; @@ -975,20 +1395,22 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, /* Triple buffering rocks! it doesn't have any processing overhead * (you don't have to wait for the vsync) and provides for a very nice * video quality (no tearing). */ + if( p_vout->p_sys->b_3buf_overlay ) + i_ret = DirectXCreateSurface( p_vout, &p_surface, + p_vout->output.i_chroma, + p_vout->p_sys->b_using_overlay, + 2 /* number of backbuffers */ ); - b_result_ok = DirectXCreateSurface( p_vout, &p_surface, - p_vout->output.i_chroma, - p_vout->p_sys->b_using_overlay, - 2 /* number of backbuffers */ ); - - if( !b_result_ok ) + if( !p_vout->p_sys->b_3buf_overlay || i_ret != VLC_SUCCESS ) + { /* Try to reduce the number of backbuffers */ - b_result_ok = DirectXCreateSurface( p_vout, &p_surface, - p_vout->output.i_chroma, - p_vout->p_sys->b_using_overlay, - 0 /* number of backbuffers */); + i_ret = DirectXCreateSurface( p_vout, &p_surface, + p_vout->output.i_chroma, + p_vout->p_sys->b_using_overlay, + 0 /* number of backbuffers */ ); + } - if( b_result_ok ) + if( i_ret == VLC_SUCCESS ) { DDSCAPS dds_caps; picture_t front_pic; @@ -1000,7 +1422,7 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, if( p_pic[0].p_sys == NULL ) { DirectXCloseSurface( p_vout, p_surface ); - return -1; + return VLC_ENOMEM; } /* set front buffer */ @@ -1022,22 +1444,22 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, p_vout->p_sys->p_current_surface = front_pic.p_sys->p_surface = p_pic[0].p_sys->p_front_surface; - /* reset the front buffer memory */ - if( DirectXGetSurfaceDesc( &front_pic ) && - UpdatePictureStruct( p_vout, &front_pic, - p_vout->output.i_chroma ) ) + /* Reset the front buffer memory */ + if( DirectXLockSurface( p_vout, &front_pic ) == VLC_SUCCESS ) { 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 ); + + DirectXUnlockSurface( p_vout, &front_pic ); } - DirectXUpdateOverlay( p_vout ); + E_(DirectXUpdateOverlay)( p_vout ); I_OUTPUTPICTURES = 1; - msg_Dbg( p_vout, "DirectX YUV overlay created successfully" ); + msg_Dbg( p_vout, "YUV overlay created successfully" ); } } @@ -1049,14 +1471,43 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, * want to display it */ if( !p_vout->p_sys->b_using_overlay ) { - if( p_vout->p_sys->b_hw_yuv ) - b_result_ok = DirectXCreateSurface( p_vout, &p_surface, - p_vout->output.i_chroma, - p_vout->p_sys->b_using_overlay, - 0 /* no back buffers */ ); + { + DWORD i_codes; + DWORD *pi_codes; + vlc_bool_t b_result = VLC_FALSE; + + /* Check if the chroma is supported first. This is required + * 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 ) == DD_OK ) + { + pi_codes = malloc( i_codes * sizeof(DWORD) ); + if( pi_codes && IDirectDraw2_GetFourCCCodes( + p_vout->p_sys->p_ddobject, &i_codes, pi_codes ) == DD_OK ) + { + for( i = 0; i < (int)i_codes; i++ ) + { + if( p_vout->output.i_chroma == pi_codes[i] ) + { + b_result = VLC_TRUE; + break; + } + } + } + } - if( !p_vout->p_sys->b_hw_yuv || !b_result_ok ) + if( b_result ) + i_ret = DirectXCreateSurface( p_vout, &p_surface, + p_vout->output.i_chroma, + 0 /* no overlay */, + 0 /* no back buffers */ ); + else + p_vout->p_sys->b_hw_yuv = VLC_FALSE; + } + + if( i_ret || !p_vout->p_sys->b_hw_yuv ) { /* Our last choice is to use a plain RGB surface */ DDPIXELFORMAT ddpfPixelFormat; @@ -1069,8 +1520,9 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, { switch( ddpfPixelFormat.dwRGBBitCount ) { - case 8: /* FIXME: set the palette */ + case 8: p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2'); + p_vout->output.pf_setpalette = SetPalette; break; case 15: p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); @@ -1086,7 +1538,7 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, break; default: msg_Err( p_vout, "unknown screen depth" ); - return 0; + return VLC_EGENERIC; } p_vout->output.i_rmask = ddpfPixelFormat.dwRBitMask; p_vout->output.i_gmask = ddpfPixelFormat.dwGBitMask; @@ -1095,27 +1547,40 @@ static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, p_vout->p_sys->b_hw_yuv = 0; - b_result_ok = DirectXCreateSurface( p_vout, &p_surface, - p_vout->output.i_chroma, - p_vout->p_sys->b_using_overlay, - 0 /* no back buffers */ ); + i_ret = DirectXCreateSurface( p_vout, &p_surface, + p_vout->output.i_chroma, + 0 /* no overlay */, + 0 /* no back buffers */ ); + + if( i_ret && !p_vout->p_sys->b_use_sysmem ) + { + /* Give it a last try with b_use_sysmem enabled */ + p_vout->p_sys->b_use_sysmem = 1; + + i_ret = DirectXCreateSurface( p_vout, &p_surface, + p_vout->output.i_chroma, + 0 /* no overlay */, + 0 /* no back buffers */ ); + } } - if( b_result_ok ) + if( i_ret == VLC_SUCCESS ) { /* Allocate internal structure */ p_pic[0].p_sys = malloc( sizeof( picture_sys_t ) ); if( p_pic[0].p_sys == NULL ) { DirectXCloseSurface( p_vout, p_surface ); - return -1; + return VLC_ENOMEM; } + p_pic[0].p_sys->p_surface = p_pic[0].p_sys->p_front_surface = p_surface; I_OUTPUTPICTURES = 1; - msg_Dbg( p_vout, "DirectX plain surface created successfully" ); + msg_Dbg( p_vout, "created plain surface of chroma:%.4s", + (char *)&p_vout->output.i_chroma ); } } @@ -1126,42 +1591,42 @@ 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]; - if( !DirectXGetSurfaceDesc( &p_pic[i] ) ) + if( DirectXLockSurface( p_vout, &p_pic[i] ) != VLC_SUCCESS ) { /* AAARRGG */ FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES ); I_OUTPUTPICTURES = 0; - return -1; - } - - if( !UpdatePictureStruct(p_vout, &p_pic[i], p_vout->output.i_chroma) ) - { - - /* Unknown chroma, tell the guy to get lost */ - msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)", - p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma ); - FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES ); - I_OUTPUTPICTURES = 0; - return -1; + msg_Err( p_vout, "cannot lock surface" ); + return VLC_EGENERIC; } + DirectXUnlockSurface( p_vout, &p_pic[i] ); } - msg_Dbg( p_vout, "End NewPictureVec"); - return 0; + msg_Dbg( p_vout, "End NewPictureVec (%s)", + I_OUTPUTPICTURES ? "succeeded" : "failed" ); + + return VLC_SUCCESS; } /***************************************************************************** * FreePicture: destroy a picture vector allocated with NewPictureVec ***************************************************************************** - * + * *****************************************************************************/ static void FreePictureVec( vout_thread_t *p_vout, picture_t *p_pic, int i_num_pics ) { 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 ); @@ -1181,7 +1646,6 @@ static void FreePictureVec( vout_thread_t *p_vout, picture_t *p_pic, static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, int i_chroma ) { - switch( p_vout->output.i_chroma ) { case VLC_FOURCC('R','G','B','2'): @@ -1191,6 +1655,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 ) { @@ -1202,11 +1667,13 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, p_pic->p->i_pixel_pitch = 2; break; case VLC_FOURCC('R','V','2','4'): + p_pic->p->i_pixel_pitch = 3; + break; case VLC_FOURCC('R','V','3','2'): p_pic->p->i_pixel_pitch = 4; break; default: - return -1; + return VLC_EGENERIC; } p_pic->p->i_visible_pitch = p_vout->output.i_width * p_pic->p->i_pixel_pitch; @@ -1214,9 +1681,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 * @@ -1225,17 +1698,19 @@ 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 * + p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * p_pic->p[V_PLANE].i_pixel_pitch; 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 * + p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * p_pic->p[U_PLANE].i_pixel_pitch; p_pic->i_planes = 3; @@ -1245,6 +1720,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 * @@ -1253,26 +1729,30 @@ 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 * + p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * p_pic->p[U_PLANE].i_pixel_pitch; 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 * + p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * p_pic->p[V_PLANE].i_pixel_pitch; 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 * @@ -1282,12 +1762,14 @@ static int UpdatePictureStruct( vout_thread_t *p_vout, picture_t *p_pic, break; default: - /* Not supported */ - return 0; - + /* Unknown chroma, tell the guy to get lost */ + msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)", + p_vout->output.i_chroma, + (char*)&p_vout->output.i_chroma ); + return VLC_EGENERIC; } - return 1; + return VLC_SUCCESS; } /***************************************************************************** @@ -1313,43 +1795,71 @@ static void DirectXGetDDrawCaps( vout_thread_t *p_vout ) } else { - BOOL bHasOverlay, bHasOverlayFourCC, bCanClipOverlay, - bHasColorKey, bCanStretch; + 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) ? 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", - bHasOverlay, bHasOverlayFourCC, bCanClipOverlay, - bHasColorKey, bCanStretch ); + "can_deinterlace_overlay=%i colorkey=%i stretch=%i " + "bltfourcc=%i", + 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; } } /***************************************************************************** - * DirectXGetSurfaceDesc: Get some more information about the surface + * DirectXLockSurface: Lock surface and get picture data pointer ***************************************************************************** - * This function get and stores the surface descriptor which among things - * has the pointer to the picture data. + * This function locks a surface and get the surface descriptor which amongst + * other things has the pointer to the picture data. *****************************************************************************/ -static int DirectXGetSurfaceDesc( picture_t *p_pic ) +static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) { HRESULT dxresult; @@ -1360,24 +1870,280 @@ static int DirectXGetSurfaceDesc( picture_t *p_pic ) NULL, &p_pic->p_sys->ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL ); - if ( dxresult == DDERR_SURFACELOST ) + if( dxresult != DD_OK ) { - /* Your surface can be lost so be sure - * to check this and restore it if needed */ - dxresult = IDirectDrawSurface2_Restore( p_pic->p_sys->p_surface ); - dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL, + if( dxresult == DDERR_INVALIDPARAMS ) + { + /* DirectX 3 doesn't support the DDLOCK_NOSYSLOCK flag, resulting + * in an invalid params error */ + dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, NULL, &p_pic->p_sys->ddsd, - DDLOCK_NOSYSLOCK | DDLOCK_WAIT, - NULL); + DDLOCK_WAIT, NULL); + } + if( dxresult == DDERR_SURFACELOST ) + { + /* Your surface can be lost so be sure + * to check this and restore it if needed */ + + /* When using overlays with back-buffers, we need to restore + * the front buffer so the back-buffers get restored as well. */ + if( p_vout->p_sys->b_using_overlay ) + IDirectDrawSurface2_Restore( p_pic->p_sys->p_front_surface ); + else + IDirectDrawSurface2_Restore( p_pic->p_sys->p_surface ); + + 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 ) + { + return VLC_EGENERIC; + } } - if( dxresult != DD_OK ) + + /* Now we have a pointer to the surface memory, we can update our picture + * structure. */ + if( UpdatePictureStruct( p_vout, p_pic, p_vout->output.i_chroma ) + != VLC_SUCCESS ) { -//X msg_Err( p_vout, "DirectXGetSurfaceDesc cannot lock surface" ); - return 0; + DirectXUnlockSurface( p_vout, p_pic ); + return VLC_EGENERIC; } + else + return VLC_SUCCESS; +} +/***************************************************************************** + * DirectXUnlockSurface: Unlock a surface locked by DirectXLockSurface(). + *****************************************************************************/ +static int DirectXUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) +{ /* Unlock the Surface */ - dxresult = IDirectDrawSurface2_Unlock( p_pic->p_sys->p_surface, NULL ); + if( IDirectDrawSurface2_Unlock( p_pic->p_sys->p_surface, NULL ) == DD_OK ) + return VLC_SUCCESS; + else + return VLC_EGENERIC; +} + +/***************************************************************************** + * DirectXFindColorkey: Finds out the 32bits RGB pixel value of the colorkey + *****************************************************************************/ +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 */ - return 1; + 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; + + 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; +} + +/***************************************************************************** + * SetPalette: sets an 8 bpp palette + *****************************************************************************/ +static void SetPalette( vout_thread_t *p_vout, + uint16_t *red, uint16_t *green, uint16_t *blue ) +{ + msg_Err( p_vout, "FIXME: SetPalette unimplemented" ); }