+/*****************************************************************************
+ * vout_directx.c: Windows DirectX video output display method
+ *****************************************************************************
+ * Copyright (C) 1998, 1999, 2000 VideoLAN
+ * $Id: vout_directx.c,v 1.1 2001/06/02 01:09:03 sam Exp $
+ *
+ * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+#define MODULE_NAME directx
+#include "modules_inner.h"
+
+/* This is a list of what needs to be fixed:
+ *
+ * For now, this plugin only works when YUV overlay is supported (which it
+ * should be nowadays on most of the video cards under Windows)...
+ *
+ * The overlay doesn't use double-buffering.
+ *
+ * Use Shane Harper's optimizations for YUV
+ */
+
+/*****************************************************************************
+ * Preamble
+ *
+ *****************************************************************************/
+#include "defs.h"
+
+#include <errno.h> /* ENOMEM */
+#include <stdlib.h> /* free() */
+#include <string.h> /* strerror() */
+
+#include <windows.h>
+#include <directx.h>
+
+#include "config.h"
+#include "common.h"
+#include "threads.h"
+#include "mtime.h"
+#include "tests.h"
+#include "netutils.h"
+
+#include "video.h"
+#include "video_output.h"
+
+#include "intf_msg.h"
+#include "interface.h"
+#include "main.h"
+
+#include "modules.h"
+#include "modules_export.h"
+
+#define OVERLAY_COLOR_KEY 1 /* color on top of which the overlay will be
+ * displayed. 1 should be almost black but
+ * not black (which is too common a color) */
+
+/*****************************************************************************
+ * 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.
+ *****************************************************************************/
+typedef struct vout_sys_s
+{
+
+ LPDIRECTDRAW p_ddobject; /* DirectDraw object */
+ LPDIRECTDRAWSURFACE p_display; /* display device */
+ LPDIRECTDRAWSURFACE p_overlay; /* overlay device */
+ LPDIRECTDRAWCLIPPER p_clipper; /* clipper */
+ HWND hwnd; /* Handle of the main */
+ /* window */
+
+ int i_image_width; /* size of the decoded image */
+ int i_image_height;
+ int i_window_width; /* size of the displayed image */
+ int i_window_height;
+
+ boolean_t b_display_enabled;
+ boolean_t b_overlay;
+ boolean_t b_cursor;
+
+ boolean_t b_cursor_autohidden;
+ mtime_t i_lastmoved;
+
+ char *p_windx_buf[2]; /* Buffer information */
+
+} vout_sys_t;
+
+/*****************************************************************************
+ * Local prototypes.
+ *****************************************************************************/
+static int vout_Probe ( probedata_t *p_data );
+static int vout_Create ( struct vout_thread_s * );
+static int vout_Init ( struct vout_thread_s * );
+static void vout_End ( struct vout_thread_s * );
+static void vout_Destroy ( struct vout_thread_s * );
+static int vout_Manage ( struct vout_thread_s * );
+static void vout_Display ( struct vout_thread_s * );
+static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
+ u16 *blue, u16 *transp );
+
+static int WinDXCreateWindow ( vout_thread_t *p_vout );
+static int WinDXInitDDraw ( vout_thread_t *p_vout );
+static int WinDXCreateDisplay ( vout_thread_t *p_vout );
+static int WinDXCreateYUVOverlay ( vout_thread_t *p_vout );
+static int WinDXUpdateOverlay ( vout_thread_t *p_vout );
+static int WinDXClipOverlay ( vout_thread_t *p_vout );
+static void WinDXCloseDDraw ( vout_thread_t *p_vout );
+static void WinDXCloseWindow ( vout_thread_t *p_vout );
+static void WinDXCloseDisplay ( vout_thread_t *p_vout );
+static void WinDXCloseYUVOverlay ( vout_thread_t *p_vout );
+
+/*****************************************************************************
+ * Functions exported as capabilities. They are declared as static so that
+ * we don't pollute the namespace too much.
+ *****************************************************************************/
+void _M( vout_getfunctions )( function_list_t * p_function_list )
+{
+ p_function_list->pf_probe = vout_Probe;
+ p_function_list->functions.vout.pf_create = vout_Create;
+ p_function_list->functions.vout.pf_init = vout_Init;
+ p_function_list->functions.vout.pf_end = vout_End;
+ p_function_list->functions.vout.pf_destroy = vout_Destroy;
+ p_function_list->functions.vout.pf_manage = vout_Manage;
+ p_function_list->functions.vout.pf_display = vout_Display;
+ p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
+}
+
+/*****************************************************************************
+ * vout_Probe: probe the video driver and return a score
+ *****************************************************************************
+ * This function tries to initialize Windows DirectX and returns a score to
+ * the plugin manager so that it can select the best plugin.
+ *****************************************************************************/
+static int vout_Probe( probedata_t *p_data )
+{
+ if( TestMethod( VOUT_METHOD_VAR, "directx" ) )
+ {
+ return( 999 );
+ }
+
+ return( 400 );
+}
+
+/*****************************************************************************
+ * vout_Create: allocate DirectX video thread output method
+ *****************************************************************************
+ * This function allocates and initialize the DirectX vout method.
+ *****************************************************************************/
+static int vout_Create( vout_thread_t *p_vout )
+{
+ /* Allocate structure */
+ p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
+ if( p_vout->p_sys == NULL )
+ {
+ intf_ErrMsg( "vout error: can't create p_sys (%s)", strerror(ENOMEM) );
+ return( 1 );
+ }
+
+ p_vout->p_sys->b_cursor = 1; /* TODO should be done with a main_GetInt.. */
+
+ p_vout->p_sys->b_cursor_autohidden = 0;
+ p_vout->p_sys->b_display_enabled = 0;
+
+ p_vout->p_sys->i_lastmoved = mdate();
+
+ p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
+ VOUT_FULLSCREEN_DEFAULT );
+ p_vout->p_sys->b_overlay = main_GetIntVariable( VOUT_OVERLAY_VAR,
+ VOUT_OVERLAY_DEFAULT );
+ p_vout->p_sys->i_window_width = main_GetIntVariable( VOUT_WIDTH_VAR,
+ VOUT_WIDTH_DEFAULT );
+ p_vout->p_sys->i_window_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
+ VOUT_HEIGHT_DEFAULT );
+ /* We don't know yet the dimensions of the video so the best guess is to
+ * pick the same as the window */
+ p_vout->p_sys->i_image_width = p_vout->p_sys->i_window_width;
+ p_vout->p_sys->i_image_height = p_vout->p_sys->i_window_height;
+
+ /* Create a window for the video */
+ /* Creating a window under Windows also initializes the thread's event
+ * message qeue */
+ if( WinDXCreateWindow( p_vout ) )
+ {
+ intf_ErrMsg( "vout error: can't create window" );
+ free( p_vout->p_sys );
+ return ( 1 );
+ }
+
+ /* Initialise DirectDraw */
+ if( WinDXInitDDraw( p_vout ) )
+ {
+ intf_ErrMsg( "vout error: can't initialise DirectDraw" );
+ WinDXCloseWindow( p_vout );
+ free( p_vout->p_sys );
+ return ( 1 );
+ }
+
+ /* create the directx display */
+ if( WinDXCreateDisplay( p_vout ) )
+ {
+ intf_ErrMsg( "vout error: can't initialise DirectDraw" );
+ WinDXCloseDDraw( p_vout );
+ WinDXCloseWindow( p_vout );
+ free( p_vout->p_sys );
+ return ( 1 );
+ }
+
+ return( 0 );
+}
+
+/*****************************************************************************
+ * vout_Init: initialize DirectX video thread output method
+ *****************************************************************************
+ *
+ *****************************************************************************/
+static int vout_Init( vout_thread_t *p_vout )
+{
+ return( 0 );
+}
+
+/*****************************************************************************
+ * vout_End: terminate Sys video thread output method
+ *****************************************************************************
+ * Terminate an output method created by vout_Create.
+ * It is called at the end of the thread.
+ *****************************************************************************/
+static void vout_End( vout_thread_t *p_vout )
+{
+ return;
+}
+
+/*****************************************************************************
+ * vout_Destroy: destroy Sys video thread output method
+ *****************************************************************************
+ * Terminate an output method created by vout_Create
+ *****************************************************************************/
+static void vout_Destroy( vout_thread_t *p_vout )
+{
+ WinDXCloseDisplay( p_vout );
+ WinDXCloseDDraw( p_vout );
+ WinDXCloseWindow( p_vout );
+
+ if( p_vout->p_sys != NULL )
+ {
+ free( p_vout->p_sys );
+ p_vout->p_sys = NULL;
+ }
+
+}
+
+/*****************************************************************************
+ * vout_Manage: handle Sys events
+ *****************************************************************************
+ * This function should be called regularly by video output thread. It returns
+ * a non null value if an error occured.
+ *****************************************************************************/
+static int vout_Manage( vout_thread_t *p_vout )
+{
+ MSG msg;
+ WINDOWPLACEMENT window_placement;
+
+ while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
+ {
+ if( GetMessage(&msg, NULL, 0, 0) >= 0 )
+ {
+ switch( msg.message )
+ {
+ case WM_QUIT:
+ intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_QUIT" );
+ p_main->p_intf->b_die = 1;
+ break;
+
+ case WM_MOVE:
+ intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_MOVE" );
+ if( !p_vout->b_need_render )
+ {
+ WinDXUpdateOverlay( p_vout );
+ }
+ /* don't create a never ending loop */
+ return( 0 );
+ break;
+
+ case WM_PAINT:
+ intf_WarnMsg( 3, "vout: WinDX vout_Manage WM_PAINT" );
+ if( !p_vout->b_need_render )
+ {
+ WinDXClipOverlay( p_vout );
+ }
+ /* don't create a never ending loop */
+ return( 0 );
+ break;
+
+ case WM_CHAR:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_CHAR" );
+ switch( msg.wParam )
+ {
+ case 'q':
+ case 'Q':
+ p_main->p_intf->b_die = 1;
+ break;
+
+ case 'f':
+ case 'F':
+ p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
+ break;
+
+ case '0': network_ChannelJoin( 0 ); break;
+ case '1': network_ChannelJoin( 1 ); break;
+ case '2': network_ChannelJoin( 2 ); break;
+ case '3': network_ChannelJoin( 3 ); break;
+ case '4': network_ChannelJoin( 4 ); break;
+ case '5': network_ChannelJoin( 5 ); break;
+ case '6': network_ChannelJoin( 6 ); break;
+ case '7': network_ChannelJoin( 7 ); break;
+ case '8': network_ChannelJoin( 8 ); break;
+ case '9': network_ChannelJoin( 9 ); break;
+
+ default:
+ if( intf_ProcessKey( p_main->p_intf,
+ (char )msg.wParam ) )
+ {
+ intf_DbgMsg( "unhandled key '%c' (%i)",
+ (char)msg.wParam, msg.wParam );
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ {
+ return( 1 );
+ }
+
+ }
+
+
+ /*
+ * Fullscreen change
+ */
+ if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
+ {
+ p_vout->b_fullscreen = ! p_vout->b_fullscreen;
+
+ /* We need to switch between Maximized and Normal sized window */
+ window_placement.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
+ if( p_vout->b_fullscreen )
+ {
+ /* Maximized window */
+ window_placement.showCmd = SW_SHOWMAXIMIZED;
+ /* Change window style, no borders and no title bar */
+ SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE, 0 );
+
+ }
+ else
+ {
+ /* Normal window */
+ window_placement.showCmd = SW_SHOWNORMAL;
+ /* Change window style, borders and title bar */
+ SetWindowLong( p_vout->p_sys->hwnd, GWL_STYLE,
+ WS_OVERLAPPEDWINDOW | WS_SIZEBOX | WS_VISIBLE );
+ }
+
+ SetWindowPlacement( p_vout->p_sys->hwnd, &window_placement );
+ /*WinDXUpdateOverlay( p_vout );*/
+
+ p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
+ }
+
+ return( 0 );
+}
+
+/*****************************************************************************
+ * vout_SetPalette: sets an 8 bpp palette
+ *****************************************************************************
+ * This function sets the palette given as an argument. It does not return
+ * anything, but could later send information on which colors it was unable
+ * to set.
+ *****************************************************************************/
+static void vout_SetPalette( p_vout_thread_t p_vout, u16 *red, u16 *green,
+ u16 *blue, u16 *transp)
+{
+ /* Nothing yet */
+ return;
+}
+
+/*****************************************************************************
+ * vout_Display: displays previously rendered output
+ *****************************************************************************
+ * This function send the currently rendered image to the display, wait until
+ * it is displayed and switch the two rendering buffer, preparing next frame.
+ *****************************************************************************/
+static void vout_Display( vout_thread_t *p_vout )
+{
+ DDSURFACEDESC ddsd;
+ HRESULT dxresult;
+ int i;
+ int i_image_width = p_vout->p_rendered_pic->i_width;
+ int i_image_height = p_vout->p_rendered_pic->i_height;
+
+
+ if( (p_vout->p_sys->p_display == NULL) )
+ {
+ intf_WarnMsg( 3, "vout error: WinDX no display!!" );
+ return;
+ }
+
+ /* The first time this function is called it enables the display */
+ p_vout->p_sys->b_display_enabled = 1;
+
+ if( p_vout->b_need_render )
+ {
+ /* Nothing yet */
+ }
+ else
+ {
+ /*
+ * p_vout->p_rendered_pic->p_y/u/v contains the YUV buffers to
+ * render
+ */
+ /* TODO: support for streams other than 4:2:0 */
+
+ /* if the size of the decoded pictures has changed then we close the
+ * YUVOverlay (which doesn't have the right size anymore). */
+ if( p_vout->p_sys->i_image_width != i_image_width
+ || p_vout->p_sys->i_image_height != i_image_height )
+ {
+ intf_WarnMsg( 3, "vout: WinDX overlay size changed" );
+ p_vout->p_sys->i_image_width = i_image_width;
+ p_vout->p_sys->i_image_height = i_image_height;
+ WinDXCloseYUVOverlay( p_vout );
+ }
+
+ if( p_vout->p_sys->p_overlay == NULL )
+ {
+ intf_WarnMsg( 3, "vout: WinDX no overlay, open one..." );
+ if( WinDXCreateYUVOverlay( p_vout ) )
+ {
+ intf_WarnMsg( 3, "vout: WinDX cannot open a new overlay !!" );
+ return;
+ }
+ /* Display the Overlay */
+ p_vout->p_sys->b_display_enabled = 1;
+ WinDXUpdateOverlay( p_vout );
+ }
+
+ /* Lock the overlay surface */
+ memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
+ ddsd.dwSize = sizeof(DDSURFACEDESC);
+ dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_overlay, NULL,
+ &ddsd, DDLOCK_NOSYSLOCK, NULL);
+ if ( dxresult == DDERR_SURFACELOST )
+ {
+ /* Your surface can be lost (thanks to windows) so be sure
+ * to check this and restore it if needed */
+ dxresult = IDirectDrawSurface_Restore( p_vout->p_sys->p_overlay );
+ dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_overlay,
+ NULL, &ddsd, DDLOCK_NOSYSLOCK
+ | DDLOCK_WAIT, NULL);
+ }
+ if( dxresult != DD_OK )
+ {
+ intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
+ return;
+ }
+
+ /* Now we can do the actual image copy.
+ * The copy has to be done line by line because of the special case
+ * when the Pitch does not equal the width of the picture */
+ for( i=0; i < ddsd.dwHeight/2; i++)
+ {
+ /* copy Y, we copy two lines at once */
+ memcpy(ddsd.lpSurface + i*2*ddsd.u1.lPitch,
+ p_vout->p_rendered_pic->p_y + i*2*i_image_width,
+ i_image_width);
+ memcpy(ddsd.lpSurface + (i*2+1)*ddsd.u1.lPitch,
+ p_vout->p_rendered_pic->p_y + (i*2+1)*i_image_width,
+ i_image_width);
+ /* then V */
+ memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
+ + i * ddsd.u1.lPitch/2,
+ p_vout->p_rendered_pic->p_v + i*i_image_width/2,
+ i_image_width/2);
+ /* and U */
+ memcpy((ddsd.lpSurface + ddsd.dwHeight * ddsd.u1.lPitch)
+ + (ddsd.dwHeight * ddsd.u1.lPitch/4)
+ + i * ddsd.u1.lPitch/2,
+ p_vout->p_rendered_pic->p_u + i*i_image_width/2,
+ i_image_width/2);
+ }
+
+ /* Unlock the Surface */
+ dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
+ ddsd.lpSurface );
+
+ }
+
+}
+
+
+/* following functions are local */
+
+
+/*****************************************************************************
+ * WinDXEventProc: 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 function).
+ * Nonqueued Messages are those that Windows will send directly to this
+ * function (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
+ *****************************************************************************/
+long FAR PASCAL WinDXEventProc( HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam )
+{
+ switch( message )
+ {
+
+ case WM_ACTIVATEAPP:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_ACTIVEAPP" );
+
+ break;
+
+ case WM_SETCURSOR:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_SETCURSOR" );
+ /* turn the cursor off by setting it's value to NULL */
+ SetCursor(NULL);
+ break;
+
+ case WM_CREATE:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_CREATE" );
+ break;
+
+ /* test your key states in this case */
+ case WM_KEYDOWN:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_KEYDOWN" );
+ switch( wParam )
+ {
+ case VK_ESCAPE:
+ case VK_F12:
+ PostMessage(hwnd,WM_CLOSE,0,0);
+ break;
+
+ }
+ break;
+
+ /* this case is touched when the application is shutting down */
+ case WM_DESTROY:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_DESTROY" );
+ PostQuitMessage( 0 );
+ break;
+
+ case WM_QUIT:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_QUIT" );
+ break;
+
+ case WM_SYSCOMMAND:
+ switch (wParam)
+ {
+ case SC_SCREENSAVE: /* catch the screensaver */
+ case SC_MONITORPOWER: /* catch the monitor turn-off */
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_SYSCOMMAND" );
+ return 0; /* this stops them from happening */
+ }
+ break;
+
+ case WM_MOVE:
+ case WM_SIZE:
+ case WM_WINDOWPOSCHANGED:
+ intf_WarnMsg( 3, "vout: WinDX WinProc WM_MOVE, WMSIZE" );
+ PostMessage(hwnd,WM_MOVE,0,0);
+
+ break;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+/*****************************************************************************
+ * WinDXCreateWindow: create a windows window where the video will play.
+ *****************************************************************************
+ * 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 WinDXCreateWindow( vout_thread_t *p_vout )
+{
+ HINSTANCE hInstance;
+ WNDCLASS wc; /* window class components */
+ RECT rect_window;
+
+ intf_WarnMsg( 3, "vout: WinDX WinDXCreateWindow" );
+
+ /* get this module's instance */
+ hInstance = GetModuleHandle(NULL);
+
+ /* fill in the window class structure */
+ wc.style = 0; /* no special styles */
+ wc.lpfnWndProc = (WNDPROC)WinDXEventProc; /* event handler */
+ wc.cbClsExtra = 0; /* no extra class data */
+ wc.cbWndExtra = 0; /* no extra window data */
+ wc.hInstance = hInstance; /* instance */
+ wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); /* load a default icon */
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* load a default cursor */
+ wc.hbrBackground = NULL; /* redraw our own bg */
+ wc.lpszMenuName = NULL; /* no menu */
+ wc.lpszClassName = "VLC DirectX"; /* use a special class */
+
+ /* register the window class */
+ if (!RegisterClass(&wc)) {
+ intf_WarnMsg( 3, "vout: WinDX register window FAILED" );
+ return (1);
+ }
+
+ /* when you create a window you give the dimensions you wish it to have.
+ * Unfortunatly these dimensions will include the borders and title bar.
+ * 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;
+ AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
+
+ /* create the window */
+ p_vout->p_sys->hwnd = CreateWindow("VLC DirectX",/* name of window class */
+ "VLC DirectX", /* window title bar text */
+ WS_OVERLAPPEDWINDOW
+ | WS_SIZEBOX | WS_VISIBLE, /* window style */
+ 10, /* default X coordinate */
+ 10, /* default Y coordinate */
+ rect_window.right - rect_window.left, /* window width */
+ rect_window.bottom - rect_window.top, /* window height */
+ NULL, /* no parent window */
+ NULL, /* no menu in this window */
+ hInstance, /* handle of this program instance */
+ NULL); /* no additional arguments */
+
+ if (p_vout->p_sys->hwnd == NULL) {
+ intf_WarnMsg( 3, "vout: WinDX create window FAILED" );
+ return (1);
+ }
+
+ /* now display the window */
+ ShowWindow(p_vout->p_sys->hwnd, SW_SHOW);
+
+ return ( 0 );
+}
+
+/*****************************************************************************
+ * WinDXInitDDraw: Takes care of all the DirectDraw initialisations
+ *****************************************************************************
+ * This function initialise and allocate resources for DirectDraw.
+ *****************************************************************************/
+static int WinDXInitDDraw( vout_thread_t *p_vout )
+{
+ HRESULT dxresult;
+ DWORD flags;
+
+ intf_WarnMsg( 3, "vout: WinDX WinDXInitDDraw" );
+
+ /* Initialize DirectDraw */
+ dxresult = DirectDrawCreate( NULL, &p_vout->p_sys->p_ddobject, NULL );
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't initialize Direct Draw" );
+ return( 1 );
+ }
+
+ /* Set DirectDraw Cooperative level, ie what control we want over Windows
+ display */
+ if( p_vout->b_fullscreen )
+ {
+ flags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN;
+ }
+ else
+ {
+ flags = DDSCL_NORMAL;
+ }
+
+ dxresult = IDirectDraw_SetCooperativeLevel( p_vout->p_sys->p_ddobject,
+ p_vout->p_sys->hwnd, flags );
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't set direct draw cooperative level." );
+ IDirectDraw_Release(p_vout->p_sys->p_ddobject);
+ p_vout->p_sys->p_ddobject = NULL;
+ return( 1 );
+ }
+
+ return( 0 );
+}
+
+/*****************************************************************************
+ * WinDXCreateDisplay: create the DirectDraw display.
+ *****************************************************************************
+ * Create and initialize display according to preferences specified in the vout
+ * thread fields.
+ *****************************************************************************/
+static int WinDXCreateDisplay( vout_thread_t *p_vout )
+{
+ DDCAPS ddcaps;
+ HRESULT dxresult;
+ DDSURFACEDESC ddsd;
+ BOOL bHasOverlay, bHasColorKey, bCanStretch;
+
+ /* Now create the primary surface. This surface is the displayed surface */
+ /* The following two steps are important! */
+ memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
+ ddsd.dwSize = sizeof(DDSURFACEDESC);
+ ddsd.dwFlags = DDSD_CAPS;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+
+ dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
+ &ddsd,
+ &p_vout->p_sys->p_display, NULL );
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't create direct draw primary surface." );
+ p_vout->p_sys->p_display = NULL;
+ return( 1 );
+ }
+
+ /* Now create a clipper for our window.
+ * This clipper prevents us to modify by mistake anything on the screen
+ * (primary surface) which doesn't belong to our window */
+ dxresult = IDirectDraw_CreateClipper(p_vout->p_sys->p_ddobject, 0,
+ &p_vout->p_sys->p_clipper, NULL);
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't create clipper." );
+ IDirectDrawSurface_Release( p_vout->p_sys->p_display );
+ p_vout->p_sys->p_display = NULL;
+ return( 1 );
+ }
+
+ dxresult = IDirectDrawClipper_SetHWnd(p_vout->p_sys->p_clipper, 0,
+ p_vout->p_sys->hwnd);
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't attach clipper to window." );
+ IDirectDrawSurface_Release( p_vout->p_sys->p_display );
+ p_vout->p_sys->p_display = NULL;
+ return( 1 );
+ }
+
+ dxresult = IDirectDrawSurface_SetClipper(p_vout->p_sys->p_display,
+ p_vout->p_sys->p_clipper);
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't attach clipper to surface." );
+ IDirectDrawSurface_Release( p_vout->p_sys->p_display );
+ p_vout->p_sys->p_display = NULL;
+ return( 1 );
+ }
+
+
+ /* Probe the capabilities of the hardware */
+ /* This is just an indication of whever 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 = IDirectDraw_GetCaps( p_vout->p_sys->p_ddobject,
+ &ddcaps, NULL );
+ if(dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't get caps." );
+ bHasOverlay = FALSE;
+ bHasColorKey = FALSE;
+ bCanStretch = FALSE;
+ }
+ else
+ {
+ /* Determine if the hardware supports overlay surfaces */
+ bHasOverlay = ((ddcaps.dwCaps & DDCAPS_OVERLAY) ==
+ DDCAPS_OVERLAY) ? TRUE : FALSE;
+ /* Determine if the hardware supports colorkeying */
+ bHasColorKey = ((ddcaps.dwCaps & DDCAPS_COLORKEY) ==
+ DDCAPS_COLORKEY) ? TRUE : FALSE;
+ /* Determine if the hardware supports scaling of the overlay surface */
+ bCanStretch = ((ddcaps.dwCaps & DDCAPS_OVERLAYSTRETCH) ==
+ DDCAPS_OVERLAYSTRETCH) ? TRUE : FALSE;
+ intf_WarnMsg( 3, "vout: WinDX Caps: overlay=%i colorkey=%i stretch=%i",
+ bHasOverlay, bHasColorKey, bCanStretch );
+ }
+
+ if( bHasOverlay && bHasColorKey && bCanStretch )
+ {
+ if( !WinDXCreateYUVOverlay( p_vout ) )
+ {
+ /* Overlay created successfully */
+ p_vout->b_need_render = 0;
+ }
+ }
+
+
+ /* Now do some initialisation for video_output */
+ if( p_vout->b_need_render )
+ {
+ /* if we want a valid pointer to the surface memory, we must lock
+ * the surface */
+ memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
+ ddsd.dwSize = sizeof(DDSURFACEDESC);
+ dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_display,
+ NULL, &ddsd,
+ DDLOCK_NOSYSLOCK, NULL);
+ if ( dxresult == DDERR_SURFACELOST )
+ {
+ /* Your surface can be lost so be sure
+ * to check this and restore it if needed */
+ dxresult = IDirectDrawSurface_Restore( p_vout->p_sys->p_display );
+ dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_display,
+ NULL, &ddsd, DDLOCK_NOSYSLOCK
+ | DDLOCK_WAIT, NULL);
+ }
+ if( dxresult != DD_OK )
+ {
+ intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
+ return( 1 );
+ }
+
+ /* Set the pointer to the surface memory */
+ p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
+ /* back buffer, none for now */
+ p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
+
+
+ /* Set thread information */
+ p_vout->i_width = ddsd.dwWidth;
+ p_vout->i_height = ddsd.dwHeight;
+ p_vout->i_bytes_per_line = ddsd.u1.lPitch;
+
+ p_vout->i_screen_depth = ddsd.ddpfPixelFormat.u1.dwRGBBitCount;
+ p_vout->i_bytes_per_pixel = ddsd.ddpfPixelFormat.u1.dwRGBBitCount/8;
+
+ p_vout->i_red_mask = ddsd.ddpfPixelFormat.u2.dwRBitMask;
+ p_vout->i_green_mask = ddsd.ddpfPixelFormat.u3.dwGBitMask;
+ p_vout->i_blue_mask = ddsd.ddpfPixelFormat.u4.dwBBitMask;
+
+ /* Unlock the Surface */
+ dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_display,
+ ddsd.lpSurface );
+ /* FIXME: palette in 8bpp ?? */
+ /* Set and initialize buffers */
+ vout_SetBuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
+ p_vout->p_sys->p_windx_buf[ 1 ] );
+ }
+ else
+ {
+ /* Lock the surface */
+ memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
+ ddsd.dwSize = sizeof(DDSURFACEDESC);
+ dxresult = IDirectDrawSurface_Lock(p_vout->p_sys->p_overlay,
+ NULL, &ddsd, DDLOCK_NOSYSLOCK, NULL);
+ if ( dxresult == DDERR_SURFACELOST )
+ {
+ /* Your surface can be lost (thanks to windows) so be sure
+ * to check this every time you want to do something with
+ * it */
+ dxresult = IDirectDrawSurface_Restore(
+ p_vout->p_sys->p_overlay );
+ dxresult = IDirectDrawSurface_Lock( p_vout->p_sys->p_overlay
+ , NULL, &ddsd,DDLOCK_NOSYSLOCK| DDLOCK_WAIT, NULL);
+ }
+ if( dxresult != DD_OK )
+ {
+ intf_WarnMsg( 3, "vout: WinDX could not lock the surface" );
+ return( 1 );
+ }
+
+ p_vout->p_sys->p_windx_buf[ 0 ] = ddsd.lpSurface;
+ p_vout->p_sys->p_windx_buf[ 1 ] = ddsd.lpSurface;
+
+ /* Set thread information */
+ p_vout->i_width = ddsd.dwWidth;
+ p_vout->i_height = ddsd.dwHeight;
+ p_vout->i_bytes_per_line = ddsd.u1.lPitch;
+
+ /* Unlock the Surface */
+ dxresult = IDirectDrawSurface_Unlock(p_vout->p_sys->p_overlay,
+ ddsd.lpSurface );
+
+ vout_SetBuffers( p_vout, p_vout->p_sys->p_windx_buf[ 0 ],
+ p_vout->p_sys->p_windx_buf[ 1 ] );
+ }
+
+ return( 0 );
+}
+
+/*****************************************************************************
+ * WinDXCreateYUVOveraly: create an YUV overlay surface for the video.
+ *****************************************************************************
+ * The best method of display is with an YUV overlay because the YUV->RGB
+ * conversion is done in hardware.
+ * This function will try to create an YUV overlay.
+ *****************************************************************************/
+static int WinDXCreateYUVOverlay( vout_thread_t *p_vout )
+{
+ HRESULT dxresult;
+ DDSURFACEDESC ddsd;
+
+ /* Now create the 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.
+ * This color will be painted by WinDXClipOverlay which in turn is called
+ * by a WM_PAINT event */
+
+ memset( &ddsd, 0, sizeof( DDSURFACEDESC ));
+ ddsd.dwSize = sizeof(DDSURFACEDESC);
+ ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
+ ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
+ ddsd.ddpfPixelFormat.dwFourCC = mmioFOURCC('Y','V','1','2');
+ ddsd.ddpfPixelFormat.u1.dwYUVBitCount = 16;
+
+ ddsd.dwSize = sizeof(DDSURFACEDESC);
+ ddsd.dwFlags = DDSD_CAPS |
+ DDSD_HEIGHT |
+ DDSD_WIDTH |
+ DDSD_PIXELFORMAT;
+ ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
+ ddsd.dwHeight = p_vout->p_sys->i_image_height;
+ ddsd.dwWidth = p_vout->p_sys->i_image_width;
+
+ dxresult = IDirectDraw_CreateSurface( p_vout->p_sys->p_ddobject,
+ &ddsd,
+ &p_vout->p_sys->p_overlay, NULL );
+ if( dxresult != DD_OK )
+ {
+ intf_ErrMsg( "vout error: can't create overlay surface." );
+ }
+ else
+ {
+ intf_WarnMsg( 3, "vout: WinDX YUV overlay created successfully" );
+ }
+ /* Hide the overlay for now */
+ IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
+ NULL,
+ p_vout->p_sys->p_display,
+ NULL,
+ DDOVER_HIDE,
+ NULL);
+
+ return ( 0 );
+}
+
+/*****************************************************************************
+ * WinDXUpdateOverlay: 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 vout_Manage).
+ *****************************************************************************/
+static int WinDXUpdateOverlay( vout_thread_t *p_vout )
+{
+ DDOVERLAYFX ddofx;
+ RECT rect_window, rect_client;
+ DWORD dwFlags;
+ HRESULT dxresult;
+
+ if( p_vout->p_sys->p_overlay == NULL || p_vout->b_need_render)
+ {
+ intf_WarnMsg( 3, "vout: WinDX no overlay !!" );
+ return( 0 );
+ }
+
+ if( !p_vout->p_sys->b_display_enabled )
+ {
+ return( 0 );
+ }
+
+
+ /* Now get the coordinates of the window. We don't actually want the
+ * window coordinates but these of the usable surface inside the window.
+ * By specification rect_client.right = rect_client.top = 0 */
+ GetWindowRect(p_vout->p_sys->hwnd, &rect_window);
+ GetClientRect(p_vout->p_sys->hwnd, &rect_client);;
+ rect_window.left = ( (rect_window.right - rect_window.left) -
+ rect_client.right ) / 2 + rect_window.left;
+ rect_window.right = rect_window.left + rect_client.right;
+ rect_window.top = rect_window.bottom - rect_client.bottom;
+
+ /* We want to keep the aspect ratio of the video */
+ if( p_vout->b_scale )
+ {
+ switch( p_vout->p_rendered_pic->i_aspect_ratio )
+ {
+ case AR_16_9_PICTURE:
+ if( ((rect_window.right-rect_window.left)*9)
+ > ((rect_window.bottom-rect_window.top)*16) )
+ {
+ int temp;
+ temp = (rect_window.bottom-rect_window.top)*16/9;
+ temp = (rect_window.right-rect_window.left) - temp;
+ rect_window.left += (temp/2);
+ rect_window.right -= (temp/2);
+ }
+ else
+ {
+ int temp;
+ temp = (rect_window.right-rect_window.left)*9/16;
+ temp = (rect_window.bottom-rect_window.top) - temp;
+ rect_window.top += (temp/2);
+ rect_window.bottom -= (temp/2);
+ }
+ break;
+
+ case AR_221_1_PICTURE:
+ if( ((rect_window.right-rect_window.left)*100)
+ > ((rect_window.bottom-rect_window.top)*221) )
+ {
+ int temp;
+ temp = (rect_window.bottom-rect_window.top)*221/100;
+ temp = (rect_window.right-rect_window.left) - temp;
+ rect_window.left += (temp/2);
+ rect_window.right -= (temp/2);
+ }
+ else
+ {
+ int temp;
+ temp = (rect_window.right-rect_window.left)*100/221;
+ temp = (rect_window.bottom-rect_window.top) - temp;
+ rect_window.top += (temp/2);
+ rect_window.bottom -= (temp/2);
+ }
+ break;
+
+ case AR_SQUARE_PICTURE:
+ if( (rect_window.right-rect_window.left)
+ > (rect_window.bottom-rect_window.top) )
+ {
+ int temp;
+ temp = (rect_window.bottom-rect_window.top);
+ temp = (rect_window.right-rect_window.left) - temp;
+ rect_window.left += (temp/2);
+ rect_window.right -= (temp/2);
+ }
+ else
+ {
+ int temp;
+ temp = (rect_window.right-rect_window.left);
+ temp = (rect_window.bottom-rect_window.top) - temp;
+ rect_window.top += (temp/2);
+ rect_window.bottom -= (temp/2);
+ }
+ break;
+
+ case AR_3_4_PICTURE:
+ default:
+ if( ((rect_window.right-rect_window.left)*3)
+ > ((rect_window.bottom-rect_window.top)*4) )
+ {
+ int temp;
+ temp = (rect_window.bottom-rect_window.top)*4/3;
+ temp = (rect_window.right-rect_window.left) - temp;
+ rect_window.left += (temp/2);
+ rect_window.right -= (temp/2);
+ }
+ else
+ {
+ int temp;
+ temp = (rect_window.right-rect_window.left)*3/4;
+ temp = (rect_window.bottom-rect_window.top) - temp;
+ rect_window.top += (temp/2);
+ rect_window.bottom -= (temp/2);
+ }
+ break;
+ }
+ }
+
+
+ /* Position and show the overlay */
+ memset(&ddofx, 0, sizeof(DDOVERLAYFX));
+ ddofx.dwSize = sizeof(DDOVERLAYFX);
+ ddofx.dckDestColorkey.dwColorSpaceLowValue = OVERLAY_COLOR_KEY;
+ ddofx.dckDestColorkey.dwColorSpaceHighValue = OVERLAY_COLOR_KEY;
+
+ dwFlags = DDOVER_KEYDESTOVERRIDE | DDOVER_SHOW;
+
+ dxresult = IDirectDrawSurface_UpdateOverlay(p_vout->p_sys->p_overlay,
+ NULL,
+ p_vout->p_sys->p_display,
+ &rect_window,
+ dwFlags,
+ &ddofx);
+ if(dxresult != DD_OK)
+ {
+ intf_WarnMsg( 3, "vout: WinDX WM_MOVE can't move or resize overlay" );
+ }
+
+ return ( 0 );
+}
+
+/*****************************************************************************
+ * WinDXClipOveraly: Clip overlay surface on video display.
+ *****************************************************************************
+ * The overlay is displayed on top of the primary surface (which is the
+ * screen).
+ * This overlay must be clipped whenever another window is supposed to cover
+ * it.
+ * For this, we use a color key which we paint on the parts of the window
+ * which aren't covered by anything else. The video adapter will then only
+ * display the overlay on the surfaces painted with this color key.
+ * This function is called whenever a WM_PAINT event is generated
+ * (in vout_Manage).
+ *****************************************************************************/
+static int WinDXClipOverlay( vout_thread_t *p_vout )
+{
+ PAINTSTRUCT ps;
+ POINT ptClient;
+ RECT rectBlt;
+ DDBLTFX ddbfx;
+
+ if( p_vout->p_sys->p_overlay == NULL || p_vout->b_need_render)
+ {
+ intf_WarnMsg( 3, "vout: WinDX no overlay !!" );
+ return( 0 );
+ }
+
+ BeginPaint(p_vout->p_sys->hwnd, &ps);
+
+ /* Fill the client area with colour key */
+ ptClient.x = ps.rcPaint.left;
+ ptClient.y = ps.rcPaint.top;
+ ClientToScreen(p_vout->p_sys->hwnd, &ptClient);
+ rectBlt.left = ptClient.x;
+ rectBlt.top = ptClient.y;
+
+ ptClient.x = ps.rcPaint.right;
+ ptClient.y = ps.rcPaint.bottom;
+ ClientToScreen(p_vout->p_sys->hwnd, &ptClient);
+ rectBlt.right = ptClient.x;
+ rectBlt.bottom = ptClient.y;
+
+ memset(&ddbfx, 0, sizeof(DDBLTFX));
+ ddbfx.dwSize = sizeof(DDBLTFX);
+ ddbfx.u5.dwFillColor = OVERLAY_COLOR_KEY;
+
+ IDirectDrawSurface_Blt(p_vout->p_sys->p_display,
+ &rectBlt,
+ NULL,
+ &rectBlt,
+ DDBLT_COLORFILL, // | DDBLT_WAIT,
+ &ddbfx);
+
+ EndPaint(p_vout->p_sys->hwnd, &ps);
+
+ return ( 0 );
+}
+
+/*****************************************************************************
+ * WinDXCloseWindow: close the window created by WinDXCreateWindow
+ *****************************************************************************
+ * This function returns all resources allocated by WinDXCreateWindow.
+ *****************************************************************************/
+static void WinDXCloseWindow( vout_thread_t *p_vout )
+{
+ HINSTANCE hInstance;
+
+ if( p_vout->p_sys->hwnd != INVALID_HANDLE_VALUE )
+ {
+ DestroyWindow( p_vout->p_sys->hwnd);
+ p_vout->p_sys->hwnd = INVALID_HANDLE_VALUE;
+ }
+
+ hInstance = GetModuleHandle(NULL);
+ UnregisterClass( "VLC DirectX", /* class name */
+ hInstance ); /* handle to application instance */
+
+}
+
+/*****************************************************************************
+ * WinDXCloseDDraw: Release the DDraw object allocated by WinDXInitDDraw
+ *****************************************************************************
+ * This function returns all resources allocated by WinDXInitDDraw.
+ *****************************************************************************/
+static void WinDXCloseDDraw( vout_thread_t *p_vout )
+{
+ if( p_vout->p_sys->p_ddobject != NULL )
+ {
+ IDirectDraw_Release(p_vout->p_sys->p_ddobject);
+ p_vout->p_sys->p_ddobject = NULL;
+ }
+}
+
+/*****************************************************************************
+ * WinDXCloseDisplay: close and reset DirectX device
+ *****************************************************************************
+ * This function returns all resources allocated by WinDXCreateDisplay and
+ * restore the original state of the device.
+ *****************************************************************************/
+static void WinDXCloseDisplay( vout_thread_t *p_vout )
+{
+ if( p_vout->p_sys->p_display != NULL )
+ {
+ if( p_vout->p_sys->p_overlay != NULL )
+ {
+ IDirectDraw_Release( p_vout->p_sys->p_overlay );
+ p_vout->p_sys->p_overlay = NULL;
+ }
+
+ if( p_vout->p_sys->p_clipper != NULL )
+ {
+ IDirectDraw_Release( p_vout->p_sys->p_clipper );
+ p_vout->p_sys->p_clipper = NULL;
+ }
+
+ IDirectDraw_Release( p_vout->p_sys->p_display );
+ p_vout->p_sys->p_display = NULL;
+ }
+}
+
+/*****************************************************************************
+ * WinDXCloseYUVOverlay: close the overlay surface
+ *****************************************************************************
+ * This function returns all resources allocated by the overlay surface.
+ * We also call this function when the decoded picture change its dimensions
+ * (in that case we close the overlay surface and reopen another with the
+ * right dimensions).
+ *****************************************************************************/
+static void WinDXCloseYUVOverlay( vout_thread_t *p_vout )
+{
+ if( p_vout->p_sys->p_overlay != NULL )
+ {
+ IDirectDraw_Release( p_vout->p_sys->p_overlay );
+ p_vout->p_sys->p_overlay = NULL;
+ }
+ p_vout->p_sys->b_display_enabled = 0;
+}
+