545 lines
16 KiB
C++
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;
|
|
}
|