/*****************************************************************************
* vout.c: Windows DirectX video output display method
*****************************************************************************
- * Copyright (C) 2001 VideoLAN
- * $Id: directx.c,v 1.19 2003/05/15 22:27:37 massiot Exp $
+ * Copyright (C) 2001-2004 VideoLAN
+ * $Id$
*
- * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ * Authors: Gildas Bazin <gbazin@videolan.org>
*
* 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
#include <windows.h>
#include <ddraw.h>
+#include <multimon.h>
+#undef GetSystemMetrics
+
+#ifndef MONITOR_DEFAULTTONEAREST
+# define MONITOR_DEFAULTTONEAREST 2
+#endif
+
#include "vout.h"
/*****************************************************************************
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 );
+
+/* Object variables callbacks */
+static int FindDevicesCallback( 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\"." )
+
+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 );
+ 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") );
+
set_description( _("DirectX video output") );
set_capability( "video output", 100 );
add_shortcut( "directx" );
{
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 ) );
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;
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 = NULL;
p_vout->p_sys->i_changes = 0;
- p_vout->p_sys->b_caps_overlay_clipping = 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( "USER32" ) ) )
+ {
+ p_vout->p_sys->MonitorFromWindow =
+ GetProcAddress( huser32, "MonitorFromWindow" );
+ p_vout->p_sys->GetMonitorInfo =
+ GetProcAddress( huser32, "GetMonitorInfoA" );
+ }
+
+ 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();
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",
+ DirectXEventThread, 0, 1 ) )
{
msg_Err( p_vout, "cannot create DirectXEventThread" );
vlc_object_destroy( p_vout->p_sys->p_event );
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 );
- val.b_bool = config_GetInt( p_vout, "directx-on-top" );
- var_Set( p_vout, "directx-on-top", val );
+ /* 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 );
return VLC_SUCCESS;
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
}
/* 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;
}
static void End( vout_thread_t *p_vout )
{
FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES );
+
+ DirectXCloseDisplay( p_vout );
+ DirectXCloseDDraw( p_vout );
+
return;
}
msg_Dbg( p_vout, "CloseVideo" );
- var_Destroy( p_vout, "directs-on-top" );
-
- DirectXCloseDisplay( p_vout );
- DirectXCloseDDraw( p_vout );
-
if( p_vout->p_sys->p_event )
{
vlc_object_detach( p_vout->p_sys->p_event );
vlc_object_destroy( p_vout->p_sys->p_event );
}
+ vlc_mutex_destroy( &p_vout->p_sys->lock );
+
if( p_vout->p_sys )
{
free( p_vout->p_sys );
static int Manage( vout_thread_t *p_vout )
{
WINDOWPLACEMENT window_placement;
- HWND hwnd;
- HMENU hMenu;
- vlc_value_t val;
/* 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. */
+
/*
* Fullscreen change
*/
if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
|| p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
{
+ int i_style = 0;
+ vlc_value_t val;
+
p_vout->b_fullscreen = ! p_vout->b_fullscreen;
/* We need to switch between Maximized and Normal sized window */
GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
if( p_vout->b_fullscreen )
{
+ if( p_vout->p_sys->hparent )
+ {
+ SetParent( p_vout->p_sys->hwnd, GetDesktopWindow() );
+ SetForegroundWindow( p_vout->p_sys->hwnd );
+ }
+
/* 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 );
-
+ i_style = WS_CLIPCHILDREN;
}
else
{
+ if( p_vout->p_sys->hparent )
+ {
+ SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
+ i_style = WS_CLIPCHILDREN | WS_VISIBLE | WS_CHILD;
+ }
+ else
+ {
+ i_style = WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW |
+ WS_SIZEBOX | WS_VISIBLE;
+ }
+
/* 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 );
+
+ /* Make sure the mouse cursor is displayed */
+ PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
}
+ /* Change window style, borders and title bar */
+ SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, i_style );
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;
}
/*
* 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();
+ }
}
/*
* "Always on top" status change
*/
- hwnd = p_vout->p_sys->hwnd;
- hMenu = GetSystemMenu( hwnd , FALSE );
- var_Get( p_vout, "directx-on-top", &val );
- if( val.b_bool )
+ if( p_vout->p_sys->b_on_top_change )
{
+ 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( !( GetWindowLong( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST ) )
+ 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( hwnd, HWND_TOPMOST, 0, 0, 0, 0,
- SWP_NOSIZE | SWP_NOMOVE );
+ SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOMOVE );
}
- }
- else
- {
+ else
/* The window shouldn't be on top */
- if( GetWindowLong( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST )
+ 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( hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+ 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_display == NULL) )
{
- msg_Warn( p_vout, "no display!!" );
+ msg_Warn( p_vout, "no display!" );
return;
}
}
}
-
/* 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
*****************************************************************************
*****************************************************************************/
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 */
+ /* Load direct draw DLL */
p_vout->p_sys->hddraw_dll = LoadLibrary("DDRAW.DLL");
if( p_vout->p_sys->hddraw_dll == NULL )
{
OurDirectDrawCreate =
(void *)GetProcAddress(p_vout->p_sys->hddraw_dll, "DirectDrawCreate");
- if ( OurDirectDrawCreate == NULL )
+ if( OurDirectDrawCreate == NULL )
{
msg_Err( p_vout, "DirectXInitDDraw failed GetProcAddress" );
goto error;
}
+ OurDirectDrawEnumerateEx =
+ (void *)GetProcAddress( p_vout->p_sys->hddraw_dll,
+ "DirectDrawEnumerateExA" );
+
+ 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" );
/* 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 );
HRESULT dxresult;
DDSURFACEDESC ddsd;
LPDIRECTDRAWSURFACE p_display;
- DDPIXELFORMAT pixel_format;
msg_Dbg( p_vout, "DirectXCreateDisplay" );
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 );
/* 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 );
+ 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.
}
/* 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)",
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;
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;
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;
}
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 VLC_EGENERIC;
}
+ 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( 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;
}
* 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 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;
+
+ 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 */
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;
}
/*****************************************************************************
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;
}
/*****************************************************************************
* 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++ )
{
{
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 );
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;
}
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;
}
}
return VLC_EGENERIC;
}
else
- return VLC_SUCCESS;
+ 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 i_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 = 0x11;
+ break;
+ case 8:
+ *(uint8_t *)ddsd.lpSurface = 0x01;
+ break;
+ case 16:
+ *(uint16_t *)ddsd.lpSurface = 0x01;
+ break;
+ default:
+ *(uint32_t *)ddsd.lpSurface = 0x01;
+ 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;
+}
+
+/*****************************************************************************
+ * 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("DDRAW.DLL");
+ if( hddraw_dll == NULL ) return VLC_SUCCESS;
+
+ OurDirectDrawEnumerateEx =
+ (void *)GetProcAddress( hddraw_dll, "DirectDrawEnumerateExA" );
+
+ if( OurDirectDrawEnumerateEx )
+ {
+ /* Enumerate displays */
+ OurDirectDrawEnumerateEx( DirectXEnumCallback2, p_item,
+ DDENUM_ATTACHEDSECONDARYDEVICES );
+ }
+
+ FreeLibrary( hddraw_dll );
+
+ return VLC_SUCCESS;
+}