From: Damien Fouilleul Date: Mon, 14 May 2007 12:23:55 +0000 (+0000) Subject: vout: merged all Microsoft Windows specific vouts under msw directory in order to... X-Git-Tag: 0.9.0-test0~7448 X-Git-Url: http://git.videolan.org/gitweb.cgi/vlc.git/?p=vlc.git;p=vlc.git;a=commitdiff_plain;h=e54cc504833e241107edd85a568338e6c3fbdccf;hp=dde6aaf8b903c6b73e773f44cdbf2a62dd77cdde vout: merged all Microsoft Windows specific vouts under msw directory in order to leverage common event handling --- diff --git a/configure.ac b/configure.ac index 9082d62060..222cd7f728 100644 --- a/configure.ac +++ b/configure.ac @@ -5916,7 +5916,7 @@ AC_CONFIG_FILES([ modules/video_chroma/Makefile modules/video_filter/Makefile modules/video_output/Makefile - modules/video_output/directx/Makefile + modules/video_output/msw/Makefile modules/video_output/qte/Makefile modules/video_output/x11/Makefile modules/visualization/Makefile diff --git a/modules/video_output/Modules.am b/modules/video_output/Modules.am index 21dcb6381f..de69658934 100644 --- a/modules/video_output/Modules.am +++ b/modules/video_output/Modules.am @@ -5,8 +5,6 @@ SOURCES_ggi = ggi.c SOURCES_glide = glide.c SOURCES_vout_sdl = sdl.c SOURCES_svgalib = svgalib.c -SOURCES_wingdi = wingdi.c -SOURCES_wingapi = wingdi.c SOURCES_mga = mga.c SOURCES_hd1000v = hd1000v.cpp SOURCES_snapshot = snapshot.c diff --git a/modules/video_output/directx/Modules.am b/modules/video_output/directx/Modules.am deleted file mode 100644 index 2db1b1e809..0000000000 --- a/modules/video_output/directx/Modules.am +++ /dev/null @@ -1,17 +0,0 @@ -SOURCES_vout_directx = \ - directx.c \ - vout.h \ - events.c \ - $(NULL) - -SOURCES_direct3d = \ - direct3d.c \ - vout.h \ - events.c \ - $(NULL) - -SOURCES_glwin32 = \ - glwin32.c \ - vout.h \ - events.c \ - $(NULL) diff --git a/modules/video_output/directx/direct3d.c b/modules/video_output/directx/direct3d.c deleted file mode 100644 index ae3f3502a3..0000000000 --- a/modules/video_output/directx/direct3d.c +++ /dev/null @@ -1,1524 +0,0 @@ -/***************************************************************************** - * direct3d.c: Windows Direct3D video output module - ***************************************************************************** - * Copyright (C) 2006 the VideoLAN team - *$Id$ - * - * Authors: Damien Fouilleul - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble: - * - * This plugin will use YUV surface if supported, using YUV will result in - * the best video quality (hardware filering when rescaling the picture) - * and the fastest display as it requires less processing. - * - * If YUV overlay is not supported this plugin will use RGB offscreen video - * surfaces that will be blitted onto the primary surface (display) to - * effectively display the pictures. - * - *****************************************************************************/ -#include /* ENOMEM */ -#include /* free() */ -#include /* strerror() */ - -#include -#include -#include -#include - -#include -#include - -#include "vout.h" - -/***************************************************************************** - * Local prototypes. - *****************************************************************************/ -static int OpenVideo ( vlc_object_t * ); -static void CloseVideo ( vlc_object_t * ); - -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 FirstDisplay( vout_thread_t *, picture_t * ); - -static int Direct3DVoutCreate ( vout_thread_t * ); -static void Direct3DVoutRelease ( vout_thread_t * ); - -static int Direct3DVoutOpen ( vout_thread_t * ); -static void Direct3DVoutClose ( vout_thread_t * ); - -static int Direct3DVoutResetDevice( vout_thread_t * ); - -static int Direct3DVoutCreatePictures ( vout_thread_t *, size_t ); -static void Direct3DVoutReleasePictures ( vout_thread_t * ); - -static int Direct3DVoutLockSurface ( vout_thread_t *, picture_t * ); -static int Direct3DVoutUnlockSurface( vout_thread_t *, picture_t * ); - -static int Direct3DVoutCreateScene ( vout_thread_t * ); -static void Direct3DVoutReleaseScene ( vout_thread_t * ); -static void Direct3DVoutRenderScene ( vout_thread_t *, picture_t * ); - -/***************************************************************************** - * Module descriptor - *****************************************************************************/ - -static vlc_bool_t _got_vista_or_above; - -static int get_capability_for_osversion(void) -{ - OSVERSIONINFO winVer; - winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - if( GetVersionEx(&winVer) ) - { - if( winVer.dwMajorVersion > 5 ) - { - /* Windows Vista or above, make this module the default */ - _got_vista_or_above = VLC_TRUE; - return 150; - } - } - /* Windows XP or lower, make sure this module isn't the default */ - _got_vista_or_above = VLC_FALSE; - return 50; -} - -vlc_module_begin(); - set_shortname( "Direct3D" ); - set_category( CAT_VIDEO ); - set_subcategory( SUBCAT_VIDEO_VOUT ); - set_description( _("DirectX 3D video output") ); - set_capability( "video output", get_capability_for_osversion() ); - add_shortcut( "direct3d" ); - 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 */ - /* check if we registered a window class because we need to - * unregister it */ - WNDCLASS wndclass; - if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) - UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); -#endif - -/***************************************************************************** - * CUSTOMVERTEX: - ***************************************************************************** - *****************************************************************************/ -typedef struct -{ - FLOAT x,y,z; // vertex untransformed position - FLOAT rhw; // eye distance - D3DCOLOR diffuse; // diffuse color - FLOAT tu, tv; // texture relative coordinates -} CUSTOMVERTEX; - -#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) - -/***************************************************************************** - * OpenVideo: allocate DirectX video thread output method - ***************************************************************************** - * This function allocates and initialize the Direct3D vout method. - *****************************************************************************/ -static int OpenVideo( vlc_object_t *p_this ) -{ - vout_thread_t * p_vout = (vout_thread_t *)p_this; - vlc_value_t val; - - /* 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 VLC_ENOMEM; - } - memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); - - if( VLC_SUCCESS != Direct3DVoutCreate( p_vout ) ) - { - msg_Err( p_vout, "Direct3D could not be initialized !"); - goto error; - } - - /* Initialisations */ - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = Manage; - p_vout->pf_render = Direct3DVoutRenderScene; - p_vout->pf_display = FirstDisplay; - - 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_wallpaper = 0; - vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); - SetRectEmpty( &p_vout->p_sys->rect_display ); - SetRectEmpty( &p_vout->p_sys->rect_parent ); - - var_Create( p_vout, "directx-hw-yuv", 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 ); - var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - - p_vout->p_sys->b_cursor_hidden = 0; - p_vout->p_sys->i_lastmoved = mdate(); - - var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - - /* Set main window's size */ - p_vout->p_sys->i_window_width = p_vout->i_window_width; - p_vout->p_sys->i_window_height = p_vout->i_window_height; - - /* Create the DirectXEventThread, this thread is created by us to isolate - * the Win32 PeekMessage function calls. We want to do this because - * Windows can stay blocked inside this call for a long time, and when - * this happens it thus blocks vlc's video_output thread. - * DirectXEventThread will take care of the creation of the video - * window (because PeekMessage has to be called from the same thread which - * created the window). */ - msg_Dbg( p_vout, "creating DirectXEventThread" ); - 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", - E_(DirectXEventThread), 0, 1 ) ) - { - msg_Err( p_vout, "cannot create DirectXEventThread" ); - vlc_object_destroy( p_vout->p_sys->p_event ); - p_vout->p_sys->p_event = NULL; - goto error; - } - - if( p_vout->p_sys->p_event->b_error ) - { - msg_Err( p_vout, "DirectXEventThread failed" ); - goto error; - } - - vlc_object_attach( p_vout->p_sys->p_event, p_vout ); - - msg_Dbg( p_vout, "DirectXEventThread running" ); - - /* 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 ); - - /* disable screensaver by temporarily changing system settings */ - p_vout->p_sys->i_spi_lowpowertimeout = 0; - p_vout->p_sys->i_spi_powerofftimeout = 0; - p_vout->p_sys->i_spi_screensavetimeout = 0; - var_Get( p_vout, "disable-screensaver", &val); - if( val.b_bool ) { - msg_Dbg(p_vout, "disabling screen saver"); - SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT, - 0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0); - if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { - SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0); - } - SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, - &(p_vout->p_sys->i_spi_powerofftimeout), 0); - if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { - SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0); - } - SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, - &(p_vout->p_sys->i_spi_screensavetimeout), 0); - if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { - SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); - } - } - return VLC_SUCCESS; - - error: - CloseVideo( VLC_OBJECT(p_vout) ); - return VLC_EGENERIC; -} - -/***************************************************************************** - * CloseVideo: destroy Sys video thread output method - ***************************************************************************** - * 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; - - Direct3DVoutRelease( p_vout ); - - 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 = 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 ); - - /* restore screensaver system settings */ - if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { - SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, - p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0); - } - if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { - SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, - p_vout->p_sys->i_spi_powerofftimeout, NULL, 0); - } - if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { - SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, - p_vout->p_sys->i_spi_screensavetimeout, NULL, 0); - } - - if( p_vout->p_sys ) - { - free( p_vout->p_sys ); - p_vout->p_sys = NULL; - } -} - -/***************************************************************************** - * Init: initialize Direct3D video thread output method - *****************************************************************************/ -static int Init( vout_thread_t *p_vout ) -{ - int i_ret; - vlc_value_t val; - - var_Get( p_vout, "directx-hw-yuv", &val ); - p_vout->p_sys->b_hw_yuv = val.b_bool; - - /* Initialise Direct3D */ - if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) ) - { - msg_Err( p_vout, "cannot initialize Direct3D" ); - return VLC_EGENERIC; - } - - /* Initialize the output structure. - * Since Direct3D can do rescaling for us, stick to the default - * coordinates and aspect. */ - 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 ); - - /* create picture pool */ - i_ret = Direct3DVoutCreatePictures(p_vout, 1); - if( VLC_SUCCESS != i_ret ) - { - msg_Err(p_vout, "Direct3D picture pool initialization failed !"); - return i_ret; - } - - /* create scene */ - i_ret = Direct3DVoutCreateScene(p_vout); - if( VLC_SUCCESS != i_ret ) - { - msg_Err(p_vout, "Direct3D scene initialization failed !"); - Direct3DVoutReleasePictures(p_vout); - return i_ret; - } - - /* Change the window title bar text */ - 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; -} - -/***************************************************************************** - * End: terminate Sys video thread output method - ***************************************************************************** - * Terminate an output method created by Create. - * It is called at the end of the thread. - *****************************************************************************/ -static void End( vout_thread_t *p_vout ) -{ - Direct3DVoutReleaseScene(p_vout); - Direct3DVoutReleasePictures(p_vout); - Direct3DVoutClose( p_vout ); -} - -/***************************************************************************** - * Manage: handle Sys events - ***************************************************************************** - * This function should be called regularly by the video output thread. - * It returns a non null value if an error occurred. - *****************************************************************************/ -static int Manage( vout_thread_t *p_vout ) -{ - /* If we do not control our window, we check for geometry changes - * ourselves because the parent might not send us its events. */ - vlc_mutex_lock( &p_vout->p_sys->lock ); - if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) - { - RECT rect_parent; - POINT point; - - 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; - - SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, - rect_parent.right - rect_parent.left, - rect_parent.bottom - rect_parent.top, - SWP_NOZORDER ); - } - } - else - { - vlc_mutex_unlock( &p_vout->p_sys->lock ); - } - - /* - * Position Change - */ - if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE ) - { -#if 0 /* need that when bicubic filter is available */ - RECT rect; - UINT width, height; - - GetClientRect(p_vout->p_sys->hvideownd, &rect); - width = rect.right-rect.left; - height = rect.bottom-rect.top; - - if( (width != p_vout->p_sys->d3dpp.BackBufferWidth) - || (height != p_vout->p_sys->d3dpp.BackBufferHeight) ) - { - msg_Dbg(p_vout, "resizing device back buffers to (%lux%lu)", width, height); - // need to reset D3D device to resize back buffer - if( VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, width, height) ) - return VLC_EGENERIC; - } -#endif - p_vout->p_sys->i_changes &= ~DX_POSITION_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. */ - - /* - * Fullscreen change - */ - if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE - || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) - { - Win32ToggleFullscreen( p_vout ); - - p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - } - - /* - * Pointer change - */ - 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 ) - { - 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 VLC_EGENERIC; /* exit */ - } - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Display: displays previously rendered output - ***************************************************************************** - * This function sends the currently rendered image to the display, wait until - * it is displayed and switch the two rendering buffers, preparing next frame. - *****************************************************************************/ -static void Display( vout_thread_t *p_vout, picture_t *p_pic ) -{ - LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; - // Present the back buffer contents to the display - // stretching and filtering happens here - HRESULT hr = IDirect3DDevice9_Present(p_d3ddev, - &(p_vout->p_sys->rect_src_clipped), - NULL, NULL, NULL); - if( FAILED(hr) ) - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); -} - -/* -** this function is only used once when the first picture is received -** this function will show the video window once a picture is ready -*/ - -static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic ) -{ - /* get initial picture presented through D3D */ - Display(p_vout, p_pic); - - /* - ** Video window is initially hidden, show it now since we got a - ** picture to show. - */ - SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0, - SWP_ASYNCWINDOWPOS| - SWP_FRAMECHANGED| - SWP_SHOWWINDOW| - SWP_NOMOVE| - SWP_NOSIZE| - SWP_NOZORDER ); - - /* use and restores proper display function for further pictures */ - p_vout->pf_display = Display; -} - -/***************************************************************************** - * DirectD3DVoutCreate: Initialize and instance of Direct3D9 - ***************************************************************************** - * This function initialize Direct3D and analyze available resources from - * default adapter. - *****************************************************************************/ -static int Direct3DVoutCreate( vout_thread_t *p_vout ) -{ - HRESULT hr; - LPDIRECT3D9 p_d3dobj; - D3DCAPS9 d3dCaps; - - LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion); - - p_vout->p_sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL")); - if( NULL == p_vout->p_sys->hd3d9_dll ) - { - msg_Warn( p_vout, "cannot load d3d9.dll, aborting" ); - return VLC_EGENERIC; - } - - OurDirect3DCreate9 = - (void *)GetProcAddress( p_vout->p_sys->hd3d9_dll, - TEXT("Direct3DCreate9") ); - if( OurDirect3DCreate9 == NULL ) - { - msg_Err( p_vout, "Cannot locate reference to Direct3DCreate9 ABI in DLL" ); - return VLC_EGENERIC; - } - - /* Create the D3D object. */ - p_d3dobj = OurDirect3DCreate9( D3D_SDK_VERSION ); - if( NULL == p_d3dobj ) - { - msg_Err( p_vout, "Could not create Direct3D9 instance."); - return VLC_EGENERIC; - } - p_vout->p_sys->p_d3dobj = p_d3dobj; - - /* - ** Get device capabilities - */ - ZeroMemory(&d3dCaps, sizeof(d3dCaps)); - hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps); - if( FAILED(hr) ) - { - msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr); - return VLC_EGENERIC; - } - /* TODO: need to test device capabilities and select the right render function */ - - return VLC_SUCCESS; -} - -/***************************************************************************** - * DirectD3DVoutRelease: release an instance of Direct3D9 - *****************************************************************************/ - -static void Direct3DVoutRelease( vout_thread_t *p_vout ) -{ - if( p_vout->p_sys->p_d3dobj ) - { - IDirect3D9_Release(p_vout->p_sys->p_d3dobj); - p_vout->p_sys->p_d3dobj = NULL; - } - if( NULL != p_vout->p_sys->hd3d9_dll ) - { - FreeLibrary(p_vout->p_sys->hd3d9_dll); - p_vout->p_sys->hd3d9_dll = NULL; - } -} - -static int Direct3DFillPresentationParameters(vout_thread_t *p_vout, D3DPRESENT_PARAMETERS *d3dpp) -{ - LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; - D3DDISPLAYMODE d3ddm; - HRESULT hr; - - /* - ** Get the current desktop display mode, so we can set up a back - ** buffer of the same format - */ - hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm ); - if( FAILED(hr)) - { - msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr); - return VLC_EGENERIC; - } - - /* keep a copy of current desktop format */ - p_vout->p_sys->bbFormat = d3ddm.Format; - - /* Set up the structure used to create the D3DDevice. */ - ZeroMemory( d3dpp, sizeof(D3DPRESENT_PARAMETERS) ); - d3dpp->Flags = D3DPRESENTFLAG_VIDEO; - d3dpp->Windowed = TRUE; - d3dpp->hDeviceWindow = p_vout->p_sys->hvideownd; - d3dpp->BackBufferWidth = p_vout->output.i_width; - d3dpp->BackBufferHeight = p_vout->output.i_height; - d3dpp->SwapEffect = D3DSWAPEFFECT_COPY; - d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE; - d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - d3dpp->BackBufferFormat = d3ddm.Format; - d3dpp->BackBufferCount = 1; - d3dpp->EnableAutoDepthStencil = FALSE; - - return VLC_SUCCESS; -} - -/***************************************************************************** - * DirectD3DVoutOpen: Takes care of all the Direct3D9 initialisations - ***************************************************************************** - * This function creates Direct3D device - * this must be called from the vout thread for performance reason, as - * all Direct3D Device APIs are used in a non multithread safe environment - *****************************************************************************/ -static int Direct3DVoutOpen( vout_thread_t *p_vout ) -{ - LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; - LPDIRECT3DDEVICE9 p_d3ddev; - D3DPRESENT_PARAMETERS d3dpp; - HRESULT hr; - - if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) ) - return VLC_EGENERIC; - - // Create the D3DDevice - hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, p_vout->p_sys->hvideownd, - D3DCREATE_SOFTWARE_VERTEXPROCESSING, - &d3dpp, &p_d3ddev ); - if( FAILED(hr) ) - { - msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr); - return VLC_EGENERIC; - } - p_vout->p_sys->p_d3ddev = p_d3ddev; - - msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" ); - return VLC_SUCCESS; -} - -/***************************************************************************** - * DirectD3DClose: release the Direct3D9 device - *****************************************************************************/ -static void Direct3DVoutClose( vout_thread_t *p_vout ) -{ - if( p_vout->p_sys->p_d3ddev ) - { - IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev); - p_vout->p_sys->p_d3ddev = NULL; - } - - p_vout->p_sys->hmonitor = NULL; -} - -/***************************************************************************** - * DirectD3DClose: reset the Direct3D9 device - ***************************************************************************** - * All resources must be deallocated before the reset occur, they will be - * realllocated once the reset has been performed successfully - *****************************************************************************/ -static int Direct3DVoutResetDevice( vout_thread_t *p_vout ) -{ - LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; - D3DPRESENT_PARAMETERS d3dpp; - HRESULT hr; - - if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) ) - return VLC_EGENERIC; - - // release all D3D objects - Direct3DVoutReleaseScene( p_vout ); - Direct3DVoutReleasePictures( p_vout ); - - hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp); - if( SUCCEEDED(hr) ) - { - // re-create them - if( (VLC_SUCCESS != Direct3DVoutCreatePictures(p_vout, 1)) - || (VLC_SUCCESS != Direct3DVoutCreateScene(p_vout)) ) - { - msg_Dbg(p_vout, "%s failed !", __FUNCTION__); - return VLC_EGENERIC; - } - } - else { - msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr); - return VLC_EGENERIC; - } - return VLC_SUCCESS; -} - -static D3DFORMAT Direct3DVoutSelectFormat( vout_thread_t *p_vout, D3DFORMAT target, - const D3DFORMAT *formats, size_t count) -{ - LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; - size_t c; - - for( c=0; cp_sys->b_hw_yuv && ! _got_vista_or_above ) - { - /* it sounds like vista does not support YUV surfaces at all */ - switch( i_chroma ) - { - case VLC_FOURCC('U','Y','V','Y'): - case VLC_FOURCC('U','Y','N','V'): - case VLC_FOURCC('Y','4','2','2'): - { - static const D3DFORMAT formats[] = - { D3DFMT_UYVY, D3DFMT_YUY2, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - case VLC_FOURCC('I','4','2','0'): - case VLC_FOURCC('I','4','2','2'): - case VLC_FOURCC('Y','V','1','2'): - { - /* typically 3D textures don't support planar format - ** fallback to packed version and use CPU for the conversion - */ - static const D3DFORMAT formats[] = - { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - case VLC_FOURCC('Y','U','Y','2'): - case VLC_FOURCC('Y','U','N','V'): - { - static const D3DFORMAT formats[] = - { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - } - } - - switch( i_chroma ) - { - case VLC_FOURCC('R', 'V', '1', '5'): - { - static const D3DFORMAT formats[] = - { D3DFMT_X1R5G5B5 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - case VLC_FOURCC('R', 'V', '1', '6'): - { - static const D3DFORMAT formats[] = - { D3DFMT_R5G6B5 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - case VLC_FOURCC('R', 'V', '2', '4'): - { - static const D3DFORMAT formats[] = - { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - case VLC_FOURCC('R', 'V', '3', '2'): - { - static const D3DFORMAT formats[] = - { D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8 }; - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - default: - { - /* use display default format */ - LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; - D3DDISPLAYMODE d3ddm; - - HRESULT hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm ); - if( SUCCEEDED(hr)) - { - /* - ** some professional cards could use some advanced pixel format as default, - ** make sure we stick with chromas that we can handle internally - */ - switch( d3ddm.Format ) - { - case D3DFMT_R8G8B8: - case D3DFMT_X8R8G8B8: - case D3DFMT_A8R8G8B8: - msg_Dbg( p_vout, "defaulting to adpater pixel format"); - return Direct3DVoutSelectFormat(p_vout, target, &d3ddm.Format, 1); - default: - { - /* if we fall here, that probably means that we need to render some YUV format */ - static const D3DFORMAT formats[] = - { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 }; - msg_Dbg( p_vout, "defaulting to built-in pixel format"); - return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); - } - } - } - } - } - return D3DFMT_UNKNOWN; -} - -static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format) -{ - switch( format ) - { - case D3DFMT_YUY2: - p_vout->output.i_chroma = VLC_FOURCC('Y', 'U', 'Y', '2'); - break; - case D3DFMT_UYVY: - p_vout->output.i_chroma = VLC_FOURCC('U', 'Y', 'V', 'Y'); - break; - case D3DFMT_R8G8B8: - p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '2', '4'); - break; - case D3DFMT_X8R8G8B8: - case D3DFMT_A8R8G8B8: - p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '3', '2'); - p_vout->output.i_rmask = 0x00ff0000; - p_vout->output.i_gmask = 0x0000ff00; - p_vout->output.i_bmask = 0x000000ff; -# if defined( WORDS_BIGENDIAN ) - p_vout->output.i_rrshift = 0; - p_vout->output.i_lrshift = 24; - p_vout->output.i_rgshift = 0; - p_vout->output.i_lgshift = 16; - p_vout->output.i_rbshift = 0; - p_vout->output.i_lbshift = 8; -# else - p_vout->output.i_rrshift = 0; - p_vout->output.i_lrshift = 8; - p_vout->output.i_rgshift = 0; - p_vout->output.i_lgshift = 16; - p_vout->output.i_rbshift = 0; - p_vout->output.i_lbshift = 24; -# endif - break; - case D3DFMT_R5G6B5: - p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '6'); - p_vout->output.i_rmask = (0x1fL)<<11; - p_vout->output.i_gmask = (0x3fL)<<5; - p_vout->output.i_bmask = (0x1fL)<<0; -# if defined( WORDS_BIGENDIAN ) - p_vout->output.i_rrshift = 0; - p_vout->output.i_lrshift = 11; - p_vout->output.i_rgshift = 0; - p_vout->output.i_lgshift = 5; - p_vout->output.i_rbshift = 0; - p_vout->output.i_lbshift = 0; -# else - /* FIXME: since components are not byte aligned, - there is no chance that this will work */ - p_vout->output.i_rrshift = 0; - p_vout->output.i_lrshift = 0; - p_vout->output.i_rgshift = 0; - p_vout->output.i_lgshift = 5; - p_vout->output.i_rbshift = 0; - p_vout->output.i_lbshift = 11; -# endif - break; - case D3DFMT_X1R5G5B5: - p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '5'); - p_vout->output.i_rmask = (0x1fL)<<10; - p_vout->output.i_gmask = (0x1fL)<<5; - p_vout->output.i_bmask = (0x1fL)<<0; -# if defined( WORDS_BIGENDIAN ) - p_vout->output.i_rrshift = 0; - p_vout->output.i_lrshift = 10; - p_vout->output.i_rgshift = 0; - p_vout->output.i_lgshift = 5; - p_vout->output.i_rbshift = 0; - p_vout->output.i_lbshift = 0; -# else - /* FIXME: since components are not byte aligned, - there is no chance that this will work */ - p_vout->output.i_rrshift = 0; - p_vout->output.i_lrshift = 1; - p_vout->output.i_rgshift = 0; - p_vout->output.i_lgshift = 5; - p_vout->output.i_rbshift = 0; - p_vout->output.i_lbshift = 11; -# endif - break; - default: - return VLC_EGENERIC; - } - return VLC_SUCCESS; -} - -/***************************************************************************** - * Direct3DVoutCreatePictures: allocate a vector of identical pictures - ***************************************************************************** - * Each picture has an associated offscreen surface in video memory - * depending on hardware capabilities the picture chroma will be as close - * as possible to the orginal render chroma to reduce CPU conversion overhead - * and delegate this work to video card GPU - *****************************************************************************/ -static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics ) -{ - LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; - D3DFORMAT format; - HRESULT hr; - size_t c; - - I_OUTPUTPICTURES = 0; - - /* - ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to - ** the requested chroma which is usable by the hardware in an offscreen surface, as they - ** typically support more formats than textures - */ - format = Direct3DVoutFindFormat(p_vout, p_vout->render.i_chroma, p_vout->p_sys->bbFormat); - if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) ) - { - msg_Err(p_vout, "surface pixel format is not supported."); - return VLC_EGENERIC; - } - - for( c=0; cp_picture+c; - - hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev, - p_vout->render.i_width, - p_vout->render.i_height, - format, - D3DPOOL_DEFAULT, - &p_d3dsurf, - NULL); - if( FAILED(hr) ) - { - msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr); - Direct3DVoutReleasePictures(p_vout); - return VLC_EGENERIC; - } - - /* fill surface with black color */ - IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) ); - - /* assign surface to internal structure */ - p_pic->p_sys = (void *)p_d3dsurf; - - /* Now that we've got our direct-buffer, we can finish filling in the - * picture_t structures */ - switch( p_vout->output.i_chroma ) - { - case VLC_FOURCC('R','G','B','2'): - 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_pixel_pitch = 1; - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - break; - case VLC_FOURCC('R','V','1','5'): - case VLC_FOURCC('R','V','1','6'): - 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_pixel_pitch = 2; - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - break; - case VLC_FOURCC('R','V','2','4'): - 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_pixel_pitch = 3; - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - break; - case VLC_FOURCC('R','V','3','2'): - 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_pixel_pitch = 4; - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - break; - case VLC_FOURCC('U','Y','V','Y'): - case VLC_FOURCC('Y','U','Y','2'): - 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_pixel_pitch = 2; - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - break; - default: - Direct3DVoutReleasePictures(p_vout); - return VLC_EGENERIC; - } - p_pic->i_status = DESTROYED_PICTURE; - p_pic->i_type = DIRECT_PICTURE; - p_pic->b_slow = VLC_TRUE; - p_pic->pf_lock = Direct3DVoutLockSurface; - p_pic->pf_unlock = Direct3DVoutUnlockSurface; - PP_OUTPUTPICTURE[c] = p_pic; - - I_OUTPUTPICTURES = ++c; - } - - msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c ); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Direct3DVoutReleasePictures: destroy a picture vector - ***************************************************************************** - * release all video resources used for pictures - *****************************************************************************/ -static void Direct3DVoutReleasePictures( vout_thread_t *p_vout) -{ - size_t i_num_pics = I_OUTPUTPICTURES; - size_t c; - for( c=0; cp_picture+c; - if( p_pic->p_sys ) - { - LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; - - p_pic->p_sys = NULL; - - if( p_d3dsurf ) - { - IDirect3DSurface9_Release(p_d3dsurf); - } - } - } - msg_Dbg( p_vout, "%u Direct3D pictures released.", c); - - I_OUTPUTPICTURES = 0; -} - -/***************************************************************************** - * Direct3DVoutLockSurface: Lock surface and get picture data pointer - ***************************************************************************** - * This function locks a surface and get the surface descriptor which amongst - * other things has the pointer to the picture data. - *****************************************************************************/ -static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) -{ - HRESULT hr; - D3DLOCKED_RECT d3drect; - LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; - - if( NULL == p_d3dsurf ) - return VLC_EGENERIC; - - /* Lock the surface to get a valid pointer to the picture buffer */ - hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, 0); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return VLC_EGENERIC; - } - - /* fill in buffer info in first plane */ - p_pic->p->p_pixels = d3drect.pBits; - p_pic->p->i_pitch = d3drect.Pitch; - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface(). - *****************************************************************************/ -static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) -{ - HRESULT hr; - LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; - - if( NULL == p_d3dsurf ) - return VLC_EGENERIC; - - /* Unlock the Surface */ - hr = IDirect3DSurface9_UnlockRect(p_d3dsurf); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return VLC_EGENERIC; - } - return VLC_SUCCESS; -} - -/***************************************************************************** - * Direct3DVoutCreateScene: allocate and initialize a 3D scene - ***************************************************************************** - * for advanced blending/filtering a texture needs be used in a 3D scene. - *****************************************************************************/ - -static int Direct3DVoutCreateScene( vout_thread_t *p_vout ) -{ - LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; - LPDIRECT3DTEXTURE9 p_d3dtex; - LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; - - HRESULT hr; - - /* - ** Create a texture for use when rendering a scene - ** for performance reason, texture format is identical to backbuffer - ** which would usually be a RGB format - */ - hr = IDirect3DDevice9_CreateTexture(p_d3ddev, - p_vout->render.i_width, - p_vout->render.i_height, - 1, - D3DUSAGE_RENDERTARGET, - p_vout->p_sys->bbFormat, - D3DPOOL_DEFAULT, - &p_d3dtex, - NULL); - if( FAILED(hr)) - { - msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr); - return VLC_EGENERIC; - } - - /* - ** Create a vertex buffer for use when rendering scene - */ - hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev, - sizeof(CUSTOMVERTEX)*4, - D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, - D3DFVF_CUSTOMVERTEX, - D3DPOOL_DEFAULT, - &p_d3dvtc, - NULL); - if( FAILED(hr) ) - { - msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr); - IDirect3DTexture9_Release(p_d3dtex); - return VLC_EGENERIC; - } - - p_vout->p_sys->p_d3dtex = p_d3dtex; - p_vout->p_sys->p_d3dvtc = p_d3dvtc; - - // Texture coordinates outside the range [0.0, 1.0] are set - // to the texture color at 0.0 or 1.0, respectively. - IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - - // Set linear filtering quality - IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - - // set maximum ambient light - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255)); - - // Turn off culling - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE); - - // Turn off the zbuffer - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE); - - // Turn off lights - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE); - - // Enable dithering - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE); - - // disable stencil - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE); - - // manage blending - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE); - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE); - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10); - IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER); - - // Set texture states - IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE); - IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE); - IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE); - - // turn off alpha operation - IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); - - msg_Dbg( p_vout, "Direct3D scene created successfully"); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Direct3DVoutReleaseScene - *****************************************************************************/ -static void Direct3DVoutReleaseScene( vout_thread_t *p_vout ) -{ - LPDIRECT3DTEXTURE9 p_d3dtex = p_vout->p_sys->p_d3dtex; - LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc; - - if( p_d3dvtc ) - { - IDirect3DVertexBuffer9_Release(p_d3dvtc); - p_vout->p_sys->p_d3dvtc = NULL; - } - - if( p_d3dtex ) - { - IDirect3DTexture9_Release(p_d3dtex); - p_vout->p_sys->p_d3dtex = NULL; - } - msg_Dbg( p_vout, "Direct3D scene released successfully"); -} - -/***************************************************************************** - * Render: copy picture surface into a texture and render into a scene - ***************************************************************************** - * This function is intented for higher end 3D cards, with pixel shader support - * and at least 64 MB of video RAM. - *****************************************************************************/ -static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic ) -{ - LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; - LPDIRECT3DTEXTURE9 p_d3dtex; - LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; - LPDIRECT3DSURFACE9 p_d3dsrc, p_d3ddest; - CUSTOMVERTEX *p_vertices; - HRESULT hr; - float f_width, f_height; - - // check if device is still available - hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev); - if( FAILED(hr) ) - { - if( (D3DERR_DEVICENOTRESET != hr) - || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout)) ) - { - // device is not usable at present (lost device, out of video mem ?) - return; - } - } - p_d3dtex = p_vout->p_sys->p_d3dtex; - p_d3dvtc = p_vout->p_sys->p_d3dvtc; - - /* Clear the backbuffer and the zbuffer */ - hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET, - D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 ); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } - - /* retrieve picture surface */ - p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys; - if( NULL == p_d3dsrc ) - { - msg_Dbg( p_vout, "no surface to render ?"); - return; - } - - /* retrieve texture top-level surface */ - hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } - - /* Copy picture surface into texture surface, color space conversion happens here */ - hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE); - IDirect3DSurface9_Release(p_d3ddest); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } - - /* Update the vertex buffer */ - hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (VOID **)(&p_vertices), D3DLOCK_DISCARD); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } - - /* Setup vertices */ - f_width = (float)(p_vout->output.i_width); - f_height = (float)(p_vout->output.i_height); - - p_vertices[0].x = 0.0f; // left - p_vertices[0].y = 0.0f; // top - p_vertices[0].z = 0.0f; - p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); - p_vertices[0].rhw = 1.0f; - p_vertices[0].tu = 0.0f; - p_vertices[0].tv = 0.0f; - - p_vertices[1].x = f_width; // right - p_vertices[1].y = 0.0f; // top - p_vertices[1].z = 0.0f; - p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); - p_vertices[1].rhw = 1.0f; - p_vertices[1].tu = 1.0f; - p_vertices[1].tv = 0.0f; - - p_vertices[2].x = f_width; // right - p_vertices[2].y = f_height; // bottom - p_vertices[2].z = 0.0f; - p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); - p_vertices[2].rhw = 1.0f; - p_vertices[2].tu = 1.0f; - p_vertices[2].tv = 1.0f; - - p_vertices[3].x = 0.0f; // left - p_vertices[3].y = f_height; // bottom - p_vertices[3].z = 0.0f; - p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); - p_vertices[3].rhw = 1.0f; - p_vertices[3].tu = 0.0f; - p_vertices[3].tv = 1.0f; - - hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } - - // Begin the scene - hr = IDirect3DDevice9_BeginScene(p_d3ddev); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } - - // Setup our texture. Using textures introduces the texture stage states, - // which govern how textures get blended together (in the case of multiple - // textures) and lighting information. In this case, we are modulating - // (blending) our texture with the diffuse color of the vertices. - hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - IDirect3DDevice9_EndScene(p_d3ddev); - return; - } - - // Render the vertex buffer contents - hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX)); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - IDirect3DDevice9_EndScene(p_d3ddev); - return; - } - - // we use FVF instead of vertex shader - hr = IDirect3DDevice9_SetVertexShader(p_d3ddev, NULL); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - IDirect3DDevice9_EndScene(p_d3ddev); - return; - } - - hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - IDirect3DDevice9_EndScene(p_d3ddev); - return; - } - - // draw rectangle - hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - IDirect3DDevice9_EndScene(p_d3ddev); - return; - } - - // End the scene - hr = IDirect3DDevice9_EndScene(p_d3ddev); - if( FAILED(hr) ) - { - msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); - return; - } -} - diff --git a/modules/video_output/directx/directx.c b/modules/video_output/directx/directx.c deleted file mode 100644 index 9c86356532..0000000000 --- a/modules/video_output/directx/directx.c +++ /dev/null @@ -1,2157 +0,0 @@ -/***************************************************************************** - * vout.c: Windows DirectX video output display method - ***************************************************************************** - * Copyright (C) 2001-2004 the VideoLAN team - * $Id$ - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble: - * - * This plugin will use YUV overlay if supported, using overlay will result in - * the best video quality (hardware interpolation when rescaling the picture) - * and the fastest display as it requires less processing. - * - * If YUV overlay is not supported this plugin will use RGB offscreen video - * 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() */ -#include /* strerror() */ - -#include -#include -#include -#include - -#include -#include -#include - -#ifndef UNDER_CE -# include -#endif -#undef GetSystemMetrics - -#ifndef MONITOR_DEFAULTTONEAREST -# define MONITOR_DEFAULTTONEAREST 2 -#endif - -#include "vout.h" - -/***************************************************************************** - * picture_sys_t: direct buffer method descriptor - ***************************************************************************** - * This structure is part of the picture descriptor, it describes the - * DirectX specific properties of a direct buffer. - *****************************************************************************/ -struct picture_sys_t -{ - LPDIRECTDRAWSURFACE2 p_surface; - LPDIRECTDRAWSURFACE2 p_front_surface; - DDSURFACEDESC ddsd; -}; - -/***************************************************************************** - * DirectDraw GUIDs. - * Defining them here allows us to get rid of the dxguid library during - * the linking stage. - *****************************************************************************/ -#include -#undef GUID_EXT -#define GUID_EXT -DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 ); -DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 ); - -/***************************************************************************** - * Local prototypes. - *****************************************************************************/ -static int OpenVideo ( vlc_object_t * ); -static void CloseVideo ( vlc_object_t * ); - -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 FirstDisplay( 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 ); -static int UpdatePictureStruct( vout_thread_t *, picture_t *, int ); - -static int DirectXInitDDraw ( vout_thread_t *p_vout ); -static void DirectXCloseDDraw ( vout_thread_t *p_vout ); -static int DirectXCreateDisplay ( vout_thread_t *p_vout ); -static void DirectXCloseDisplay ( vout_thread_t *p_vout ); -static int DirectXCreateSurface ( vout_thread_t *p_vout, - LPDIRECTDRAWSURFACE2 *, int, int, int ); -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 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_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 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(); - 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 */ - /* check if we registered a window class because we need to - * unregister it */ - WNDCLASS wndclass; - if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) - UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); -#endif - -/***************************************************************************** - * OpenVideo: allocate DirectX video thread output method - ***************************************************************************** - * This function allocates and initialize the DirectX vout method. - *****************************************************************************/ -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 VLC_ENOMEM; - } - memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); - - /* Initialisations */ - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = Manage; - p_vout->pf_render = NULL; - p_vout->pf_display = FirstDisplay; - - p_vout->p_sys->p_ddobject = NULL; - 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->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_wallpaper = 0; - vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); - SetRectEmpty( &p_vout->p_sys->rect_display ); - 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 ); - var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - - p_vout->p_sys->b_cursor_hidden = 0; - p_vout->p_sys->i_lastmoved = mdate(); - - /* Set main window's size */ - p_vout->p_sys->i_window_width = p_vout->i_window_width; - p_vout->p_sys->i_window_height = p_vout->i_window_height; - - /* Create the DirectXEventThread, this thread is created by us to isolate - * the Win32 PeekMessage function calls. We want to do this because - * Windows can stay blocked inside this call for a long time, and when - * this happens it thus blocks vlc's video_output thread. - * DirectXEventThread will take care of the creation of the video - * window (because PeekMessage has to be called from the same thread which - * created the window). */ - msg_Dbg( p_vout, "creating DirectXEventThread" ); - 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", - E_(DirectXEventThread), 0, 1 ) ) - { - msg_Err( p_vout, "cannot create DirectXEventThread" ); - vlc_object_destroy( p_vout->p_sys->p_event ); - p_vout->p_sys->p_event = NULL; - goto error; - } - - if( p_vout->p_sys->p_event->b_error ) - { - msg_Err( p_vout, "DirectXEventThread failed" ); - goto error; - } - - vlc_object_attach( p_vout->p_sys->p_event, p_vout ); - - msg_Dbg( p_vout, "DirectXEventThread running" ); - - /* Initialise DirectDraw */ - if( DirectXInitDDraw( p_vout ) ) - { - msg_Err( p_vout, "cannot initialize DirectDraw" ); - goto error; - } - - /* Create the directx display */ - if( DirectXCreateDisplay( p_vout ) ) - { - msg_Err( p_vout, "cannot initialize DirectDraw" ); - goto error; - } - - /* 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 ); - - /* disable screensaver by temporarily changing system settings */ - p_vout->p_sys->i_spi_lowpowertimeout = 0; - p_vout->p_sys->i_spi_powerofftimeout = 0; - p_vout->p_sys->i_spi_screensavetimeout = 0; - var_Get( p_vout, "disable-screensaver", &val); - if( val.b_bool ) { - msg_Dbg(p_vout, "disabling screen saver"); - SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT, - 0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0); - if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { - SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0); - } - SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, - &(p_vout->p_sys->i_spi_powerofftimeout), 0); - if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { - SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0); - } - SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, - &(p_vout->p_sys->i_spi_screensavetimeout), 0); - if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { - SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); - } - } - - return VLC_SUCCESS; - - error: - CloseVideo( VLC_OBJECT(p_vout) ); - return VLC_EGENERIC; -} - -/***************************************************************************** - * Init: initialize DirectX video thread output method - ***************************************************************************** - * This function create the directx surfaces needed by the output thread. - * It is called at the beginning of the thread. - *****************************************************************************/ -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 - * coordinates and aspect. */ - 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 - * video decoder to decode directly into direct buffers as they are - * created into video memory and video memory is _really_ slow */ - - /* Choose the chroma we will try first. */ - switch( p_vout->render.i_chroma ) - { - case VLC_FOURCC('Y','U','Y','2'): - case VLC_FOURCC('Y','U','N','V'): - p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2'); - break; - case VLC_FOURCC('U','Y','V','Y'): - case VLC_FOURCC('U','Y','N','V'): - case VLC_FOURCC('Y','4','2','2'): - p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y'); - break; - case VLC_FOURCC('Y','V','Y','U'): - p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U'); - break; - default: - p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2'); - break; - } - - NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); - - i_chroma_backup = p_vout->output.i_chroma; - - if( !I_OUTPUTPICTURES ) - { - /* hmmm, it didn't work! Let's try commonly supported chromas */ - 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 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 ); - } - } - - if( !I_OUTPUTPICTURES ) - { - /* If it still didn't work then don't try to use an overlay */ - p_vout->output.i_chroma = i_chroma_backup; - p_vout->p_sys->b_using_overlay = 0; - NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); - } - - /* Change the window title bar text */ - 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; -} - -/***************************************************************************** - * End: terminate Sys video thread output method - ***************************************************************************** - * Terminate an output method created by Create. - * It is called at the end of the thread. - *****************************************************************************/ -static void End( vout_thread_t *p_vout ) -{ - FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES ); - - DirectXCloseDisplay( p_vout ); - DirectXCloseDDraw( p_vout ); - - return; -} - -/***************************************************************************** - * CloseVideo: destroy Sys video thread output method - ***************************************************************************** - * 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" ); - - 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 = 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 ); - - /* restore screensaver system settings */ - if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { - SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, - p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0); - } - if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { - SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, - p_vout->p_sys->i_spi_powerofftimeout, NULL, 0); - } - if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { - SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, - p_vout->p_sys->i_spi_screensavetimeout, NULL, 0); - } - - if( p_vout->p_sys ) - { - free( p_vout->p_sys ); - p_vout->p_sys = NULL; - } -} - -/***************************************************************************** - * Manage: handle Sys events - ***************************************************************************** - * This function should be called regularly by the video output thread. - * It returns a non null value if an error occurred. - *****************************************************************************/ -static int Manage( vout_thread_t *p_vout ) -{ - /* If we do not control our window, we check for geometry changes - * ourselves because the parent might not send us its events. */ - vlc_mutex_lock( &p_vout->p_sys->lock ); - if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) - { - RECT rect_parent; - POINT point; - - 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 - { - vlc_mutex_unlock( &p_vout->p_sys->lock ); - } - - /* - * Position Change - */ - if( p_vout->p_sys->i_changes & DX_POSITION_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 ); - } - - /* - * Fullscreen change - */ - if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE - || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) - { - Win32ToggleFullscreen( p_vout ); - - p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - } - - /* - * Pointer change - */ - 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 ) - { - 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 VLC_EGENERIC; /* exit */ - } - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Display: displays previously rendered output - ***************************************************************************** - * This function sends the currently rendered image to the display, wait until - * it is displayed and switch the two rendering buffers, preparing next frame. - *****************************************************************************/ -static void Display( vout_thread_t *p_vout, picture_t *p_pic ) -{ - HRESULT dxresult; - - if( (p_vout->p_sys->p_display == NULL) ) - { - 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; - - /* We ask for the "NOTEARING" option */ - memset( &ddbltfx, 0, sizeof(DDBLTFX) ); - ddbltfx.dwSize = sizeof(DDBLTFX); - 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 != DD_OK ) - { - 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 != DD_OK ) - { - msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult ); - } - - /* set currently displayed pic */ - p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface; - - /* Lock surface to get all the required info */ - if( DirectXLockSurface( p_vout, p_pic ) ) - { - /* AAARRGG */ - msg_Warn( p_vout, "cannot lock surface" ); - return; - } - DirectXUnlockSurface( p_vout, p_pic ); - } -} - -/* -** this function is only used once when the first picture is received -** this function will show the video window once video is ready for -** display. -*/ - -static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic ) -{ - /* get initial picture rendered on overlay surface */ - Display(p_vout, p_pic); - - IDirectDraw_WaitForVerticalBlank(p_vout->p_sys->p_ddobject, - DDWAITVB_BLOCKBEGIN, NULL); - - if( p_vout->p_sys->b_using_overlay ) - { - /* set the colorkey as the backgound brush for the video window */ - SetClassLong( p_vout->p_sys->hvideownd, GCL_HBRBACKGROUND, - (LONG)CreateSolidBrush( p_vout->p_sys->i_rgb_colorkey ) ); - } - /* - ** Video window is initially hidden, show it now since we got a - ** picture to show. - */ - SetWindowPos( p_vout->p_sys->hvideownd, NULL, 0, 0, 0, 0, - SWP_ASYNCWINDOWPOS| - SWP_FRAMECHANGED| - SWP_SHOWWINDOW| - SWP_NOMOVE| - SWP_NOSIZE| - SWP_NOZORDER ); - - /* use and restores proper display function for further pictures */ - p_vout->pf_display = Display; -} - -/* 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 %ld,%ld", 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 - ***************************************************************************** - * This function initialise and allocate resources for DirectDraw. - *****************************************************************************/ -static int DirectXInitDDraw( vout_thread_t *p_vout ) -{ - 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(_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, - _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( p_vout->p_sys->p_display_driver, - &p_ddobject, NULL ); - if( dxresult != DD_OK ) - { - msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" ); - goto error; - } - - /* Get the IDirectDraw2 interface */ - dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2, - (LPVOID *)&p_vout->p_sys->p_ddobject ); - /* Release the unused interface */ - IDirectDraw_Release( p_ddobject ); - if( dxresult != DD_OK ) - { - msg_Err( p_vout, "cannot get IDirectDraw2 interface" ); - goto error; - } - - /* Set DirectDraw Cooperative level, ie what control we want over Windows - * display */ - dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject, - 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 (%lix%li,%lix%li)", - 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 VLC_SUCCESS; - - error: - if( p_vout->p_sys->p_ddobject ) - IDirectDraw2_Release( p_vout->p_sys->p_ddobject ); - if( p_vout->p_sys->hddraw_dll ) - FreeLibrary( p_vout->p_sys->hddraw_dll ); - p_vout->p_sys->hddraw_dll = NULL; - p_vout->p_sys->p_ddobject = NULL; - return VLC_EGENERIC; -} - -/***************************************************************************** - * DirectXCreateDisplay: create the DirectDraw display. - ***************************************************************************** - * Create and initialize display according to preferences specified in the vout - * thread fields. - *****************************************************************************/ -static int DirectXCreateDisplay( vout_thread_t *p_vout ) -{ - HRESULT dxresult; - DDSURFACEDESC ddsd; - LPDIRECTDRAWSURFACE p_display; - - msg_Dbg( p_vout, "DirectXCreateDisplay" ); - - /* Now get the primary surface. This surface is what you actually see - * on your screen */ - memset( &ddsd, 0, sizeof( DDSURFACEDESC )); - ddsd.dwSize = sizeof(DDSURFACEDESC); - ddsd.dwFlags = DDSD_CAPS; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; - - dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, - &ddsd, &p_display, NULL ); - if( dxresult != DD_OK ) - { - msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult ); - return VLC_EGENERIC; - } - - dxresult = IDirectDrawSurface_QueryInterface( p_display, - &IID_IDirectDrawSurface2, - (LPVOID *)&p_vout->p_sys->p_display ); - /* Release the old interface */ - IDirectDrawSurface_Release( p_display ); - if ( dxresult != DD_OK ) - { - 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 ); - - E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * DirectXCreateClipper: Create a clipper that will be used when blitting the - * RGB surface to the main display. - ***************************************************************************** - * This clipper prevents us to modify by mistake anything on the screen - * which doesn't belong to our window. For example when a part of our video - * window is hidden by another window. - *****************************************************************************/ -static int DirectXCreateClipper( vout_thread_t *p_vout ) -{ - HRESULT dxresult; - - msg_Dbg( p_vout, "DirectXCreateClipper" ); - - /* Create the clipper */ - dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0, - &p_vout->p_sys->p_clipper, NULL ); - if( dxresult != DD_OK ) - { - 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->hvideownd ); - if( dxresult != DD_OK ) - { - msg_Warn( p_vout, "cannot attach clipper to window (error %li)", - dxresult ); - goto error; - } - - /* associate the clipper with the surface */ - dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display, - p_vout->p_sys->p_clipper); - if( dxresult != DD_OK ) - { - msg_Warn( p_vout, "cannot attach clipper to surface (error %li)", - dxresult ); - goto error; - } - - 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 VLC_EGENERIC; -} - -/***************************************************************************** - * DirectXCreateSurface: create an YUV overlay or RGB surface for the video. - ***************************************************************************** - * The best method of display is with an YUV overlay because the YUV->RGB - * conversion is done in hardware. - * You can also create a plain RGB surface. - * ( Maybe we could also try an RGB overlay surface, which could have hardware - * scaling and which would also be faster in window mode because you don't - * need to do any blitting to the main display...) - *****************************************************************************/ -static int DirectXCreateSurface( vout_thread_t *p_vout, - LPDIRECTDRAWSURFACE2 *pp_surface_final, - int i_chroma, int b_overlay, - int i_backbuffers ) -{ - HRESULT dxresult; - LPDIRECTDRAWSURFACE p_surface; - DDSURFACEDESC ddsd; - - /* Create the video surface */ - if( b_overlay ) - { - /* Now try to create the YUV overlay surface. - * This overlay will be displayed on top of the primary surface. - * A color key is used to determine whether or not the overlay will be - * displayed, ie the overlay will be displayed in place of the primary - * surface wherever the primary surface will have this color. - * The video window has been created with a background of this color so - * the overlay will be only displayed on top of this window */ - - memset( &ddsd, 0, sizeof( DDSURFACEDESC )); - ddsd.dwSize = sizeof(DDSURFACEDESC); - 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 |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0); - 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.dwWidth = p_vout->render.i_width; - ddsd.dwBackBufferCount = i_backbuffers; - - dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, - &ddsd, &p_surface, NULL ); - if( dxresult != DD_OK ) - { - *pp_surface_final = NULL; - return VLC_EGENERIC; - } - } - - if( !b_overlay ) - { - vlc_bool_t b_rgb_surface = - ( i_chroma == VLC_FOURCC('R','G','B','2') ) - || ( i_chroma == VLC_FOURCC('R','V','1','5') ) - || ( i_chroma == VLC_FOURCC('R','V','1','6') ) - || ( i_chroma == VLC_FOURCC('R','V','2','4') ) - || ( i_chroma == VLC_FOURCC('R','V','3','2') ); - - memset( &ddsd, 0, sizeof( DDSURFACEDESC ) ); - ddsd.dwSize = sizeof(DDSURFACEDESC); - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - 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; - - if( p_vout->p_sys->b_use_sysmem ) - ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; - else - ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; - - if( !b_rgb_surface ) - { - ddsd.dwFlags |= DDSD_PIXELFORMAT; - ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; - ddsd.ddpfPixelFormat.dwFourCC = i_chroma; - } - - dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, - &ddsd, &p_surface, NULL ); - if( dxresult != DD_OK ) - { - *pp_surface_final = NULL; - return VLC_EGENERIC; - } - } - - /* Now that the surface is created, try to get a newer DirectX interface */ - dxresult = IDirectDrawSurface_QueryInterface( p_surface, - &IID_IDirectDrawSurface2, - (LPVOID *)pp_surface_final ); - IDirectDrawSurface_Release( p_surface ); /* Release the old interface */ - if ( dxresult != DD_OK ) - { - msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface " - "(error %li)", dxresult ); - *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( 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; -} - -/***************************************************************************** - * DirectXUpdateOverlay: Move or resize overlay surface on video display. - ***************************************************************************** - * This function is used to move or resize an overlay surface on the screen. - * Ususally the overlay is moved by the user and thus, by a move or resize - * event (in Manage). - *****************************************************************************/ -int E_(DirectXUpdateOverlay)( vout_thread_t *p_vout ) -{ - DDOVERLAYFX ddofx; - DWORD dwFlags; - HRESULT dxresult; - RECT rect_src = p_vout->p_sys->rect_src_clipped; - RECT rect_dest = p_vout->p_sys->rect_dest_clipped; - - if( !p_vout->p_sys->b_using_overlay ) return VLC_EGENERIC; - - if( p_vout->p_sys->b_wallpaper ) - { - int i_x, i_y, i_width, i_height; - - 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 */ - - /* Position and show the overlay */ - memset(&ddofx, 0, sizeof(DDOVERLAYFX)); - ddofx.dwSize = sizeof(DDOVERLAYFX); - ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey; - ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey; - - dwFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE; - - dxresult = IDirectDrawSurface2_UpdateOverlay( - 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/resize overlay" ); - return VLC_EGENERIC; - } - - return VLC_SUCCESS; -} - -/***************************************************************************** - * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw - ***************************************************************************** - * This function returns all resources allocated by DirectXInitDDraw. - *****************************************************************************/ -static void DirectXCloseDDraw( vout_thread_t *p_vout ) -{ - msg_Dbg( p_vout, "DirectXCloseDDraw" ); - if( p_vout->p_sys->p_ddobject != NULL ) - { - IDirectDraw2_Release(p_vout->p_sys->p_ddobject); - p_vout->p_sys->p_ddobject = NULL; - } - - if( p_vout->p_sys->hddraw_dll != NULL ) - { - 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; -} - -/***************************************************************************** - * DirectXCloseDisplay: close and reset the DirectX display device - ***************************************************************************** - * This function returns all resources allocated by DirectXCreateDisplay. - *****************************************************************************/ -static void DirectXCloseDisplay( vout_thread_t *p_vout ) -{ - msg_Dbg( p_vout, "DirectXCloseDisplay" ); - - if( p_vout->p_sys->p_clipper != NULL ) - { - msg_Dbg( p_vout, "DirectXCloseDisplay clipper" ); - IDirectDrawClipper_Release( p_vout->p_sys->p_clipper ); - p_vout->p_sys->p_clipper = NULL; - } - - if( p_vout->p_sys->p_display != NULL ) - { - msg_Dbg( p_vout, "DirectXCloseDisplay display" ); - IDirectDrawSurface2_Release( p_vout->p_sys->p_display ); - p_vout->p_sys->p_display = NULL; - } -} - -/***************************************************************************** - * DirectXCloseSurface: close the YUV overlay or RGB surface. - ***************************************************************************** - * This function returns all resources allocated for the surface. - *****************************************************************************/ -static void DirectXCloseSurface( vout_thread_t *p_vout, - LPDIRECTDRAWSURFACE2 p_surface ) -{ - msg_Dbg( p_vout, "DirectXCloseSurface" ); - if( p_surface != NULL ) - { - IDirectDrawSurface2_Release( p_surface ); - } -} - -/***************************************************************************** - * NewPictureVec: allocate a vector of identical pictures - ***************************************************************************** - * Returns 0 on success, -1 otherwise - *****************************************************************************/ -static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, - int i_num_pics ) -{ - int i; - int i_ret = VLC_SUCCESS; - LPDIRECTDRAWSURFACE2 p_surface; - - 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; - - /* First we try to use an YUV overlay surface. - * The overlay surface that we create won't be used to decode directly - * into it because accessing video memory directly is way to slow (remember - * that pictures are decoded macroblock per macroblock). Instead the video - * will be decoded in picture buffers in system memory which will then be - * memcpy() to the overlay surface. */ - if( p_vout->p_sys->b_using_overlay ) - { - /* 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 */ ); - - if( !p_vout->p_sys->b_3buf_overlay || i_ret != VLC_SUCCESS ) - { - /* Try to reduce the 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( i_ret == VLC_SUCCESS ) - { - DDSCAPS dds_caps; - picture_t front_pic; - picture_sys_t front_pic_sys; - front_pic.p_sys = &front_pic_sys; - - /* 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 VLC_ENOMEM; - } - - /* set front buffer */ - p_pic[0].p_sys->p_front_surface = p_surface; - - /* Get the back buffer */ - memset( &dds_caps, 0, sizeof( DDSCAPS ) ); - dds_caps.dwCaps = DDSCAPS_BACKBUFFER; - if( DD_OK != IDirectDrawSurface2_GetAttachedSurface( - p_surface, &dds_caps, - &p_pic[0].p_sys->p_surface ) ) - { - msg_Warn( p_vout, "NewPictureVec could not get back buffer" ); - /* front buffer is the same as back buffer */ - p_pic[0].p_sys->p_surface = p_surface; - } - - - 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( 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_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 ); - } - - E_(DirectXUpdateOverlay)( p_vout ); - I_OUTPUTPICTURES = 1; - msg_Dbg( p_vout, "YUV overlay created successfully" ); - } - } - - /* As we can't have an overlay, we'll try to create a plain offscreen - * surface. This surface will reside in video memory because there's a - * better chance then that we'll be able to use some kind of hardware - * acceleration like rescaling, blitting or YUV->RGB conversions. - * We then only need to blit this surface onto the main display when we - * want to display it */ - if( !p_vout->p_sys->b_using_overlay ) - { - if( p_vout->p_sys->b_hw_yuv ) - { - 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( 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; - - ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - IDirectDrawSurface2_GetPixelFormat( p_vout->p_sys->p_display, - &ddpfPixelFormat ); - - if( ddpfPixelFormat.dwFlags & DDPF_RGB ) - { - switch( ddpfPixelFormat.dwRGBBitCount ) - { - 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'); - break; - case 16: - p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); - break; - case 24: - p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); - break; - case 32: - p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); - break; - default: - msg_Err( p_vout, "unknown screen depth" ); - return VLC_EGENERIC; - } - p_vout->output.i_rmask = ddpfPixelFormat.dwRBitMask; - p_vout->output.i_gmask = ddpfPixelFormat.dwGBitMask; - p_vout->output.i_bmask = ddpfPixelFormat.dwBBitMask; - } - - p_vout->p_sys->b_hw_yuv = 0; - - 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( 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 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, "created plain surface of chroma:%.4s", - (char *)&p_vout->output.i_chroma ); - } - } - - - /* Now that we've got all our direct-buffers, we can finish filling in the - * picture_t structures */ - for( i = 0; i < I_OUTPUTPICTURES; i++ ) - { - 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( DirectXLockSurface( p_vout, &p_pic[i] ) != VLC_SUCCESS ) - { - /* AAARRGG */ - FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES ); - I_OUTPUTPICTURES = 0; - msg_Err( p_vout, "cannot lock surface" ); - return VLC_EGENERIC; - } - DirectXUnlockSurface( p_vout, &p_pic[i] ); - } - - 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 ); - - for( i = 0; i < i_num_pics; i++ ) - { - free( p_pic[i].p_sys ); - } - } -} - -/***************************************************************************** - * UpdatePictureStruct: updates the internal data in the picture_t structure - ***************************************************************************** - * This will setup stuff for use by the video_output thread - *****************************************************************************/ -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'): - case VLC_FOURCC('R','V','1','5'): - case VLC_FOURCC('R','V','1','6'): - case VLC_FOURCC('R','V','2','4'): - 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 ) - { - case VLC_FOURCC('R','G','B','2'): - p_pic->p->i_pixel_pitch = 1; - break; - case VLC_FOURCC('R','V','1','5'): - case VLC_FOURCC('R','V','1','6'): - 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 VLC_EGENERIC; - } - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - 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 * - p_pic->p[Y_PLANE].i_pixel_pitch; - - p_pic->V_PIXELS = p_pic->Y_PIXELS - + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch; - p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2; - p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2; - p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; - p_pic->p[V_PLANE].i_pixel_pitch = 1; - p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * - 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 / 2 * - p_pic->p[U_PLANE].i_pixel_pitch; - - p_pic->i_planes = 3; - break; - - case VLC_FOURCC('I','Y','U','V'): - - 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 * - p_pic->p[Y_PLANE].i_pixel_pitch; - - p_pic->U_PIXELS = p_pic->Y_PIXELS - + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch; - p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2; - p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2; - p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; - p_pic->p[U_PLANE].i_pixel_pitch = 1; - p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * - 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 / 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 * - p_pic->p->i_pixel_pitch; - - p_pic->i_planes = 1; - break; - - default: - /* 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 VLC_SUCCESS; -} - -/***************************************************************************** - * DirectXGetDDrawCaps: Probe the capabilities of the hardware - ***************************************************************************** - * It is nice to know which features are supported by the hardware so we can - * find ways to optimize our rendering. - *****************************************************************************/ -static void DirectXGetDDrawCaps( vout_thread_t *p_vout ) -{ - DDCAPS ddcaps; - HRESULT dxresult; - - /* This is just an indication of whether or not we'll support overlay, - * but with this test we don't know if we support YUV overlay */ - memset( &ddcaps, 0, sizeof( DDCAPS )); - ddcaps.dwSize = sizeof(DDCAPS); - dxresult = IDirectDraw2_GetCaps( p_vout->p_sys->p_ddobject, - &ddcaps, NULL ); - if(dxresult != DD_OK ) - { - msg_Warn( p_vout, "cannot get caps" ); - } - else - { - 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) ? 1 : 0; - /* Determine if the hardware supports overlay surfaces */ - 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) ? 1 : 0; - /* Determine if the hardware supports scaling of the overlay surface */ - 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_deinterlace_overlay=%i colorkey=%i stretch=%i " - "bltfourcc=%i", - bHasOverlay, bHasOverlayFourCC, bCanDeinterlace, - bHasColorKey, bCanStretch, bCanBltFourcc ); - - 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; - } -} - -/***************************************************************************** - * DirectXLockSurface: Lock surface and get picture data pointer - ***************************************************************************** - * This function locks a surface and get the surface descriptor which amongst - * other things has the pointer to the picture data. - *****************************************************************************/ -static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) -{ - HRESULT dxresult; - - /* Lock the surface to get a valid pointer to the picture buffer */ - memset( &p_pic->p_sys->ddsd, 0, sizeof( DDSURFACEDESC )); - p_pic->p_sys->ddsd.dwSize = sizeof(DDSURFACEDESC); - dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, - NULL, &p_pic->p_sys->ddsd, - DDLOCK_NOSYSLOCK | DDLOCK_WAIT, - NULL ); - if( dxresult != DD_OK ) - { - 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_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; - } - } - - /* 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 ) - { - 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 */ - 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 */ - - 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" ); -} diff --git a/modules/video_output/directx/events.c b/modules/video_output/directx/events.c deleted file mode 100644 index 646563ed83..0000000000 --- a/modules/video_output/directx/events.c +++ /dev/null @@ -1,1179 +0,0 @@ -/***************************************************************************** - * events.c: Windows DirectX video output events handler - ***************************************************************************** - * Copyright (C) 2001-2004 the VideoLAN team - * $Id$ - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - - -/***************************************************************************** - * Preamble: This file contains the functions related to the creation of - * a window and the handling of its messages (events). - *****************************************************************************/ -#include /* ENOMEM */ -#include /* free() */ -#include /* tolower() */ -#include /* strerror() */ - -#ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0500 -#endif - -#include -#include -#include -#include - -#include -#include -#include - -#ifdef MODULE_NAME_IS_vout_directx -#include -#endif -#ifdef MODULE_NAME_IS_direct3d -#include -#endif -#ifdef MODULE_NAME_IS_glwin32 -#include -#endif - -#include "vlc_keys.h" -#include "vout.h" - -/***************************************************************************** - * Local prototypes. - *****************************************************************************/ -static int DirectXCreateWindow( vout_thread_t *p_vout ); -static void DirectXCloseWindow ( vout_thread_t *p_vout ); -static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM ); - -static int Control( vout_thread_t *p_vout, int i_query, va_list args ); - -static void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open ) -{ - playlist_t *p_playlist = - vlc_object_find( p_event, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); - if( p_playlist != NULL ) - { - vlc_value_t val; - val.b_bool = b_open; - var_Set( p_playlist, "intf-popupmenu", val ); - vlc_object_release( p_playlist ); - } -} - -static int DirectXConvertKey( int i_key ); - -/***************************************************************************** - * DirectXEventThread: Create video window & handle its messages - ***************************************************************************** - * This function creates a video window and then enters an infinite loop - * that handles the messages sent to that window. - * The main goal of this thread is to isolate the Win32 PeekMessage function - * because this one can block for a long time. - *****************************************************************************/ -void E_(DirectXEventThread)( event_thread_t *p_event ) -{ - MSG msg; - POINT old_mouse_pos = {0,0}, mouse_pos; - vlc_value_t val; - int i_width, i_height, i_x, i_y; - HMODULE hkernel32; - - /* Initialisation */ - p_event->p_vout->pf_control = Control; - - /* Create a window for the video */ - /* Creating a window under Windows also initializes the thread's event - * message queue */ - if( DirectXCreateWindow( p_event->p_vout ) ) - { - msg_Err( p_event, "out of memory" ); - p_event->b_dead = VLC_TRUE; - } - - /* Signal the creation of the window */ - vlc_thread_ready( p_event ); - - /* Set power management stuff */ - if( (hkernel32 = GetModuleHandle( _T("KERNEL32") ) ) ) - { - ULONG (WINAPI* OurSetThreadExecutionState)( ULONG ); - - OurSetThreadExecutionState = (ULONG (WINAPI*)( ULONG )) - GetProcAddress( hkernel32, _T("SetThreadExecutionState") ); - - if( OurSetThreadExecutionState ) - /* Prevent monitor from powering off */ - OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS ); - else - msg_Dbg( p_event, "no support for SetThreadExecutionState()" ); - } - - /* Main loop */ - /* GetMessage will sleep if there's no message in the queue */ - while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) ) - { - /* Check if we are asked to exit */ - if( p_event->b_die ) - break; - - switch( msg.message ) - { - - case WM_MOUSEMOVE: - vout_PlacePicture( p_event->p_vout, - p_event->p_vout->p_sys->i_window_width, - p_event->p_vout->p_sys->i_window_height, - &i_x, &i_y, &i_width, &i_height ); - - if( msg.hwnd == p_event->p_vout->p_sys->hvideownd ) - { - /* Child window */ - i_x = i_y = 0; - } - - if( i_width && i_height ) - { - val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x ) * - p_event->p_vout->fmt_in.i_visible_width / i_width + - p_event->p_vout->fmt_in.i_x_offset; - var_Set( p_event->p_vout, "mouse-x", val ); - val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y ) * - p_event->p_vout->fmt_in.i_visible_height / i_height + - p_event->p_vout->fmt_in.i_y_offset; - var_Set( p_event->p_vout, "mouse-y", val ); - - val.b_bool = VLC_TRUE; - var_Set( p_event->p_vout, "mouse-moved", val ); - } - - case WM_NCMOUSEMOVE: - GetCursorPos( &mouse_pos ); - if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 || - (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) ) - { - GetCursorPos( &old_mouse_pos ); - p_event->p_vout->p_sys->i_lastmoved = mdate(); - - if( p_event->p_vout->p_sys->b_cursor_hidden ) - { - p_event->p_vout->p_sys->b_cursor_hidden = 0; - ShowCursor( TRUE ); - } - } - break; - - case WM_VLC_HIDE_MOUSE: - if( p_event->p_vout->p_sys->b_cursor_hidden ) break; - p_event->p_vout->p_sys->b_cursor_hidden = VLC_TRUE; - GetCursorPos( &old_mouse_pos ); - ShowCursor( FALSE ); - break; - - case WM_VLC_SHOW_MOUSE: - if( !p_event->p_vout->p_sys->b_cursor_hidden ) break; - p_event->p_vout->p_sys->b_cursor_hidden = VLC_FALSE; - GetCursorPos( &old_mouse_pos ); - ShowCursor( TRUE ); - break; - - case WM_LBUTTONDOWN: - var_Get( p_event->p_vout, "mouse-button-down", &val ); - val.i_int |= 1; - var_Set( p_event->p_vout, "mouse-button-down", val ); - DirectXPopupMenu( p_event, VLC_FALSE ); - break; - - case WM_LBUTTONUP: - var_Get( p_event->p_vout, "mouse-button-down", &val ); - val.i_int &= ~1; - var_Set( p_event->p_vout, "mouse-button-down", val ); - - val.b_bool = VLC_TRUE; - var_Set( p_event->p_vout, "mouse-clicked", val ); - break; - - case WM_LBUTTONDBLCLK: - p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE; - break; - - case WM_MBUTTONDOWN: - var_Get( p_event->p_vout, "mouse-button-down", &val ); - val.i_int |= 2; - var_Set( p_event->p_vout, "mouse-button-down", val ); - DirectXPopupMenu( p_event, VLC_FALSE ); - break; - - case WM_MBUTTONUP: - var_Get( p_event->p_vout, "mouse-button-down", &val ); - val.i_int &= ~2; - var_Set( p_event->p_vout, "mouse-button-down", val ); - break; - - case WM_RBUTTONDOWN: - var_Get( p_event->p_vout, "mouse-button-down", &val ); - val.i_int |= 4; - var_Set( p_event->p_vout, "mouse-button-down", val ); - DirectXPopupMenu( p_event, VLC_FALSE ); - break; - - case WM_RBUTTONUP: - var_Get( p_event->p_vout, "mouse-button-down", &val ); - val.i_int &= ~4; - var_Set( p_event->p_vout, "mouse-button-down", val ); - DirectXPopupMenu( p_event, VLC_TRUE ); - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - /* The key events are first processed here and not translated - * into WM_CHAR events because we need to know the status of the - * modifier keys. */ - val.i_int = DirectXConvertKey( msg.wParam ); - if( !val.i_int ) - { - /* This appears to be a "normal" (ascii) key */ - val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) ); - } - - if( val.i_int ) - { - if( GetKeyState(VK_CONTROL) & 0x8000 ) - { - val.i_int |= KEY_MODIFIER_CTRL; - } - if( GetKeyState(VK_SHIFT) & 0x8000 ) - { - val.i_int |= KEY_MODIFIER_SHIFT; - } - if( GetKeyState(VK_MENU) & 0x8000 ) - { - val.i_int |= KEY_MODIFIER_ALT; - } - - var_Set( p_event->p_libvlc, "key-pressed", val ); - } - break; - - case WM_MOUSEWHEEL: - if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 ) - { - val.i_int = KEY_MOUSEWHEELUP; - } - else - { - val.i_int = KEY_MOUSEWHEELDOWN; - } - if( val.i_int ) - { - if( GetKeyState(VK_CONTROL) & 0x8000 ) - { - val.i_int |= KEY_MODIFIER_CTRL; - } - if( GetKeyState(VK_SHIFT) & 0x8000 ) - { - val.i_int |= KEY_MODIFIER_SHIFT; - } - if( GetKeyState(VK_MENU) & 0x8000 ) - { - val.i_int |= KEY_MODIFIER_ALT; - } - - var_Set( p_event->p_libvlc, "key-pressed", val ); - } - break; - - case WM_VLC_CHANGE_TEXT: - var_Get( p_event->p_vout, "video-title", &val ); - if( !val.psz_string || !*val.psz_string ) /* Default video title */ - { - if( val.psz_string ) free( val.psz_string ); - -#ifdef MODULE_NAME_IS_glwin32 - val.psz_string = strdup( VOUT_TITLE " (OpenGL output)" ); -#endif -#ifdef MODULE_NAME_IS_direct3d - val.psz_string = strdup( VOUT_TITLE " (Direct3D output)" ); -#endif -#ifdef MODULE_NAME_IS_vout_directx - if( p_event->p_vout->p_sys->b_using_overlay ) val.psz_string = - strdup( VOUT_TITLE " (hardware YUV overlay DirectX output)" ); - else if( p_event->p_vout->p_sys->b_hw_yuv ) val.psz_string = - strdup( VOUT_TITLE " (hardware YUV DirectX output)" ); - else val.psz_string = - strdup( VOUT_TITLE " (software RGB DirectX output)" ); -#endif - } - -#ifdef UNICODE - { - wchar_t *psz_title = malloc( strlen(val.psz_string) * 2 + 2 ); - mbstowcs( psz_title, val.psz_string, strlen(val.psz_string)*2); - psz_title[strlen(val.psz_string)] = 0; - free( val.psz_string ); val.psz_string = (char *)psz_title; - } -#endif - - SetWindowText( p_event->p_vout->p_sys->hwnd, - (LPCTSTR)val.psz_string ); - if( p_event->p_vout->p_sys->hfswnd ) - SetWindowText( p_event->p_vout->p_sys->hfswnd, - (LPCTSTR)val.psz_string ); - free( val.psz_string ); - break; - - default: - /* Messages we don't handle directly are dispatched to the - * window procedure */ - TranslateMessage(&msg); - DispatchMessage(&msg); - break; - - } /* End Switch */ - - } /* End Main loop */ - - /* Check for WM_QUIT if we created the window */ - if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT ) - { - msg_Warn( p_event, "WM_QUIT... should not happen!!" ); - p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */ - } - - msg_Dbg( p_event, "DirectXEventThread terminating" ); - - /* clear the changes formerly signaled */ - p_event->p_vout->p_sys->i_changes = 0; - - DirectXCloseWindow( p_event->p_vout ); -} - - -/* following functions are local */ - -/***************************************************************************** - * DirectXCreateWindow: create a window for the video. - ***************************************************************************** - * Before creating a direct draw surface, we need to create a window in which - * the video will be displayed. This window will also allow us to capture the - * events. - *****************************************************************************/ -static int DirectXCreateWindow( vout_thread_t *p_vout ) -{ - HINSTANCE hInstance; - HMENU hMenu; - RECT rect_window; - WNDCLASS wc; /* window class components */ - HICON vlc_icon = NULL; - char vlc_path[MAX_PATH+1]; - int i_style, i_stylex; - - msg_Dbg( p_vout, "DirectXCreateWindow" ); - - /* Get this module's instance */ - hInstance = GetModuleHandle(NULL); - - /* If an external window was specified, we'll draw in it. */ - p_vout->p_sys->hparent = - vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x, - &p_vout->p_sys->i_window_y, - &p_vout->p_sys->i_window_width, - &p_vout->p_sys->i_window_height ); - - /* We create the window ourself, there is no previous window proc. */ - p_vout->p_sys->pf_wndproc = NULL; - - /* Get the Icon from the main app */ - vlc_icon = NULL; -#ifndef UNDER_CE - if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) ) - { - vlc_icon = ExtractIcon( hInstance, vlc_path, 0 ); - } -#endif - - /* Fill in the window class structure */ - wc.style = CS_OWNDC|CS_DBLCLKS; /* style: dbl click */ - wc.lpfnWndProc = (WNDPROC)DirectXEventProc; /* event handler */ - wc.cbClsExtra = 0; /* no extra class data */ - wc.cbWndExtra = 0; /* no extra window data */ - wc.hInstance = hInstance; /* instance */ - wc.hIcon = vlc_icon; /* load the vlc big icon */ - wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* default cursor */ - wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* background color */ - wc.lpszMenuName = NULL; /* no menu */ - wc.lpszClassName = _T("VLC DirectX"); /* use a special class */ - - /* Register the window class */ - if( !RegisterClass(&wc) ) - { - WNDCLASS wndclass; - - if( vlc_icon ) DestroyIcon( vlc_icon ); - - /* Check why it failed. If it's because one already exists - * then fine, otherwise return with an error. */ - if( !GetClassInfo( hInstance, _T("VLC DirectX"), &wndclass ) ) - { - msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() ); - return VLC_EGENERIC; - } - } - - /* Register the video sub-window class */ - wc.lpszClassName = _T("VLC DirectX video"); wc.hIcon = 0; - wc.hbrBackground = NULL; /* no background color */ - if( !RegisterClass(&wc) ) - { - WNDCLASS wndclass; - - /* Check why it failed. If it's because one already exists - * then fine, otherwise return with an error. */ - if( !GetClassInfo( hInstance, _T("VLC DirectX video"), &wndclass ) ) - { - msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() ); - return VLC_EGENERIC; - } - } - - /* When you create a window you give the dimensions you wish it to - * have. Unfortunatly these dimensions will include the borders and - * titlebar. We use the following function to find out the size of - * the window corresponding to the useable surface we want */ - rect_window.top = 10; - rect_window.left = 10; - rect_window.right = rect_window.left + p_vout->p_sys->i_window_width; - rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height; - - if( var_GetBool( p_vout, "video-deco" ) ) - { - /* Open with window decoration */ - AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 ); - i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN; - i_stylex = 0; - } - else - { - /* No window decoration */ - AdjustWindowRect( &rect_window, WS_POPUP, 0 ); - i_style = WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN; - i_stylex = 0; // WS_EX_TOOLWINDOW; Is TOOLWINDOW really needed ? - // It messes up the fullscreen window. - } - - if( p_vout->p_sys->hparent ) - { - i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD; - i_stylex = 0; - } - - p_vout->p_sys->i_window_style = i_style; - - /* Create the window */ - p_vout->p_sys->hwnd = - CreateWindowEx( WS_EX_NOPARENTNOTIFY | i_stylex, - _T("VLC DirectX"), /* name of window class */ - _T(VOUT_TITLE) _T(" (DirectX Output)"), /* window title */ - i_style, /* window style */ - (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT : - (UINT)p_vout->p_sys->i_window_x, /* default X coordinate */ - (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT : - (UINT)p_vout->p_sys->i_window_y, /* default Y coordinate */ - rect_window.right - rect_window.left, /* window width */ - rect_window.bottom - rect_window.top, /* window height */ - p_vout->p_sys->hparent, /* parent window */ - NULL, /* no menu in this window */ - hInstance, /* handle of this program instance */ - (LPVOID)p_vout ); /* send p_vout to WM_CREATE */ - - if( !p_vout->p_sys->hwnd ) - { - msg_Warn( p_vout, "DirectXCreateWindow create window FAILED (err=%lu)", GetLastError() ); - return VLC_EGENERIC; - } - - if( p_vout->p_sys->hparent ) - { - LONG i_style; - - /* We don't want the window owner to overwrite our client area */ - i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE ); - - if( !(i_style & WS_CLIPCHILDREN) ) - /* Hmmm, apparently this is a blocking call... */ - SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE, - i_style | WS_CLIPCHILDREN ); - - /* Create our fullscreen window */ - p_vout->p_sys->hfswnd = - CreateWindowEx( WS_EX_APPWINDOW, _T("VLC DirectX"), - _T(VOUT_TITLE) _T(" (DirectX Output)"), - WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX, - CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, - NULL, NULL, hInstance, NULL ); - } - - /* Append a "Always On Top" entry in the system menu */ - hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE ); - AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") ); - AppendMenu( hMenu, MF_STRING | MF_UNCHECKED, - IDM_TOGGLE_ON_TOP, _T("Always on &Top") ); - - /* Create video sub-window. This sub window will always exactly match - * the size of the video, which allows us to use crazy overlay colorkeys - * without having them shown outside of the video area. */ - p_vout->p_sys->hvideownd = - CreateWindow( _T("VLC DirectX video"), _T(""), /* window class */ - WS_CHILD, /* window style, not visible initially */ - 0, 0, - p_vout->render.i_width, /* default width */ - p_vout->render.i_height, /* default height */ - p_vout->p_sys->hwnd, /* parent window */ - NULL, hInstance, - (LPVOID)p_vout ); /* send p_vout to WM_CREATE */ - - if( !p_vout->p_sys->hvideownd ) - msg_Warn( p_vout, "can't create video sub-window" ); - else - msg_Dbg( p_vout, "created video sub-window" ); - - /* Now display the window */ - ShowWindow( p_vout->p_sys->hwnd, SW_SHOW ); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * DirectXCloseWindow: close the window created by DirectXCreateWindow - ***************************************************************************** - * This function returns all resources allocated by DirectXCreateWindow. - *****************************************************************************/ -static void DirectXCloseWindow( vout_thread_t *p_vout ) -{ - msg_Dbg( p_vout, "DirectXCloseWindow" ); - - DestroyWindow( p_vout->p_sys->hwnd ); - if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd ); - - if( p_vout->p_sys->hparent ) - vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent ); - - p_vout->p_sys->hwnd = NULL; - - /* We don't unregister the Window Class because it could lead to race - * conditions and it will be done anyway by the system when the app will - * exit */ -} - -/***************************************************************************** - * DirectXUpdateRects: update clipping rectangles - ***************************************************************************** - * This function is called when the window position or size are changed, and - * its job is to update the source and destination RECTs used to display the - * picture. - *****************************************************************************/ -void E_(DirectXUpdateRects)( vout_thread_t *p_vout, vlc_bool_t b_force ) -{ -#define rect_src p_vout->p_sys->rect_src -#define rect_src_clipped p_vout->p_sys->rect_src_clipped -#define rect_dest p_vout->p_sys->rect_dest -#define rect_dest_clipped p_vout->p_sys->rect_dest_clipped - - int i_width, i_height, i_x, i_y; - - RECT rect; - POINT point; - - /* Retrieve the window size */ - GetClientRect( p_vout->p_sys->hwnd, &rect ); - - /* Retrieve the window position */ - point.x = point.y = 0; - ClientToScreen( p_vout->p_sys->hwnd, &point ); - - /* If nothing changed, we can return */ - if( !b_force - && p_vout->p_sys->i_window_width == rect.right - && p_vout->p_sys->i_window_height == rect.bottom - && p_vout->p_sys->i_window_x == point.x - && p_vout->p_sys->i_window_y == point.y ) - { - return; - } - - /* Update the window position and size */ - p_vout->p_sys->i_window_x = point.x; - p_vout->p_sys->i_window_y = point.y; - p_vout->p_sys->i_window_width = rect.right; - p_vout->p_sys->i_window_height = rect.bottom; - - vout_PlacePicture( p_vout, rect.right, rect.bottom, - &i_x, &i_y, &i_width, &i_height ); - - if( p_vout->p_sys->hvideownd ) - SetWindowPos( p_vout->p_sys->hvideownd, 0, - i_x, i_y, i_width, i_height, - SWP_NOCOPYBITS|SWP_NOZORDER|SWP_ASYNCWINDOWPOS ); - - /* Destination image position and dimensions */ - rect_dest.left = point.x + i_x; - rect_dest.right = rect_dest.left + i_width; - rect_dest.top = point.y + i_y; - rect_dest.bottom = rect_dest.top + i_height; - -#ifdef MODULE_NAME_IS_vout_directx - /* Apply overlay hardware constraints */ - if( p_vout->p_sys->b_using_overlay ) - { - if( p_vout->p_sys->i_align_dest_boundary ) - rect_dest.left = ( rect_dest.left + - p_vout->p_sys->i_align_dest_boundary / 2 ) & - ~p_vout->p_sys->i_align_dest_boundary; - - if( p_vout->p_sys->i_align_dest_size ) - rect_dest.right = (( rect_dest.right - rect_dest.left + - p_vout->p_sys->i_align_dest_size / 2 ) & - ~p_vout->p_sys->i_align_dest_size) + rect_dest.left; - } - - /* UpdateOverlay directdraw function doesn't automatically clip to the - * display size so we need to do it otherwise it will fail */ - - /* Clip the destination window */ - if( !IntersectRect( &rect_dest_clipped, &rect_dest, - &p_vout->p_sys->rect_display ) ) - { - SetRectEmpty( &rect_src_clipped ); - return; - } - -#if 0 - msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:" - " %i,%i,%i,%i", - rect_dest_clipped.left, rect_dest_clipped.top, - rect_dest_clipped.right, rect_dest_clipped.bottom ); -#endif - - /* the 2 following lines are to fix a bug when clicking on the desktop */ - if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 || - (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 ) - { - SetRectEmpty( &rect_src_clipped ); - return; - } -#else /* MODULE_NAME_IS_vout_directx */ - - /* AFAIK, there are no clipping constraints in Direct3D or OpenGL */ - rect_dest_clipped = rect_dest; - -#endif - - /* src image dimensions */ - rect_src.left = 0; - rect_src.top = 0; - rect_src.right = p_vout->render.i_width; - rect_src.bottom = p_vout->render.i_height; - - /* Clip the source image */ - rect_src_clipped.left = p_vout->fmt_out.i_x_offset + - (rect_dest_clipped.left - rect_dest.left) * - p_vout->fmt_out.i_visible_width / (rect_dest.right - rect_dest.left); - rect_src_clipped.right = p_vout->fmt_out.i_x_offset + - p_vout->fmt_out.i_visible_width - - (rect_dest.right - rect_dest_clipped.right) * - p_vout->fmt_out.i_visible_width / (rect_dest.right - rect_dest.left); - rect_src_clipped.top = p_vout->fmt_out.i_y_offset + - (rect_dest_clipped.top - rect_dest.top) * - p_vout->fmt_out.i_visible_height / (rect_dest.bottom - rect_dest.top); - rect_src_clipped.bottom = p_vout->fmt_out.i_y_offset + - p_vout->fmt_out.i_visible_height - - (rect_dest.bottom - rect_dest_clipped.bottom) * - p_vout->fmt_out.i_visible_height / (rect_dest.bottom - rect_dest.top); - -#ifdef MODULE_NAME_IS_vout_directx - /* Apply overlay hardware constraints */ - if( p_vout->p_sys->b_using_overlay ) - { - if( p_vout->p_sys->i_align_src_boundary ) - rect_src_clipped.left = ( rect_src_clipped.left + - p_vout->p_sys->i_align_src_boundary / 2 ) & - ~p_vout->p_sys->i_align_src_boundary; - - if( p_vout->p_sys->i_align_src_size ) - rect_src_clipped.right = (( rect_src_clipped.right - - rect_src_clipped.left + - p_vout->p_sys->i_align_src_size / 2 ) & - ~p_vout->p_sys->i_align_src_size) + rect_src_clipped.left; - } -#endif - -#if 0 - msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped" - " coords: %i,%i,%i,%i", - rect_src_clipped.left, rect_src_clipped.top, - rect_src_clipped.right, rect_src_clipped.bottom ); -#endif - -#ifdef MODULE_NAME_IS_vout_directx - /* The destination coordinates need to be relative to the current - * directdraw primary surface (display) */ - rect_dest_clipped.left -= p_vout->p_sys->rect_display.left; - rect_dest_clipped.right -= p_vout->p_sys->rect_display.left; - rect_dest_clipped.top -= p_vout->p_sys->rect_display.top; - rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top; - - if( p_vout->p_sys->b_using_overlay ) - E_(DirectXUpdateOverlay)( p_vout ); -#endif - - /* Signal the change in size/position */ - p_vout->p_sys->i_changes |= DX_POSITION_CHANGE; - -#undef rect_src -#undef rect_src_clipped -#undef rect_dest -#undef rect_dest_clipped -} - -/***************************************************************************** - * DirectXEventProc: This is the window event processing function. - ***************************************************************************** - * On Windows, when you create a window you have to attach an event processing - * function to it. The aim of this function is to manage "Queued Messages" and - * "Nonqueued Messages". - * Queued Messages are those picked up and retransmitted by vout_Manage - * (using the GetMessage and DispatchMessage functions). - * Nonqueued Messages are those that Windows will send directly to this - * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...) - *****************************************************************************/ -static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message, - WPARAM wParam, LPARAM lParam ) -{ - vout_thread_t *p_vout; - - if( message == WM_CREATE ) - { - /* Store p_vout for future use */ - p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams; - SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout ); - return TRUE; - } - else - { - p_vout = (vout_thread_t *)GetWindowLongPtr( hwnd, GWLP_USERDATA ); - if( !p_vout ) - { - /* Hmmm mozilla does manage somehow to save the pointer to our - * windowproc and still calls it after the vout has been closed. */ - return DefWindowProc(hwnd, message, wParam, lParam); - } - } - - /* Catch the screensaver and the monitor turn-off */ - if( message == WM_SYSCOMMAND && - ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) ) - { - //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" ); - return 0; /* this stops them from happening */ - } - - if( hwnd == p_vout->p_sys->hvideownd ) - { - switch( message ) - { -#ifdef MODULE_NAME_IS_vout_directx - case WM_ERASEBKGND: - /* For overlay, we need to erase background */ - return !p_vout->p_sys->b_using_overlay ? - 1 : DefWindowProc(hwnd, message, wParam, lParam); - case WM_PAINT: - /* - ** For overlay, DefWindowProc() will erase dirty regions - ** with colorkey. - ** For non-overlay, vout will paint the whole window at - ** regular interval, therefore dirty regions can be ignored - ** to minimize repaint. - */ - if( !p_vout->p_sys->b_using_overlay ) - { - ValidateRect(hwnd, NULL); - } - // fall through to default -#else - /* - ** For OpenGL and Direct3D, vout will update the whole - ** window at regular interval, therefore dirty region - ** can be ignored to minimize repaint. - */ - case WM_ERASEBKGND: - /* nothing to erase */ - return 1; - case WM_PAINT: - /* nothing to repaint */ - ValidateRect(hwnd, NULL); - // fall through -#endif - default: - return DefWindowProc(hwnd, message, wParam, lParam); - } - } - - switch( message ) - { - - case WM_WINDOWPOSCHANGED: - E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); - return 0; - - /* the user wants to close the window */ - case WM_CLOSE: - { - playlist_t * p_playlist = - (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( p_playlist == NULL ) - { - return 0; - } - - playlist_Stop( p_playlist ); - vlc_object_release( p_playlist ); - return 0; - } - - /* the window has been closed so shut down everything now */ - case WM_DESTROY: - msg_Dbg( p_vout, "WinProc WM_DESTROY" ); - /* just destroy the window */ - PostQuitMessage( 0 ); - return 0; - - case WM_SYSCOMMAND: - switch (wParam) - { - case IDM_TOGGLE_ON_TOP: /* toggle the "on top" status */ - { - vlc_value_t val; - msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP"); - - /* Change the current value */ - var_Get( p_vout, "video-on-top", &val ); - val.b_bool = !val.b_bool; - var_Set( p_vout, "video-on-top", val ); - return 0; - } - } - break; - - case WM_PAINT: - case WM_NCPAINT: - case WM_ERASEBKGND: - return DefWindowProc(hwnd, message, wParam, lParam); - - default: - //msg_Dbg( p_vout, "WinProc WM Default %i", message ); - break; - } - - /* Let windows handle the message */ - return DefWindowProc(hwnd, message, wParam, lParam); -} - -static struct -{ - int i_dxkey; - int i_vlckey; - -} dxkeys_to_vlckeys[] = -{ - { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 }, - { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 }, - { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 }, - { VK_F12, KEY_F12 }, - - { VK_RETURN, KEY_ENTER }, - { VK_SPACE, KEY_SPACE }, - { VK_ESCAPE, KEY_ESC }, - - { VK_LEFT, KEY_LEFT }, - { VK_RIGHT, KEY_RIGHT }, - { VK_UP, KEY_UP }, - { VK_DOWN, KEY_DOWN }, - - { VK_HOME, KEY_HOME }, - { VK_END, KEY_END }, - { VK_PRIOR, KEY_PAGEUP }, - { VK_NEXT, KEY_PAGEDOWN }, - - { VK_INSERT, KEY_INSERT }, - { VK_DELETE, KEY_DELETE }, - - { VK_CONTROL, 0 }, - { VK_SHIFT, 0 }, - { VK_MENU, 0 }, - - { VK_BROWSER_BACK, KEY_BROWSER_BACK }, - { VK_BROWSER_FORWARD, KEY_BROWSER_FORWARD }, - { VK_BROWSER_REFRESH, KEY_BROWSER_REFRESH }, - { VK_BROWSER_STOP, KEY_BROWSER_STOP }, - { VK_BROWSER_SEARCH, KEY_BROWSER_SEARCH }, - { VK_BROWSER_FAVORITES, KEY_BROWSER_FAVORITES }, - { VK_BROWSER_HOME, KEY_BROWSER_HOME }, - { VK_VOLUME_MUTE, KEY_VOLUME_MUTE }, - { VK_VOLUME_DOWN, KEY_VOLUME_DOWN }, - { VK_VOLUME_UP, KEY_VOLUME_UP }, - { VK_MEDIA_NEXT_TRACK, KEY_MEDIA_NEXT_TRACK }, - { VK_MEDIA_PREV_TRACK, KEY_MEDIA_PREV_TRACK }, - { VK_MEDIA_STOP, KEY_MEDIA_STOP }, - { VK_MEDIA_PLAY_PAUSE, KEY_MEDIA_PLAY_PAUSE }, - - { 0, 0 } -}; - -static int DirectXConvertKey( int i_key ) -{ - int i; - - for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ ) - { - if( dxkeys_to_vlckeys[i].i_dxkey == i_key ) - { - return dxkeys_to_vlckeys[i].i_vlckey; - } - } - - return 0; -} - -/***************************************************************************** - * Control: control facility for the vout - *****************************************************************************/ -static int Control( vout_thread_t *p_vout, int i_query, va_list args ) -{ - unsigned int *pi_width, *pi_height; - RECT rect_window; - POINT point; - - switch( i_query ) - { - case VOUT_GET_SIZE: - if( p_vout->p_sys->hparent ) - return vout_ControlWindow( p_vout, - (void *)p_vout->p_sys->hparent, i_query, args ); - - pi_width = va_arg( args, unsigned int * ); - pi_height = va_arg( args, unsigned int * ); - - GetClientRect( p_vout->p_sys->hwnd, &rect_window ); - - *pi_width = rect_window.right - rect_window.left; - *pi_height = rect_window.bottom - rect_window.top; - return VLC_SUCCESS; - - case VOUT_SET_SIZE: - if( p_vout->p_sys->hparent ) - return vout_ControlWindow( p_vout, - (void *)p_vout->p_sys->hparent, i_query, args ); - - /* Update dimensions */ - rect_window.top = rect_window.left = 0; - rect_window.right = va_arg( args, unsigned int ); - rect_window.bottom = va_arg( args, unsigned int ); - if( !rect_window.right ) rect_window.right = p_vout->i_window_width; - if( !rect_window.bottom ) rect_window.bottom = p_vout->i_window_height; - AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 ); - - SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, - rect_window.right - rect_window.left, - rect_window.bottom - rect_window.top, SWP_NOMOVE ); - - return VLC_SUCCESS; - - case VOUT_CLOSE: - ShowWindow( p_vout->p_sys->hwnd, SW_HIDE ); - case VOUT_REPARENT: - /* Retrieve the window position */ - point.x = point.y = 0; - ClientToScreen( p_vout->p_sys->hwnd, &point ); - - HWND d = 0; - - if( i_query == VOUT_REPARENT ) d = (HWND)va_arg( args, int ); - if( !d ) - { - vlc_mutex_lock( &p_vout->p_sys->lock ); - p_vout->p_sys->hparent = 0; - vlc_mutex_unlock( &p_vout->p_sys->lock ); - SetParent( p_vout->p_sys->hwnd, 0 ); - p_vout->p_sys->i_window_style = - WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_SIZEBOX; - SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, - p_vout->p_sys->i_window_style | - (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) ); - SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW ); - SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0, - SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED ); - } - else - { - vlc_mutex_lock( &p_vout->p_sys->lock ); - p_vout->p_sys->hparent = d; - vlc_mutex_unlock( &p_vout->p_sys->lock ); - - SetParent( p_vout->p_sys->hwnd, d ); - p_vout->p_sys->i_window_style = WS_CLIPCHILDREN; - SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, - p_vout->p_sys->i_window_style | - (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) ); - SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW ); - - /* Retrieve the parent size */ - RECT rect; - GetClientRect( d, &rect ); - SetWindowPos( p_vout->p_sys->hwnd, d, point.x, point.y, rect.right, rect.bottom, - SWP_FRAMECHANGED ); - } - - return vout_vaControlDefault( p_vout, i_query, args ); - - case VOUT_SET_STAY_ON_TOP: - if( p_vout->p_sys->hparent && !var_GetBool( p_vout, "fullscreen" ) ) - return vout_ControlWindow( p_vout, - (void *)p_vout->p_sys->hparent, i_query, args ); - - p_vout->p_sys->b_on_top_change = VLC_TRUE; - return VLC_SUCCESS; - - default: - return vout_vaControlDefault( p_vout, i_query, args ); - } -} - - -/* Internal wrapper over GetWindowPlacement / SetWindowPlacement */ -static void SetWindowState(HWND hwnd, int nShowCmd) -{ - WINDOWPLACEMENT window_placement; - window_placement.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement( hwnd, &window_placement ); - window_placement.showCmd = nShowCmd; - SetWindowPlacement( hwnd, &window_placement ); - SetWindowPos( hwnd, 0, 0, 0, 0, 0, - SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); -} - -/* Internal wrapper to call vout_ControlWindow for hparent */ -static void ControlParentWindow( vout_thread_t *p_vout, int i_query, ... ) -{ - va_list args; - va_start( args, i_query ); - vout_ControlWindow( p_vout, - (void *)p_vout->p_sys->hparent, i_query, args ); - va_end( args ); -} - -void Win32ToggleFullscreen( vout_thread_t *p_vout ) -{ - 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; - - if( p_vout->b_fullscreen ) - { - msg_Dbg( p_vout, "entering fullscreen mode" ); - /* Change window style, no borders and no title bar */ - 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 ); - } - - /* Maximize window */ - SetWindowState( hwnd, SW_SHOWMAXIMIZED ); - - 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 ); - - HWND topLevelParent = GetAncestor( p_vout->p_sys->hparent, GA_ROOT ); - ShowWindow( topLevelParent, SW_HIDE ); - } - - SetForegroundWindow( hwnd ); - } - else - { - msg_Dbg( p_vout, "leaving fullscreen mode" ); - /* Change window style, no borders and no title bar */ - SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style ); - - /* Normal window */ - SetWindowState( hwnd, SW_SHOWNORMAL ); - - 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 ); - - HWND topLevelParent = GetAncestor( p_vout->p_sys->hparent, GA_ROOT ); - ShowWindow( topLevelParent, SW_SHOW ); - SetForegroundWindow( p_vout->p_sys->hparent ); - ShowWindow( hwnd, SW_HIDE ); - - /* Update "video-on-top" status for main interface window, it - needs to be updated as we were hiding VOUT_SET_STAY_ON_TOP - queries from it while we were in fullscreen mode */ - int b_ontop = var_GetBool( p_vout, "video-on-top" ); - ControlParentWindow( p_vout, VOUT_SET_STAY_ON_TOP, b_ontop ); - } - - /* Make sure the mouse cursor is displayed */ - PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 ); - } - - { - vlc_value_t val; - /* Update the object variable and trigger callback */ - val.b_bool = p_vout->b_fullscreen; - var_Set( p_vout, "fullscreen", val ); - } -} diff --git a/modules/video_output/directx/glwin32.c b/modules/video_output/directx/glwin32.c deleted file mode 100644 index 14fe9b2aea..0000000000 --- a/modules/video_output/directx/glwin32.c +++ /dev/null @@ -1,431 +0,0 @@ -/***************************************************************************** - * glwin32.c: Windows OpenGL provider - ***************************************************************************** - * Copyright (C) 2001-2004 the VideoLAN team - * $Id$ - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -#include /* ENOMEM */ -#include /* free() */ -#include /* strerror() */ - -#include -#include -#include - -#include -#include -#include - -#include -#undef GetSystemMetrics - -#ifndef MONITOR_DEFAULTTONEAREST -# define MONITOR_DEFAULTTONEAREST 2 -#endif - -#include - -#include "vout.h" - -/***************************************************************************** - * Local prototypes. - *****************************************************************************/ -static int OpenVideo ( vlc_object_t * ); -static void CloseVideo ( vlc_object_t * ); - -static int Init ( vout_thread_t * ); -static void End ( vout_thread_t * ); -static int Manage ( vout_thread_t * ); -static void GLSwapBuffers( vout_thread_t * ); -static void FirstSwap( vout_thread_t * ); - -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -vlc_module_begin(); - set_category( CAT_VIDEO ); - set_subcategory( SUBCAT_VIDEO_VOUT ); - set_shortname( "OpenGL" ); - set_description( _("OpenGL video output") ); - set_capability( "opengl provider", 100 ); - add_shortcut( "glwin32" ); - 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 */ - /* check if we registered a window class because we need to - * unregister it */ - WNDCLASS wndclass; - if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) - UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); -#endif - -/***************************************************************************** - * OpenVideo: allocate OpenGL provider - ***************************************************************************** - * This function creates and initializes a video window. - *****************************************************************************/ -static int OpenVideo( vlc_object_t *p_this ) -{ - vout_thread_t * p_vout = (vout_thread_t *)p_this; - vlc_value_t val; - - /* 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 VLC_ENOMEM; - } - memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); - - /* Initialisations */ - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = Manage; - p_vout->pf_swap = FirstSwap; - - 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_wallpaper = 0; - vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); - SetRectEmpty( &p_vout->p_sys->rect_display ); - SetRectEmpty( &p_vout->p_sys->rect_parent ); - - 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(); - - /* Set main window's size */ - p_vout->p_sys->i_window_width = p_vout->i_window_width; - p_vout->p_sys->i_window_height = p_vout->i_window_height; - - /* Create the DirectXEventThread, this thread is created by us to isolate - * the Win32 PeekMessage function calls. We want to do this because - * Windows can stay blocked inside this call for a long time, and when - * this happens it thus blocks vlc's video_output thread. - * DirectXEventThread will take care of the creation of the video - * window (because PeekMessage has to be called from the same thread which - * created the window). */ - msg_Dbg( p_vout, "creating DirectXEventThread" ); - 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", - E_(DirectXEventThread), 0, 1 ) ) - { - msg_Err( p_vout, "cannot create DirectXEventThread" ); - vlc_object_destroy( p_vout->p_sys->p_event ); - p_vout->p_sys->p_event = NULL; - goto error; - } - - if( p_vout->p_sys->p_event->b_error ) - { - msg_Err( p_vout, "DirectXEventThread failed" ); - goto error; - } - - vlc_object_attach( p_vout->p_sys->p_event, p_vout ); - - msg_Dbg( p_vout, "DirectXEventThread running" ); - - /* 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; - - error: - CloseVideo( VLC_OBJECT(p_vout) ); - return VLC_EGENERIC; -} - -/***************************************************************************** - * Init: initialize video thread output method - *****************************************************************************/ -static int Init( vout_thread_t *p_vout ) -{ - PIXELFORMATDESCRIPTOR pfd; - int iFormat; - - /* Change the window title bar text */ - PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 ); - - p_vout->p_sys->hGLDC = GetDC( p_vout->p_sys->hvideownd ); - - /* Set the pixel format for the DC */ - memset( &pfd, 0, sizeof( pfd ) ); - pfd.nSize = sizeof( pfd ); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 24; - pfd.cDepthBits = 16; - pfd.iLayerType = PFD_MAIN_PLANE; - iFormat = ChoosePixelFormat( p_vout->p_sys->hGLDC, &pfd ); - SetPixelFormat( p_vout->p_sys->hGLDC, iFormat, &pfd ); - - /* Create and enable the render context */ - p_vout->p_sys->hGLRC = wglCreateContext( p_vout->p_sys->hGLDC ); - wglMakeCurrent( p_vout->p_sys->hGLDC, p_vout->p_sys->hGLRC ); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * End: terminate Sys video thread output method - ***************************************************************************** - * Terminate an output method created by Create. - * It is called at the end of the thread. - *****************************************************************************/ -static void End( vout_thread_t *p_vout ) -{ - wglMakeCurrent( NULL, NULL ); - wglDeleteContext( p_vout->p_sys->hGLRC ); - ReleaseDC( p_vout->p_sys->hvideownd, p_vout->p_sys->hGLDC ); - return; -} - -/***************************************************************************** - * CloseVideo: destroy Sys video thread output method - ***************************************************************************** - * 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, "closing video" ); - - 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 = 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 ); - - if( p_vout->p_sys ) - { - free( p_vout->p_sys ); - p_vout->p_sys = NULL; - } -} - -/***************************************************************************** - * Manage: handle Sys events - ***************************************************************************** - * This function should be called regularly by the video output thread. - * It returns a non null value if an error occurred. - *****************************************************************************/ -static int Manage( vout_thread_t *p_vout ) -{ - int i_width = p_vout->p_sys->rect_dest.right - - p_vout->p_sys->rect_dest.left; - int i_height = p_vout->p_sys->rect_dest.bottom - - p_vout->p_sys->rect_dest.top; - glViewport( 0, 0, i_width, i_height ); - - /* 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; - - 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 - { - vlc_mutex_unlock( &p_vout->p_sys->lock ); - } - - /* 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. */ - - /* - * Fullscreen change - */ - if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE - || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) - { - Win32ToggleFullscreen( p_vout ); - - p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; - } - - /* - * Pointer change - */ - 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 ) - { - 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 VLC_EGENERIC; /* exit */ - } - - return VLC_SUCCESS; -} - -/***************************************************************************** - * GLSwapBuffers: swap front/back buffers - *****************************************************************************/ -static void GLSwapBuffers( vout_thread_t *p_vout ) -{ - SwapBuffers( p_vout->p_sys->hGLDC ); -} - -/* -** this function is only used once when the first picture is received -** this function will show the video window once a picture is ready -*/ - -static void FirstSwap( vout_thread_t *p_vout ) -{ - /* get initial picture buffer swapped to front buffer */ - GLSwapBuffers( p_vout ); - - /* - ** Video window is initially hidden, show it now since we got a - ** picture to show. - */ - SetWindowPos( p_vout->p_sys->hvideownd, NULL, 0, 0, 0, 0, - SWP_ASYNCWINDOWPOS| - SWP_FRAMECHANGED| - SWP_SHOWWINDOW| - SWP_NOMOVE| - SWP_NOSIZE| - SWP_NOZORDER ); - - /* use and restores proper swap function for further pictures */ - p_vout->pf_swap = GLSwapBuffers; -} diff --git a/modules/video_output/directx/vout.h b/modules/video_output/directx/vout.h deleted file mode 100644 index 5fe7d9880b..0000000000 --- a/modules/video_output/directx/vout.h +++ /dev/null @@ -1,215 +0,0 @@ -/***************************************************************************** - * vout.h: Windows DirectX video output header file - ***************************************************************************** - * Copyright (C) 2001-2004 the VideoLAN team - * $Id$ - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/***************************************************************************** - * event_thread_t: DirectX event thread - *****************************************************************************/ -typedef struct event_thread_t -{ - VLC_COMMON_MEMBERS - - vout_thread_t * p_vout; - -} event_thread_t; - -/***************************************************************************** - * vout_sys_t: video output DirectX method descriptor - ***************************************************************************** - * This structure is part of the video output thread descriptor. - * It describes the DirectX specific properties of an output thread. - *****************************************************************************/ -struct vout_sys_t -{ - HWND hwnd; /* Handle of the main window */ - HWND hvideownd; /* Handle of the video sub-window */ - HWND hparent; /* Handle of the parent window */ - HWND hfswnd; /* Handle of the fullscreen window */ - WNDPROC pf_wndproc; /* Window handling callback */ - - /* Multi-monitor support */ - HMONITOR hmonitor; /* handle of the current monitor */ - GUID *p_display_driver; - HMONITOR (WINAPI* MonitorFromWindow)( HWND, DWORD ); - BOOL (WINAPI* GetMonitorInfo)( HMONITOR, LPMONITORINFO ); - - /* size of the display */ - RECT rect_display; - int i_display_depth; - - /* size of the overall window (including black bands) */ - RECT rect_parent; - - /* Window position and size */ - int i_window_x; - int i_window_y; - int i_window_width; - int i_window_height; - int i_window_style; - - volatile uint16_t i_changes; /* changes made to the video display */ - - /* Mouse */ - volatile vlc_bool_t b_cursor_hidden; - volatile mtime_t i_lastmoved; - - /* Misc */ - vlc_bool_t b_on_top_change; - - vlc_bool_t b_wallpaper; - - /* screensaver system settings to be restored when vout is closed */ - UINT i_spi_lowpowertimeout; - UINT i_spi_powerofftimeout; - UINT i_spi_screensavetimeout; - - /* Coordinates of src and dest images (used when blitting to display) */ - RECT rect_src; - RECT rect_src_clipped; - RECT rect_dest; - RECT rect_dest_clipped; - - vlc_bool_t b_hw_yuv; /* Should we use hardware YUV->RGB conversions */ - -#ifdef MODULE_NAME_IS_vout_directx - /* Overlay alignment restrictions */ - int i_align_src_boundary; - int i_align_src_size; - int i_align_dest_boundary; - int i_align_dest_size; - - vlc_bool_t b_using_overlay; /* Are we using an overlay surface */ - vlc_bool_t b_use_sysmem; /* Should we use system memory for surfaces */ - vlc_bool_t b_3buf_overlay; /* Should we use triple buffered overlays */ - - /* DDraw capabilities */ - int b_caps_overlay_clipping; - - int i_rgb_colorkey; /* colorkey in RGB used by the overlay */ - int i_colorkey; /* colorkey used by the overlay */ - - COLORREF color_bkg; - COLORREF color_bkgtxt; - - LPDIRECTDRAW2 p_ddobject; /* DirectDraw object */ - LPDIRECTDRAWSURFACE2 p_display; /* Display device */ - LPDIRECTDRAWSURFACE2 p_current_surface; /* surface currently displayed */ - LPDIRECTDRAWCLIPPER p_clipper; /* clipper used for blitting */ - HINSTANCE hddraw_dll; /* handle of the opened ddraw dll */ -#endif - -#ifdef MODULE_NAME_IS_glwin32 - HDC hGLDC; - HGLRC hGLRC; -#endif - -#ifdef MODULE_NAME_IS_direct3d - // core objects - HINSTANCE hd3d9_dll; /* handle of the opened d3d9 dll */ - LPDIRECT3D9 p_d3dobj; - LPDIRECT3DDEVICE9 p_d3ddev; - D3DFORMAT bbFormat; - // scene objects - LPDIRECT3DTEXTURE9 p_d3dtex; - LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; -#endif - - event_thread_t *p_event; - vlc_mutex_t lock; -}; - -/***************************************************************************** - * Prototypes from vout.c - *****************************************************************************/ -int E_(DirectXUpdateOverlay)( vout_thread_t *p_vout ); - -/***************************************************************************** - * Prototypes from events.c - *****************************************************************************/ -void E_(DirectXEventThread) ( event_thread_t *p_event ); -void E_(DirectXUpdateRects) ( vout_thread_t *p_vout, vlc_bool_t b_force ); -void Win32ToggleFullscreen ( vout_thread_t *p_vout ); - -/***************************************************************************** - * Constants - *****************************************************************************/ -#define WM_VLC_HIDE_MOUSE WM_APP -#define WM_VLC_SHOW_MOUSE WM_APP + 1 -#define WM_VLC_CHANGE_TEXT WM_APP + 2 -#define IDM_TOGGLE_ON_TOP WM_USER + 1 -#define DX_POSITION_CHANGE 0x1000 -#define DX_WALLPAPER_CHANGE 0x2000 - -/***************************************************************************** - * WinCE helpers - *****************************************************************************/ -#ifdef UNDER_CE - -#define AdjustWindowRect(a,b,c) - -#ifndef GCL_HBRBACKGROUND -# define GCL_HBRBACKGROUND (-10) -#endif - -#define FindWindowEx(a,b,c,d) 0 - -#define GetWindowPlacement(a,b) -#define SetWindowPlacement(a,b) -typedef struct _WINDOWPLACEMENT { - UINT length; - UINT flags; - UINT showCmd; - POINT ptMinPosition; - POINT ptMaxPosition; - RECT rcNormalPosition; -} WINDOWPLACEMENT; - -#ifndef WM_NCMOUSEMOVE -# define WM_NCMOUSEMOVE 160 -#endif -#ifndef CS_OWNDC -# define CS_OWNDC 32 -#endif -#ifndef SC_SCREENSAVE -# define SC_SCREENSAVE 0xF140 -#endif -#ifndef SC_MONITORPOWER -# define SC_MONITORPOWER 0xF170 -#endif -#ifndef WM_NCPAINT -# define WM_NCPAINT 133 -#endif -#ifndef WS_OVERLAPPEDWINDOW -# define WS_OVERLAPPEDWINDOW 0xcf0000 -#endif -#ifndef WS_EX_NOPARENTNOTIFY -# define WS_EX_NOPARENTNOTIFY 4 -#endif -#ifndef WS_EX_APPWINDOW -#define WS_EX_APPWINDOW 0x40000 -#endif - -#define SetWindowLongPtr SetWindowLong -#define GetWindowLongPtr GetWindowLong -#define GWLP_USERDATA GWL_USERDATA - -#endif //UNDER_CE diff --git a/modules/video_output/msw/Modules.am b/modules/video_output/msw/Modules.am new file mode 100644 index 0000000000..ba1636b614 --- /dev/null +++ b/modules/video_output/msw/Modules.am @@ -0,0 +1,29 @@ +SOURCES_vout_directx = \ + directx.c \ + vout.h \ + events.c \ + $(NULL) + +SOURCES_direct3d = \ + direct3d.c \ + vout.h \ + events.c \ + $(NULL) + +SOURCES_glwin32 = \ + glwin32.c \ + vout.h \ + events.c \ + $(NULL) + +SOURCES_wingdi = \ + wingdi.c \ + vout.h \ + events.c \ + $(NULL) + +SOURCES_wingapi = \ + windi.c \ + vout.h \ + events.c \ + $(NULL) diff --git a/modules/video_output/msw/direct3d.c b/modules/video_output/msw/direct3d.c new file mode 100644 index 0000000000..5077c7a421 --- /dev/null +++ b/modules/video_output/msw/direct3d.c @@ -0,0 +1,1522 @@ +/***************************************************************************** + * direct3d.c: Windows Direct3D video output module + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + *$Id$ + * + * Authors: Damien Fouilleul + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble: + * + * This plugin will use YUV surface if supported, using YUV will result in + * the best video quality (hardware filering when rescaling the picture) + * and the fastest display as it requires less processing. + * + * If YUV overlay is not supported this plugin will use RGB offscreen video + * surfaces that will be blitted onto the primary surface (display) to + * effectively display the pictures. + * + *****************************************************************************/ +#include /* ENOMEM */ +#include /* free() */ +#include /* strerror() */ + +#include +#include +#include +#include + +#include +#include + +#include "vout.h" + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ +static int OpenVideo ( vlc_object_t * ); +static void CloseVideo ( vlc_object_t * ); + +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 FirstDisplay( vout_thread_t *, picture_t * ); + +static int Direct3DVoutCreate ( vout_thread_t * ); +static void Direct3DVoutRelease ( vout_thread_t * ); + +static int Direct3DVoutOpen ( vout_thread_t * ); +static void Direct3DVoutClose ( vout_thread_t * ); + +static int Direct3DVoutResetDevice( vout_thread_t * ); + +static int Direct3DVoutCreatePictures ( vout_thread_t *, size_t ); +static void Direct3DVoutReleasePictures ( vout_thread_t * ); + +static int Direct3DVoutLockSurface ( vout_thread_t *, picture_t * ); +static int Direct3DVoutUnlockSurface( vout_thread_t *, picture_t * ); + +static int Direct3DVoutCreateScene ( vout_thread_t * ); +static void Direct3DVoutReleaseScene ( vout_thread_t * ); +static void Direct3DVoutRenderScene ( vout_thread_t *, picture_t * ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ + +static vlc_bool_t _got_vista_or_above; + +static int get_capability_for_osversion(void) +{ + OSVERSIONINFO winVer; + winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if( GetVersionEx(&winVer) ) + { + if( winVer.dwMajorVersion > 5 ) + { + /* Windows Vista or above, make this module the default */ + _got_vista_or_above = VLC_TRUE; + return 150; + } + } + /* Windows XP or lower, make sure this module isn't the default */ + _got_vista_or_above = VLC_FALSE; + return 50; +} + +vlc_module_begin(); + set_shortname( "Direct3D" ); + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VOUT ); + set_description( _("DirectX 3D video output") ); + set_capability( "video output", get_capability_for_osversion() ); + add_shortcut( "direct3d" ); + 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 */ + /* check if we registered a window class because we need to + * unregister it */ + WNDCLASS wndclass; + if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) + UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); +#endif + +/***************************************************************************** + * CUSTOMVERTEX: + ***************************************************************************** + *****************************************************************************/ +typedef struct +{ + FLOAT x,y,z; // vertex untransformed position + FLOAT rhw; // eye distance + D3DCOLOR diffuse; // diffuse color + FLOAT tu, tv; // texture relative coordinates +} CUSTOMVERTEX; + +#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) + +/***************************************************************************** + * OpenVideo: allocate Vout video thread output method + ***************************************************************************** + * This function allocates and initialize the Direct3D vout method. + *****************************************************************************/ +static int OpenVideo( vlc_object_t *p_this ) +{ + vout_thread_t * p_vout = (vout_thread_t *)p_this; + vlc_value_t val; + + /* 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 VLC_ENOMEM; + } + memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); + + if( VLC_SUCCESS != Direct3DVoutCreate( p_vout ) ) + { + msg_Err( p_vout, "Direct3D could not be initialized !"); + goto error; + } + + /* Initialisations */ + p_vout->pf_init = Init; + p_vout->pf_end = End; + p_vout->pf_manage = Manage; + p_vout->pf_render = Direct3DVoutRenderScene; + p_vout->pf_display = FirstDisplay; + + 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; + vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); + SetRectEmpty( &p_vout->p_sys->rect_display ); + SetRectEmpty( &p_vout->p_sys->rect_parent ); + + var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + + p_vout->p_sys->b_cursor_hidden = 0; + p_vout->p_sys->i_lastmoved = mdate(); + + var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + /* Set main window's size */ + p_vout->p_sys->i_window_width = p_vout->i_window_width; + p_vout->p_sys->i_window_height = p_vout->i_window_height; + + /* Create the Vout EventThread, this thread is created by us to isolate + * the Win32 PeekMessage function calls. We want to do this because + * Windows can stay blocked inside this call for a long time, and when + * this happens it thus blocks vlc's video_output thread. + * Vout EventThread will take care of the creation of the video + * window (because PeekMessage has to be called from the same thread which + * created the window). */ + msg_Dbg( p_vout, "creating Vout EventThread" ); + 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, "Vout Events Thread", + E_(EventThread), 0, 1 ) ) + { + msg_Err( p_vout, "cannot create Vout EventThread" ); + vlc_object_destroy( p_vout->p_sys->p_event ); + p_vout->p_sys->p_event = NULL; + goto error; + } + + if( p_vout->p_sys->p_event->b_error ) + { + msg_Err( p_vout, "Vout EventThread failed" ); + goto error; + } + + vlc_object_attach( p_vout->p_sys->p_event, p_vout ); + + msg_Dbg( p_vout, "Vout EventThread running" ); + + /* 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 ); + + /* disable screensaver by temporarily changing system settings */ + p_vout->p_sys->i_spi_lowpowertimeout = 0; + p_vout->p_sys->i_spi_powerofftimeout = 0; + p_vout->p_sys->i_spi_screensavetimeout = 0; + var_Get( p_vout, "disable-screensaver", &val); + if( val.b_bool ) { + msg_Dbg(p_vout, "disabling screen saver"); + SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT, + 0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0); + if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { + SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0); + } + SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, + &(p_vout->p_sys->i_spi_powerofftimeout), 0); + if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { + SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0); + } + SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, + &(p_vout->p_sys->i_spi_screensavetimeout), 0); + if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); + } + } + return VLC_SUCCESS; + + error: + CloseVideo( VLC_OBJECT(p_vout) ); + return VLC_EGENERIC; +} + +/***************************************************************************** + * CloseVideo: destroy Sys video thread output method + ***************************************************************************** + * 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; + + Direct3DVoutRelease( p_vout ); + + if( p_vout->p_sys->p_event ) + { + vlc_object_detach( p_vout->p_sys->p_event ); + + /* Kill Vout EventThread */ + p_vout->p_sys->p_event->b_die = VLC_TRUE; + + /* we need to be sure Vout EventThread 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 ); + + /* restore screensaver system settings */ + if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { + SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, + p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0); + } + if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { + SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, + p_vout->p_sys->i_spi_powerofftimeout, NULL, 0); + } + if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, + p_vout->p_sys->i_spi_screensavetimeout, NULL, 0); + } + + if( p_vout->p_sys ) + { + free( p_vout->p_sys ); + p_vout->p_sys = NULL; + } +} + +/***************************************************************************** + * Init: initialize Direct3D video thread output method + *****************************************************************************/ +static int Init( vout_thread_t *p_vout ) +{ + int i_ret; + vlc_value_t val; + + var_Get( p_vout, "directx-hw-yuv", &val ); + p_vout->p_sys->b_hw_yuv = val.b_bool; + + /* Initialise Direct3D */ + if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) ) + { + msg_Err( p_vout, "cannot initialize Direct3D" ); + return VLC_EGENERIC; + } + + /* Initialize the output structure. + * Since Direct3D can do rescaling for us, stick to the default + * coordinates and aspect. */ + 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_(UpdateRects)( p_vout, VLC_TRUE ); + + /* create picture pool */ + i_ret = Direct3DVoutCreatePictures(p_vout, 1); + if( VLC_SUCCESS != i_ret ) + { + msg_Err(p_vout, "Direct3D picture pool initialization failed !"); + return i_ret; + } + + /* create scene */ + i_ret = Direct3DVoutCreateScene(p_vout); + if( VLC_SUCCESS != i_ret ) + { + msg_Err(p_vout, "Direct3D scene initialization failed !"); + Direct3DVoutReleasePictures(p_vout); + return i_ret; + } + + /* Change the window title bar text */ + 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; +} + +/***************************************************************************** + * End: terminate Sys video thread output method + ***************************************************************************** + * Terminate an output method created by Create. + * It is called at the end of the thread. + *****************************************************************************/ +static void End( vout_thread_t *p_vout ) +{ + Direct3DVoutReleaseScene(p_vout); + Direct3DVoutReleasePictures(p_vout); + Direct3DVoutClose( p_vout ); +} + +/***************************************************************************** + * Manage: handle Sys events + ***************************************************************************** + * This function should be called regularly by the video output thread. + * It returns a non null value if an error occurred. + *****************************************************************************/ +static int Manage( vout_thread_t *p_vout ) +{ + /* If we do not control our window, we check for geometry changes + * ourselves because the parent might not send us its events. */ + vlc_mutex_lock( &p_vout->p_sys->lock ); + if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) + { + RECT rect_parent; + POINT point; + + 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; + + SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, + rect_parent.right - rect_parent.left, + rect_parent.bottom - rect_parent.top, + SWP_NOZORDER ); + } + } + else + { + vlc_mutex_unlock( &p_vout->p_sys->lock ); + } + + /* + * Position Change + */ + if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE ) + { +#if 0 /* need that when bicubic filter is available */ + RECT rect; + UINT width, height; + + GetClientRect(p_vout->p_sys->hvideownd, &rect); + width = rect.right-rect.left; + height = rect.bottom-rect.top; + + if( (width != p_vout->p_sys->d3dpp.BackBufferWidth) + || (height != p_vout->p_sys->d3dpp.BackBufferHeight) ) + { + msg_Dbg(p_vout, "resizing device back buffers to (%lux%lu)", width, height); + // need to reset D3D device to resize back buffer + if( VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, width, height) ) + return VLC_EGENERIC; + } +#endif + p_vout->p_sys->i_changes &= ~DX_POSITION_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_(UpdateRects)( 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. */ + + /* + * Fullscreen change + */ + if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE + || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) + { + Win32ToggleFullscreen( p_vout ); + + p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + } + + /* + * Pointer change + */ + 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 ) + { + 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 VLC_EGENERIC; /* exit */ + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Display: displays previously rendered output + ***************************************************************************** + * This function sends the currently rendered image to the display, wait until + * it is displayed and switch the two rendering buffers, preparing next frame. + *****************************************************************************/ +static void Display( vout_thread_t *p_vout, picture_t *p_pic ) +{ + LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; + // Present the back buffer contents to the display + // stretching and filtering happens here + HRESULT hr = IDirect3DDevice9_Present(p_d3ddev, + &(p_vout->p_sys->rect_src_clipped), + NULL, NULL, NULL); + if( FAILED(hr) ) + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); +} + +/* +** this function is only used once when the first picture is received +** this function will show the video window once a picture is ready +*/ + +static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic ) +{ + /* get initial picture presented through D3D */ + Display(p_vout, p_pic); + + /* + ** Video window is initially hidden, show it now since we got a + ** picture to show. + */ + SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0, + SWP_ASYNCWINDOWPOS| + SWP_FRAMECHANGED| + SWP_SHOWWINDOW| + SWP_NOMOVE| + SWP_NOSIZE| + SWP_NOZORDER ); + + /* use and restores proper display function for further pictures */ + p_vout->pf_display = Display; +} + +/***************************************************************************** + * DirectD3DVoutCreate: Initialize and instance of Direct3D9 + ***************************************************************************** + * This function initialize Direct3D and analyze available resources from + * default adapter. + *****************************************************************************/ +static int Direct3DVoutCreate( vout_thread_t *p_vout ) +{ + HRESULT hr; + LPDIRECT3D9 p_d3dobj; + D3DCAPS9 d3dCaps; + + LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion); + + p_vout->p_sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL")); + if( NULL == p_vout->p_sys->hd3d9_dll ) + { + msg_Warn( p_vout, "cannot load d3d9.dll, aborting" ); + return VLC_EGENERIC; + } + + OurDirect3DCreate9 = + (void *)GetProcAddress( p_vout->p_sys->hd3d9_dll, + TEXT("Direct3DCreate9") ); + if( OurDirect3DCreate9 == NULL ) + { + msg_Err( p_vout, "Cannot locate reference to Direct3DCreate9 ABI in DLL" ); + return VLC_EGENERIC; + } + + /* Create the D3D object. */ + p_d3dobj = OurDirect3DCreate9( D3D_SDK_VERSION ); + if( NULL == p_d3dobj ) + { + msg_Err( p_vout, "Could not create Direct3D9 instance."); + return VLC_EGENERIC; + } + p_vout->p_sys->p_d3dobj = p_d3dobj; + + /* + ** Get device capabilities + */ + ZeroMemory(&d3dCaps, sizeof(d3dCaps)); + hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps); + if( FAILED(hr) ) + { + msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr); + return VLC_EGENERIC; + } + /* TODO: need to test device capabilities and select the right render function */ + + return VLC_SUCCESS; +} + +/***************************************************************************** + * DirectD3DVoutRelease: release an instance of Direct3D9 + *****************************************************************************/ + +static void Direct3DVoutRelease( vout_thread_t *p_vout ) +{ + if( p_vout->p_sys->p_d3dobj ) + { + IDirect3D9_Release(p_vout->p_sys->p_d3dobj); + p_vout->p_sys->p_d3dobj = NULL; + } + if( NULL != p_vout->p_sys->hd3d9_dll ) + { + FreeLibrary(p_vout->p_sys->hd3d9_dll); + p_vout->p_sys->hd3d9_dll = NULL; + } +} + +static int Direct3DFillPresentationParameters(vout_thread_t *p_vout, D3DPRESENT_PARAMETERS *d3dpp) +{ + LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; + D3DDISPLAYMODE d3ddm; + HRESULT hr; + + /* + ** Get the current desktop display mode, so we can set up a back + ** buffer of the same format + */ + hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm ); + if( FAILED(hr)) + { + msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr); + return VLC_EGENERIC; + } + + /* keep a copy of current desktop format */ + p_vout->p_sys->bbFormat = d3ddm.Format; + + /* Set up the structure used to create the D3DDevice. */ + ZeroMemory( d3dpp, sizeof(D3DPRESENT_PARAMETERS) ); + d3dpp->Flags = D3DPRESENTFLAG_VIDEO; + d3dpp->Windowed = TRUE; + d3dpp->hDeviceWindow = p_vout->p_sys->hvideownd; + d3dpp->BackBufferWidth = p_vout->output.i_width; + d3dpp->BackBufferHeight = p_vout->output.i_height; + d3dpp->SwapEffect = D3DSWAPEFFECT_COPY; + d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE; + d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + d3dpp->BackBufferFormat = d3ddm.Format; + d3dpp->BackBufferCount = 1; + d3dpp->EnableAutoDepthStencil = FALSE; + + return VLC_SUCCESS; +} + +/***************************************************************************** + * DirectD3DVoutOpen: Takes care of all the Direct3D9 initialisations + ***************************************************************************** + * This function creates Direct3D device + * this must be called from the vout thread for performance reason, as + * all Direct3D Device APIs are used in a non multithread safe environment + *****************************************************************************/ +static int Direct3DVoutOpen( vout_thread_t *p_vout ) +{ + LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; + LPDIRECT3DDEVICE9 p_d3ddev; + D3DPRESENT_PARAMETERS d3dpp; + HRESULT hr; + + if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) ) + return VLC_EGENERIC; + + // Create the D3DDevice + hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, p_vout->p_sys->hvideownd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING| + D3DCREATE_MULTITHREADED, + &d3dpp, &p_d3ddev ); + if( FAILED(hr) ) + { + msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr); + return VLC_EGENERIC; + } + p_vout->p_sys->p_d3ddev = p_d3ddev; + + msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" ); + return VLC_SUCCESS; +} + +/***************************************************************************** + * DirectD3DClose: release the Direct3D9 device + *****************************************************************************/ +static void Direct3DVoutClose( vout_thread_t *p_vout ) +{ + if( p_vout->p_sys->p_d3ddev ) + { + IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev); + p_vout->p_sys->p_d3ddev = NULL; + } + + p_vout->p_sys->hmonitor = NULL; +} + +/***************************************************************************** + * DirectD3DClose: reset the Direct3D9 device + ***************************************************************************** + * All resources must be deallocated before the reset occur, they will be + * realllocated once the reset has been performed successfully + *****************************************************************************/ +static int Direct3DVoutResetDevice( vout_thread_t *p_vout ) +{ + LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; + D3DPRESENT_PARAMETERS d3dpp; + HRESULT hr; + + if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) ) + return VLC_EGENERIC; + + // release all D3D objects + Direct3DVoutReleaseScene( p_vout ); + Direct3DVoutReleasePictures( p_vout ); + + hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp); + if( SUCCEEDED(hr) ) + { + // re-create them + if( (VLC_SUCCESS != Direct3DVoutCreatePictures(p_vout, 1)) + || (VLC_SUCCESS != Direct3DVoutCreateScene(p_vout)) ) + { + msg_Dbg(p_vout, "%s failed !", __FUNCTION__); + return VLC_EGENERIC; + } + } + else { + msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +static D3DFORMAT Direct3DVoutSelectFormat( vout_thread_t *p_vout, D3DFORMAT target, + const D3DFORMAT *formats, size_t count) +{ + LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; + size_t c; + + for( c=0; cp_sys->b_hw_yuv && ! _got_vista_or_above ) + { + /* it sounds like vista does not support YUV surfaces at all */ + switch( i_chroma ) + { + case VLC_FOURCC('U','Y','V','Y'): + case VLC_FOURCC('U','Y','N','V'): + case VLC_FOURCC('Y','4','2','2'): + { + static const D3DFORMAT formats[] = + { D3DFMT_UYVY, D3DFMT_YUY2, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + case VLC_FOURCC('I','4','2','0'): + case VLC_FOURCC('I','4','2','2'): + case VLC_FOURCC('Y','V','1','2'): + { + /* typically 3D textures don't support planar format + ** fallback to packed version and use CPU for the conversion + */ + static const D3DFORMAT formats[] = + { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + case VLC_FOURCC('Y','U','Y','2'): + case VLC_FOURCC('Y','U','N','V'): + { + static const D3DFORMAT formats[] = + { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + } + } + + switch( i_chroma ) + { + case VLC_FOURCC('R', 'V', '1', '5'): + { + static const D3DFORMAT formats[] = + { D3DFMT_X1R5G5B5 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + case VLC_FOURCC('R', 'V', '1', '6'): + { + static const D3DFORMAT formats[] = + { D3DFMT_R5G6B5 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + case VLC_FOURCC('R', 'V', '2', '4'): + { + static const D3DFORMAT formats[] = + { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + case VLC_FOURCC('R', 'V', '3', '2'): + { + static const D3DFORMAT formats[] = + { D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8 }; + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + default: + { + /* use display default format */ + LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; + D3DDISPLAYMODE d3ddm; + + HRESULT hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm ); + if( SUCCEEDED(hr)) + { + /* + ** some professional cards could use some advanced pixel format as default, + ** make sure we stick with chromas that we can handle internally + */ + switch( d3ddm.Format ) + { + case D3DFMT_R8G8B8: + case D3DFMT_X8R8G8B8: + case D3DFMT_A8R8G8B8: + msg_Dbg( p_vout, "defaulting to adpater pixel format"); + return Direct3DVoutSelectFormat(p_vout, target, &d3ddm.Format, 1); + default: + { + /* if we fall here, that probably means that we need to render some YUV format */ + static const D3DFORMAT formats[] = + { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 }; + msg_Dbg( p_vout, "defaulting to built-in pixel format"); + return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT)); + } + } + } + } + } + return D3DFMT_UNKNOWN; +} + +static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format) +{ + switch( format ) + { + case D3DFMT_YUY2: + p_vout->output.i_chroma = VLC_FOURCC('Y', 'U', 'Y', '2'); + break; + case D3DFMT_UYVY: + p_vout->output.i_chroma = VLC_FOURCC('U', 'Y', 'V', 'Y'); + break; + case D3DFMT_R8G8B8: + p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '2', '4'); + break; + case D3DFMT_X8R8G8B8: + case D3DFMT_A8R8G8B8: + p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '3', '2'); + p_vout->output.i_rmask = 0x00ff0000; + p_vout->output.i_gmask = 0x0000ff00; + p_vout->output.i_bmask = 0x000000ff; +# if defined( WORDS_BIGENDIAN ) + p_vout->output.i_rrshift = 0; + p_vout->output.i_lrshift = 24; + p_vout->output.i_rgshift = 0; + p_vout->output.i_lgshift = 16; + p_vout->output.i_rbshift = 0; + p_vout->output.i_lbshift = 8; +# else + p_vout->output.i_rrshift = 0; + p_vout->output.i_lrshift = 8; + p_vout->output.i_rgshift = 0; + p_vout->output.i_lgshift = 16; + p_vout->output.i_rbshift = 0; + p_vout->output.i_lbshift = 24; +# endif + break; + case D3DFMT_R5G6B5: + p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '6'); + p_vout->output.i_rmask = (0x1fL)<<11; + p_vout->output.i_gmask = (0x3fL)<<5; + p_vout->output.i_bmask = (0x1fL)<<0; +# if defined( WORDS_BIGENDIAN ) + p_vout->output.i_rrshift = 0; + p_vout->output.i_lrshift = 11; + p_vout->output.i_rgshift = 0; + p_vout->output.i_lgshift = 5; + p_vout->output.i_rbshift = 0; + p_vout->output.i_lbshift = 0; +# else + /* FIXME: since components are not byte aligned, + there is no chance that this will work */ + p_vout->output.i_rrshift = 0; + p_vout->output.i_lrshift = 0; + p_vout->output.i_rgshift = 0; + p_vout->output.i_lgshift = 5; + p_vout->output.i_rbshift = 0; + p_vout->output.i_lbshift = 11; +# endif + break; + case D3DFMT_X1R5G5B5: + p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '5'); + p_vout->output.i_rmask = (0x1fL)<<10; + p_vout->output.i_gmask = (0x1fL)<<5; + p_vout->output.i_bmask = (0x1fL)<<0; +# if defined( WORDS_BIGENDIAN ) + p_vout->output.i_rrshift = 0; + p_vout->output.i_lrshift = 10; + p_vout->output.i_rgshift = 0; + p_vout->output.i_lgshift = 5; + p_vout->output.i_rbshift = 0; + p_vout->output.i_lbshift = 0; +# else + /* FIXME: since components are not byte aligned, + there is no chance that this will work */ + p_vout->output.i_rrshift = 0; + p_vout->output.i_lrshift = 1; + p_vout->output.i_rgshift = 0; + p_vout->output.i_lgshift = 5; + p_vout->output.i_rbshift = 0; + p_vout->output.i_lbshift = 11; +# endif + break; + default: + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +/***************************************************************************** + * Direct3DVoutCreatePictures: allocate a vector of identical pictures + ***************************************************************************** + * Each picture has an associated offscreen surface in video memory + * depending on hardware capabilities the picture chroma will be as close + * as possible to the orginal render chroma to reduce CPU conversion overhead + * and delegate this work to video card GPU + *****************************************************************************/ +static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics ) +{ + LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; + D3DFORMAT format; + HRESULT hr; + size_t c; + + I_OUTPUTPICTURES = 0; + + /* + ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to + ** the requested chroma which is usable by the hardware in an offscreen surface, as they + ** typically support more formats than textures + */ + format = Direct3DVoutFindFormat(p_vout, p_vout->render.i_chroma, p_vout->p_sys->bbFormat); + if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) ) + { + msg_Err(p_vout, "surface pixel format is not supported."); + return VLC_EGENERIC; + } + + for( c=0; cp_picture+c; + + hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev, + p_vout->render.i_width, + p_vout->render.i_height, + format, + D3DPOOL_DEFAULT, + &p_d3dsurf, + NULL); + if( FAILED(hr) ) + { + msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr); + Direct3DVoutReleasePictures(p_vout); + return VLC_EGENERIC; + } + + /* fill surface with black color */ + IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) ); + + /* assign surface to internal structure */ + p_pic->p_sys = (void *)p_d3dsurf; + + /* Now that we've got our direct-buffer, we can finish filling in the + * picture_t structures */ + switch( p_vout->output.i_chroma ) + { + case VLC_FOURCC('R','G','B','2'): + 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_pixel_pitch = 1; + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + break; + case VLC_FOURCC('R','V','1','5'): + case VLC_FOURCC('R','V','1','6'): + 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_pixel_pitch = 2; + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + break; + case VLC_FOURCC('R','V','2','4'): + 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_pixel_pitch = 3; + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + break; + case VLC_FOURCC('R','V','3','2'): + 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_pixel_pitch = 4; + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + break; + case VLC_FOURCC('U','Y','V','Y'): + case VLC_FOURCC('Y','U','Y','2'): + 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_pixel_pitch = 2; + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + break; + default: + Direct3DVoutReleasePictures(p_vout); + return VLC_EGENERIC; + } + p_pic->i_status = DESTROYED_PICTURE; + p_pic->i_type = DIRECT_PICTURE; + p_pic->b_slow = VLC_TRUE; + p_pic->pf_lock = Direct3DVoutLockSurface; + p_pic->pf_unlock = Direct3DVoutUnlockSurface; + PP_OUTPUTPICTURE[c] = p_pic; + + I_OUTPUTPICTURES = ++c; + } + + msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Direct3DVoutReleasePictures: destroy a picture vector + ***************************************************************************** + * release all video resources used for pictures + *****************************************************************************/ +static void Direct3DVoutReleasePictures( vout_thread_t *p_vout) +{ + size_t i_num_pics = I_OUTPUTPICTURES; + size_t c; + for( c=0; cp_picture+c; + if( p_pic->p_sys ) + { + LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; + + p_pic->p_sys = NULL; + + if( p_d3dsurf ) + { + IDirect3DSurface9_Release(p_d3dsurf); + } + } + } + msg_Dbg( p_vout, "%u Direct3D pictures released.", c); + + I_OUTPUTPICTURES = 0; +} + +/***************************************************************************** + * Direct3DVoutLockSurface: Lock surface and get picture data pointer + ***************************************************************************** + * This function locks a surface and get the surface descriptor which amongst + * other things has the pointer to the picture data. + *****************************************************************************/ +static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) +{ + HRESULT hr; + D3DLOCKED_RECT d3drect; + LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; + + if( NULL == p_d3dsurf ) + return VLC_EGENERIC; + + /* Lock the surface to get a valid pointer to the picture buffer */ + hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, 0); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return VLC_EGENERIC; + } + + /* fill in buffer info in first plane */ + p_pic->p->p_pixels = d3drect.pBits; + p_pic->p->i_pitch = d3drect.Pitch; + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface(). + *****************************************************************************/ +static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) +{ + HRESULT hr; + LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; + + if( NULL == p_d3dsurf ) + return VLC_EGENERIC; + + /* Unlock the Surface */ + hr = IDirect3DSurface9_UnlockRect(p_d3dsurf); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +/***************************************************************************** + * Direct3DVoutCreateScene: allocate and initialize a 3D scene + ***************************************************************************** + * for advanced blending/filtering a texture needs be used in a 3D scene. + *****************************************************************************/ + +static int Direct3DVoutCreateScene( vout_thread_t *p_vout ) +{ + LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; + LPDIRECT3DTEXTURE9 p_d3dtex; + LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; + + HRESULT hr; + + /* + ** Create a texture for use when rendering a scene + ** for performance reason, texture format is identical to backbuffer + ** which would usually be a RGB format + */ + hr = IDirect3DDevice9_CreateTexture(p_d3ddev, + p_vout->render.i_width, + p_vout->render.i_height, + 1, + D3DUSAGE_RENDERTARGET, + p_vout->p_sys->bbFormat, + D3DPOOL_DEFAULT, + &p_d3dtex, + NULL); + if( FAILED(hr)) + { + msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr); + return VLC_EGENERIC; + } + + /* + ** Create a vertex buffer for use when rendering scene + */ + hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev, + sizeof(CUSTOMVERTEX)*4, + D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, + D3DFVF_CUSTOMVERTEX, + D3DPOOL_DEFAULT, + &p_d3dvtc, + NULL); + if( FAILED(hr) ) + { + msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr); + IDirect3DTexture9_Release(p_d3dtex); + return VLC_EGENERIC; + } + + p_vout->p_sys->p_d3dtex = p_d3dtex; + p_vout->p_sys->p_d3dvtc = p_d3dvtc; + + // Texture coordinates outside the range [0.0, 1.0] are set + // to the texture color at 0.0 or 1.0, respectively. + IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + + // Set linear filtering quality + IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + + // set maximum ambient light + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255)); + + // Turn off culling + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE); + + // Turn off the zbuffer + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE); + + // Turn off lights + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE); + + // Enable dithering + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE); + + // disable stencil + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE); + + // manage blending + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE); + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE); + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10); + IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER); + + // Set texture states + IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE); + IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE); + IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE); + + // turn off alpha operation + IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + msg_Dbg( p_vout, "Direct3D scene created successfully"); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Direct3DVoutReleaseScene + *****************************************************************************/ +static void Direct3DVoutReleaseScene( vout_thread_t *p_vout ) +{ + LPDIRECT3DTEXTURE9 p_d3dtex = p_vout->p_sys->p_d3dtex; + LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc; + + if( p_d3dvtc ) + { + IDirect3DVertexBuffer9_Release(p_d3dvtc); + p_vout->p_sys->p_d3dvtc = NULL; + } + + if( p_d3dtex ) + { + IDirect3DTexture9_Release(p_d3dtex); + p_vout->p_sys->p_d3dtex = NULL; + } + msg_Dbg( p_vout, "Direct3D scene released successfully"); +} + +/***************************************************************************** + * Render: copy picture surface into a texture and render into a scene + ***************************************************************************** + * This function is intented for higher end 3D cards, with pixel shader support + * and at least 64 MB of video RAM. + *****************************************************************************/ +static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic ) +{ + LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; + LPDIRECT3DTEXTURE9 p_d3dtex; + LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; + LPDIRECT3DSURFACE9 p_d3dsrc, p_d3ddest; + CUSTOMVERTEX *p_vertices; + HRESULT hr; + float f_width, f_height; + + // check if device is still available + hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev); + if( FAILED(hr) ) + { + if( (D3DERR_DEVICENOTRESET != hr) + || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout)) ) + { + // device is not usable at present (lost device, out of video mem ?) + return; + } + } + p_d3dtex = p_vout->p_sys->p_d3dtex; + p_d3dvtc = p_vout->p_sys->p_d3dvtc; + + /* Clear the backbuffer and the zbuffer */ + hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET, + D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 ); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } + + /* retrieve picture surface */ + p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys; + if( NULL == p_d3dsrc ) + { + msg_Dbg( p_vout, "no surface to render ?"); + return; + } + + /* retrieve texture top-level surface */ + hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } + + /* Copy picture surface into texture surface, color space conversion happens here */ + hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE); + IDirect3DSurface9_Release(p_d3ddest); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } + + /* Update the vertex buffer */ + hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (VOID **)(&p_vertices), D3DLOCK_DISCARD); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } + + /* Setup vertices */ + f_width = (float)(p_vout->output.i_width); + f_height = (float)(p_vout->output.i_height); + + p_vertices[0].x = 0.0f; // left + p_vertices[0].y = 0.0f; // top + p_vertices[0].z = 0.0f; + p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); + p_vertices[0].rhw = 1.0f; + p_vertices[0].tu = 0.0f; + p_vertices[0].tv = 0.0f; + + p_vertices[1].x = f_width; // right + p_vertices[1].y = 0.0f; // top + p_vertices[1].z = 0.0f; + p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); + p_vertices[1].rhw = 1.0f; + p_vertices[1].tu = 1.0f; + p_vertices[1].tv = 0.0f; + + p_vertices[2].x = f_width; // right + p_vertices[2].y = f_height; // bottom + p_vertices[2].z = 0.0f; + p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); + p_vertices[2].rhw = 1.0f; + p_vertices[2].tu = 1.0f; + p_vertices[2].tv = 1.0f; + + p_vertices[3].x = 0.0f; // left + p_vertices[3].y = f_height; // bottom + p_vertices[3].z = 0.0f; + p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); + p_vertices[3].rhw = 1.0f; + p_vertices[3].tu = 0.0f; + p_vertices[3].tv = 1.0f; + + hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } + + // Begin the scene + hr = IDirect3DDevice9_BeginScene(p_d3ddev); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } + + // Setup our texture. Using textures introduces the texture stage states, + // which govern how textures get blended together (in the case of multiple + // textures) and lighting information. In this case, we are modulating + // (blending) our texture with the diffuse color of the vertices. + hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + IDirect3DDevice9_EndScene(p_d3ddev); + return; + } + + // Render the vertex buffer contents + hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX)); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + IDirect3DDevice9_EndScene(p_d3ddev); + return; + } + + // we use FVF instead of vertex shader + hr = IDirect3DDevice9_SetVertexShader(p_d3ddev, NULL); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + IDirect3DDevice9_EndScene(p_d3ddev); + return; + } + + hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + IDirect3DDevice9_EndScene(p_d3ddev); + return; + } + + // draw rectangle + hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + IDirect3DDevice9_EndScene(p_d3ddev); + return; + } + + // End the scene + hr = IDirect3DDevice9_EndScene(p_d3ddev); + if( FAILED(hr) ) + { + msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); + return; + } +} + diff --git a/modules/video_output/msw/directx.c b/modules/video_output/msw/directx.c new file mode 100644 index 0000000000..e6500b7260 --- /dev/null +++ b/modules/video_output/msw/directx.c @@ -0,0 +1,2157 @@ +/***************************************************************************** + * vout.c: Windows DirectX video output display method + ***************************************************************************** + * Copyright (C) 2001-2004 the VideoLAN team + * $Id$ + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble: + * + * This plugin will use YUV overlay if supported, using overlay will result in + * the best video quality (hardware interpolation when rescaling the picture) + * and the fastest display as it requires less processing. + * + * If YUV overlay is not supported this plugin will use RGB offscreen video + * 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() */ +#include /* strerror() */ + +#include +#include +#include +#include + +#include +#include +#include + +#ifndef UNDER_CE +# include +#endif +#undef GetSystemMetrics + +#ifndef MONITOR_DEFAULTTONEAREST +# define MONITOR_DEFAULTTONEAREST 2 +#endif + +#include "vout.h" + +/***************************************************************************** + * picture_sys_t: direct buffer method descriptor + ***************************************************************************** + * This structure is part of the picture descriptor, it describes the + * DirectX specific properties of a direct buffer. + *****************************************************************************/ +struct picture_sys_t +{ + LPDIRECTDRAWSURFACE2 p_surface; + LPDIRECTDRAWSURFACE2 p_front_surface; + DDSURFACEDESC ddsd; +}; + +/***************************************************************************** + * DirectDraw GUIDs. + * Defining them here allows us to get rid of the dxguid library during + * the linking stage. + *****************************************************************************/ +#include +#undef GUID_EXT +#define GUID_EXT +DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 ); + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ +static int OpenVideo ( vlc_object_t * ); +static void CloseVideo ( vlc_object_t * ); + +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 FirstDisplay( 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 ); +static int UpdatePictureStruct( vout_thread_t *, picture_t *, int ); + +static int DirectXInitDDraw ( vout_thread_t *p_vout ); +static void DirectXCloseDDraw ( vout_thread_t *p_vout ); +static int DirectXCreateDisplay ( vout_thread_t *p_vout ); +static void DirectXCloseDisplay ( vout_thread_t *p_vout ); +static int DirectXCreateSurface ( vout_thread_t *p_vout, + LPDIRECTDRAWSURFACE2 *, int, int, int ); +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 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_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 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(); + 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 */ + /* check if we registered a window class because we need to + * unregister it */ + WNDCLASS wndclass; + if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) + UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); +#endif + +/***************************************************************************** + * OpenVideo: allocate DirectX video thread output method + ***************************************************************************** + * This function allocates and initialize the DirectX vout method. + *****************************************************************************/ +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 VLC_ENOMEM; + } + memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); + + /* Initialisations */ + p_vout->pf_init = Init; + p_vout->pf_end = End; + p_vout->pf_manage = Manage; + p_vout->pf_render = NULL; + p_vout->pf_display = FirstDisplay; + + p_vout->p_sys->p_ddobject = NULL; + 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->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_wallpaper = 0; + vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); + SetRectEmpty( &p_vout->p_sys->rect_display ); + 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 ); + var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + p_vout->p_sys->b_cursor_hidden = 0; + p_vout->p_sys->i_lastmoved = mdate(); + + /* Set main window's size */ + p_vout->p_sys->i_window_width = p_vout->i_window_width; + p_vout->p_sys->i_window_height = p_vout->i_window_height; + + /* Create the Vout EventThread, this thread is created by us to isolate + * the Win32 PeekMessage function calls. We want to do this because + * Windows can stay blocked inside this call for a long time, and when + * this happens it thus blocks vlc's video_output thread. + * DirectXEventThread will take care of the creation of the video + * window (because PeekMessage has to be called from the same thread which + * created the window). */ + msg_Dbg( p_vout, "creating DirectXEventThread" ); + 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, "Vout Events Thread", + E_(EventThread), 0, 1 ) ) + { + msg_Err( p_vout, "cannot create Vout EventThread" ); + vlc_object_destroy( p_vout->p_sys->p_event ); + p_vout->p_sys->p_event = NULL; + goto error; + } + + if( p_vout->p_sys->p_event->b_error ) + { + msg_Err( p_vout, "Vout EventThread failed" ); + goto error; + } + + vlc_object_attach( p_vout->p_sys->p_event, p_vout ); + + msg_Dbg( p_vout, "Vout EventThread running" ); + + /* Initialise DirectDraw */ + if( DirectXInitDDraw( p_vout ) ) + { + msg_Err( p_vout, "cannot initialize DirectX DirectDraw" ); + goto error; + } + + /* Create the directx display */ + if( DirectXCreateDisplay( p_vout ) ) + { + msg_Err( p_vout, "cannot initialize DirectX DirectDraw" ); + goto error; + } + + /* 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 ); + + /* disable screensaver by temporarily changing system settings */ + p_vout->p_sys->i_spi_lowpowertimeout = 0; + p_vout->p_sys->i_spi_powerofftimeout = 0; + p_vout->p_sys->i_spi_screensavetimeout = 0; + var_Get( p_vout, "disable-screensaver", &val); + if( val.b_bool ) { + msg_Dbg(p_vout, "disabling screen saver"); + SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT, + 0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0); + if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { + SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0); + } + SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, + &(p_vout->p_sys->i_spi_powerofftimeout), 0); + if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { + SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0); + } + SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, + &(p_vout->p_sys->i_spi_screensavetimeout), 0); + if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); + } + } + + return VLC_SUCCESS; + + error: + CloseVideo( VLC_OBJECT(p_vout) ); + return VLC_EGENERIC; +} + +/***************************************************************************** + * Init: initialize DirectX video thread output method + ***************************************************************************** + * This function create the directx surfaces needed by the output thread. + * It is called at the beginning of the thread. + *****************************************************************************/ +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 + * coordinates and aspect. */ + 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_(UpdateRects)( p_vout, VLC_TRUE ); + +#define MAX_DIRECTBUFFERS 1 + /* Right now we use only 1 directbuffer because we don't want the + * video decoder to decode directly into direct buffers as they are + * created into video memory and video memory is _really_ slow */ + + /* Choose the chroma we will try first. */ + switch( p_vout->render.i_chroma ) + { + case VLC_FOURCC('Y','U','Y','2'): + case VLC_FOURCC('Y','U','N','V'): + p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2'); + break; + case VLC_FOURCC('U','Y','V','Y'): + case VLC_FOURCC('U','Y','N','V'): + case VLC_FOURCC('Y','4','2','2'): + p_vout->output.i_chroma = VLC_FOURCC('U','Y','V','Y'); + break; + case VLC_FOURCC('Y','V','Y','U'): + p_vout->output.i_chroma = VLC_FOURCC('Y','V','Y','U'); + break; + default: + p_vout->output.i_chroma = VLC_FOURCC('Y','V','1','2'); + break; + } + + NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); + + i_chroma_backup = p_vout->output.i_chroma; + + if( !I_OUTPUTPICTURES ) + { + /* hmmm, it didn't work! Let's try commonly supported chromas */ + 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 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 ); + } + } + + if( !I_OUTPUTPICTURES ) + { + /* If it still didn't work then don't try to use an overlay */ + p_vout->output.i_chroma = i_chroma_backup; + p_vout->p_sys->b_using_overlay = 0; + NewPictureVec( p_vout, p_vout->p_picture, MAX_DIRECTBUFFERS ); + } + + /* Change the window title bar text */ + 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; +} + +/***************************************************************************** + * End: terminate Sys video thread output method + ***************************************************************************** + * Terminate an output method created by Create. + * It is called at the end of the thread. + *****************************************************************************/ +static void End( vout_thread_t *p_vout ) +{ + FreePictureVec( p_vout, p_vout->p_picture, I_OUTPUTPICTURES ); + + DirectXCloseDisplay( p_vout ); + DirectXCloseDDraw( p_vout ); + + return; +} + +/***************************************************************************** + * CloseVideo: destroy Sys video thread output method + ***************************************************************************** + * 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" ); + + if( p_vout->p_sys->p_event ) + { + vlc_object_detach( p_vout->p_sys->p_event ); + + /* Kill Vout EventThread */ + p_vout->p_sys->p_event->b_die = VLC_TRUE; + + /* we need to be sure Vout EventThread 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 ); + + /* restore screensaver system settings */ + if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { + SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, + p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0); + } + if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { + SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, + p_vout->p_sys->i_spi_powerofftimeout, NULL, 0); + } + if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, + p_vout->p_sys->i_spi_screensavetimeout, NULL, 0); + } + + if( p_vout->p_sys ) + { + free( p_vout->p_sys ); + p_vout->p_sys = NULL; + } +} + +/***************************************************************************** + * Manage: handle Sys events + ***************************************************************************** + * This function should be called regularly by the video output thread. + * It returns a non null value if an error occurred. + *****************************************************************************/ +static int Manage( vout_thread_t *p_vout ) +{ + /* If we do not control our window, we check for geometry changes + * ourselves because the parent might not send us its events. */ + vlc_mutex_lock( &p_vout->p_sys->lock ); + if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) + { + RECT rect_parent; + POINT point; + + 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 + { + vlc_mutex_unlock( &p_vout->p_sys->lock ); + } + + /* + * Position Change + */ + if( p_vout->p_sys->i_changes & DX_POSITION_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_(UpdateRects)( 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; + DirectDrawUpdateOverlay( p_vout ); + } + + /* + * Fullscreen change + */ + if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE + || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) + { + Win32ToggleFullscreen( p_vout ); + + p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + } + + /* + * Pointer change + */ + 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 ) + { + 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 VLC_EGENERIC; /* exit */ + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Display: displays previously rendered output + ***************************************************************************** + * This function sends the currently rendered image to the display, wait until + * it is displayed and switch the two rendering buffers, preparing next frame. + *****************************************************************************/ +static void Display( vout_thread_t *p_vout, picture_t *p_pic ) +{ + HRESULT dxresult; + + if( (p_vout->p_sys->p_display == NULL) ) + { + 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 ) + DirectDrawUpdateOverlay( p_vout ); + } + + if( !p_vout->p_sys->b_using_overlay ) + { + DDBLTFX ddbltfx; + + /* We ask for the "NOTEARING" option */ + memset( &ddbltfx, 0, sizeof(DDBLTFX) ); + ddbltfx.dwSize = sizeof(DDBLTFX); + 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 != DD_OK ) + { + 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 != DD_OK ) + { + msg_Warn( p_vout, "could not flip overlay (error %li)", dxresult ); + } + + /* set currently displayed pic */ + p_vout->p_sys->p_current_surface = p_pic->p_sys->p_front_surface; + + /* Lock surface to get all the required info */ + if( DirectXLockSurface( p_vout, p_pic ) ) + { + /* AAARRGG */ + msg_Warn( p_vout, "cannot lock surface" ); + return; + } + DirectXUnlockSurface( p_vout, p_pic ); + } +} + +/* +** this function is only used once when the first picture is received +** this function will show the video window once video is ready for +** display. +*/ + +static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic ) +{ + /* get initial picture rendered on overlay surface */ + Display(p_vout, p_pic); + + IDirectDraw_WaitForVerticalBlank(p_vout->p_sys->p_ddobject, + DDWAITVB_BLOCKBEGIN, NULL); + + if( p_vout->p_sys->b_using_overlay ) + { + /* set the colorkey as the backgound brush for the video window */ + SetClassLong( p_vout->p_sys->hvideownd, GCL_HBRBACKGROUND, + (LONG)CreateSolidBrush( p_vout->p_sys->i_rgb_colorkey ) ); + } + /* + ** Video window is initially hidden, show it now since we got a + ** picture to show. + */ + SetWindowPos( p_vout->p_sys->hvideownd, NULL, 0, 0, 0, 0, + SWP_ASYNCWINDOWPOS| + SWP_FRAMECHANGED| + SWP_SHOWWINDOW| + SWP_NOMOVE| + SWP_NOSIZE| + SWP_NOZORDER ); + + /* use and restores proper display function for further pictures */ + p_vout->pf_display = Display; +} + +/* 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 %ld,%ld", 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 + ***************************************************************************** + * This function initialise and allocate resources for DirectDraw. + *****************************************************************************/ +static int DirectXInitDDraw( vout_thread_t *p_vout ) +{ + 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(_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, + _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( p_vout->p_sys->p_display_driver, + &p_ddobject, NULL ); + if( dxresult != DD_OK ) + { + msg_Err( p_vout, "DirectXInitDDraw cannot initialize DDraw" ); + goto error; + } + + /* Get the IDirectDraw2 interface */ + dxresult = IDirectDraw_QueryInterface( p_ddobject, &IID_IDirectDraw2, + (LPVOID *)&p_vout->p_sys->p_ddobject ); + /* Release the unused interface */ + IDirectDraw_Release( p_ddobject ); + if( dxresult != DD_OK ) + { + msg_Err( p_vout, "cannot get IDirectDraw2 interface" ); + goto error; + } + + /* Set DirectDraw Cooperative level, ie what control we want over Windows + * display */ + dxresult = IDirectDraw2_SetCooperativeLevel( p_vout->p_sys->p_ddobject, + 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 (%lix%li,%lix%li)", + 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 VLC_SUCCESS; + + error: + if( p_vout->p_sys->p_ddobject ) + IDirectDraw2_Release( p_vout->p_sys->p_ddobject ); + if( p_vout->p_sys->hddraw_dll ) + FreeLibrary( p_vout->p_sys->hddraw_dll ); + p_vout->p_sys->hddraw_dll = NULL; + p_vout->p_sys->p_ddobject = NULL; + return VLC_EGENERIC; +} + +/***************************************************************************** + * DirectXCreateDisplay: create the DirectDraw display. + ***************************************************************************** + * Create and initialize display according to preferences specified in the vout + * thread fields. + *****************************************************************************/ +static int DirectXCreateDisplay( vout_thread_t *p_vout ) +{ + HRESULT dxresult; + DDSURFACEDESC ddsd; + LPDIRECTDRAWSURFACE p_display; + + msg_Dbg( p_vout, "DirectXCreateDisplay" ); + + /* Now get the primary surface. This surface is what you actually see + * on your screen */ + memset( &ddsd, 0, sizeof( DDSURFACEDESC )); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, + &ddsd, &p_display, NULL ); + if( dxresult != DD_OK ) + { + msg_Err( p_vout, "cannot get primary surface (error %li)", dxresult ); + return VLC_EGENERIC; + } + + dxresult = IDirectDrawSurface_QueryInterface( p_display, + &IID_IDirectDrawSurface2, + (LPVOID *)&p_vout->p_sys->p_display ); + /* Release the old interface */ + IDirectDrawSurface_Release( p_display ); + if ( dxresult != DD_OK ) + { + 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 ); + + E_(UpdateRects)( p_vout, VLC_TRUE ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * DirectXCreateClipper: Create a clipper that will be used when blitting the + * RGB surface to the main display. + ***************************************************************************** + * This clipper prevents us to modify by mistake anything on the screen + * which doesn't belong to our window. For example when a part of our video + * window is hidden by another window. + *****************************************************************************/ +static int DirectXCreateClipper( vout_thread_t *p_vout ) +{ + HRESULT dxresult; + + msg_Dbg( p_vout, "DirectXCreateClipper" ); + + /* Create the clipper */ + dxresult = IDirectDraw2_CreateClipper( p_vout->p_sys->p_ddobject, 0, + &p_vout->p_sys->p_clipper, NULL ); + if( dxresult != DD_OK ) + { + 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->hvideownd ); + if( dxresult != DD_OK ) + { + msg_Warn( p_vout, "cannot attach clipper to window (error %li)", + dxresult ); + goto error; + } + + /* associate the clipper with the surface */ + dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display, + p_vout->p_sys->p_clipper); + if( dxresult != DD_OK ) + { + msg_Warn( p_vout, "cannot attach clipper to surface (error %li)", + dxresult ); + goto error; + } + + 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 VLC_EGENERIC; +} + +/***************************************************************************** + * DirectXCreateSurface: create an YUV overlay or RGB surface for the video. + ***************************************************************************** + * The best method of display is with an YUV overlay because the YUV->RGB + * conversion is done in hardware. + * You can also create a plain RGB surface. + * ( Maybe we could also try an RGB overlay surface, which could have hardware + * scaling and which would also be faster in window mode because you don't + * need to do any blitting to the main display...) + *****************************************************************************/ +static int DirectXCreateSurface( vout_thread_t *p_vout, + LPDIRECTDRAWSURFACE2 *pp_surface_final, + int i_chroma, int b_overlay, + int i_backbuffers ) +{ + HRESULT dxresult; + LPDIRECTDRAWSURFACE p_surface; + DDSURFACEDESC ddsd; + + /* Create the video surface */ + if( b_overlay ) + { + /* Now try to create the YUV overlay surface. + * This overlay will be displayed on top of the primary surface. + * A color key is used to determine whether or not the overlay will be + * displayed, ie the overlay will be displayed in place of the primary + * surface wherever the primary surface will have this color. + * The video window has been created with a background of this color so + * the overlay will be only displayed on top of this window */ + + memset( &ddsd, 0, sizeof( DDSURFACEDESC )); + ddsd.dwSize = sizeof(DDSURFACEDESC); + 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 |= (i_backbuffers ? DDSD_BACKBUFFERCOUNT : 0); + 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.dwWidth = p_vout->render.i_width; + ddsd.dwBackBufferCount = i_backbuffers; + + dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, + &ddsd, &p_surface, NULL ); + if( dxresult != DD_OK ) + { + *pp_surface_final = NULL; + return VLC_EGENERIC; + } + } + + if( !b_overlay ) + { + vlc_bool_t b_rgb_surface = + ( i_chroma == VLC_FOURCC('R','G','B','2') ) + || ( i_chroma == VLC_FOURCC('R','V','1','5') ) + || ( i_chroma == VLC_FOURCC('R','V','1','6') ) + || ( i_chroma == VLC_FOURCC('R','V','2','4') ) + || ( i_chroma == VLC_FOURCC('R','V','3','2') ); + + memset( &ddsd, 0, sizeof( DDSURFACEDESC ) ); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + 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; + + if( p_vout->p_sys->b_use_sysmem ) + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + else + ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; + + if( !b_rgb_surface ) + { + ddsd.dwFlags |= DDSD_PIXELFORMAT; + ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + ddsd.ddpfPixelFormat.dwFourCC = i_chroma; + } + + dxresult = IDirectDraw2_CreateSurface( p_vout->p_sys->p_ddobject, + &ddsd, &p_surface, NULL ); + if( dxresult != DD_OK ) + { + *pp_surface_final = NULL; + return VLC_EGENERIC; + } + } + + /* Now that the surface is created, try to get a newer DirectX interface */ + dxresult = IDirectDrawSurface_QueryInterface( p_surface, + &IID_IDirectDrawSurface2, + (LPVOID *)pp_surface_final ); + IDirectDrawSurface_Release( p_surface ); /* Release the old interface */ + if ( dxresult != DD_OK ) + { + msg_Err( p_vout, "cannot query IDirectDrawSurface2 interface " + "(error %li)", dxresult ); + *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( DirectDrawUpdateOverlay( 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; +} + +/***************************************************************************** + * DirectDrawUpdateOverlay: Move or resize overlay surface on video display. + ***************************************************************************** + * This function is used to move or resize an overlay surface on the screen. + * Ususally the overlay is moved by the user and thus, by a move or resize + * event (in Manage). + *****************************************************************************/ +int DirectDrawUpdateOverlay( vout_thread_t *p_vout ) +{ + DDOVERLAYFX ddofx; + DWORD dwFlags; + HRESULT dxresult; + RECT rect_src = p_vout->p_sys->rect_src_clipped; + RECT rect_dest = p_vout->p_sys->rect_dest_clipped; + + if( !p_vout->p_sys->b_using_overlay ) return VLC_EGENERIC; + + if( p_vout->p_sys->b_wallpaper ) + { + int i_x, i_y, i_width, i_height; + + 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 */ + + /* Position and show the overlay */ + memset(&ddofx, 0, sizeof(DDOVERLAYFX)); + ddofx.dwSize = sizeof(DDOVERLAYFX); + ddofx.dckDestColorkey.dwColorSpaceLowValue = p_vout->p_sys->i_colorkey; + ddofx.dckDestColorkey.dwColorSpaceHighValue = p_vout->p_sys->i_colorkey; + + dwFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE; + + dxresult = IDirectDrawSurface2_UpdateOverlay( + 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, "DirectDrawUpdateOverlay cannot move/resize overlay" ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * DirectXCloseDDraw: Release the DDraw object allocated by DirectXInitDDraw + ***************************************************************************** + * This function returns all resources allocated by DirectXInitDDraw. + *****************************************************************************/ +static void DirectXCloseDDraw( vout_thread_t *p_vout ) +{ + msg_Dbg( p_vout, "DirectXCloseDDraw" ); + if( p_vout->p_sys->p_ddobject != NULL ) + { + IDirectDraw2_Release(p_vout->p_sys->p_ddobject); + p_vout->p_sys->p_ddobject = NULL; + } + + if( p_vout->p_sys->hddraw_dll != NULL ) + { + 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; +} + +/***************************************************************************** + * DirectXCloseDisplay: close and reset the DirectX display device + ***************************************************************************** + * This function returns all resources allocated by DirectXCreateDisplay. + *****************************************************************************/ +static void DirectXCloseDisplay( vout_thread_t *p_vout ) +{ + msg_Dbg( p_vout, "DirectXCloseDisplay" ); + + if( p_vout->p_sys->p_clipper != NULL ) + { + msg_Dbg( p_vout, "DirectXCloseDisplay clipper" ); + IDirectDrawClipper_Release( p_vout->p_sys->p_clipper ); + p_vout->p_sys->p_clipper = NULL; + } + + if( p_vout->p_sys->p_display != NULL ) + { + msg_Dbg( p_vout, "DirectXCloseDisplay display" ); + IDirectDrawSurface2_Release( p_vout->p_sys->p_display ); + p_vout->p_sys->p_display = NULL; + } +} + +/***************************************************************************** + * DirectXCloseSurface: close the YUV overlay or RGB surface. + ***************************************************************************** + * This function returns all resources allocated for the surface. + *****************************************************************************/ +static void DirectXCloseSurface( vout_thread_t *p_vout, + LPDIRECTDRAWSURFACE2 p_surface ) +{ + msg_Dbg( p_vout, "DirectXCloseSurface" ); + if( p_surface != NULL ) + { + IDirectDrawSurface2_Release( p_surface ); + } +} + +/***************************************************************************** + * NewPictureVec: allocate a vector of identical pictures + ***************************************************************************** + * Returns 0 on success, -1 otherwise + *****************************************************************************/ +static int NewPictureVec( vout_thread_t *p_vout, picture_t *p_pic, + int i_num_pics ) +{ + int i; + int i_ret = VLC_SUCCESS; + LPDIRECTDRAWSURFACE2 p_surface; + + 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; + + /* First we try to use an YUV overlay surface. + * The overlay surface that we create won't be used to decode directly + * into it because accessing video memory directly is way to slow (remember + * that pictures are decoded macroblock per macroblock). Instead the video + * will be decoded in picture buffers in system memory which will then be + * memcpy() to the overlay surface. */ + if( p_vout->p_sys->b_using_overlay ) + { + /* 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 */ ); + + if( !p_vout->p_sys->b_3buf_overlay || i_ret != VLC_SUCCESS ) + { + /* Try to reduce the 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( i_ret == VLC_SUCCESS ) + { + DDSCAPS dds_caps; + picture_t front_pic; + picture_sys_t front_pic_sys; + front_pic.p_sys = &front_pic_sys; + + /* 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 VLC_ENOMEM; + } + + /* set front buffer */ + p_pic[0].p_sys->p_front_surface = p_surface; + + /* Get the back buffer */ + memset( &dds_caps, 0, sizeof( DDSCAPS ) ); + dds_caps.dwCaps = DDSCAPS_BACKBUFFER; + if( DD_OK != IDirectDrawSurface2_GetAttachedSurface( + p_surface, &dds_caps, + &p_pic[0].p_sys->p_surface ) ) + { + msg_Warn( p_vout, "NewPictureVec could not get back buffer" ); + /* front buffer is the same as back buffer */ + p_pic[0].p_sys->p_surface = p_surface; + } + + + 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( 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_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 ); + } + + DirectDrawUpdateOverlay( p_vout ); + I_OUTPUTPICTURES = 1; + msg_Dbg( p_vout, "YUV overlay created successfully" ); + } + } + + /* As we can't have an overlay, we'll try to create a plain offscreen + * surface. This surface will reside in video memory because there's a + * better chance then that we'll be able to use some kind of hardware + * acceleration like rescaling, blitting or YUV->RGB conversions. + * We then only need to blit this surface onto the main display when we + * want to display it */ + if( !p_vout->p_sys->b_using_overlay ) + { + if( p_vout->p_sys->b_hw_yuv ) + { + 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( 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; + + ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + IDirectDrawSurface2_GetPixelFormat( p_vout->p_sys->p_display, + &ddpfPixelFormat ); + + if( ddpfPixelFormat.dwFlags & DDPF_RGB ) + { + switch( ddpfPixelFormat.dwRGBBitCount ) + { + 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'); + break; + case 16: + p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); + break; + case 24: + p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); + break; + case 32: + p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); + break; + default: + msg_Err( p_vout, "unknown screen depth" ); + return VLC_EGENERIC; + } + p_vout->output.i_rmask = ddpfPixelFormat.dwRBitMask; + p_vout->output.i_gmask = ddpfPixelFormat.dwGBitMask; + p_vout->output.i_bmask = ddpfPixelFormat.dwBBitMask; + } + + p_vout->p_sys->b_hw_yuv = 0; + + 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( 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 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, "created plain surface of chroma:%.4s", + (char *)&p_vout->output.i_chroma ); + } + } + + + /* Now that we've got all our direct-buffers, we can finish filling in the + * picture_t structures */ + for( i = 0; i < I_OUTPUTPICTURES; i++ ) + { + 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( DirectXLockSurface( p_vout, &p_pic[i] ) != VLC_SUCCESS ) + { + /* AAARRGG */ + FreePictureVec( p_vout, p_pic, I_OUTPUTPICTURES ); + I_OUTPUTPICTURES = 0; + msg_Err( p_vout, "cannot lock surface" ); + return VLC_EGENERIC; + } + DirectXUnlockSurface( p_vout, &p_pic[i] ); + } + + 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 ); + + for( i = 0; i < i_num_pics; i++ ) + { + free( p_pic[i].p_sys ); + } + } +} + +/***************************************************************************** + * UpdatePictureStruct: updates the internal data in the picture_t structure + ***************************************************************************** + * This will setup stuff for use by the video_output thread + *****************************************************************************/ +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'): + case VLC_FOURCC('R','V','1','5'): + case VLC_FOURCC('R','V','1','6'): + case VLC_FOURCC('R','V','2','4'): + 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 ) + { + case VLC_FOURCC('R','G','B','2'): + p_pic->p->i_pixel_pitch = 1; + break; + case VLC_FOURCC('R','V','1','5'): + case VLC_FOURCC('R','V','1','6'): + 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 VLC_EGENERIC; + } + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + 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 * + p_pic->p[Y_PLANE].i_pixel_pitch; + + p_pic->V_PIXELS = p_pic->Y_PIXELS + + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch; + p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2; + p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2; + p_pic->p[V_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; + p_pic->p[V_PLANE].i_pixel_pitch = 1; + p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * + 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 / 2 * + p_pic->p[U_PLANE].i_pixel_pitch; + + p_pic->i_planes = 3; + break; + + case VLC_FOURCC('I','Y','U','V'): + + 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 * + p_pic->p[Y_PLANE].i_pixel_pitch; + + p_pic->U_PIXELS = p_pic->Y_PIXELS + + p_pic->p[Y_PLANE].i_lines * p_pic->p[Y_PLANE].i_pitch; + p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2; + p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2; + p_pic->p[U_PLANE].i_pitch = p_pic->p[Y_PLANE].i_pitch / 2; + p_pic->p[U_PLANE].i_pixel_pitch = 1; + p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2 * + 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 / 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 * + p_pic->p->i_pixel_pitch; + + p_pic->i_planes = 1; + break; + + default: + /* 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 VLC_SUCCESS; +} + +/***************************************************************************** + * DirectXGetDDrawCaps: Probe the capabilities of the hardware + ***************************************************************************** + * It is nice to know which features are supported by the hardware so we can + * find ways to optimize our rendering. + *****************************************************************************/ +static void DirectXGetDDrawCaps( vout_thread_t *p_vout ) +{ + DDCAPS ddcaps; + HRESULT dxresult; + + /* This is just an indication of whether or not we'll support overlay, + * but with this test we don't know if we support YUV overlay */ + memset( &ddcaps, 0, sizeof( DDCAPS )); + ddcaps.dwSize = sizeof(DDCAPS); + dxresult = IDirectDraw2_GetCaps( p_vout->p_sys->p_ddobject, + &ddcaps, NULL ); + if(dxresult != DD_OK ) + { + msg_Warn( p_vout, "cannot get caps" ); + } + else + { + 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) ? 1 : 0; + /* Determine if the hardware supports overlay surfaces */ + 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) ? 1 : 0; + /* Determine if the hardware supports scaling of the overlay surface */ + 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_deinterlace_overlay=%i colorkey=%i stretch=%i " + "bltfourcc=%i", + bHasOverlay, bHasOverlayFourCC, bCanDeinterlace, + bHasColorKey, bCanStretch, bCanBltFourcc ); + + 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; + } +} + +/***************************************************************************** + * DirectXLockSurface: Lock surface and get picture data pointer + ***************************************************************************** + * This function locks a surface and get the surface descriptor which amongst + * other things has the pointer to the picture data. + *****************************************************************************/ +static int DirectXLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) +{ + HRESULT dxresult; + + /* Lock the surface to get a valid pointer to the picture buffer */ + memset( &p_pic->p_sys->ddsd, 0, sizeof( DDSURFACEDESC )); + p_pic->p_sys->ddsd.dwSize = sizeof(DDSURFACEDESC); + dxresult = IDirectDrawSurface2_Lock( p_pic->p_sys->p_surface, + NULL, &p_pic->p_sys->ddsd, + DDLOCK_NOSYSLOCK | DDLOCK_WAIT, + NULL ); + if( dxresult != DD_OK ) + { + 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_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; + } + } + + /* 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 ) + { + 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 */ + 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 */ + + 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" ); +} diff --git a/modules/video_output/msw/events.c b/modules/video_output/msw/events.c new file mode 100644 index 0000000000..bbed9dbceb --- /dev/null +++ b/modules/video_output/msw/events.c @@ -0,0 +1,1249 @@ +/***************************************************************************** + * events.c: Windows DirectX video output events handler + ***************************************************************************** + * Copyright (C) 2001-2004 the VideoLAN team + * $Id$ + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + + +/***************************************************************************** + * Preamble: This file contains the functions related to the creation of + * a window and the handling of its messages (events). + *****************************************************************************/ +#include /* ENOMEM */ +#include /* free() */ +#include /* tolower() */ +#include /* strerror() */ + +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef MODULE_NAME_IS_vout_directx +#include +#endif +#ifdef MODULE_NAME_IS_direct3d +#include +#endif +#ifdef MODULE_NAME_IS_glwin32 +#include +#endif + +#include "vlc_keys.h" +#include "vout.h" + +#if defined(UNDER_CE) && !defined(__PLUGIN__) /*FIXME*/ +# define SHFS_SHOWSIPBUTTON 0x0004 +# define SHFS_HIDESIPBUTTON 0x0008 +# define MENU_HEIGHT 26 + BOOL SHFullScreen(HWND hwndRequester, DWORD dwState); +#endif + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ +static int DirectXCreateWindow( vout_thread_t *p_vout ); +static void DirectXCloseWindow ( vout_thread_t *p_vout ); +static long FAR PASCAL DirectXEventProc( HWND, UINT, WPARAM, LPARAM ); + +static int Control( vout_thread_t *p_vout, int i_query, va_list args ); + +static void DirectXPopupMenu( event_thread_t *p_event, vlc_bool_t b_open ) +{ + playlist_t *p_playlist = + vlc_object_find( p_event, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE ); + if( p_playlist != NULL ) + { + vlc_value_t val; + val.b_bool = b_open; + var_Set( p_playlist, "intf-popupmenu", val ); + vlc_object_release( p_playlist ); + } +} + +static int DirectXConvertKey( int i_key ); + +/***************************************************************************** + * EventThread: Create video window & handle its messages + ***************************************************************************** + * This function creates a video window and then enters an infinite loop + * that handles the messages sent to that window. + * The main goal of this thread is to isolate the Win32 PeekMessage function + * because this one can block for a long time. + *****************************************************************************/ +void E_(EventThread)( event_thread_t *p_event ) +{ + MSG msg; + POINT old_mouse_pos = {0,0}, mouse_pos; + vlc_value_t val; + int i_width, i_height, i_x, i_y; + HMODULE hkernel32; + + /* Initialisation */ + p_event->p_vout->pf_control = Control; + + /* Create a window for the video */ + /* Creating a window under Windows also initializes the thread's event + * message queue */ + if( DirectXCreateWindow( p_event->p_vout ) ) + { + msg_Err( p_event, "out of memory" ); + p_event->b_dead = VLC_TRUE; + } + + /* Signal the creation of the window */ + vlc_thread_ready( p_event ); + +#ifndef UNDER_CE + /* Set power management stuff */ + if( (hkernel32 = GetModuleHandle( _T("KERNEL32") ) ) ) + { + ULONG (WINAPI* OurSetThreadExecutionState)( ULONG ); + + OurSetThreadExecutionState = (ULONG (WINAPI*)( ULONG )) + GetProcAddress( hkernel32, _T("SetThreadExecutionState") ); + + if( OurSetThreadExecutionState ) + /* Prevent monitor from powering off */ + OurSetThreadExecutionState( ES_DISPLAY_REQUIRED | ES_CONTINUOUS ); + else + msg_Dbg( p_event, "no support for SetThreadExecutionState()" ); + } +#endif + + /* Main loop */ + /* GetMessage will sleep if there's no message in the queue */ + while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) ) + { + /* Check if we are asked to exit */ + if( p_event->b_die ) + break; + + switch( msg.message ) + { + + case WM_MOUSEMOVE: + vout_PlacePicture( p_event->p_vout, + p_event->p_vout->p_sys->i_window_width, + p_event->p_vout->p_sys->i_window_height, + &i_x, &i_y, &i_width, &i_height ); + + if( msg.hwnd == p_event->p_vout->p_sys->hvideownd ) + { + /* Child window */ + i_x = i_y = 0; + } + + if( i_width && i_height ) + { + val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x ) * + p_event->p_vout->fmt_in.i_visible_width / i_width + + p_event->p_vout->fmt_in.i_x_offset; + var_Set( p_event->p_vout, "mouse-x", val ); + val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y ) * + p_event->p_vout->fmt_in.i_visible_height / i_height + + p_event->p_vout->fmt_in.i_y_offset; + var_Set( p_event->p_vout, "mouse-y", val ); + + val.b_bool = VLC_TRUE; + var_Set( p_event->p_vout, "mouse-moved", val ); + } + + case WM_NCMOUSEMOVE: + GetCursorPos( &mouse_pos ); + if( (abs(mouse_pos.x - old_mouse_pos.x) > 2 || + (abs(mouse_pos.y - old_mouse_pos.y)) > 2 ) ) + { + GetCursorPos( &old_mouse_pos ); + p_event->p_vout->p_sys->i_lastmoved = mdate(); + + if( p_event->p_vout->p_sys->b_cursor_hidden ) + { + p_event->p_vout->p_sys->b_cursor_hidden = 0; + ShowCursor( TRUE ); + } + } + break; + + case WM_VLC_HIDE_MOUSE: + if( p_event->p_vout->p_sys->b_cursor_hidden ) break; + p_event->p_vout->p_sys->b_cursor_hidden = VLC_TRUE; + GetCursorPos( &old_mouse_pos ); + ShowCursor( FALSE ); + break; + + case WM_VLC_SHOW_MOUSE: + if( !p_event->p_vout->p_sys->b_cursor_hidden ) break; + p_event->p_vout->p_sys->b_cursor_hidden = VLC_FALSE; + GetCursorPos( &old_mouse_pos ); + ShowCursor( TRUE ); + break; + + case WM_LBUTTONDOWN: + var_Get( p_event->p_vout, "mouse-button-down", &val ); + val.i_int |= 1; + var_Set( p_event->p_vout, "mouse-button-down", val ); + DirectXPopupMenu( p_event, VLC_FALSE ); + break; + + case WM_LBUTTONUP: + var_Get( p_event->p_vout, "mouse-button-down", &val ); + val.i_int &= ~1; + var_Set( p_event->p_vout, "mouse-button-down", val ); + + val.b_bool = VLC_TRUE; + var_Set( p_event->p_vout, "mouse-clicked", val ); + break; + + case WM_LBUTTONDBLCLK: + p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE; + break; + + case WM_MBUTTONDOWN: + var_Get( p_event->p_vout, "mouse-button-down", &val ); + val.i_int |= 2; + var_Set( p_event->p_vout, "mouse-button-down", val ); + DirectXPopupMenu( p_event, VLC_FALSE ); + break; + + case WM_MBUTTONUP: + var_Get( p_event->p_vout, "mouse-button-down", &val ); + val.i_int &= ~2; + var_Set( p_event->p_vout, "mouse-button-down", val ); + break; + + case WM_RBUTTONDOWN: + var_Get( p_event->p_vout, "mouse-button-down", &val ); + val.i_int |= 4; + var_Set( p_event->p_vout, "mouse-button-down", val ); + DirectXPopupMenu( p_event, VLC_FALSE ); + break; + + case WM_RBUTTONUP: + var_Get( p_event->p_vout, "mouse-button-down", &val ); + val.i_int &= ~4; + var_Set( p_event->p_vout, "mouse-button-down", val ); + DirectXPopupMenu( p_event, VLC_TRUE ); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + /* The key events are first processed here and not translated + * into WM_CHAR events because we need to know the status of the + * modifier keys. */ + val.i_int = DirectXConvertKey( msg.wParam ); + if( !val.i_int ) + { + /* This appears to be a "normal" (ascii) key */ + val.i_int = tolower( MapVirtualKey( msg.wParam, 2 ) ); + } + + if( val.i_int ) + { + if( GetKeyState(VK_CONTROL) & 0x8000 ) + { + val.i_int |= KEY_MODIFIER_CTRL; + } + if( GetKeyState(VK_SHIFT) & 0x8000 ) + { + val.i_int |= KEY_MODIFIER_SHIFT; + } + if( GetKeyState(VK_MENU) & 0x8000 ) + { + val.i_int |= KEY_MODIFIER_ALT; + } + + var_Set( p_event->p_libvlc, "key-pressed", val ); + } + break; + + case WM_MOUSEWHEEL: + if( GET_WHEEL_DELTA_WPARAM( msg.wParam ) > 0 ) + { + val.i_int = KEY_MOUSEWHEELUP; + } + else + { + val.i_int = KEY_MOUSEWHEELDOWN; + } + if( val.i_int ) + { + if( GetKeyState(VK_CONTROL) & 0x8000 ) + { + val.i_int |= KEY_MODIFIER_CTRL; + } + if( GetKeyState(VK_SHIFT) & 0x8000 ) + { + val.i_int |= KEY_MODIFIER_SHIFT; + } + if( GetKeyState(VK_MENU) & 0x8000 ) + { + val.i_int |= KEY_MODIFIER_ALT; + } + + var_Set( p_event->p_libvlc, "key-pressed", val ); + } + break; + + case WM_VLC_CHANGE_TEXT: + var_Get( p_event->p_vout, "video-title", &val ); + if( !val.psz_string || !*val.psz_string ) /* Default video title */ + { + if( val.psz_string ) free( val.psz_string ); + +#ifdef MODULE_NAME_IS_wingdi + val.psz_string = strdup( VOUT_TITLE " (WinGDI output)" ); +#endif +#ifdef MODULE_NAME_IS_wingapi + val.psz_string = strdup( VOUT_TITLE " (WinGAPI output)" ); +#endif +#ifdef MODULE_NAME_IS_glwin32 + val.psz_string = strdup( VOUT_TITLE " (OpenGL output)" ); +#endif +#ifdef MODULE_NAME_IS_direct3d + val.psz_string = strdup( VOUT_TITLE " (Direct3D output)" ); +#endif +#ifdef MODULE_NAME_IS_vout_directx + if( p_event->p_vout->p_sys->b_using_overlay ) val.psz_string = + strdup( VOUT_TITLE " (hardware YUV overlay DirectX output)" ); + else if( p_event->p_vout->p_sys->b_hw_yuv ) val.psz_string = + strdup( VOUT_TITLE " (hardware YUV DirectX output)" ); + else val.psz_string = + strdup( VOUT_TITLE " (software RGB DirectX output)" ); +#endif + } + +#ifdef UNICODE + { + wchar_t *psz_title = malloc( strlen(val.psz_string) * 2 + 2 ); + mbstowcs( psz_title, val.psz_string, strlen(val.psz_string)*2); + psz_title[strlen(val.psz_string)] = 0; + free( val.psz_string ); val.psz_string = (char *)psz_title; + } +#endif + + SetWindowText( p_event->p_vout->p_sys->hwnd, + (LPCTSTR)val.psz_string ); + if( p_event->p_vout->p_sys->hfswnd ) + SetWindowText( p_event->p_vout->p_sys->hfswnd, + (LPCTSTR)val.psz_string ); + free( val.psz_string ); + break; + + default: + /* Messages we don't handle directly are dispatched to the + * window procedure */ + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + + } /* End Switch */ + + } /* End Main loop */ + + /* Check for WM_QUIT if we created the window */ + if( !p_event->p_vout->p_sys->hparent && msg.message == WM_QUIT ) + { + msg_Warn( p_event, "WM_QUIT... should not happen!!" ); + p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */ + } + + msg_Dbg( p_event, "DirectXEventThread terminating" ); + + /* clear the changes formerly signaled */ + p_event->p_vout->p_sys->i_changes = 0; + + DirectXCloseWindow( p_event->p_vout ); +} + + +/* following functions are local */ + +/***************************************************************************** + * DirectXCreateWindow: create a window for the video. + ***************************************************************************** + * Before creating a direct draw surface, we need to create a window in which + * the video will be displayed. This window will also allow us to capture the + * events. + *****************************************************************************/ +static int DirectXCreateWindow( vout_thread_t *p_vout ) +{ + HINSTANCE hInstance; + HMENU hMenu; + RECT rect_window; + WNDCLASS wc; /* window class components */ + HICON vlc_icon = NULL; + char vlc_path[MAX_PATH+1]; + int i_style, i_stylex; + + msg_Dbg( p_vout, "DirectXCreateWindow" ); + + /* Get this module's instance */ + hInstance = GetModuleHandle(NULL); + + /* If an external window was specified, we'll draw in it. */ + p_vout->p_sys->hparent = + vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x, + &p_vout->p_sys->i_window_y, + &p_vout->p_sys->i_window_width, + &p_vout->p_sys->i_window_height ); + + /* We create the window ourself, there is no previous window proc. */ + p_vout->p_sys->pf_wndproc = NULL; + + /* Get the Icon from the main app */ + vlc_icon = NULL; +#ifndef UNDER_CE + if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) ) + { + vlc_icon = ExtractIcon( hInstance, vlc_path, 0 ); + } +#endif + + /* Fill in the window class structure */ + wc.style = CS_OWNDC|CS_DBLCLKS; /* style: dbl click */ + wc.lpfnWndProc = (WNDPROC)DirectXEventProc; /* event handler */ + wc.cbClsExtra = 0; /* no extra class data */ + wc.cbWndExtra = 0; /* no extra window data */ + wc.hInstance = hInstance; /* instance */ + wc.hIcon = vlc_icon; /* load the vlc big icon */ + wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* default cursor */ + wc.hbrBackground = GetStockObject(BLACK_BRUSH); /* background color */ + wc.lpszMenuName = NULL; /* no menu */ + wc.lpszClassName = _T("VLC DirectX"); /* use a special class */ + + /* Register the window class */ + if( !RegisterClass(&wc) ) + { + WNDCLASS wndclass; + + if( vlc_icon ) DestroyIcon( vlc_icon ); + + /* Check why it failed. If it's because one already exists + * then fine, otherwise return with an error. */ + if( !GetClassInfo( hInstance, _T("VLC DirectX"), &wndclass ) ) + { + msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() ); + return VLC_EGENERIC; + } + } + + /* Register the video sub-window class */ + wc.lpszClassName = _T("VLC DirectX video"); wc.hIcon = 0; + wc.hbrBackground = NULL; /* no background color */ + if( !RegisterClass(&wc) ) + { + WNDCLASS wndclass; + + /* Check why it failed. If it's because one already exists + * then fine, otherwise return with an error. */ + if( !GetClassInfo( hInstance, _T("VLC DirectX video"), &wndclass ) ) + { + msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED (err=%lu)", GetLastError() ); + return VLC_EGENERIC; + } + } + + /* When you create a window you give the dimensions you wish it to + * have. Unfortunatly these dimensions will include the borders and + * titlebar. We use the following function to find out the size of + * the window corresponding to the useable surface we want */ + rect_window.top = 10; + rect_window.left = 10; + rect_window.right = rect_window.left + p_vout->p_sys->i_window_width; + rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height; + + if( var_GetBool( p_vout, "video-deco" ) ) + { + /* Open with window decoration */ + AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 ); + i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN; + i_stylex = 0; + } + else + { + /* No window decoration */ + AdjustWindowRect( &rect_window, WS_POPUP, 0 ); + i_style = WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN; + i_stylex = 0; // WS_EX_TOOLWINDOW; Is TOOLWINDOW really needed ? + // It messes up the fullscreen window. + } + + if( p_vout->p_sys->hparent ) + { + i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD; + i_stylex = 0; + } + + p_vout->p_sys->i_window_style = i_style; + + /* Create the window */ + p_vout->p_sys->hwnd = + CreateWindowEx( WS_EX_NOPARENTNOTIFY | i_stylex, + _T("VLC DirectX"), /* name of window class */ + _T(VOUT_TITLE) _T(" (DirectX Output)"), /* window title */ + i_style, /* window style */ + (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT : + (UINT)p_vout->p_sys->i_window_x, /* default X coordinate */ + (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT : + (UINT)p_vout->p_sys->i_window_y, /* default Y coordinate */ + rect_window.right - rect_window.left, /* window width */ + rect_window.bottom - rect_window.top, /* window height */ + p_vout->p_sys->hparent, /* parent window */ + NULL, /* no menu in this window */ + hInstance, /* handle of this program instance */ + (LPVOID)p_vout ); /* send p_vout to WM_CREATE */ + + if( !p_vout->p_sys->hwnd ) + { + msg_Warn( p_vout, "DirectXCreateWindow create window FAILED (err=%lu)", GetLastError() ); + return VLC_EGENERIC; + } + + if( p_vout->p_sys->hparent ) + { + LONG i_style; + + /* We don't want the window owner to overwrite our client area */ + i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE ); + + if( !(i_style & WS_CLIPCHILDREN) ) + /* Hmmm, apparently this is a blocking call... */ + SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE, + i_style | WS_CLIPCHILDREN ); + + /* Create our fullscreen window */ + p_vout->p_sys->hfswnd = + CreateWindowEx( WS_EX_APPWINDOW, _T("VLC DirectX"), + _T(VOUT_TITLE) _T(" (DirectX Output)"), + WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_SIZEBOX, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInstance, NULL ); + } + + /* Append a "Always On Top" entry in the system menu */ + hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE ); + AppendMenu( hMenu, MF_SEPARATOR, 0, _T("") ); + AppendMenu( hMenu, MF_STRING | MF_UNCHECKED, + IDM_TOGGLE_ON_TOP, _T("Always on &Top") ); + + /* Create video sub-window. This sub window will always exactly match + * the size of the video, which allows us to use crazy overlay colorkeys + * without having them shown outside of the video area. */ + p_vout->p_sys->hvideownd = + CreateWindow( _T("VLC DirectX video"), _T(""), /* window class */ + WS_CHILD, /* window style, not visible initially */ + 0, 0, + p_vout->render.i_width, /* default width */ + p_vout->render.i_height, /* default height */ + p_vout->p_sys->hwnd, /* parent window */ + NULL, hInstance, + (LPVOID)p_vout ); /* send p_vout to WM_CREATE */ + + if( !p_vout->p_sys->hvideownd ) + msg_Warn( p_vout, "can't create video sub-window" ); + else + msg_Dbg( p_vout, "created video sub-window" ); + + /* Now display the window */ + ShowWindow( p_vout->p_sys->hwnd, SW_SHOW ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * DirectXCloseWindow: close the window created by DirectXCreateWindow + ***************************************************************************** + * This function returns all resources allocated by DirectXCreateWindow. + *****************************************************************************/ +static void DirectXCloseWindow( vout_thread_t *p_vout ) +{ + msg_Dbg( p_vout, "DirectXCloseWindow" ); + + DestroyWindow( p_vout->p_sys->hwnd ); + if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd ); + + if( p_vout->p_sys->hparent ) + vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent ); + + p_vout->p_sys->hwnd = NULL; + + /* We don't unregister the Window Class because it could lead to race + * conditions and it will be done anyway by the system when the app will + * exit */ +} + +/***************************************************************************** + * UpdateRects: update clipping rectangles + ***************************************************************************** + * This function is called when the window position or size are changed, and + * its job is to update the source and destination RECTs used to display the + * picture. + *****************************************************************************/ +void E_(UpdateRects)( vout_thread_t *p_vout, vlc_bool_t b_force ) +{ +#define rect_src p_vout->p_sys->rect_src +#define rect_src_clipped p_vout->p_sys->rect_src_clipped +#define rect_dest p_vout->p_sys->rect_dest +#define rect_dest_clipped p_vout->p_sys->rect_dest_clipped + + int i_width, i_height, i_x, i_y; + + RECT rect; + POINT point; + + /* Retrieve the window size */ + GetClientRect( p_vout->p_sys->hwnd, &rect ); + + /* Retrieve the window position */ + point.x = point.y = 0; + ClientToScreen( p_vout->p_sys->hwnd, &point ); + + /* If nothing changed, we can return */ + if( !b_force + && p_vout->p_sys->i_window_width == rect.right + && p_vout->p_sys->i_window_height == rect.bottom + && p_vout->p_sys->i_window_x == point.x + && p_vout->p_sys->i_window_y == point.y ) + { + return; + } + + /* Update the window position and size */ + p_vout->p_sys->i_window_x = point.x; + p_vout->p_sys->i_window_y = point.y; + p_vout->p_sys->i_window_width = rect.right; + p_vout->p_sys->i_window_height = rect.bottom; + + vout_PlacePicture( p_vout, rect.right, rect.bottom, + &i_x, &i_y, &i_width, &i_height ); + + if( p_vout->p_sys->hvideownd ) + SetWindowPos( p_vout->p_sys->hvideownd, 0, + i_x, i_y, i_width, i_height, + SWP_NOCOPYBITS|SWP_NOZORDER|SWP_ASYNCWINDOWPOS ); + + /* Destination image position and dimensions */ + rect_dest.left = point.x + i_x; + rect_dest.right = rect_dest.left + i_width; + rect_dest.top = point.y + i_y; + rect_dest.bottom = rect_dest.top + i_height; + +#ifdef MODULE_NAME_IS_vout_directx + /* Apply overlay hardware constraints */ + if( p_vout->p_sys->b_using_overlay ) + { + if( p_vout->p_sys->i_align_dest_boundary ) + rect_dest.left = ( rect_dest.left + + p_vout->p_sys->i_align_dest_boundary / 2 ) & + ~p_vout->p_sys->i_align_dest_boundary; + + if( p_vout->p_sys->i_align_dest_size ) + rect_dest.right = (( rect_dest.right - rect_dest.left + + p_vout->p_sys->i_align_dest_size / 2 ) & + ~p_vout->p_sys->i_align_dest_size) + rect_dest.left; + } + + /* UpdateOverlay directdraw function doesn't automatically clip to the + * display size so we need to do it otherwise it will fail */ + + /* Clip the destination window */ + if( !IntersectRect( &rect_dest_clipped, &rect_dest, + &p_vout->p_sys->rect_display ) ) + { + SetRectEmpty( &rect_src_clipped ); + return; + } + +#if 0 + msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:" + " %i,%i,%i,%i", + rect_dest_clipped.left, rect_dest_clipped.top, + rect_dest_clipped.right, rect_dest_clipped.bottom ); +#endif + + /* the 2 following lines are to fix a bug when clicking on the desktop */ + if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 || + (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 ) + { + SetRectEmpty( &rect_src_clipped ); + return; + } +#else /* MODULE_NAME_IS_vout_directx */ + + /* AFAIK, there are no clipping constraints in Direct3D, OpenGL and GDI */ + rect_dest_clipped = rect_dest; + +#endif + + /* src image dimensions */ + rect_src.left = 0; + rect_src.top = 0; + rect_src.right = p_vout->render.i_width; + rect_src.bottom = p_vout->render.i_height; + + /* Clip the source image */ + rect_src_clipped.left = p_vout->fmt_out.i_x_offset + + (rect_dest_clipped.left - rect_dest.left) * + p_vout->fmt_out.i_visible_width / (rect_dest.right - rect_dest.left); + rect_src_clipped.right = p_vout->fmt_out.i_x_offset + + p_vout->fmt_out.i_visible_width - + (rect_dest.right - rect_dest_clipped.right) * + p_vout->fmt_out.i_visible_width / (rect_dest.right - rect_dest.left); + rect_src_clipped.top = p_vout->fmt_out.i_y_offset + + (rect_dest_clipped.top - rect_dest.top) * + p_vout->fmt_out.i_visible_height / (rect_dest.bottom - rect_dest.top); + rect_src_clipped.bottom = p_vout->fmt_out.i_y_offset + + p_vout->fmt_out.i_visible_height - + (rect_dest.bottom - rect_dest_clipped.bottom) * + p_vout->fmt_out.i_visible_height / (rect_dest.bottom - rect_dest.top); + +#ifdef MODULE_NAME_IS_vout_directx + /* Apply overlay hardware constraints */ + if( p_vout->p_sys->b_using_overlay ) + { + if( p_vout->p_sys->i_align_src_boundary ) + rect_src_clipped.left = ( rect_src_clipped.left + + p_vout->p_sys->i_align_src_boundary / 2 ) & + ~p_vout->p_sys->i_align_src_boundary; + + if( p_vout->p_sys->i_align_src_size ) + rect_src_clipped.right = (( rect_src_clipped.right - + rect_src_clipped.left + + p_vout->p_sys->i_align_src_size / 2 ) & + ~p_vout->p_sys->i_align_src_size) + rect_src_clipped.left; + } +#endif + +#if 0 + msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped" + " coords: %i,%i,%i,%i", + rect_src_clipped.left, rect_src_clipped.top, + rect_src_clipped.right, rect_src_clipped.bottom ); +#endif + +#ifdef MODULE_NAME_IS_vout_directx + /* The destination coordinates need to be relative to the current + * directdraw primary surface (display) */ + rect_dest_clipped.left -= p_vout->p_sys->rect_display.left; + rect_dest_clipped.right -= p_vout->p_sys->rect_display.left; + rect_dest_clipped.top -= p_vout->p_sys->rect_display.top; + rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top; + + if( p_vout->p_sys->b_using_overlay ) + DirectDrawUpdateOverlay( p_vout ); +#endif + + /* Signal the change in size/position */ + p_vout->p_sys->i_changes |= DX_POSITION_CHANGE; + +#undef rect_src +#undef rect_src_clipped +#undef rect_dest +#undef rect_dest_clipped +} + +/***************************************************************************** + * DirectXEventProc: This is the window event processing function. + ***************************************************************************** + * On Windows, when you create a window you have to attach an event processing + * function to it. The aim of this function is to manage "Queued Messages" and + * "Nonqueued Messages". + * Queued Messages are those picked up and retransmitted by vout_Manage + * (using the GetMessage and DispatchMessage functions). + * Nonqueued Messages are those that Windows will send directly to this + * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...) + *****************************************************************************/ +static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam ) +{ + vout_thread_t *p_vout; + + if( message == WM_CREATE ) + { + /* Store p_vout for future use */ + p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams; + SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)p_vout ); + return TRUE; + } + else + { + p_vout = (vout_thread_t *)GetWindowLongPtr( hwnd, GWLP_USERDATA ); + if( !p_vout ) + { + /* Hmmm mozilla does manage somehow to save the pointer to our + * windowproc and still calls it after the vout has been closed. */ + return DefWindowProc(hwnd, message, wParam, lParam); + } + } + +#ifndef UNDER_CE + /* Catch the screensaver and the monitor turn-off */ + if( message == WM_SYSCOMMAND && + ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) ) + { + //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" ); + return 0; /* this stops them from happening */ + } +#endif + + if( hwnd == p_vout->p_sys->hvideownd ) + { + switch( message ) + { +#ifdef MODULE_NAME_IS_vout_directx + case WM_ERASEBKGND: + /* For overlay, we need to erase background */ + return !p_vout->p_sys->b_using_overlay ? + 1 : DefWindowProc(hwnd, message, wParam, lParam); + case WM_PAINT: + /* + ** For overlay, DefWindowProc() will erase dirty regions + ** with colorkey. + ** For non-overlay, vout will paint the whole window at + ** regular interval, therefore dirty regions can be ignored + ** to minimize repaint. + */ + if( !p_vout->p_sys->b_using_overlay ) + { + ValidateRect(hwnd, NULL); + } + // fall through to default +#else + /* + ** For OpenGL and Direct3D, vout will update the whole + ** window at regular interval, therefore dirty region + ** can be ignored to minimize repaint. + */ + case WM_ERASEBKGND: + /* nothing to erase */ + return 1; + case WM_PAINT: + /* nothing to repaint */ + ValidateRect(hwnd, NULL); + // fall through +#endif + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + } + + switch( message ) + { + + case WM_WINDOWPOSCHANGED: + E_(UpdateRects)( p_vout, VLC_TRUE ); + return 0; + + /* the user wants to close the window */ + case WM_CLOSE: + { + playlist_t * p_playlist = + (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + if( p_playlist == NULL ) + { + return 0; + } + + playlist_Stop( p_playlist ); + vlc_object_release( p_playlist ); + return 0; + } + + /* the window has been closed so shut down everything now */ + case WM_DESTROY: + msg_Dbg( p_vout, "WinProc WM_DESTROY" ); + /* just destroy the window */ + PostQuitMessage( 0 ); + return 0; + + case WM_SYSCOMMAND: + switch (wParam) + { + case IDM_TOGGLE_ON_TOP: /* toggle the "on top" status */ + { + vlc_value_t val; + msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP"); + + /* Change the current value */ + var_Get( p_vout, "video-on-top", &val ); + val.b_bool = !val.b_bool; + var_Set( p_vout, "video-on-top", val ); + return 0; + } + } + break; + + case WM_PAINT: + case WM_NCPAINT: + case WM_ERASEBKGND: + return DefWindowProc(hwnd, message, wParam, lParam); + + case WM_KILLFOCUS: +#ifdef MODULE_NAME_IS_wingapi + p_vout->p_sys->b_focus = VLC_FALSE; + if( !p_vout->p_sys->b_parent_focus ) GXSuspend(); +#endif +#ifdef UNDER_CE + if( hWnd == p_vout->p_sys->hfswnd ) + { + HWND htbar = FindWindow( _T("HHTaskbar"), NULL ); + ShowWindow( htbar, SW_SHOW ); + } + + if( !p_vout->p_sys->hparent || + hWnd == p_vout->p_sys->hfswnd ) + { + SHFullScreen( hWnd, SHFS_SHOWSIPBUTTON ); + } +#endif + return 0; + + case WM_SETFOCUS: +#ifdef MODULE_NAME_IS_wingapi + p_vout->p_sys->b_focus = VLC_TRUE; + GXResume(); +#endif +#ifdef UNDER_CE + if( p_vout->p_sys->hparent && + hWnd != p_vout->p_sys->hfswnd && p_vout->b_fullscreen ) + p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE; + + if( hWnd == p_vout->p_sys->hfswnd ) + { + HWND htbar = FindWindow( _T("HHTaskbar"), NULL ); + ShowWindow( htbar, SW_HIDE ); + } + + if( !p_vout->p_sys->hparent || + hWnd == p_vout->p_sys->hfswnd ) + { + SHFullScreen( hWnd, SHFS_HIDESIPBUTTON ); + } +#endif + return 0; + + default: + //msg_Dbg( p_vout, "WinProc WM Default %i", message ); + break; + } + + /* Let windows handle the message */ + return DefWindowProc(hwnd, message, wParam, lParam); +} + +static struct +{ + int i_dxkey; + int i_vlckey; + +} dxkeys_to_vlckeys[] = +{ + { VK_F1, KEY_F1 }, { VK_F2, KEY_F2 }, { VK_F3, KEY_F3 }, { VK_F4, KEY_F4 }, + { VK_F5, KEY_F5 }, { VK_F6, KEY_F6 }, { VK_F7, KEY_F7 }, { VK_F8, KEY_F8 }, + { VK_F9, KEY_F9 }, { VK_F10, KEY_F10 }, { VK_F11, KEY_F11 }, + { VK_F12, KEY_F12 }, + + { VK_RETURN, KEY_ENTER }, + { VK_SPACE, KEY_SPACE }, + { VK_ESCAPE, KEY_ESC }, + + { VK_LEFT, KEY_LEFT }, + { VK_RIGHT, KEY_RIGHT }, + { VK_UP, KEY_UP }, + { VK_DOWN, KEY_DOWN }, + + { VK_HOME, KEY_HOME }, + { VK_END, KEY_END }, + { VK_PRIOR, KEY_PAGEUP }, + { VK_NEXT, KEY_PAGEDOWN }, + + { VK_INSERT, KEY_INSERT }, + { VK_DELETE, KEY_DELETE }, + + { VK_CONTROL, 0 }, + { VK_SHIFT, 0 }, + { VK_MENU, 0 }, + + { VK_BROWSER_BACK, KEY_BROWSER_BACK }, + { VK_BROWSER_FORWARD, KEY_BROWSER_FORWARD }, + { VK_BROWSER_REFRESH, KEY_BROWSER_REFRESH }, + { VK_BROWSER_STOP, KEY_BROWSER_STOP }, + { VK_BROWSER_SEARCH, KEY_BROWSER_SEARCH }, + { VK_BROWSER_FAVORITES, KEY_BROWSER_FAVORITES }, + { VK_BROWSER_HOME, KEY_BROWSER_HOME }, + { VK_VOLUME_MUTE, KEY_VOLUME_MUTE }, + { VK_VOLUME_DOWN, KEY_VOLUME_DOWN }, + { VK_VOLUME_UP, KEY_VOLUME_UP }, + { VK_MEDIA_NEXT_TRACK, KEY_MEDIA_NEXT_TRACK }, + { VK_MEDIA_PREV_TRACK, KEY_MEDIA_PREV_TRACK }, + { VK_MEDIA_STOP, KEY_MEDIA_STOP }, + { VK_MEDIA_PLAY_PAUSE, KEY_MEDIA_PLAY_PAUSE }, + + { 0, 0 } +}; + +static int DirectXConvertKey( int i_key ) +{ + int i; + + for( i = 0; dxkeys_to_vlckeys[i].i_dxkey != 0; i++ ) + { + if( dxkeys_to_vlckeys[i].i_dxkey == i_key ) + { + return dxkeys_to_vlckeys[i].i_vlckey; + } + } + + return 0; +} + +/***************************************************************************** + * Control: control facility for the vout + *****************************************************************************/ +static int Control( vout_thread_t *p_vout, int i_query, va_list args ) +{ + unsigned int *pi_width, *pi_height; + RECT rect_window; + POINT point; + + switch( i_query ) + { + case VOUT_GET_SIZE: + if( p_vout->p_sys->hparent ) + return vout_ControlWindow( p_vout, + (void *)p_vout->p_sys->hparent, i_query, args ); + + pi_width = va_arg( args, unsigned int * ); + pi_height = va_arg( args, unsigned int * ); + + GetClientRect( p_vout->p_sys->hwnd, &rect_window ); + + *pi_width = rect_window.right - rect_window.left; + *pi_height = rect_window.bottom - rect_window.top; + return VLC_SUCCESS; + + case VOUT_SET_SIZE: + if( p_vout->p_sys->hparent ) + return vout_ControlWindow( p_vout, + (void *)p_vout->p_sys->hparent, i_query, args ); + + /* Update dimensions */ + rect_window.top = rect_window.left = 0; + rect_window.right = va_arg( args, unsigned int ); + rect_window.bottom = va_arg( args, unsigned int ); + if( !rect_window.right ) rect_window.right = p_vout->i_window_width; + if( !rect_window.bottom ) rect_window.bottom = p_vout->i_window_height; + AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 ); + + SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, + rect_window.right - rect_window.left, + rect_window.bottom - rect_window.top, SWP_NOMOVE ); + + return VLC_SUCCESS; + + case VOUT_CLOSE: + ShowWindow( p_vout->p_sys->hwnd, SW_HIDE ); + case VOUT_REPARENT: + /* Retrieve the window position */ + point.x = point.y = 0; + ClientToScreen( p_vout->p_sys->hwnd, &point ); + + HWND d = 0; + + if( i_query == VOUT_REPARENT ) d = (HWND)va_arg( args, int ); + if( !d ) + { + vlc_mutex_lock( &p_vout->p_sys->lock ); + p_vout->p_sys->hparent = 0; + vlc_mutex_unlock( &p_vout->p_sys->lock ); + SetParent( p_vout->p_sys->hwnd, 0 ); + p_vout->p_sys->i_window_style = + WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_SIZEBOX; + SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, + p_vout->p_sys->i_window_style | + (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) ); + SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW ); + SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0, + SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED ); + } + else + { + vlc_mutex_lock( &p_vout->p_sys->lock ); + p_vout->p_sys->hparent = d; + vlc_mutex_unlock( &p_vout->p_sys->lock ); + + SetParent( p_vout->p_sys->hwnd, d ); + p_vout->p_sys->i_window_style = WS_CLIPCHILDREN; + SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, + p_vout->p_sys->i_window_style | + (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) ); + SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW ); + + /* Retrieve the parent size */ + RECT rect; + GetClientRect( d, &rect ); + SetWindowPos( p_vout->p_sys->hwnd, d, point.x, point.y, rect.right, rect.bottom, + SWP_FRAMECHANGED ); + } + + return vout_vaControlDefault( p_vout, i_query, args ); + + case VOUT_SET_STAY_ON_TOP: + if( p_vout->p_sys->hparent && !var_GetBool( p_vout, "fullscreen" ) ) + return vout_ControlWindow( p_vout, + (void *)p_vout->p_sys->hparent, i_query, args ); + + p_vout->p_sys->b_on_top_change = VLC_TRUE; + return VLC_SUCCESS; + +#ifdef MODULE_NAME_IS_wingapi + case VOUT_SET_FOCUS: + b_bool = va_arg( args, vlc_bool_t ); + p_vout->p_sys->b_parent_focus = b_bool; + if( b_bool ) GXResume(); + else if( !p_vout->p_sys->b_focus ) GXSuspend(); + return VLC_SUCCESS; +#endif + + default: + return vout_vaControlDefault( p_vout, i_query, args ); + } +} + + +/* Internal wrapper over GetWindowPlacement / SetWindowPlacement */ +static void SetWindowState(HWND hwnd, int nShowCmd) +{ + WINDOWPLACEMENT window_placement; + window_placement.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement( hwnd, &window_placement ); + window_placement.showCmd = nShowCmd; + SetWindowPlacement( hwnd, &window_placement ); + SetWindowPos( hwnd, 0, 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); +} + +/* Internal wrapper to call vout_ControlWindow for hparent */ +static void ControlParentWindow( vout_thread_t *p_vout, int i_query, ... ) +{ + va_list args; + va_start( args, i_query ); + vout_ControlWindow( p_vout, + (void *)p_vout->p_sys->hparent, i_query, args ); + va_end( args ); +} + +void Win32ToggleFullscreen( vout_thread_t *p_vout ) +{ + 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; + + if( p_vout->b_fullscreen ) + { + msg_Dbg( p_vout, "entering fullscreen mode" ); + /* Change window style, no borders and no title bar */ + 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 ); + } + + /* Maximize window */ + SetWindowState( hwnd, SW_SHOWMAXIMIZED ); + + 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 ); + + HWND topLevelParent = GetAncestor( p_vout->p_sys->hparent, GA_ROOT ); + ShowWindow( topLevelParent, SW_HIDE ); + } + + SetForegroundWindow( hwnd ); + } + else + { + msg_Dbg( p_vout, "leaving fullscreen mode" ); + /* Change window style, no borders and no title bar */ + SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style ); + + /* Normal window */ + SetWindowState( hwnd, SW_SHOWNORMAL ); + + 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 ); + + HWND topLevelParent = GetAncestor( p_vout->p_sys->hparent, GA_ROOT ); + ShowWindow( topLevelParent, SW_SHOW ); + SetForegroundWindow( p_vout->p_sys->hparent ); + ShowWindow( hwnd, SW_HIDE ); + + /* Update "video-on-top" status for main interface window, it + needs to be updated as we were hiding VOUT_SET_STAY_ON_TOP + queries from it while we were in fullscreen mode */ + int b_ontop = var_GetBool( p_vout, "video-on-top" ); + ControlParentWindow( p_vout, VOUT_SET_STAY_ON_TOP, b_ontop ); + } + + /* Make sure the mouse cursor is displayed */ + PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 ); + } + + { + vlc_value_t val; + /* Update the object variable and trigger callback */ + val.b_bool = p_vout->b_fullscreen; + var_Set( p_vout, "fullscreen", val ); + } +} diff --git a/modules/video_output/msw/glwin32.c b/modules/video_output/msw/glwin32.c new file mode 100644 index 0000000000..b2549fa131 --- /dev/null +++ b/modules/video_output/msw/glwin32.c @@ -0,0 +1,430 @@ +/***************************************************************************** + * glwin32.c: Windows OpenGL provider + ***************************************************************************** + * Copyright (C) 2001-2004 the VideoLAN team + * $Id$ + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include /* ENOMEM */ +#include /* free() */ +#include /* strerror() */ + +#include +#include +#include + +#include +#include +#include + +#include +#undef GetSystemMetrics + +#ifndef MONITOR_DEFAULTTONEAREST +# define MONITOR_DEFAULTTONEAREST 2 +#endif + +#include + +#include "vout.h" + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ +static int OpenVideo ( vlc_object_t * ); +static void CloseVideo ( vlc_object_t * ); + +static int Init ( vout_thread_t * ); +static void End ( vout_thread_t * ); +static int Manage ( vout_thread_t * ); +static void GLSwapBuffers( vout_thread_t * ); +static void FirstSwap( vout_thread_t * ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VOUT ); + set_shortname( "OpenGL" ); + set_description( _("OpenGL video output") ); + set_capability( "opengl provider", 100 ); + add_shortcut( "glwin32" ); + 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 */ + /* check if we registered a window class because we need to + * unregister it */ + WNDCLASS wndclass; + if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) + UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); +#endif + +/***************************************************************************** + * OpenVideo: allocate OpenGL provider + ***************************************************************************** + * This function creates and initializes a video window. + *****************************************************************************/ +static int OpenVideo( vlc_object_t *p_this ) +{ + vout_thread_t * p_vout = (vout_thread_t *)p_this; + vlc_value_t val; + + /* 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 VLC_ENOMEM; + } + memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); + + /* Initialisations */ + p_vout->pf_init = Init; + p_vout->pf_end = End; + p_vout->pf_manage = Manage; + p_vout->pf_swap = FirstSwap; + + 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; + vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); + SetRectEmpty( &p_vout->p_sys->rect_display ); + SetRectEmpty( &p_vout->p_sys->rect_parent ); + + 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(); + + /* Set main window's size */ + p_vout->p_sys->i_window_width = p_vout->i_window_width; + p_vout->p_sys->i_window_height = p_vout->i_window_height; + + /* Create the Vout EventThread, this thread is created by us to isolate + * the Win32 PeekMessage function calls. We want to do this because + * Windows can stay blocked inside this call for a long time, and when + * this happens it thus blocks vlc's video_output thread. + * Vout EventThread will take care of the creation of the video + * window (because PeekMessage has to be called from the same thread which + * created the window). */ + msg_Dbg( p_vout, "creating Vout EventThread" ); + 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, "Vout Events Thread", + E_(EventThread), 0, 1 ) ) + { + msg_Err( p_vout, "cannot create Vout EventThread" ); + vlc_object_destroy( p_vout->p_sys->p_event ); + p_vout->p_sys->p_event = NULL; + goto error; + } + + if( p_vout->p_sys->p_event->b_error ) + { + msg_Err( p_vout, "Vout EventThread failed" ); + goto error; + } + + vlc_object_attach( p_vout->p_sys->p_event, p_vout ); + + msg_Dbg( p_vout, "Vout EventThread running" ); + + /* 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; + + error: + CloseVideo( VLC_OBJECT(p_vout) ); + return VLC_EGENERIC; +} + +/***************************************************************************** + * Init: initialize video thread output method + *****************************************************************************/ +static int Init( vout_thread_t *p_vout ) +{ + PIXELFORMATDESCRIPTOR pfd; + int iFormat; + + /* Change the window title bar text */ + PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 ); + + p_vout->p_sys->hGLDC = GetDC( p_vout->p_sys->hvideownd ); + + /* Set the pixel format for the DC */ + memset( &pfd, 0, sizeof( pfd ) ); + pfd.nSize = sizeof( pfd ); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + iFormat = ChoosePixelFormat( p_vout->p_sys->hGLDC, &pfd ); + SetPixelFormat( p_vout->p_sys->hGLDC, iFormat, &pfd ); + + /* Create and enable the render context */ + p_vout->p_sys->hGLRC = wglCreateContext( p_vout->p_sys->hGLDC ); + wglMakeCurrent( p_vout->p_sys->hGLDC, p_vout->p_sys->hGLRC ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * End: terminate Sys video thread output method + ***************************************************************************** + * Terminate an output method created by Create. + * It is called at the end of the thread. + *****************************************************************************/ +static void End( vout_thread_t *p_vout ) +{ + wglMakeCurrent( NULL, NULL ); + wglDeleteContext( p_vout->p_sys->hGLRC ); + ReleaseDC( p_vout->p_sys->hvideownd, p_vout->p_sys->hGLDC ); + return; +} + +/***************************************************************************** + * CloseVideo: destroy Sys video thread output method + ***************************************************************************** + * 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, "closing video" ); + + if( p_vout->p_sys->p_event ) + { + vlc_object_detach( p_vout->p_sys->p_event ); + + /* Kill Vout EventThread */ + p_vout->p_sys->p_event->b_die = VLC_TRUE; + + /* we need to be sure Vout EventThread 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 ); + + if( p_vout->p_sys ) + { + free( p_vout->p_sys ); + p_vout->p_sys = NULL; + } +} + +/***************************************************************************** + * Manage: handle Sys events + ***************************************************************************** + * This function should be called regularly by the video output thread. + * It returns a non null value if an error occurred. + *****************************************************************************/ +static int Manage( vout_thread_t *p_vout ) +{ + int i_width = p_vout->p_sys->rect_dest.right - + p_vout->p_sys->rect_dest.left; + int i_height = p_vout->p_sys->rect_dest.bottom - + p_vout->p_sys->rect_dest.top; + glViewport( 0, 0, i_width, i_height ); + + /* 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; + + 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 + { + vlc_mutex_unlock( &p_vout->p_sys->lock ); + } + + /* 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_(UpdateRects)( 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. */ + + /* + * Fullscreen change + */ + if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE + || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) + { + Win32ToggleFullscreen( p_vout ); + + p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + } + + /* + * Pointer change + */ + 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 ) + { + 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 VLC_EGENERIC; /* exit */ + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * GLSwapBuffers: swap front/back buffers + *****************************************************************************/ +static void GLSwapBuffers( vout_thread_t *p_vout ) +{ + SwapBuffers( p_vout->p_sys->hGLDC ); +} + +/* +** this function is only used once when the first picture is received +** this function will show the video window once a picture is ready +*/ + +static void FirstSwap( vout_thread_t *p_vout ) +{ + /* get initial picture buffer swapped to front buffer */ + GLSwapBuffers( p_vout ); + + /* + ** Video window is initially hidden, show it now since we got a + ** picture to show. + */ + SetWindowPos( p_vout->p_sys->hvideownd, NULL, 0, 0, 0, 0, + SWP_ASYNCWINDOWPOS| + SWP_FRAMECHANGED| + SWP_SHOWWINDOW| + SWP_NOMOVE| + SWP_NOSIZE| + SWP_NOZORDER ); + + /* use and restores proper swap function for further pictures */ + p_vout->pf_swap = GLSwapBuffers; +} diff --git a/modules/video_output/msw/vout.h b/modules/video_output/msw/vout.h new file mode 100644 index 0000000000..05b4afb65c --- /dev/null +++ b/modules/video_output/msw/vout.h @@ -0,0 +1,263 @@ +/***************************************************************************** + * vout.h: Windows video output header file + ***************************************************************************** + * Copyright (C) 2001-2004 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * Damien Fouilleul + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * event_thread_t: event thread + *****************************************************************************/ +typedef struct event_thread_t +{ + VLC_COMMON_MEMBERS + + vout_thread_t * p_vout; + +} event_thread_t; + +/***************************************************************************** + * vout_sys_t: video output method descriptor + ***************************************************************************** + * This structure is part of the video output thread descriptor. + * It describes the module specific properties of an output thread. + *****************************************************************************/ +struct vout_sys_t +{ + HWND hwnd; /* Handle of the main window */ + HWND hvideownd; /* Handle of the video sub-window */ + HWND hparent; /* Handle of the parent window */ + HWND hfswnd; /* Handle of the fullscreen window */ + WNDPROC pf_wndproc; /* Window handling callback */ + + /* Multi-monitor support */ + HMONITOR hmonitor; /* handle of the current monitor */ + GUID *p_display_driver; + HMONITOR (WINAPI* MonitorFromWindow)( HWND, DWORD ); + BOOL (WINAPI* GetMonitorInfo)( HMONITOR, LPMONITORINFO ); + + /* size of the display */ + RECT rect_display; + int i_display_depth; + + /* size of the overall window (including black bands) */ + RECT rect_parent; + + /* Window position and size */ + int i_window_x; + int i_window_y; + int i_window_width; + int i_window_height; + int i_window_style; + + volatile uint16_t i_changes; /* changes made to the video display */ + + /* Mouse */ + volatile vlc_bool_t b_cursor_hidden; + volatile mtime_t i_lastmoved; + + /* Misc */ + vlc_bool_t b_on_top_change; + +#ifndef UNDER_CE + + /* screensaver system settings to be restored when vout is closed */ + UINT i_spi_lowpowertimeout; + UINT i_spi_powerofftimeout; + UINT i_spi_screensavetimeout; + +#endif + + /* Coordinates of src and dest images (used when blitting to display) */ + RECT rect_src; + RECT rect_src_clipped; + RECT rect_dest; + RECT rect_dest_clipped; + + vlc_bool_t b_hw_yuv; /* Should we use hardware YUV->RGB conversions */ + + +#ifdef MODULE_NAME_IS_vout_directx + /* Overlay alignment restrictions */ + int i_align_src_boundary; + int i_align_src_size; + int i_align_dest_boundary; + int i_align_dest_size; + + vlc_bool_t b_wallpaper; /* show as desktop wallpaper ? */ + + vlc_bool_t b_using_overlay; /* Are we using an overlay surface */ + vlc_bool_t b_use_sysmem; /* Should we use system memory for surfaces */ + vlc_bool_t b_3buf_overlay; /* Should we use triple buffered overlays */ + + /* DDraw capabilities */ + int b_caps_overlay_clipping; + + int i_rgb_colorkey; /* colorkey in RGB used by the overlay */ + int i_colorkey; /* colorkey used by the overlay */ + + COLORREF color_bkg; + COLORREF color_bkgtxt; + + LPDIRECTDRAW2 p_ddobject; /* DirectDraw object */ + LPDIRECTDRAWSURFACE2 p_display; /* Display device */ + LPDIRECTDRAWSURFACE2 p_current_surface; /* surface currently displayed */ + LPDIRECTDRAWCLIPPER p_clipper; /* clipper used for blitting */ + HINSTANCE hddraw_dll; /* handle of the opened ddraw dll */ +#endif + +#ifdef MODULE_NAME_IS_glwin32 + HDC hGLDC; + HGLRC hGLRC; +#endif + +#ifdef MODULE_NAME_IS_direct3d + // core objects + HINSTANCE hd3d9_dll; /* handle of the opened d3d9 dll */ + LPDIRECT3D9 p_d3dobj; + LPDIRECT3DDEVICE9 p_d3ddev; + D3DFORMAT bbFormat; + // scene objects + LPDIRECT3DTEXTURE9 p_d3dtex; + LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; +#endif + +#ifdef MODULE_NAME_IS_wingdi + + int i_depth; + + /* Our offscreen bitmap and its framebuffer */ + HDC off_dc; + HBITMAP off_bitmap; + uint8_t * p_pic_buffer; + int i_pic_pitch; + int i_pic_pixel_pitch; + + BITMAPINFO bitmapinfo; + RGBQUAD red; + RGBQUAD green; + RGBQUAD blue; +#endif + +#ifdef MODULE_NAME_IS_wingapi + int i_depth; + int render_width; + int render_height; + + vlc_bool_t b_focus; + vlc_bool_t b_parent_focus; + + HINSTANCE gapi_dll; /* handle of the opened gapi dll */ + + /* GAPI functions */ + int (*GXOpenDisplay)( HWND hWnd, DWORD dwFlags ); + int (*GXCloseDisplay)(); + void *(*GXBeginDraw)(); + int (*GXEndDraw)(); + GXDisplayProperties (*GXGetDisplayProperties)(); + int (*GXSuspend)(); + int (*GXResume)(); +#endif + +#ifndef UNDER_CE + /* suspend display */ + vlc_bool_t b_suspend_display; +#endif + + event_thread_t *p_event; + vlc_mutex_t lock; +}; + +/***************************************************************************** + * Prototypes from directx.c + *****************************************************************************/ +int DirectDrawUpdateOverlay( vout_thread_t *p_vout ); + +/***************************************************************************** + * Prototypes from events.c + *****************************************************************************/ +void E_(EventThread) ( event_thread_t *p_event ); +void E_(UpdateRects) ( vout_thread_t *p_vout, vlc_bool_t b_force ); +void Win32ToggleFullscreen ( vout_thread_t *p_vout ); + +/***************************************************************************** + * Constants + *****************************************************************************/ +#define WM_VLC_HIDE_MOUSE WM_APP +#define WM_VLC_SHOW_MOUSE WM_APP + 1 +#define WM_VLC_CHANGE_TEXT WM_APP + 2 +#define IDM_TOGGLE_ON_TOP WM_USER + 1 +#define DX_POSITION_CHANGE 0x1000 +#define DX_WALLPAPER_CHANGE 0x2000 + +/***************************************************************************** + * WinCE helpers + *****************************************************************************/ +#ifdef UNDER_CE + +#define AdjustWindowRect(a,b,c) + +#ifndef GCL_HBRBACKGROUND +# define GCL_HBRBACKGROUND (-10) +#endif + +#define FindWindowEx(a,b,c,d) 0 + +#define GetWindowPlacement(a,b) +#define SetWindowPlacement(a,b) +typedef struct _WINDOWPLACEMENT { + UINT length; + UINT flags; + UINT showCmd; + POINT ptMinPosition; + POINT ptMaxPosition; + RECT rcNormalPosition; +} WINDOWPLACEMENT; + +#ifndef WM_NCMOUSEMOVE +# define WM_NCMOUSEMOVE 160 +#endif +#ifndef CS_OWNDC +# define CS_OWNDC 32 +#endif +#ifndef SC_SCREENSAVE +# define SC_SCREENSAVE 0xF140 +#endif +#ifndef SC_MONITORPOWER +# define SC_MONITORPOWER 0xF170 +#endif +#ifndef WM_NCPAINT +# define WM_NCPAINT 133 +#endif +#ifndef WS_OVERLAPPEDWINDOW +# define WS_OVERLAPPEDWINDOW 0xcf0000 +#endif +#ifndef WS_EX_NOPARENTNOTIFY +# define WS_EX_NOPARENTNOTIFY 4 +#endif +#ifndef WS_EX_APPWINDOW +#define WS_EX_APPWINDOW 0x40000 +#endif + +#define SetWindowLongPtr SetWindowLong +#define GetWindowLongPtr GetWindowLong +#define GWLP_USERDATA GWL_USERDATA + +#endif //UNDER_CE diff --git a/modules/video_output/msw/wingdi.c b/modules/video_output/msw/wingdi.c new file mode 100755 index 0000000000..ba2367be7f --- /dev/null +++ b/modules/video_output/msw/wingdi.c @@ -0,0 +1,919 @@ +/***************************************************************************** + * wingdi.c : Win32 / WinCE GDI video output plugin for vlc + ***************************************************************************** + * Copyright (C) 2002 the VideoLAN team + * $Id: wingdi.c 18074 2006-11-26 16:26:44Z zorglub $ + * + * Authors: Gildas Bazin + * Samuel Hocevar + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* malloc(), free() */ +#include + +#include +#include +#include +#include + +#include + +#include "vout.h" + +#ifdef MODULE_NAME_IS_wingapi + typedef struct GXDisplayProperties { + DWORD cxWidth; + DWORD cyHeight; + long cbxPitch; + long cbyPitch; + long cBPP; + DWORD ffFormat; + } GXDisplayProperties; + + typedef struct GXScreenRect { + DWORD dwTop; + DWORD dwLeft; + DWORD dwWidth; + DWORD dwHeight; + } GXScreenRect; + +# define GX_FULLSCREEN 0x01 +# define GX_NORMALKEYS 0x02 +# define GX_LANDSCAPEKEYS 0x03 + +# ifndef kfLandscape +# define kfLandscape 0x8 +# define kfPalette 0x10 +# define kfDirect 0x20 +# define kfDirect555 0x40 +# define kfDirect565 0x80 +# define kfDirect888 0x100 +# define kfDirect444 0x200 +# define kfDirectInverted 0x400 +# endif +#endif /* MODULE_NAME_IS_wingapi */ + +#define MAX_DIRECTBUFFERS 10 + +#ifdef UNDER_CE +#ifndef WS_OVERLAPPEDWINDOW +# define WS_OVERLAPPEDWINDOW 0xcf0000 +#endif +#ifndef WS_EX_NOPARENTNOTIFY +# define WS_EX_NOPARENTNOTIFY 4 +#endif +#ifndef WS_EX_APPWINDOW +#define WS_EX_APPWINDOW 0x40000 +#endif +#define SetWindowLongPtr SetWindowLong +#define GetWindowLongPtr GetWindowLong +#define GWLP_USERDATA GWL_USERDATA +#define AdjustWindowRect(a,b,c) +#endif //UNDER_CE + +#ifndef WS_NONAVDONEBUTTON +#define WS_NONAVDONEBUTTON 0 +#endif +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int OpenVideo ( vlc_object_t * ); +static void CloseVideo ( vlc_object_t * ); + +static int Init ( vout_thread_t * ); +static void End ( vout_thread_t * ); +static int Manage ( vout_thread_t * ); +static void Render ( vout_thread_t *, picture_t * ); +#ifdef MODULE_NAME_IS_wingapi +static void FirstDisplayGAPI( vout_thread_t *, picture_t * ); +static void DisplayGAPI( vout_thread_t *, picture_t * ); +static int GAPILockSurface( vout_thread_t *, picture_t * ); +static int GAPIUnlockSurface( vout_thread_t *, picture_t * ); +#else +static void FirstDisplayGDI( vout_thread_t *, picture_t * ); +static void DisplayGDI( vout_thread_t *, picture_t * ); +#endif +static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * ); + +static void InitBuffers ( vout_thread_t * ); + +#ifdef MODULE_NAME_IS_wingapi +# define GXOpenDisplay p_vout->p_sys->GXOpenDisplay +# define GXCloseDisplay p_vout->p_sys->GXCloseDisplay +# define GXBeginDraw p_vout->p_sys->GXBeginDraw +# define GXEndDraw p_vout->p_sys->GXEndDraw +# define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties +# define GXSuspend p_vout->p_sys->GXSuspend +# define GXResume p_vout->p_sys->GXResume +#endif + +#define DX_POSITION_CHANGE 0x1000 + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VOUT ); +#ifdef MODULE_NAME_IS_wingapi + set_shortname( "Windows GAPI" ); + set_description( _("Windows GAPI video output") ); + set_capability( "video output", 20 ); +#else + set_shortname( "Windows GDI" ); + set_description( _("Windows GDI video output") ); + set_capability( "video output", 10 ); +#endif + set_callbacks( OpenVideo, CloseVideo ); +vlc_module_end(); + +/***************************************************************************** + * OpenVideo: activate GDI video thread output method + *****************************************************************************/ +static int OpenVideo ( vlc_object_t *p_this ) +{ + vout_thread_t * p_vout = (vout_thread_t *)p_this; + vlc_value_t val; + + p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) ); + if( !p_vout->p_sys ) return VLC_ENOMEM; + memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); + +#ifdef MODULE_NAME_IS_wingapi + /* Load GAPI */ + p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") ); + if( p_vout->p_sys->gapi_dll == NULL ) + { + msg_Warn( p_vout, "failed loading gx.dll" ); + free( p_vout->p_sys ); + return VLC_EGENERIC; + } + + GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") ); + GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXCloseDisplay@@YAHXZ") ); + GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXBeginDraw@@YAPAXXZ") ); + GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXEndDraw@@YAHXZ") ); + GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") ); + GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXSuspend@@YAHXZ") ); + GXResume = GetProcAddress( p_vout->p_sys->gapi_dll, + _T("?GXResume@@YAHXZ") ); + + if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw || + !GXGetDisplayProperties || !GXSuspend || !GXResume ) + { + msg_Err( p_vout, "failed GetProcAddress on gapi.dll" ); + free( p_vout->p_sys ); + return VLC_EGENERIC; + } + + msg_Dbg( p_vout, "GAPI DLL loaded" ); + + p_vout->p_sys->render_width = p_vout->render.i_width; + p_vout->p_sys->render_height = p_vout->render.i_height; +#endif + + p_vout->p_sys->p_event = (vlc_object_t *) + vlc_object_create( p_vout, VLC_OBJECT_GENERIC ); + if( !p_vout->p_sys->p_event ) + { + free( p_vout->p_sys ); + return VLC_ENOMEM; + } + + p_vout->pf_init = Init; + p_vout->pf_end = End; + p_vout->pf_manage = Manage; + p_vout->pf_render = Render; +#ifdef MODULE_NAME_IS_wingapi + p_vout->pf_display = FirstDisplayGAPI; + + p_vout->p_sys->b_focus = 0; + p_vout->p_sys->b_parent_focus = 0; + +#else + p_vout->pf_display = FirstDisplayGDI; +#endif + + 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; + vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); + SetRectEmpty( &p_vout->p_sys->rect_display ); + SetRectEmpty( &p_vout->p_sys->rect_parent ); + + var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + p_vout->p_sys->b_cursor_hidden = 0; + p_vout->p_sys->i_lastmoved = mdate(); + + /* Set main window's size */ + p_vout->p_sys->i_window_width = p_vout->i_window_width; + p_vout->p_sys->i_window_height = p_vout->i_window_height; + + /* Create the EventThread, this thread is created by us to isolate + * the Win32 PeekMessage function calls. We want to do this because + * Windows can stay blocked inside this call for a long time, and when + * this happens it thus blocks vlc's video_output thread. + * Vout EventThread will take care of the creation of the video + * window (because PeekMessage has to be called from the same thread which + * created the window). */ + msg_Dbg( p_vout, "creating Vout EventThread" ); + 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, "VLC Vout Events Thread", + E_(EventThread), 0, 1 ) ) + { + msg_Err( p_vout, "cannot create Vout EventThread" ); + vlc_object_destroy( p_vout->p_sys->p_event ); + p_vout->p_sys->p_event = NULL; + goto error; + } + + if( p_vout->p_sys->p_event->b_error ) + { + msg_Err( p_vout, "Vout EventThread failed" ); + goto error; + } + + vlc_object_attach( p_vout->p_sys->p_event, p_vout ); + + msg_Dbg( p_vout, "Vout EventThread running" ); + +#ifndef UNDER_CE + /* 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 ); + + /* disable screensaver by temporarily changing system settings */ + p_vout->p_sys->i_spi_lowpowertimeout = 0; + p_vout->p_sys->i_spi_powerofftimeout = 0; + p_vout->p_sys->i_spi_screensavetimeout = 0; + var_Get( p_vout, "disable-screensaver", &val); + if( val.b_bool ) { + msg_Dbg(p_vout, "disabling screen saver"); + SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT, + 0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0); + if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { + SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0); + } + SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, + &(p_vout->p_sys->i_spi_powerofftimeout), 0); + if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { + SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0); + } + SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, + &(p_vout->p_sys->i_spi_screensavetimeout), 0); + if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); + } + } +#endif + return VLC_SUCCESS; + +error: + CloseVideo( VLC_OBJECT(p_vout) ); + return VLC_EGENERIC; +} + +/***************************************************************************** + * CloseVideo: deactivate the GDI video output + *****************************************************************************/ +static void CloseVideo ( vlc_object_t *p_this ) +{ + vout_thread_t * p_vout = (vout_thread_t *)p_this; + + if( p_vout->p_sys->p_event ) + { + vlc_object_detach( p_vout->p_sys->p_event ); + + /* Kill Vout EventThread */ + p_vout->p_sys->p_event->b_die = VLC_TRUE; + + /* we need to be sure Vout EventThread 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 ); + +#ifndef UNDER_CE + /* restore screensaver system settings */ + if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { + SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, + p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0); + } + if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { + SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, + p_vout->p_sys->i_spi_powerofftimeout, NULL, 0); + } + if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { + SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, + p_vout->p_sys->i_spi_screensavetimeout, NULL, 0); + } +#endif + +#ifdef MODULE_NAME_IS_wingapi + FreeLibrary( p_vout->p_sys->gapi_dll ); +#endif + + if( p_vout->p_sys ) + { + free( p_vout->p_sys ); + p_vout->p_sys = NULL; + } +} + +/***************************************************************************** + * Init: initialize video thread output method + *****************************************************************************/ +static int Init( vout_thread_t *p_vout ) +{ + picture_t *p_pic; + + /* Initialize offscreen buffer */ + InitBuffers( p_vout ); + + 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); + + I_OUTPUTPICTURES = 0; + + /* Initialize the output structure */ + switch( p_vout->p_sys->i_depth ) + { + 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'); + p_vout->output.i_rmask = 0x7c00; + p_vout->output.i_gmask = 0x03e0; + p_vout->output.i_bmask = 0x001f; + break; + case 16: + p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); + p_vout->output.i_rmask = 0xf800; + p_vout->output.i_gmask = 0x07e0; + p_vout->output.i_bmask = 0x001f; + break; + case 24: + p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); + p_vout->output.i_rmask = 0x00ff0000; + p_vout->output.i_gmask = 0x0000ff00; + p_vout->output.i_bmask = 0x000000ff; + break; + case 32: + p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); + p_vout->output.i_rmask = 0x00ff0000; + p_vout->output.i_gmask = 0x0000ff00; + p_vout->output.i_bmask = 0x000000ff; + break; + default: + msg_Err( p_vout, "screen depth %i not supported", + p_vout->p_sys->i_depth ); + return VLC_EGENERIC; + break; + } + + p_pic = &p_vout->p_picture[0]; + +#ifdef MODULE_NAME_IS_wingapi + p_vout->output.i_width = 0; + p_vout->output.i_height = 0; + p_pic->pf_lock = GAPILockSurface; + p_pic->pf_unlock = GAPIUnlockSurface; + Manage( p_vout ); + GAPILockSurface( p_vout, p_pic ); + p_vout->i_changes = 0; + p_vout->output.i_width = p_vout->p_sys->render_width; + p_vout->output.i_height = p_vout->p_sys->render_height; + +#else + p_vout->output.i_width = p_vout->render.i_width; + p_vout->output.i_height = p_vout->render.i_height; + + p_vout->fmt_out = p_vout->fmt_in; + p_vout->fmt_out.i_chroma = p_vout->output.i_chroma; +#endif + + p_vout->output.i_aspect = p_vout->render.i_aspect; + + p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer; + 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_vout->p_sys->i_pic_pitch; + p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch; + p_pic->p->i_visible_pitch = p_vout->output.i_width * + p_pic->p->i_pixel_pitch; + p_pic->i_planes = 1; + p_pic->i_status = DESTROYED_PICTURE; + p_pic->i_type = DIRECT_PICTURE; + + PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic; + + /* Change the window title bar text */ + PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 ); + E_(UpdateRects)( p_vout, VLC_TRUE ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * End: terminate video thread output method + *****************************************************************************/ +static void End( vout_thread_t *p_vout ) +{ +#ifdef MODULE_NAME_IS_wingapi + GXCloseDisplay(); +#else + DeleteDC( p_vout->p_sys->off_dc ); + DeleteObject( p_vout->p_sys->off_bitmap ); +#endif +} + +/***************************************************************************** + * Manage: handle events + ***************************************************************************** + * This function should be called regularly by video output thread. It manages + * console events. It returns a non null value on error. + *****************************************************************************/ +static int Manage( vout_thread_t *p_vout ) +{ + /* If we do not control our window, we check for geometry changes + * ourselves because the parent might not send us its events. */ + vlc_mutex_lock( &p_vout->p_sys->lock ); + if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) + { + RECT rect_parent; + POINT point; + + 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 ) ) + { + int i_x, i_y, i_width, i_height; + 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 ); + + vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left, + rect_parent.bottom - rect_parent.top, + &i_x, &i_y, &i_width, &i_height ); + + SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP, + i_x, i_y, i_width, i_height, 0 ); + } + } + else + { + vlc_mutex_unlock( &p_vout->p_sys->lock ); + } + + /* 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_(UpdateRects)( p_vout, VLC_TRUE ); + } + + /* + * Position Change + */ + if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE ) + { + p_vout->p_sys->i_changes &= ~DX_POSITION_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 ) + { + Win32ToggleFullscreen( p_vout ); + + p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; + } + + /* + * Pointer change + */ + 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 ) + { + 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 VLC_EGENERIC; /* exit */ + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Render: render previously calculated output + *****************************************************************************/ +static void Render( vout_thread_t *p_vout, picture_t *p_pic ) +{ + /* No need to do anything, the fake direct buffers stay as they are */ +} + +/***************************************************************************** + * Display: displays previously rendered output + *****************************************************************************/ +#define rect_src p_vout->p_sys->rect_src +#define rect_src_clipped p_vout->p_sys->rect_src_clipped +#define rect_dest p_vout->p_sys->rect_dest +#define rect_dest_clipped p_vout->p_sys->rect_dest_clipped + +#ifndef MODULE_NAME_IS_wingapi +static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vout_sys_t *p_sys = p_vout->p_sys; + RECT rect_dst = rect_dest_clipped; + HDC hdc = GetDC( p_sys->hvideownd ); + + OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top ); + SelectObject( p_sys->off_dc, p_sys->off_bitmap ); + + if( rect_dest_clipped.right - rect_dest_clipped.left != + rect_src_clipped.right - rect_src_clipped.left || + rect_dest_clipped.bottom - rect_dest_clipped.top != + rect_src_clipped.bottom - rect_src_clipped.top ) + { + StretchBlt( hdc, rect_dst.left, rect_dst.top, + rect_dst.right, rect_dst.bottom, + p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top, + rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY ); + } + else + { + BitBlt( hdc, rect_dst.left, rect_dst.top, + rect_dst.right, rect_dst.bottom, + p_sys->off_dc, rect_src_clipped.left, + rect_src_clipped.top, SRCCOPY ); + } + + ReleaseDC( p_sys->hvideownd, hdc ); +} + +static void FirstDisplayGDI( vout_thread_t *p_vout, picture_t *p_pic ) +{ + /* + ** Video window is initially hidden, show it now since we got a + ** picture to show. + */ + SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0, + SWP_ASYNCWINDOWPOS| + SWP_FRAMECHANGED| + SWP_SHOWWINDOW| + SWP_NOMOVE| + SWP_NOSIZE| + SWP_NOZORDER ); + + /* get initial picture presented */ + DisplayGDI(p_vout, p_pic); + + /* use and restores proper display function for further pictures */ + p_vout->pf_display = DisplayGDI; +} + +#else + +static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vout_sys_t *p_sys = p_vout->p_sys; + int i_x, i_y, i_width, i_height; + RECT video_rect; + POINT point; + + GetClientRect( p_sys->hwnd, &video_rect); + vout_PlacePicture( p_vout, video_rect.right - video_rect.left, + video_rect.bottom - video_rect.top, + &i_x, &i_y, &i_width, &i_height ); + point.x = point.y = 0; + ClientToScreen( p_sys->hwnd, &point ); + i_x += point.x + video_rect.left; + i_y += point.y + video_rect.top; + + if( i_width != p_vout->output.i_width || + i_height != p_vout->output.i_height ) + { + GXDisplayProperties gxdisplayprop = GXGetDisplayProperties(); + + p_sys->render_width = i_width; + p_sys->render_height = i_height; + p_vout->i_changes |= VOUT_SIZE_CHANGE; + + msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)", + i_width, i_height, p_vout->output.i_width, + p_vout->output.i_height ); + + p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch; + p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch; + return VLC_EGENERIC; + } + else + { + GXDisplayProperties gxdisplayprop; + RECT display_rect, dest_rect; + uint8_t *p_dest, *p_src = p_pic->p->p_pixels; + + video_rect.left = i_x; video_rect.top = i_y; + video_rect.right = i_x + i_width; + video_rect.bottom = i_y + i_height; + + gxdisplayprop = GXGetDisplayProperties(); + display_rect.left = 0; display_rect.top = 0; + display_rect.right = gxdisplayprop.cxWidth; + display_rect.bottom = gxdisplayprop.cyHeight; + + if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) ) + { + return VLC_EGENERIC; + } + +#if 0 + msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) " + "dest (%d,%d,%d,%d)", + video_rect.left, video_rect.right, + video_rect.top, video_rect.bottom, + display_rect.left, display_rect.right, + display_rect.top, display_rect.bottom, + dest_rect.left, dest_rect.right, + dest_rect.top, dest_rect.bottom ); +#endif + + if( !(p_dest = GXBeginDraw()) ) + { +#if 0 + msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() ); +#endif + return VLC_EGENERIC; + } + + p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch + + (dest_rect.top - video_rect.top) * p_pic->p->i_pitch; + p_dest += dest_rect.left * gxdisplayprop.cbxPitch + + dest_rect.top * gxdisplayprop.cbyPitch; + i_width = dest_rect.right - dest_rect.left; + i_height = dest_rect.bottom - dest_rect.top; + + p_pic->p->p_pixels = p_dest; + } + + return VLC_SUCCESS; +} + +static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) +{ + GXEndDraw(); + return VLC_SUCCESS; +} + +static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic ) +{ +} + +static void FirstDisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic ) +{ + /* get initial picture presented through D3D */ + DisplayGAPI(p_vout, p_pic); + + /* + ** Video window is initially hidden, show it now since we got a + ** picture to show. + */ + SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0, + SWP_ASYNCWINDOWPOS| + SWP_FRAMECHANGED| + SWP_SHOWWINDOW| + SWP_NOMOVE| + SWP_NOSIZE| + SWP_NOZORDER ); + + /* use and restores proper display function for further pictures */ + p_vout->pf_display = DisplayGAPI; +} + +#endif + +#undef rect_src +#undef rect_src_clipped +#undef rect_dest +#undef rect_dest_clipped +/***************************************************************************** + * 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" ); +} + +/***************************************************************************** + * InitBuffers: initialize an offscreen bitmap for direct buffer operations. + *****************************************************************************/ +static void InitBuffers( vout_thread_t *p_vout ) +{ + BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader; + BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo; + int i_pixels = p_vout->render.i_height * p_vout->render.i_width; + HDC window_dc = GetDC( p_vout->p_sys->hvideownd ); + + /* Get screen properties */ +#ifdef MODULE_NAME_IS_wingapi + GXDisplayProperties gx_displayprop = GXGetDisplayProperties(); + p_vout->p_sys->i_depth = gx_displayprop.cBPP; +#else + p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) * + GetDeviceCaps( window_dc, BITSPIXEL ); +#endif + msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth ); + +#ifdef MODULE_NAME_IS_wingapi + GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN ); + +#else + + /* Initialize offscreen bitmap */ + memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) ); + + p_header->biSize = sizeof( BITMAPINFOHEADER ); + p_header->biSizeImage = 0; + p_header->biPlanes = 1; + switch( p_vout->p_sys->i_depth ) + { + case 8: + p_header->biBitCount = 8; + p_header->biCompression = BI_RGB; + /* FIXME: we need a palette here */ + break; + case 15: + p_header->biBitCount = 15; + p_header->biCompression = BI_BITFIELDS;//BI_RGB; + ((DWORD*)p_info->bmiColors)[0] = 0x00007c00; + ((DWORD*)p_info->bmiColors)[1] = 0x000003e0; + ((DWORD*)p_info->bmiColors)[2] = 0x0000001f; + break; + case 16: + p_header->biBitCount = 16; + p_header->biCompression = BI_BITFIELDS;//BI_RGB; + ((DWORD*)p_info->bmiColors)[0] = 0x0000f800; + ((DWORD*)p_info->bmiColors)[1] = 0x000007e0; + ((DWORD*)p_info->bmiColors)[2] = 0x0000001f; + break; + case 24: + p_header->biBitCount = 24; + p_header->biCompression = BI_RGB; + ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000; + ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00; + ((DWORD*)p_info->bmiColors)[2] = 0x000000ff; + break; + case 32: + p_header->biBitCount = 32; + p_header->biCompression = BI_RGB; + ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000; + ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00; + ((DWORD*)p_info->bmiColors)[2] = 0x000000ff; + break; + default: + msg_Err( p_vout, "screen depth %i not supported", + p_vout->p_sys->i_depth ); + return; + break; + } + p_header->biWidth = p_vout->render.i_width; + p_header->biHeight = -p_vout->render.i_height; + p_header->biClrImportant = 0; + p_header->biClrUsed = 0; + p_header->biXPelsPerMeter = 0; + p_header->biYPelsPerMeter = 0; + + p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8; + p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8; + + p_vout->p_sys->off_bitmap = + CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS, + (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 ); + + p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc ); + + SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap ); + ReleaseDC( p_vout->p_sys->hvideownd, window_dc ); +#endif +} + diff --git a/modules/video_output/wingdi.c b/modules/video_output/wingdi.c deleted file mode 100644 index 25585071fd..0000000000 --- a/modules/video_output/wingdi.c +++ /dev/null @@ -1,1362 +0,0 @@ -/***************************************************************************** - * wingdi.c : Win32 / WinCE GDI video output plugin for vlc - ***************************************************************************** - * Copyright (C) 2002 the VideoLAN team - * $Id$ - * - * Authors: Gildas Bazin - * Samuel Hocevar - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/***************************************************************************** - * Preamble - *****************************************************************************/ -#include /* malloc(), free() */ -#include - -#include -#include -#include -#include - -#include - -#define SHFS_SHOWSIPBUTTON 0x0004 -#define SHFS_HIDESIPBUTTON 0x0008 - -#if defined(UNDER_CE) && !defined(__PLUGIN__) /*FIXME*/ -# define MENU_HEIGHT 26 - BOOL SHFullScreen(HWND hwndRequester, DWORD dwState); -#else -# define MENU_HEIGHT 0 -# define SHFullScreen(a,b) -#endif - -#ifdef MODULE_NAME_IS_wingapi - typedef struct GXDisplayProperties { - DWORD cxWidth; - DWORD cyHeight; - long cbxPitch; - long cbyPitch; - long cBPP; - DWORD ffFormat; - } GXDisplayProperties; - - typedef struct GXScreenRect { - DWORD dwTop; - DWORD dwLeft; - DWORD dwWidth; - DWORD dwHeight; - } GXScreenRect; - -# define GX_FULLSCREEN 0x01 -# define GX_NORMALKEYS 0x02 -# define GX_LANDSCAPEKEYS 0x03 - -# ifndef kfLandscape -# define kfLandscape 0x8 -# define kfPalette 0x10 -# define kfDirect 0x20 -# define kfDirect555 0x40 -# define kfDirect565 0x80 -# define kfDirect888 0x100 -# define kfDirect444 0x200 -# define kfDirectInverted 0x400 -# endif -#endif /* MODULE_NAME_IS_wingapi */ - -#define MAX_DIRECTBUFFERS 10 - -#ifdef UNDER_CE -#ifndef WS_OVERLAPPEDWINDOW -# define WS_OVERLAPPEDWINDOW 0xcf0000 -#endif -#ifndef WS_EX_NOPARENTNOTIFY -# define WS_EX_NOPARENTNOTIFY 4 -#endif -#ifndef WS_EX_APPWINDOW -#define WS_EX_APPWINDOW 0x40000 -#endif -#define SetWindowLongPtr SetWindowLong -#define GetWindowLongPtr GetWindowLong -#define GWLP_USERDATA GWL_USERDATA -#define AdjustWindowRect(a,b,c) -#endif //UNDER_CE - -#ifndef WS_NONAVDONEBUTTON -#define WS_NONAVDONEBUTTON 0 -#endif -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int OpenVideo ( vlc_object_t * ); -static void CloseVideo ( vlc_object_t * ); - -static int Init ( vout_thread_t * ); -static void End ( vout_thread_t * ); -static int Manage ( vout_thread_t * ); -static void Render ( vout_thread_t *, picture_t * ); -#ifdef MODULE_NAME_IS_wingapi -static void DisplayGAPI( vout_thread_t *, picture_t * ); -static int GAPILockSurface( vout_thread_t *, picture_t * ); -static int GAPIUnlockSurface( vout_thread_t *, picture_t * ); -#else -static void DisplayGDI( vout_thread_t *, picture_t * ); -#endif -static void SetPalette( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * ); - -static void EventThread ( vlc_object_t * ); -static long FAR PASCAL WndProc ( HWND, UINT, WPARAM, LPARAM ); -static void InitBuffers ( vout_thread_t * ); -static void UpdateRects ( vout_thread_t *, vlc_bool_t ); - -static int Control( vout_thread_t *p_vout, int i_query, va_list args ); - -/***************************************************************************** - * Private structure - *****************************************************************************/ -struct vout_sys_t -{ - /* The event thread */ - vlc_object_t * p_event; - - /* Our video output window */ - HWND hwnd; - HWND hvideownd; - HWND hfswnd; - int i_depth; - HWND hparent; /* Handle of the parent window */ - WNDPROC pf_wndproc; /* Window handling callback */ - volatile uint16_t i_changes; /* changes made to the video display */ - RECT window_placement; - - /* Window position and size */ - int i_window_x; - int i_window_y; - int i_window_width; - int i_window_height; - int i_window_style; - int render_width; - int render_height; - - /* Coordinates of src and dest images (used when blitting to display) */ - RECT rect_src; - RECT rect_src_clipped; - RECT rect_dest; - RECT rect_dest_clipped; - RECT rect_parent; - RECT rect_display; - - /* Our offscreen bitmap and its framebuffer */ - HDC off_dc; - HBITMAP off_bitmap; - uint8_t * p_pic_buffer; - int i_pic_pitch; - int i_pic_pixel_pitch; - - BITMAPINFO bitmapinfo; - RGBQUAD red; - RGBQUAD green; - RGBQUAD blue; - - /* WINCE stuff */ - vlc_bool_t b_video_display; - - /* Window focus states */ - vlc_bool_t b_focus; - vlc_bool_t b_parent_focus; - -#ifdef MODULE_NAME_IS_wingapi - HINSTANCE gapi_dll; /* handle of the opened gapi dll */ - - /* GAPI functions */ - int (*GXOpenDisplay)( HWND hWnd, DWORD dwFlags ); - int (*GXCloseDisplay)(); - void *(*GXBeginDraw)(); - int (*GXEndDraw)(); - GXDisplayProperties (*GXGetDisplayProperties)(); - int (*GXSuspend)(); - int (*GXResume)(); -#endif -}; - -#define GXOpenDisplay p_vout->p_sys->GXOpenDisplay -#define GXCloseDisplay p_vout->p_sys->GXCloseDisplay -#define GXBeginDraw p_vout->p_sys->GXBeginDraw -#define GXEndDraw p_vout->p_sys->GXEndDraw -#define GXGetDisplayProperties p_vout->p_sys->GXGetDisplayProperties - -#ifdef MODULE_NAME_IS_wingapi -# define GXSuspend p_vout->p_sys->GXSuspend -# define GXResume p_vout->p_sys->GXResume -#else -# define GXSuspend() -# define GXResume() -#endif - -#define DX_POSITION_CHANGE 0x1000 - -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -vlc_module_begin(); - set_category( CAT_VIDEO ); - set_subcategory( SUBCAT_VIDEO_VOUT ); -#ifdef MODULE_NAME_IS_wingapi - set_shortname( "Windows GAPI" ); - set_description( _("Windows GAPI video output") ); - set_capability( "video output", 20 ); -#else - set_shortname( "Windows GDI" ); - set_description( _("Windows GDI video output") ); - set_capability( "video output", 10 ); -#endif - set_callbacks( OpenVideo, CloseVideo ); -vlc_module_end(); - -/***************************************************************************** - * OpenVideo: activate GDI video thread output method - *****************************************************************************/ -static int OpenVideo ( vlc_object_t *p_this ) -{ - vout_thread_t * p_vout = (vout_thread_t *)p_this; - vlc_value_t val; - - p_vout->p_sys = (vout_sys_t *)malloc( sizeof(vout_sys_t) ); - if( !p_vout->p_sys ) return VLC_ENOMEM; - -#ifdef MODULE_NAME_IS_wingapi - /* Load GAPI */ - p_vout->p_sys->gapi_dll = LoadLibrary( _T("GX.DLL") ); - if( p_vout->p_sys->gapi_dll == NULL ) - { - msg_Warn( p_vout, "failed loading gx.dll" ); - free( p_vout->p_sys ); - return VLC_EGENERIC; - } - - GXOpenDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXOpenDisplay@@YAHPAUHWND__@@K@Z") ); - GXCloseDisplay = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXCloseDisplay@@YAHXZ") ); - GXBeginDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXBeginDraw@@YAPAXXZ") ); - GXEndDraw = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXEndDraw@@YAHXZ") ); - GXGetDisplayProperties = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ") ); - GXSuspend = (void *)GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXSuspend@@YAHXZ") ); - GXResume = GetProcAddress( p_vout->p_sys->gapi_dll, - _T("?GXResume@@YAHXZ") ); - - if( !GXOpenDisplay || !GXCloseDisplay || !GXBeginDraw || !GXEndDraw || - !GXGetDisplayProperties || !GXSuspend || !GXResume ) - { - msg_Err( p_vout, "failed GetProcAddress on gapi.dll" ); - free( p_vout->p_sys ); - return VLC_EGENERIC; - } - - msg_Dbg( p_vout, "GAPI DLL loaded" ); - - p_vout->p_sys->render_width = p_vout->render.i_width; - p_vout->p_sys->render_height = p_vout->render.i_height; -#endif - - p_vout->p_sys->p_event = (vlc_object_t *) - vlc_object_create( p_vout, VLC_OBJECT_GENERIC ); - if( !p_vout->p_sys->p_event ) - { - free( p_vout->p_sys ); - return VLC_ENOMEM; - } - - var_Create( p_vout->p_sys->p_event, "p_vout", VLC_VAR_ADDRESS ); - val.p_address = (void *)p_vout; - var_Set( p_vout->p_sys->p_event, "p_vout", val ); - - SetRectEmpty( &p_vout->p_sys->rect_display ); - SetRectEmpty( &p_vout->p_sys->rect_parent ); - - if( vlc_thread_create( p_vout->p_sys->p_event, "GDI Event Thread", - EventThread, 0, 1 ) ) - { - msg_Err( p_vout, "cannot spawn EventThread" ); - return VLC_ETHREAD; - } - - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = Manage; - p_vout->pf_render = Render; -#ifdef MODULE_NAME_IS_wingapi - p_vout->pf_display = DisplayGAPI; -#else - p_vout->pf_display = DisplayGDI; -#endif - p_vout->p_sys->i_changes = 0; - - p_vout->p_sys->b_focus = 0; - p_vout->p_sys->b_parent_focus = 0; - - return VLC_SUCCESS; -} - -/***************************************************************************** - * CloseVideo: deactivate the GDI video output - *****************************************************************************/ -static void CloseVideo ( vlc_object_t *p_this ) -{ - vout_thread_t * p_vout = (vout_thread_t *)p_this; - - p_vout->p_sys->p_event->b_die = VLC_TRUE; - PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0 ); - vlc_thread_join( p_vout->p_sys->p_event ); - -#ifdef MODULE_NAME_IS_wingapi - FreeLibrary( p_vout->p_sys->gapi_dll ); -#endif - - var_Destroy( p_vout->p_sys->p_event, "p_vout" ); - vlc_object_destroy( p_vout->p_sys->p_event ); - free( p_vout->p_sys ); -} - -/***************************************************************************** - * Init: initialize video thread output method - *****************************************************************************/ -static int Init( vout_thread_t *p_vout ) -{ - picture_t *p_pic; - - 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); - - p_vout->p_sys->b_video_display = VLC_TRUE; - p_vout->p_sys->p_event->b_die = VLC_FALSE; - - I_OUTPUTPICTURES = 0; - - /* Initialize the output structure */ - switch( p_vout->p_sys->i_depth ) - { - 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'); - p_vout->output.i_rmask = 0x7c00; - p_vout->output.i_gmask = 0x03e0; - p_vout->output.i_bmask = 0x001f; - break; - case 16: - p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); - p_vout->output.i_rmask = 0xf800; - p_vout->output.i_gmask = 0x07e0; - p_vout->output.i_bmask = 0x001f; - break; - case 24: - p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); - p_vout->output.i_rmask = 0x00ff0000; - p_vout->output.i_gmask = 0x0000ff00; - p_vout->output.i_bmask = 0x000000ff; - break; - case 32: - p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); - p_vout->output.i_rmask = 0x00ff0000; - p_vout->output.i_gmask = 0x0000ff00; - p_vout->output.i_bmask = 0x000000ff; - break; - default: - msg_Err( p_vout, "screen depth %i not supported", - p_vout->p_sys->i_depth ); - return VLC_EGENERIC; - break; - } - - p_pic = &p_vout->p_picture[0]; - -#ifdef MODULE_NAME_IS_wingapi - p_vout->output.i_width = 0; - p_vout->output.i_height = 0; - p_pic->pf_lock = GAPILockSurface; - p_pic->pf_unlock = GAPIUnlockSurface; - Manage( p_vout ); - GAPILockSurface( p_vout, p_pic ); - p_vout->i_changes = 0; - p_vout->output.i_width = p_vout->p_sys->render_width; - p_vout->output.i_height = p_vout->p_sys->render_height; - -#else - p_vout->output.i_width = p_vout->render.i_width; - p_vout->output.i_height = p_vout->render.i_height; - - p_vout->fmt_out = p_vout->fmt_in; - p_vout->fmt_out.i_chroma = p_vout->output.i_chroma; -#endif - - p_vout->output.i_aspect = p_vout->render.i_aspect; - - p_pic->p->p_pixels = p_vout->p_sys->p_pic_buffer; - 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_vout->p_sys->i_pic_pitch; - p_pic->p->i_pixel_pitch = p_vout->p_sys->i_pic_pixel_pitch; - p_pic->p->i_visible_pitch = p_vout->output.i_width * - p_pic->p->i_pixel_pitch; - p_pic->i_planes = 1; - p_pic->i_status = DESTROYED_PICTURE; - p_pic->i_type = DIRECT_PICTURE; - - PP_OUTPUTPICTURE[ I_OUTPUTPICTURES++ ] = p_pic; - - return VLC_SUCCESS; -} - -/***************************************************************************** - * End: terminate video thread output method - *****************************************************************************/ -static void End( vout_thread_t *p_vout ) -{ -} - -/***************************************************************************** - * Manage: handle events - ***************************************************************************** - * This function should be called regularly by video output thread. It manages - * console events. It returns a non null value on error. - *****************************************************************************/ -static int Manage( vout_thread_t *p_vout ) -{ -#ifndef UNDER_CE - WINDOWPLACEMENT window_placement; -#endif - - /* 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 && !p_vout->b_fullscreen ) - { - RECT rect_parent; - POINT point; - - 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 ) ) - { - int i_x, i_y, i_width, i_height; - 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 ); - - vout_PlacePicture( p_vout, rect_parent.right - rect_parent.left, - rect_parent.bottom - rect_parent.top, - &i_x, &i_y, &i_width, &i_height ); - - SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP, - i_x, i_y, i_width, i_height, 0 ); - } - } - - /* 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; - - 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 */ -#ifndef UNDER_CE - window_placement.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement( hwnd, &window_placement ); -#endif - if( p_vout->b_fullscreen ) - { -#ifndef UNDER_CE - /* Change window style, no borders and no title bar */ - 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); -#endif - - 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 ); - } - - ShowWindow( hwnd, SW_SHOW ); - SetForegroundWindow( hwnd ); - } - else - { - /* Change window style, no borders and no title bar */ - //SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style ); - -#ifndef UNDER_CE - /* Normal window */ - window_placement.showCmd = SW_SHOWNORMAL; - SetWindowPlacement( hwnd, &window_placement ); - SetWindowPos( hwnd, 0, 0, 0, 0, 0, - SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); -#endif - - 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 ); - } - - /* Change window style, borders and title bar */ - ShowWindow( p_vout->p_sys->hwnd, SW_SHOW ); - UpdateWindow( p_vout->p_sys->hwnd ); - - /* 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; - } - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Render: render previously calculated output - *****************************************************************************/ -static void Render( vout_thread_t *p_vout, picture_t *p_pic ) -{ - /* No need to do anything, the fake direct buffers stay as they are */ -} - -/***************************************************************************** - * Display: displays previously rendered output - *****************************************************************************/ -#define rect_src p_vout->p_sys->rect_src -#define rect_src_clipped p_vout->p_sys->rect_src_clipped -#define rect_dest p_vout->p_sys->rect_dest -#define rect_dest_clipped p_vout->p_sys->rect_dest_clipped - -#ifndef MODULE_NAME_IS_wingapi -static void DisplayGDI( vout_thread_t *p_vout, picture_t *p_pic ) -{ - vout_sys_t *p_sys = p_vout->p_sys; - RECT rect_dst = rect_dest_clipped; - HDC hdc = GetDC( p_sys->hvideownd ); - - OffsetRect( &rect_dst, -rect_dest.left, -rect_dest.top ); - SelectObject( p_sys->off_dc, p_sys->off_bitmap ); - - if( rect_dest_clipped.right - rect_dest_clipped.left != - rect_src_clipped.right - rect_src_clipped.left || - rect_dest_clipped.bottom - rect_dest_clipped.top != - rect_src_clipped.bottom - rect_src_clipped.top ) - { - StretchBlt( hdc, rect_dst.left, rect_dst.top, - rect_dst.right, rect_dst.bottom, - p_sys->off_dc, rect_src_clipped.left, rect_src_clipped.top, - rect_src_clipped.right, rect_src_clipped.bottom, SRCCOPY ); - } - else - { - BitBlt( hdc, rect_dst.left, rect_dst.top, - rect_dst.right, rect_dst.bottom, - p_sys->off_dc, rect_src_clipped.left, - rect_src_clipped.top, SRCCOPY ); - } - - ReleaseDC( p_sys->hwnd, hdc ); -} -#else - -static int GAPILockSurface( vout_thread_t *p_vout, picture_t *p_pic ) -{ - vout_sys_t *p_sys = p_vout->p_sys; - int i_x, i_y, i_width, i_height; - RECT video_rect; - POINT point; - - /* Undo the display */ - if( ( GetForegroundWindow() != GetParent(p_sys->hwnd) ) || - ( p_sys->b_video_display == VLC_FALSE ) ) - { - //return VLC_EGENERIC; - } - - GetClientRect( p_sys->hwnd, &video_rect); - vout_PlacePicture( p_vout, video_rect.right - video_rect.left, - video_rect.bottom - video_rect.top, - &i_x, &i_y, &i_width, &i_height ); - point.x = point.y = 0; - ClientToScreen( p_sys->hwnd, &point ); - i_x += point.x + video_rect.left; - i_y += point.y + video_rect.top; - - if( i_width != p_vout->output.i_width || - i_height != p_vout->output.i_height ) - { - GXDisplayProperties gxdisplayprop = GXGetDisplayProperties(); - - p_sys->render_width = i_width; - p_sys->render_height = i_height; - p_vout->i_changes |= VOUT_SIZE_CHANGE; - - msg_Dbg( p_vout, "vout size change (%ix%i -> %ix%i)", - i_width, i_height, p_vout->output.i_width, - p_vout->output.i_height ); - - p_vout->p_sys->i_pic_pixel_pitch = gxdisplayprop.cbxPitch; - p_vout->p_sys->i_pic_pitch = gxdisplayprop.cbyPitch; - return VLC_EGENERIC; - } - else - { - GXDisplayProperties gxdisplayprop; - RECT display_rect, dest_rect; - uint8_t *p_dest, *p_src = p_pic->p->p_pixels; - - video_rect.left = i_x; video_rect.top = i_y; - video_rect.right = i_x + i_width; - video_rect.bottom = i_y + i_height; - - gxdisplayprop = GXGetDisplayProperties(); - display_rect.left = 0; display_rect.top = 0; - display_rect.right = gxdisplayprop.cxWidth; - display_rect.bottom = gxdisplayprop.cyHeight; - - if( !IntersectRect( &dest_rect, &video_rect, &display_rect ) ) - { - return VLC_EGENERIC; - } - -#if 0 - msg_Err( p_vout, "video (%d,%d,%d,%d) display (%d,%d,%d,%d) " - "dest (%d,%d,%d,%d)", - video_rect.left, video_rect.right, - video_rect.top, video_rect.bottom, - display_rect.left, display_rect.right, - display_rect.top, display_rect.bottom, - dest_rect.left, dest_rect.right, - dest_rect.top, dest_rect.bottom ); -#endif - - if( !(p_dest = GXBeginDraw()) ) - { -#if 0 - msg_Err( p_vout, "GXBeginDraw error %d ", GetLastError() ); -#endif - return VLC_EGENERIC; - } - - p_src += (dest_rect.left - video_rect.left) * gxdisplayprop.cbxPitch + - (dest_rect.top - video_rect.top) * p_pic->p->i_pitch; - p_dest += dest_rect.left * gxdisplayprop.cbxPitch + - dest_rect.top * gxdisplayprop.cbyPitch; - i_width = dest_rect.right - dest_rect.left; - i_height = dest_rect.bottom - dest_rect.top; - - p_pic->p->p_pixels = p_dest; - } - - return VLC_SUCCESS; -} - -static int GAPIUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) -{ - GXEndDraw(); - return VLC_SUCCESS; -} - -static void DisplayGAPI( vout_thread_t *p_vout, picture_t *p_pic ) -{ -} -#endif - -#undef rect_src -#undef rect_src_clipped -#undef rect_dest -#undef rect_dest_clipped -/***************************************************************************** - * 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" ); -} - -/***************************************************************************** - * EventThread: Event handling thread - *****************************************************************************/ -static void EventThread ( vlc_object_t *p_event ) -{ - vout_thread_t *p_vout; - vlc_value_t val; - - int i_style; - WNDCLASS wc; - MSG msg; - - /* Initialisations */ - var_Get( p_event, "p_vout", &val ); - p_vout = (vout_thread_t *)val.p_address; - - /* Register window class */ - memset( &wc, 0, sizeof(wc) ); - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = (WNDPROC)WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = GetModuleHandle(NULL); - wc.hIcon = 0; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); - wc.lpszMenuName = 0; - wc.lpszClassName = _T("VLC WinGDI"); - RegisterClass( &wc ); - - /* Register the video sub-window class */ - wc.lpszClassName = _T("VLC WinGDI video"); wc.hIcon = 0; - RegisterClass(&wc); - - /* Create output window */ - p_vout->p_sys->hparent = (HWND) - vout_RequestWindow( p_vout, &p_vout->p_sys->i_window_x, - &p_vout->p_sys->i_window_y, - (unsigned int *)&p_vout->p_sys->i_window_width, - (unsigned int *)&p_vout->p_sys->i_window_height ); - - if( p_vout->p_sys->hparent ) - ShowWindow( p_vout->p_sys->hparent, SW_SHOW ); - - if( p_vout->p_sys->hparent ) - i_style = WS_VISIBLE|WS_CLIPCHILDREN|WS_CHILD; - else - i_style = WS_OVERLAPPEDWINDOW|WS_SIZEBOX|WS_VISIBLE|WS_CLIPCHILDREN; - - p_vout->p_sys->i_window_style = i_style; - - p_vout->p_sys->hwnd = - CreateWindow( _T("VLC WinGDI"), _T(VOUT_TITLE), i_style, - (p_vout->p_sys->i_window_x < 0) ? CW_USEDEFAULT : - p_vout->p_sys->i_window_x, /* default X coordinate */ - (p_vout->p_sys->i_window_y < 0) ? CW_USEDEFAULT : - p_vout->p_sys->i_window_y, /* default Y coordinate */ - p_vout->p_sys->i_window_width, - p_vout->p_sys->i_window_height + 10, - p_vout->p_sys->hparent, NULL, - GetModuleHandle(NULL), (LPVOID)p_vout ); - - if( !p_vout->p_sys->hwnd ) - { - msg_Warn( p_vout, "couldn't create window" ); - return; - } - msg_Warn( p_vout, "Created WinGDI window" ); - - if( p_vout->p_sys->hparent ) - { - LONG i_style; - - /* We don't want the window owner to overwrite our client area */ - i_style = GetWindowLong( p_vout->p_sys->hparent, GWL_STYLE ); - - if( !(i_style & WS_CLIPCHILDREN) ) - /* Hmmm, apparently this is a blocking call... */ - SetWindowLong( p_vout->p_sys->hparent, GWL_STYLE, - i_style | WS_CLIPCHILDREN ); - - /* Create our fullscreen window */ - p_vout->p_sys->hfswnd = - CreateWindowEx( WS_EX_APPWINDOW, _T("VLC WinGDI"), - _T(VOUT_TITLE), - WS_NONAVDONEBUTTON|WS_CLIPCHILDREN, - CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, - NULL, NULL, GetModuleHandle(NULL), (LPVOID)p_vout); - } - - /* Display our window */ - ShowWindow( p_vout->p_sys->hwnd, SW_SHOW ); - UpdateWindow( p_vout->p_sys->hwnd ); - - /* Create video sub-window */ - p_vout->p_sys->hvideownd = - CreateWindow( _T("VLC WinGDI video"), _T(""), /* window class */ - WS_CHILD | WS_VISIBLE, /* window style */ - CW_USEDEFAULT, CW_USEDEFAULT, /* default coordinates */ - CW_USEDEFAULT, CW_USEDEFAULT, - p_vout->p_sys->hwnd, /* parent window */ - NULL, GetModuleHandle(NULL), - (LPVOID)p_vout ); /* send p_vout to WM_CREATE */ - - /* Initialize offscreen buffer */ - InitBuffers( p_vout ); - - p_vout->pf_control = Control; - - /* Tell the video output we're ready to receive data */ - vlc_thread_ready( p_event ); - - while( !p_event->b_die && GetMessage( &msg, 0, 0, 0 ) ) - { - /* Check if we are asked to exit */ - if( p_event->b_die ) break; - - switch( msg.message ) - { - case WM_KEYDOWN: - switch( msg.wParam ) - { - case VK_ESCAPE: - p_event->p_libvlc->b_die = VLC_TRUE; - break; - } - TranslateMessage( &msg ); - break; - - case WM_CHAR: - switch( msg.wParam ) - { - case 'q': - case 'Q': - p_event->p_libvlc->b_die = VLC_TRUE; - break; - } - break; - - default: - TranslateMessage( &msg ); - DispatchMessage( &msg ); - break; - } - } - - msg_Dbg( p_vout, "CloseWindow" ); - -#ifdef MODULE_NAME_IS_wingapi - GXCloseDisplay(); -#else - DeleteDC( p_vout->p_sys->off_dc ); - DeleteObject( p_vout->p_sys->off_bitmap ); -#endif - - DestroyWindow( p_vout->p_sys->hwnd ); - if( p_vout->p_sys->hfswnd ) DestroyWindow( p_vout->p_sys->hfswnd ); - - if( p_vout->p_sys->hparent ) - vout_ReleaseWindow( p_vout, (void *)p_vout->p_sys->hparent ); -} - -/***************************************************************************** - * UpdateRects: update clipping rectangles - ***************************************************************************** - * This function is called when the window position or size are changed, and - * its job is to update the source and destination RECTs used to display the - * picture. - *****************************************************************************/ -static void UpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force ) -{ -#define rect_src p_vout->p_sys->rect_src -#define rect_src_clipped p_vout->p_sys->rect_src_clipped -#define rect_dest p_vout->p_sys->rect_dest -#define rect_dest_clipped p_vout->p_sys->rect_dest_clipped - - int i_width, i_height, i_x, i_y; - - RECT rect; - POINT point; - - /* Retrieve the window size */ - GetClientRect( p_vout->p_sys->hwnd, &rect ); - - /* Retrieve the window position */ - point.x = point.y = 0; - ClientToScreen( p_vout->p_sys->hwnd, &point ); - - /* If nothing changed, we can return */ - if( !b_force - && p_vout->p_sys->i_window_width == rect.right - && p_vout->p_sys->i_window_height == rect.bottom - && p_vout->p_sys->i_window_x == point.x - && p_vout->p_sys->i_window_y == point.y ) - { - return; - } - - /* Update the window position and size */ - p_vout->p_sys->i_window_x = point.x; - p_vout->p_sys->i_window_y = point.y; - p_vout->p_sys->i_window_width = rect.right; - p_vout->p_sys->i_window_height = rect.bottom; - - vout_PlacePicture( p_vout, rect.right, rect.bottom, - &i_x, &i_y, &i_width, &i_height ); - - if( p_vout->p_sys->hvideownd ) - SetWindowPos( p_vout->p_sys->hvideownd, HWND_TOP, - i_x, i_y, i_width, i_height, 0 ); - - /* Destination image position and dimensions */ - rect_dest.left = point.x + i_x; - rect_dest.right = rect_dest.left + i_width; - rect_dest.top = point.y + i_y; - rect_dest.bottom = rect_dest.top + i_height; - - /* Clip the destination window */ - if( !IntersectRect( &rect_dest_clipped, &rect_dest, - &p_vout->p_sys->rect_display ) ) - { - SetRectEmpty( &rect_src_clipped ); - return; - } - -#if 0 - msg_Dbg( p_vout, "image_dst_clipped coords: %i,%i,%i,%i", - rect_dest_clipped.left, rect_dest_clipped.top, - rect_dest_clipped.right, rect_dest_clipped.bottom ); -#endif - - /* the 2 following lines are to fix a bug when clicking on the desktop */ - if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 || - (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 ) - { - SetRectEmpty( &rect_src_clipped ); - return; - } - - /* src image dimensions */ - rect_src.left = 0; - rect_src.top = 0; - rect_src.right = p_vout->output.i_width; - rect_src.bottom = p_vout->output.i_height; - - /* Clip the source image */ - rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) * - p_vout->output.i_width / (rect_dest.right - rect_dest.left); - rect_src_clipped.right = p_vout->output.i_width - - (rect_dest.right - rect_dest_clipped.right) * p_vout->output.i_width / - (rect_dest.right - rect_dest.left); - rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) * - p_vout->output.i_height / (rect_dest.bottom - rect_dest.top); - rect_src_clipped.bottom = p_vout->output.i_height - - (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->output.i_height / - (rect_dest.bottom - rect_dest.top); - -#if 0 - msg_Dbg( p_vout, "image_src_clipped coords: %i,%i,%i,%i", - rect_src_clipped.left, rect_src_clipped.top, - rect_src_clipped.right, rect_src_clipped.bottom ); -#endif - - /* The destination coordinates need to be relative to the current - * directdraw primary surface (display) */ - rect_dest_clipped.left -= p_vout->p_sys->rect_display.left; - rect_dest_clipped.right -= p_vout->p_sys->rect_display.left; - rect_dest_clipped.top -= p_vout->p_sys->rect_display.top; - rect_dest_clipped.bottom -= p_vout->p_sys->rect_display.top; - - /* Signal the change in size/position */ - p_vout->p_sys->i_changes |= DX_POSITION_CHANGE; - -#undef rect_src -#undef rect_src_clipped -#undef rect_dest -#undef rect_dest_clipped -} - -/***************************************************************************** - * Message handler for the main window - *****************************************************************************/ -static long FAR PASCAL WndProc( HWND hWnd, UINT message, - WPARAM wParam, LPARAM lParam ) -{ - vout_thread_t *p_vout; - - if( message == WM_CREATE ) - { - /* Store p_vout for future use */ - p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams; - SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)p_vout ); - if( p_vout ) msg_Dbg( p_vout, "create: %p", hWnd ); - } - else - { - p_vout = (vout_thread_t *)GetWindowLongPtr( hWnd, GWLP_USERDATA ); - } - -#ifndef UNDER_CE - /* Catch the screensaver and the monitor turn-off */ - if( message == WM_SYSCOMMAND && - ( (wParam & 0xFFF0) == SC_SCREENSAVE || (wParam & 0xFFF0) == SC_MONITORPOWER ) ) - { - //if( p_vout ) msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND screensaver" ); - return 0; /* this stops them from happening */ - } -#endif - - if( !p_vout ) - { - /* Hmmm mozilla does manage somehow to save the pointer to our - * windowproc and still calls it after the vout has been closed. */ - return DefWindowProc(hWnd, message, wParam, lParam); - } - - if( hWnd != p_vout->p_sys->hwnd && - hWnd != p_vout->p_sys->hfswnd && - hWnd != p_vout->p_sys->hvideownd ) - return DefWindowProc(hWnd, message, wParam, lParam); - - switch( message ) - { - case WM_WINDOWPOSCHANGED: - if( hWnd == p_vout->p_sys->hwnd ) - UpdateRects( p_vout, VLC_TRUE ); - break; - -#if 0 - case WM_ACTIVATE: - msg_Err( p_vout, "WM_ACTIVATE: %i", wParam ); - if( wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE ) - GXResume(); - else if( wParam == WA_INACTIVE ) - GXSuspend(); - break; -#endif - - case WM_KILLFOCUS: - p_vout->p_sys->b_focus = VLC_FALSE; - if( !p_vout->p_sys->b_parent_focus ) GXSuspend(); - - if( hWnd == p_vout->p_sys->hfswnd ) - { -#ifdef UNDER_CE - HWND htbar = FindWindow( _T("HHTaskbar"), NULL ); - ShowWindow( htbar, SW_SHOW ); -#endif - } - - if( !p_vout->p_sys->hparent || - hWnd == p_vout->p_sys->hfswnd ) - { - SHFullScreen( hWnd, SHFS_SHOWSIPBUTTON ); - } - break; - - case WM_SETFOCUS: - p_vout->p_sys->b_focus = VLC_TRUE; - GXResume(); - - if( p_vout->p_sys->hparent && - hWnd != p_vout->p_sys->hfswnd && p_vout->b_fullscreen ) - p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE; - - if( hWnd == p_vout->p_sys->hfswnd ) - { -#ifdef UNDER_CE - HWND htbar = FindWindow( _T("HHTaskbar"), NULL ); - ShowWindow( htbar, SW_HIDE ); -#endif - } - - if( !p_vout->p_sys->hparent || - hWnd == p_vout->p_sys->hfswnd ) - { - SHFullScreen( hWnd, SHFS_HIDESIPBUTTON ); - } - break; - - case WM_LBUTTONDOWN: - p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE; - break; - case WM_MOUSEMOVE: - break; - case WM_LBUTTONUP: - break; - - case WM_INITMENUPOPUP: - p_vout->p_sys->b_video_display = VLC_FALSE; - break; - - case WM_NOTIFY: - // Redo the video display because menu can be closed - // FIXME verify if p_child_window exits - if( (((NMHDR *)lParam)->code) == NM_CUSTOMDRAW ) - p_vout->p_sys->b_video_display = VLC_TRUE; - break; - - /* the user wants to close the window */ - case WM_CLOSE: - { - playlist_t * p_playlist = - (playlist_t *)vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( p_playlist == NULL ) return 0; - - playlist_Stop( p_playlist ); - vlc_object_release( p_playlist ); - return 0; - } - - case WM_DESTROY: - msg_Dbg( p_vout, "WinProc WM_DESTROY" ); - PostQuitMessage( 0 ); - break; - - default: - return DefWindowProc( hWnd, message, wParam, lParam ); - } - - return 0; -} - -/***************************************************************************** - * InitBuffers: initialize an offscreen bitmap for direct buffer operations. - *****************************************************************************/ -static void InitBuffers( vout_thread_t *p_vout ) -{ - BITMAPINFOHEADER *p_header = &p_vout->p_sys->bitmapinfo.bmiHeader; - BITMAPINFO *p_info = &p_vout->p_sys->bitmapinfo; - int i_pixels = p_vout->render.i_height * p_vout->render.i_width; - HDC window_dc = GetDC( p_vout->p_sys->hvideownd ); - - /* Get screen properties */ -#ifdef MODULE_NAME_IS_wingapi - GXDisplayProperties gx_displayprop = GXGetDisplayProperties(); - p_vout->p_sys->i_depth = gx_displayprop.cBPP; -#else - p_vout->p_sys->i_depth = GetDeviceCaps( window_dc, PLANES ) * - GetDeviceCaps( window_dc, BITSPIXEL ); -#endif - msg_Dbg( p_vout, "GDI depth is %i", p_vout->p_sys->i_depth ); - -#ifdef MODULE_NAME_IS_wingapi - GXOpenDisplay( p_vout->p_sys->hvideownd, GX_FULLSCREEN ); - -#else - - /* Initialize offscreen bitmap */ - memset( p_info, 0, sizeof( BITMAPINFO ) + 3 * sizeof( RGBQUAD ) ); - - p_header->biSize = sizeof( BITMAPINFOHEADER ); - p_header->biSizeImage = 0; - p_header->biPlanes = 1; - switch( p_vout->p_sys->i_depth ) - { - case 8: - p_header->biBitCount = 8; - p_header->biCompression = BI_RGB; - /* FIXME: we need a palette here */ - break; - case 15: - p_header->biBitCount = 15; - p_header->biCompression = BI_BITFIELDS;//BI_RGB; - ((DWORD*)p_info->bmiColors)[0] = 0x00007c00; - ((DWORD*)p_info->bmiColors)[1] = 0x000003e0; - ((DWORD*)p_info->bmiColors)[2] = 0x0000001f; - break; - case 16: - p_header->biBitCount = 16; - p_header->biCompression = BI_BITFIELDS;//BI_RGB; - ((DWORD*)p_info->bmiColors)[0] = 0x0000f800; - ((DWORD*)p_info->bmiColors)[1] = 0x000007e0; - ((DWORD*)p_info->bmiColors)[2] = 0x0000001f; - break; - case 24: - p_header->biBitCount = 24; - p_header->biCompression = BI_RGB; - ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000; - ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00; - ((DWORD*)p_info->bmiColors)[2] = 0x000000ff; - break; - case 32: - p_header->biBitCount = 32; - p_header->biCompression = BI_RGB; - ((DWORD*)p_info->bmiColors)[0] = 0x00ff0000; - ((DWORD*)p_info->bmiColors)[1] = 0x0000ff00; - ((DWORD*)p_info->bmiColors)[2] = 0x000000ff; - break; - default: - msg_Err( p_vout, "screen depth %i not supported", - p_vout->p_sys->i_depth ); - return; - break; - } - p_header->biWidth = p_vout->render.i_width; - p_header->biHeight = -p_vout->render.i_height; - p_header->biClrImportant = 0; - p_header->biClrUsed = 0; - p_header->biXPelsPerMeter = 0; - p_header->biYPelsPerMeter = 0; - - p_vout->p_sys->i_pic_pixel_pitch = p_header->biBitCount / 8; - p_vout->p_sys->i_pic_pitch = p_header->biBitCount * p_header->biWidth / 8; - - p_vout->p_sys->off_bitmap = - CreateDIBSection( window_dc, (BITMAPINFO *)p_header, DIB_RGB_COLORS, - (void**)&p_vout->p_sys->p_pic_buffer, NULL, 0 ); - - p_vout->p_sys->off_dc = CreateCompatibleDC( window_dc ); - - SelectObject( p_vout->p_sys->off_dc, p_vout->p_sys->off_bitmap ); - ReleaseDC( 0, window_dc ); -#endif -} - -/***************************************************************************** - * Control: control facility for the vout - *****************************************************************************/ -static int Control( vout_thread_t *p_vout, int i_query, va_list args ) -{ - unsigned int *pi_width, *pi_height; - vlc_bool_t b_bool; - RECT rect_window; - POINT point; - - switch( i_query ) - { - case VOUT_GET_SIZE: - if( p_vout->p_sys->hparent ) - return vout_ControlWindow( p_vout, - (void *)p_vout->p_sys->hparent, i_query, args ); - - pi_width = va_arg( args, unsigned int * ); - pi_height = va_arg( args, unsigned int * ); - - GetClientRect( p_vout->p_sys->hwnd, &rect_window ); - - *pi_width = rect_window.right - rect_window.left; - *pi_height = rect_window.bottom - rect_window.top; - return VLC_SUCCESS; - - case VOUT_SET_SIZE: - if( p_vout->p_sys->hparent ) - return vout_ControlWindow( p_vout, - (void *)p_vout->p_sys->hparent, i_query, args ); - - /* Update dimensions */ - rect_window.top = rect_window.left = 0; - rect_window.right = va_arg( args, unsigned int ); - rect_window.bottom = va_arg( args, unsigned int ); - if( !rect_window.right ) rect_window.right = p_vout->i_window_width; - if( !rect_window.bottom ) rect_window.bottom = p_vout->i_window_height; - AdjustWindowRect( &rect_window, p_vout->p_sys->i_window_style, 0 ); - - SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, - rect_window.right - rect_window.left, - rect_window.bottom - rect_window.top, SWP_NOMOVE ); - - return VLC_SUCCESS; - - case VOUT_CLOSE: - ShowWindow( p_vout->p_sys->hwnd, SW_HIDE ); - case VOUT_REPARENT: - /* Change window style, borders and title bar */ - //vlc_mutex_lock( &p_vout->p_sys->lock ); - p_vout->p_sys->hparent = 0; - //vlc_mutex_unlock( &p_vout->p_sys->lock ); - - /* Retrieve the window position */ - point.x = point.y = 0; - ClientToScreen( p_vout->p_sys->hwnd, &point ); - - SetParent( p_vout->p_sys->hwnd, 0 ); - p_vout->p_sys->i_window_style = - WS_CLIPCHILDREN | WS_OVERLAPPEDWINDOW | WS_SIZEBOX; - SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, - p_vout->p_sys->i_window_style | - (i_query == VOUT_CLOSE ? 0 : WS_VISIBLE) ); - SetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW ); - SetWindowPos( p_vout->p_sys->hwnd, 0, point.x, point.y, 0, 0, - SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED ); - - return vout_vaControlDefault( p_vout, i_query, args ); - - case VOUT_SET_FOCUS: - b_bool = va_arg( args, vlc_bool_t ); - - p_vout->p_sys->b_parent_focus = b_bool; - if( b_bool ) GXResume(); - else if( !p_vout->p_sys->b_focus ) GXSuspend(); - - return VLC_SUCCESS; - - default: - return vout_vaControlDefault( p_vout, i_query, args ); - } -}