/****************************************************************************** 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; }