deargui-vpl/ref/virtools/Samples/Runtime/Standalone Player/CustomPlayerApp.cpp

545 lines
16 KiB
C++

/******************************************************************************
File : CustomPlayerApp.cpp
Description: This file contains the CCustomPlayerApp which is the main class of
this project. For a good starting point go to CCustomPlayerApp::InitInstance in
CCustomPlayerApp.cpp
Virtools SDK
Copyright (c) Virtools 2005, All Rights Reserved.
******************************************************************************/
#include "CPStdAfx.h"
#include "CustomPlayerDefines.h"
#include "CustomPlayerApp.h"
#include "CustomPlayer.h"
BEGIN_MESSAGE_MAP(CCustomPlayerApp, CWinApp)
//{{AFX_MSG_MAP(CCustomPlayerApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
////////////////////////////////////////////////////////////////////////////////
//
// CCustomPlayerApp: PUBLIC METHODS
//
////////////////////////////////////////////////////////////////////////////////
CCustomPlayerApp::CCustomPlayerApp()
: m_MainWindow(0),m_RenderWindow(0),m_Splash(0),
m_PlayerClass(MAINWINDOW_CLASSNAME),m_RenderClass(RENDERWINDOW_CLASSNAME),
m_PlayerTitle(MAINWINDOW_TITLE),
m_Config(0)
{
}
CCustomPlayerApp::~CCustomPlayerApp()
{
// simply destroy the windows ...
if (m_RenderWindow) {
DestroyWindow(m_RenderWindow);
}
if (m_MainWindow) {
DestroyWindow(m_MainWindow);
}
}
// the unique (global) instance of the winapp
CCustomPlayerApp theApp;
CCustomPlayer* thePlayer = 0;
BOOL CCustomPlayerApp::InitInstance()
{
// register the windows class the main and the render window
_RegisterClass();
// read the configuration
XString filename;
const char* fileBuffer = 0;
XDWORD fileSize = 0;
if(!_ReadConfig(filename,fileBuffer,fileSize)) {
MessageBox(NULL,CANNOT_READ_CONFIG,INIT_ERROR,MB_OK|MB_ICONERROR);
return FALSE;
}
// display the splash windows
_DisplaySplashWindow();
// create the main and the render window
if(!_CreateWindows()) {
MessageBox(NULL,FAILED_TO_CREATE_WINDOWS,INIT_ERROR,MB_OK|MB_ICONERROR);
DestroyWindow(m_Splash);
return FALSE;
}
// retrieve the unique instance of the player (CCustomPlayer class)
CCustomPlayer& player = CCustomPlayer::Instance();
// initialize the player
if(!player.InitPlayer(m_MainWindow,m_RenderWindow,m_Config,filename.Length()==0?fileBuffer:filename.CStr(),filename.Length()==0?fileSize:0)) {
DestroyWindow(m_Splash);
return FALSE;
}
// as the player is ready we destroy the splash screen window
DestroyWindow(m_Splash);
if (m_Config&eAutoFullscreen) {
// we are a auto fullscreen mode
// so we hide the main window
ShowWindow(m_MainWindow,SW_HIDE);
UpdateWindow(m_MainWindow);
// we show the render window
ShowWindow(m_RenderWindow,theApp.m_nCmdShow);
UpdateWindow(m_RenderWindow);
// and set the focus to it
SetFocus(m_RenderWindow);
} else {
// we are in windowed mode
// so we show the main window
ShowWindow(m_MainWindow,theApp.m_nCmdShow);
UpdateWindow(m_MainWindow);
// the render window too
ShowWindow(m_RenderWindow,theApp.m_nCmdShow);
UpdateWindow(m_RenderWindow);
// and set the focus to it
SetFocus(m_RenderWindow);
}
// we reset the player to start it
player.Reset();
///////////////////////////////////////////////////////////////////////////
// NOTE: other important things to read after initialization
// - the main loop of the player is executed by CCustomPlayerApp::Run
// - the windowproc of the main and render window is
// CCustomPlayerApp::_MainWindowWndProc
// - but the main part of the application is implemented by the
// CCustomPlayer class.
///////////////////////////////////////////////////////////////////////////
return TRUE;
}
int CCustomPlayerApp::ExitInstance()
{
if (thePlayer) {
delete thePlayer;
thePlayer = 0;
}
return CWinApp::ExitInstance();
}
int CCustomPlayerApp::Run()
{
MSG msg;
while(1) {
if(PeekMessage(&msg, NULL,0,0,PM_REMOVE)) {
if(msg.message==WM_QUIT) {
return CWinApp::Run();
}
else if(!TranslateAccelerator(msg.hwnd,m_hAccelTable,&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else {
// process the player
if(!CCustomPlayer::Instance().Process(m_Config)) {
PostQuitMessage(0);
return CWinApp::Run();
}
}
}
return msg.wParam;
}
////////////////////////////////////////////////////////////////////////////////
//
// CCustomPlayerApp: PROTECTED/PRIVATE METHODS
//
////////////////////////////////////////////////////////////////////////////////
BOOL CCustomPlayerApp::_CreateWindows()
{
RECT mainRect;
// compute the main window rectangle, so the window is centered
mainRect.left = (GetSystemMetrics(SM_CXSCREEN)-CCustomPlayer::Instance().WindowedWidth())/2;
mainRect.right = CCustomPlayer::Instance().WindowedWidth()+mainRect.left;
mainRect.top = (GetSystemMetrics(SM_CYSCREEN)-CCustomPlayer::Instance().WindowedHeight())/2;
mainRect.bottom = CCustomPlayer::Instance().WindowedHeight()+mainRect.top;
BOOL ret = AdjustWindowRect(&mainRect,WS_OVERLAPPEDWINDOW & ~(WS_SYSMENU|WS_SIZEBOX|WS_MAXIMIZEBOX|WS_MINIMIZEBOX|WS_SIZEBOX),FALSE);
// create the main window
m_MainWindow = CreateWindow(m_PlayerClass.CStr(),m_PlayerTitle.CStr(),
WS_OVERLAPPEDWINDOW & ~(WS_SIZEBOX|WS_MAXIMIZEBOX),
mainRect.left,mainRect.top,mainRect.right-mainRect.left,mainRect.bottom-mainRect.top,
NULL,NULL,m_hInstance,NULL);
if(!m_MainWindow) {
return FALSE;
}
// create the render window
if (!(m_Config&eAutoFullscreen)) {
m_RenderWindow = CreateWindowEx(WS_EX_TOPMOST,m_RenderClass.CStr(),"",(WS_CHILD|WS_OVERLAPPED|WS_VISIBLE)&~WS_CAPTION,
0,0,CCustomPlayer::Instance().FullscreenWidth(),CCustomPlayer::Instance().FullscreenHeight(),m_MainWindow,NULL,m_hInstance,0);
} else {
m_RenderWindow = CreateWindowEx(WS_EX_TOPMOST,m_RenderClass.CStr(),"",(WS_CHILD|WS_OVERLAPPED|WS_VISIBLE)&~WS_CAPTION,
0,0,CCustomPlayer::Instance().WindowedWidth(),CCustomPlayer::Instance().WindowedHeight(),m_MainWindow,NULL,m_hInstance,0);
}
if(!m_RenderWindow) {
return FALSE;
}
return TRUE;
}
void CCustomPlayerApp::_DisplaySplashWindow()
{
// display the splash windows centered.
RECT rect;
m_Splash = CreateDialog(theApp.m_hInstance,(LPCTSTR)IDD_SPLASH,NULL,(DLGPROC)_LoadingDlgWndProc);
GetWindowRect(m_Splash,&rect);
SetWindowPos(m_Splash,NULL,(GetSystemMetrics(SM_CXSCREEN)-(rect.right-rect.left))/2,
(GetSystemMetrics(SM_CYSCREEN)-(rect.bottom-rect.top))/2,0,0,SWP_NOZORDER|SWP_NOSIZE);
ShowWindow(m_Splash, SW_SHOW);
UpdateWindow(m_Splash);
}
BOOL CCustomPlayerApp::_ReadCommandLine(const char* iArguments, XString& oFilename)
{
// parameters are:
// -disable_keys (or -d) : disable ALT+ENTER to switch from/to fullscreen
// and ALT+F4 to close the application
// -auto_fullscreen (or -f) : the player start automatically in fullscreen mode
// -file filename : specify the file to load
// -title title : specify the title of the main window
// -width width : specify the width in windowed mode
// -height height : specify the height in windowed mode
// -fwidth fullscreenwidth : specify the width in fullscreen mode
// -fheight fullscreeneight : specify the height in fullscreen mode
// -fbpp fullscreenbpp : specify the bit per pixel in fullscreen mode
// -rasterizer family,flags : specify the rasterizer family (see CKRST_RSTFAMILY)
// and flags to choose the rasterizer (see CKRST_SPECIFICCAPS)
// default values are:
// - title = Virtools Custom Player
// - width = 640
// - height = 480
// - fullscreen width = 640
// - fullscreen height = 480
// - fullscreen bpp = 32
// - rasterizer = CKRST_DIRECTX,CKRST_SPECIFICCAPS_HARDWARE|CKRST_SPECIFICCAPS_DX9
// Please note that if there are "space" characters in a parameter value the value must be between " "
XStringTokenizer st(iArguments,"-");
const char* token = 0;
const char* ptr = 0;
const char* ptr2 = 0;
while (token=st.NextToken(token)) {
ptr = _NextBlank(token);
ptr2 = _SkipBlank(ptr);
// the parameter is not followed by a value
if (*ptr2=='\0') {
// -disable_keys (or -d)
if (strncmp(token,"d",XMin((int)(ptr-token),(int)(sizeof("d")-1)))==0 ||
strncmp(token,"disable_keys",XMin((int)(ptr-token),(int)(sizeof("disable_keys")-1)))==0) {
m_Config |= eDisableKeys;
}
// -auto_fullscreen (or -f)
else if (strncmp(token,"f",XMin((int)(ptr-token),(int)(sizeof("f")-1)))==0 ||
strncmp(token,"auto_fullscreen",XMin((int)(ptr-token),(int)(sizeof("auto_fullscreen")-1)))==0) {
m_Config |= eAutoFullscreen;
} else {
// unknow parameter
return FALSE;
}
continue;
}
// parameter followed by values
// -file
if (strncmp(token,"file",XMin((int)(ptr-token),(int)(sizeof("file")-1)))==0) {
if (!_ComputeParamValue(ptr2,oFilename)) {
return FALSE;
}
}
// -title
else if (strncmp(token,"title",XMin((int)(ptr-token),(int)(sizeof("title")-1)))==0) {
if (!_ComputeParamValue(ptr2,m_PlayerTitle)) {
return FALSE;
}
}
// -width
else if (strncmp(token,"width",XMin((int)(ptr-token),(int)(sizeof("width")-1)))==0) {
XString value;
if (!_ComputeParamValue(ptr2,value)) {
return FALSE;
}
int tmp = 0;
if(sscanf(value.CStr(),"%d",&tmp)==1) {
CCustomPlayer::Instance().WindowedWidth() = tmp;
}
}
// -height
else if (strncmp(token,"height",XMin((int)(ptr-token),(int)(sizeof("height")-1)))==0) {
XString value;
if (!_ComputeParamValue(ptr2,value)) {
return FALSE;
}
int tmp = 0;
if(sscanf(value.CStr(),"%d",&tmp)==1) {
CCustomPlayer::Instance().WindowedHeight() = tmp;
}
}
// -fwidth
else if (strncmp(token,"fwidth",XMin((int)(ptr-token),(int)(sizeof("fwidth")-1)))==0) {
XString value;
if (!_ComputeParamValue(ptr2,value)) {
return FALSE;
}
int tmp = 0;
if(sscanf(value.CStr(),"%d",&tmp)==1) {
CCustomPlayer::Instance().FullscreenWidth() = tmp;
}
}
// -fheight
else if (strncmp(token,"fheight",XMin((int)(ptr-token),(int)(sizeof("fheight")-1)))==0) {
XString value;
if (!_ComputeParamValue(ptr2,value)) {
return FALSE;
}
int tmp = 0;
if(sscanf(value.CStr(),"%d",&tmp)==1) {
CCustomPlayer::Instance().FullscreenHeight() = tmp;
}
}
// -fbpp
else if (strncmp(token,"fbpp",XMin((int)(ptr-token),(int)(sizeof("fbpp")-1)))==0) {
XString value;
if (!_ComputeParamValue(ptr2,value)) {
return FALSE;
}
int tmp = 0;
if(sscanf(value.CStr(),"%d",&tmp)==1) {
CCustomPlayer::Instance().FullscreenBpp() = tmp;
}
}
// -rasterizer
else if (strncmp(token,"rasterizer",XMin((int)(ptr-token),(int)(sizeof("rasterizer")-1)))==0) {
XString value;
if (!_ComputeParamValue(ptr2,value)) {
return FALSE;
}
int tmp = 0;
int tmp2 = 0;
if(sscanf(value.CStr(),"%d,%d",&tmp,&tmp2)==2) {
CCustomPlayer::Instance().RasterizerFamily() = tmp;
CCustomPlayer::Instance().RasterizerFlags() = tmp2;
}
} else {
// unknow parameter
return FALSE;
}
}
return TRUE;
}
BOOL CCustomPlayerApp::_ReadConfig(XString& oFilename, const char*& oBufferFile, XDWORD& oSize)
{
const char* cmdLine = GetCommandLine();
const char* ptr = 0;
// the command line start with a '"' (it means the first parameters
// contains at least one space)
if (*cmdLine=='"') {
// the first parameter is something like
// "e:\directory name\customplayer.exe"
// here we look for the second '"' to be after
// the first parameter
ptr = strchr(cmdLine+1,'"');
if (!ptr) {
// cannot find it.
// the command line is invalid
return FALSE;
}
// move on the character after the second '"'
ptr++;
ptr = _SkipBlank(ptr);
} else {
// if there is no space in the first parameter, the
// first space we can found is the one after the first parameter
// if any
ptr = strchr(cmdLine,' ');
if (ptr) {
ptr = _SkipBlank(ptr);
}
}
if (ptr==0 || *ptr=='\0') {
// there is no parameter on the command line (excepted the name of the exe)
// try to read the "internal" configuration
return _ReadInternalConfig(oBufferFile,oSize);
}
// any argument must begin with a '-'
if (*ptr!='-') {
return FALSE;
}
// read the command line
return _ReadCommandLine(ptr,oFilename);
}
BOOL CCustomPlayerApp::_ReadInternalConfig(const char*& oBufferFile, XDWORD& oSize)
{
// NOTE: the internal configuration is not supported at this time
// The idea behind "internal" configuration is to embed all data needed by the player
// in the executable itself. The vmo and the parameters can be happend to the executable file.
// In ouput oBufferFile must contains the address where the vmo is located in memory and
// oSize must contains the size of the vmo in memory.
return FALSE;
}
ATOM CCustomPlayerApp::_RegisterClass()
{
// register window classes
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
wcex.lpfnWndProc = (WNDPROC)_MainWindowWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = m_hInstance;
wcex.hIcon = LoadIcon(IDI_VIRTOOLS);
wcex.hCursor = NULL;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = m_PlayerClass.CStr();
wcex.hIconSm = 0;
WNDCLASS MyRenderClass;
ZeroMemory(&MyRenderClass,sizeof(MyRenderClass));
MyRenderClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
MyRenderClass.lpfnWndProc = (WNDPROC)_MainWindowWndProc;
MyRenderClass.hInstance = m_hInstance;
MyRenderClass.lpszClassName = m_RenderClass.CStr();
::RegisterClass(&MyRenderClass);
return ::RegisterClassEx(&wcex);
}
LRESULT CALLBACK CCustomPlayerApp::_MainWindowWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_ACTIVATEAPP:
{
CCustomPlayer& player = CCustomPlayer::Instance();
player.OnActivateApp(wParam);
}
break;
// Minimum size of the player window
case WM_GETMINMAXINFO:
// this message is not very useful because
// the main window of the player is not resizable ...
// but perhaps it will change so we manage this message.
{
CCustomPlayer& player = CCustomPlayer::Instance();
if((LPMINMAXINFO)lParam) {
((LPMINMAXINFO)lParam)->ptMinTrackSize.x=player.MininumWindowedWidth();
((LPMINMAXINFO)lParam)->ptMinTrackSize.y=player.MininumWindowedHeight();
}
}
break;
// Sends a Message "OnClick" or "OnDblClick" if any object is under mouse cursor
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
{
CCustomPlayer& player = CCustomPlayer::Instance();
player.OnMouseClick(message);
}
break;
// Size and focus management
case WM_SIZE:
// if the window is maximized or minimized
// we get/lost focus.
{
CCustomPlayer& player = CCustomPlayer::Instance();
if (wParam==SIZE_MINIMIZED) {
player.OnFocusChange(FALSE);
} else if (wParam==SIZE_MAXIMIZED) {
player.OnFocusChange(TRUE);
}
}
break;
// Manage system key (ALT + KEY)
case WM_SYSKEYDOWN:
{
CCustomPlayer& player = CCustomPlayer::Instance();
return player.OnSysKeyDownMainWindow(theApp.m_Config,(int)wParam);
}
break;
// Repaint main frame
case WM_PAINT:
{
CCustomPlayer& player = CCustomPlayer::Instance();
player.OnPaint();
}
break;
// The main windows has been closed by the user
case WM_CLOSE:
PostQuitMessage(0);
break;
// Focus management
case WM_KILLFOCUS:
case WM_SETFOCUS:
{
CCustomPlayer& player = CCustomPlayer::Instance();
player.OnFocusChange(message==WM_SETFOCUS);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK CCustomPlayerApp::_LoadingDlgWndProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}