//
// CApplication.cpp
//
// Copyright 2005 Paul Higinbotham
//
#include "CApplication.h"
#include "..\graphicsengine\GraphicsEngine.h"
#include "..\graphicsengine\collections\String.h"
#include "..\graphicsengine\renderer\directxrenderer\d3dsettings.h"
#include <cassert>
#include <TCHAR.H>
#include <stdio.h>
// Forward declarations
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
// statics
static char szWindowClassName[] = "ZGraphicsApplication";
static char szWindowName[] = "HL Level Viewer";
static char szFileMenu[] = "&File";
static char szFileOpen[] = "&Open...";
static char szFileExit[] = "E&xit";
static char szOptionsMenu[] = "&Options";
static char szOptionsDisplay[] = "&Display Settings...";
static char szOptionsFullScreen[] = "Full &Screen";
// Default camera movement keys
static const UINT sWKey = DIK_W;
static const UINT sSKey = DIK_S;
static const UINT sAKey = DIK_A;
static const UINT sDKey = DIK_D;
static const UINT sFKey = DIK_F;
static const UINT sIKey = DIK_I;
static const UINT sXKey = DIK_X;
static const UINT sZKey = DIK_Z;
static const UINT sUpKey = DIK_UP;
static const UINT sDownKey = DIK_DOWN;
static const UINT sLeftKey = DIK_LEFT;
static const UINT sRightKey = DIK_RIGHT;
static const UINT sSpaceKey = DIK_SPACE;
static const UINT sLCtlKey = DIK_LCONTROL;
static const UINT sLShftKey = DIK_LSHIFT;
static const UINT sESCKey = DIK_ESCAPE;
static const UINT sTABKey = DIK_TAB;
// Movement constants
static const float s_fWalkVel = 250.0f; // (units / sec)
static const float s_fRunVel = 350.0f; // (units / sec)
static const float s_fYawRate = 0.006f;
static const float s_fPitchRate = 0.006f;
// View constants
static const float s_fFocalL = 1.2f; // Focal length, 1.0 -> 90 degree FOV, 1.3 -> 75 degree FOV
static const float s_fNearD = 1.2f; // Near clip distance
static const float s_fFarD = 7000.0f; // Far clip distance
// Camera bounding box dimensions (roughly human size, for Freeman)
//static const Vector3f s_vBB[2] = { Vector3f(-15.0f, -15.0f, -70.0f), Vector3f(15.0f, 15.0f, 15.0f) };
static const Vector3f s_vBB[2] = { Vector3f(-18.0f, -18.0f, -70.0f), Vector3f(18.0f, 18.0f, 15.0f) };
static const float s_fCrouchZ = 45.0f;
// Application settings constants
static const char szAppKey[] = "Software\\ZGraphics\\Application";
static const int s_nDefClientWidth = 640;
static const int s_nDefClientHeight = 480;
// Main window style
static const DWORD s_WinStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
enum MenuItems
{
mnuBarFile = 0,
mnuBarOptions,
mnuFileOpen,
mnuFileSep1,
mnuFileExit,
mnuOptionsDisplay,
mnuOptionsFullScreen
};
enum CameraActions
{
eMoveForward,
eMoveBackward,
eMoveLeft,
eMoveRight,
eLevitate,
ePitchUp,
ePitchDown,
eYawLeft,
eYawRight,
eJump,
eCrouch,
eRun,
eQuit,
eToggleScreen,
eFlashlight
};
int CompareKey(const int & nKey1, const int & nKey2)
{
return (nKey1 - nKey2);
}
CApplication * CApplication::ms_pApplication = NULL;
CApplication::~CApplication()
{
// release any engine resources
delete m_pRenderer;
delete m_pScene;
delete m_pCamera;
}
//
// Register and creates a window for rendering.
// Creates and initializes the DX input object.
// Creates and initializes the camera, scene, and rendering objects.
//
bool CApplication::Initialize(HINSTANCE hInstance)
{
// register the window class
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszClassName = szWindowClassName;
wc.lpszMenuName = NULL;
RegisterClass(&wc);
// Read settings from registry to get client width/height
_readSettings();
// set window client area
RECT windowRect = {0, 0, m_nClientWidth, m_nClientHeight};
AdjustWindowRect(&windowRect, s_WinStyle, true);
_centerWindowInShell(windowRect);
// create menu for application window
HMENU hMenu = _createSimpleMenu();
// create the application window
m_hWnd = CreateWindow(
szWindowClassName, szWindowName,
s_WinStyle,
windowRect.left, windowRect.top, (windowRect.right - windowRect.left), (windowRect.bottom - windowRect.top),
(HWND)0, hMenu, hInstance, NULL);
if (!m_hWnd)
return false;
// Initialize DX input object
if (!m_Input.Init(m_hWnd, hInstance))
return false;
m_Input.AcquireKeyboard();
m_Input.AcquireMouse();
// Create graphic engine objects for rendering
if (!_initializeEngineObjects())
return false;
_initializeKeyMap();
// Get high performance count frequency (if supported)
QueryPerformanceFrequency(&m_liCountFrequency);
// display window
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return true;
}
//
// Main application rendering loop and window message pump.
// For windowed mode Windows messages are processed before scene rendering occurs.
// For full-screen mode no messages are processed and only scene rendering occurs.
// Note that the application can exit only from windowed mode.
//
void CApplication::Run()
{
// Get base time.
_resetBaseTime();
// Start Windows message pump. Application always starts in windowed mode.
while (1)
{
MSG sMsg;
BOOL bHaveMessage = false;
if (m_bWindowedMode)
{
// Check message queue.
if (_isActive())
bHaveMessage = ::PeekMessage(&sMsg, (HWND)0, 0, 0, PM_REMOVE); // Returns immmediately
else
bHaveMessage = ::GetMessage(&sMsg, (HWND)0, 0, 0); // Saves CPU cycles
// We quit only when in windowed mode.
if (sMsg.message == WM_QUIT)
goto _quit;
if (bHaveMessage)
{
// Take care of any messages in the Windows message queue.
HACCEL hAccel = (HACCEL)0;
if (!TranslateAccelerator(m_hWnd, hAccel, &sMsg))
{
TranslateMessage(&sMsg);
DispatchMessage(&sMsg);
}
}
}
// Update and render scene for both windowed and full-screen modes.
if (m_bActive && !bHaveMessage)
{
// Update player motion and render scene.
_updateAndRender();
}
}
_quit:
_resetBaseTime();
}
//
// Internal methods
//
// Uses Windows commong open file dialog to allow user to browse for a scene file
void CApplication::_openFile()
{
OPENFILENAME openFileName;
memset(&openFileName, 0, sizeof(openFileName));
openFileName.lStructSize = sizeof(openFileName);
openFileName.hwndOwner = m_hWnd;
openFileName.lpstrFilter = _T("HF BSP Files\0*.BSP\0"); // Look only for bsp files.
TCHAR strFilePath[2048] = "\0";
openFileName.lpstrFile = strFilePath;
openFileName.nMaxFile = 2048;
openFileName.lpstrTitle = _T("Open BSP Scene File");
if ( GetOpenFileName(&openFileName) )
{
// Load scene file
m_bSceneLoaded = m_pScene->LoadScene(openFileName.lpstrFile);
if (m_bSceneLoaded)
{
// Update window title
cString szTitle = szWindowName;
cString szFilename(openFileName.lpstrFile);
int nPos = szFilename.FindLastChar('\\');
if (nPos == -1)
nPos = szFilename.FindLastChar('/');
if (nPos > -1)
szFilename = szFilename.GetPtr(nPos+1);
szTitle.AppendString(" - ");
szTitle.AppendString(szFilename);
SetWindowText(m_hWnd, szTitle.GetPtr());
// Enable Options menu items
EnableMenuItem(m_hOptionsSubMenu, mnuOptionsDisplay, MF_ENABLED);
EnableMenuItem(m_hOptionsSubMenu, mnuOptionsFullScreen, MF_ENABLED);
m_bActive = true;
_resetBaseTime();
}
}
}
// Launches the display settings dialog
void CApplication::_displaySettings()
{
int nReturn = m_pRenderer->LaunchSettingsDialog();
if (nReturn == IDOK)
{
_pause();
// Check for window mode change
CD3DSettings d3dSettings = m_pRenderer->GetDisplaySettings();
D3DDEVTYPE devType = d3dSettings.DevType();
if (m_bWindowedMode != d3dSettings.IsWindowed)
{
_changeWindowedMode(d3dSettings.IsWindowed);
}
// Reset render object
m_pScene->ReleaseRenderObjects();
m_pRenderer->ReCreateDevice();
m_pScene->RestoreRenderObjects(m_pRenderer);
_resume();
}
}
// Toggles application back and forth from windowed mode and fullscreen mode.
void CApplication::_toggleFullScreen()
{
if (!m_bSceneLoaded)
return;
_pause();
CD3DSettings d3dSettings = m_pRenderer->GetDisplaySettings();
_changeWindowedMode(!d3dSettings.IsWindowed);
// Reset render object
m_pRenderer->SetWindowMode(m_bWindowedMode);
m_pScene->ReleaseRenderObjects();
m_pRenderer->ReCreateDevice();
// Reset view information in case screen aspect changes.
_setViewInfo();
m_pScene->RestoreRenderObjects(m_pRenderer);
if (m_bWindowedMode)
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
else
{
SetCursor(NULL);
}
_resume();
}
void CApplication::_closeApplication()
{
PostMessage(m_hWnd, WM_CLOSE, 0, 0);
}
inline const unsigned char CApplication::_getKey(int eAction) const
{
const int *pnKey = m_pkeyMap->IsInMap(eAction);
if (pnKey)
return (unsigned char) (*pnKey);
else
return 0;
}
// Method to check current keyboard state and perform keyboard function as defined
// in the keyboard map object.
void CApplication::_handleKeyboard(DWORD ulTimeDelUSec)
{
float fRunVel = s_fRunVel;
float fWalkVel = s_fWalkVel;
// Crouch key
if (m_Input.IsKeydown(_getKey(eCrouch)))
{
// Set camera to crouch mode
m_pCamera->SetCrouched(true);
}
else if (m_pCamera->IsCrouched())
{
// Command camera to return to upright height.
m_pCamera->SetCrouched(false);
}
if (m_pCamera->IsCrouched())
{
// Reduce the player walk/run velocity when crouched.
fRunVel /= 2.0f;
fWalkVel /= 2.0f;
}
// Run key
m_bRun = m_Input.IsKeydown(_getKey(eRun));
// Forward/Backward keys
if (m_Input.IsKeydown(_getKey(eMoveForward)))
{
// Set camera forward velocity.
m_pCamera->Walk(m_bRun ? fRunVel : fWalkVel);
}
else if (m_Input.IsKeydown(_getKey(eMoveBackward)))
{
// Set camera backward velocity by changing sign.
m_pCamera->Walk(m_bRun ? -fRunVel : -fWalkVel);
}
else
{
// Set camera forward/backward velocity to zero.
m_pCamera->Walk(0.0f);
}
// Strafe (right/left) movement keys
if (m_Input.IsKeydown(_getKey(eMoveRight)))
{
// Set camera right strafing velocity.
m_pCamera->Strafe(m_bRun ? -fRunVel : -fWalkVel);
}
else if (m_Input.IsKeydown(_getKey(eMoveLeft)))
{
// Set camera left strafing velocity.
m_pCamera->Strafe(m_bRun ? fRunVel : fWalkVel);
}
else
{
// Set camera strafing velocity to zero.
m_pCamera->Strafe(0.0f);
}
if (_isWindowed())
{
// Levitate key
if (m_Input.IsKeydown(_getKey(eLevitate)))
{
// Tell camera to levitate.
m_pCamera->StartLevitate();
m_bLevitate = true;
}
else if (m_bLevitate)
{
// Tell camera to stop levitating.
m_pCamera->StopLevitate();
m_bLevitate = false;
}
}
// Pitch up/down keys
if (m_Input.IsKeydown(_getKey(ePitchUp)))
{
// Set camera pitch up angle increment.
m_pCamera->Pitch(0.000003f * ulTimeDelUSec);
}
else if (m_Input.IsKeydown(_getKey(ePitchDown)))
{
// Set camera pitch down angle increment.
m_pCamera->Pitch(-0.000003f * ulTimeDelUSec);
}
// Yaw right/left keys
if (m_Input.IsKeydown(_getKey(eYawLeft)))
{
// Set camera yaw left angle increment.
m_pCamera->Yaw(0.000003f * ulTimeDelUSec);
}
else if (m_Input.IsKeydown(_getKey(eYawRight)))
{
// Set camera yaw right angle increment.
m_pCamera->Yaw(-0.000003f * ulTimeDelUSec);
}
// Jump key
if (m_Input.IsKeydown(_getKey(eJump)))
{
// Start jump motion only if camera is currently on the floor.
// Player can jump only if from a ground surface.
if (!m_bJump && m_pCamera->IsCamOnFloor())
{
// Adjust jump initial velocity and "gravity" acceleration based on what feels right.
// Note that windowed mode and full-screen mode time updating behave differently.
if (_isWindowed())
{
m_pCamera->Jump(2.2f, -0.08f);
}
else
{
m_pCamera->Jump(25.0f, -10.0f);
}
m_bJump = true;
}
}
else if (m_bJump)
{
m_bJump = false;
}
// Quit key
if (m_Input.IsKeydown(_getKey(eQuit)))
{
_closeApplication();
}
// Toggle fullscreen key
if (m_Input.IsKeydown(_getKey(eToggleScreen)))
{
_toggleFullScreen();
}
// Flashlight key
// Add time delay to flashlight key change do "de-bounce" fast key repeats.
if (m_Input.IsKeydown(_getKey(eFlashlight)) && (_getTime() - m_dwFLToggleTime) > 250)
{
m_pScene->ToggleFlashlight();
m_dwFLToggleTime = _getTime();
}
}
// Method to check current mouse state and perform appropriate mouse based functions.
void CApplication::_handleMouse()
{
// Mouse input is only used in full-screen mode. For windowed mode the mouse works
// normally for Windows applications (selecting menus, etc.).
if (_isWindowed())
return;
// Retrieve mouse left/right movement, as well as mouse button states.
LONG lXDel, lYDel;
bool bLeftBtn, bRightBtn;
m_Input.GetMouseInfo(lXDel, lYDel, bLeftBtn, bRightBtn);
if (bRightBtn)
{
// Do camera levitation movement if right mouse button is pressed.
m_pCamera->StartLevitate();
SetCursor(NULL);
m_bLevitate = true;
}
else if (m_bLevitate)
{
// Otherwise terminate any levitation movement (let gravity pull camera back to ground).
m_pCamera->StopLevitate();
m_bLevitate = false;
}
if (lXDel == 0 && lYDel == 0)
return;
// Mouse movement adjustment, sort of based on screen aspect.
// Make adjustments based on what feels right.
// @todo - Think about mouse motion smoothing.
float fXAdj = (float)lXDel / 4.0f;
float fYAdj = (float)lYDel / 3.0f;
// Update camera yaw and pitch angles based on mouse movement and static rate values.
// Adjust static rate values based on what feels right.
float fYawDelAngle = -fXAdj * s_fYawRate;
float fPitchDelAngle = -fYAdj * s_fPitchRate;
// Set camera yaw and pitch angle changes.
m_pCamera->Pitch(fPitchDelAngle);
m_pCamera->Yaw(fYawDelAngle);
}
void CApplication::_handleClose()
{
_saveSettings();
}
// Create and initialize all objects needed to load and render HL levels.
bool CApplication::_initializeEngineObjects()
{
// Create render object
m_pRenderer = ObjectFactory::CreateDirectXRenderer();
assert(m_pRenderer);
if (!m_pRenderer)
return false;
// Initialize render object
if (!m_pRenderer->Initialize(m_hWnd))
{
MessageBox(NULL, "Unable to initialize the D3D rendering system. Make sure your video hardware meets the requirements and that you have correct drivers.", "Renderer Initialization Error", MB_ICONERROR | MB_OK);
return false;
}
// Create camera object
m_pCamera = ObjectFactory::CreateFPSCamera();
assert(m_pCamera);
if (!m_pCamera)
return false;
// Set up default view information for camera
_setViewInfo();
// Set camera bounding boxes
m_pCamera->SetBB(s_vBB, s_fCrouchZ);
// Create scene object
m_pScene = ObjectFactory::CreateHFSceneGraph();
assert(m_pScene);
if (!m_pScene)
return false;
// Provide the scene object with the camera and renderer objects
m_pScene->SetCamera(m_pCamera);
m_pScene->SetRenderer(m_pRenderer);
return true;
}
// Update mouse and keyboard actions based on provide time change (time delta).
void CApplication::_updateInput(DWORD ulTimeDelUSec)
{
// Retrieve current input state.
m_Input.Update();
// Update keyboard state
_handleKeyboard(ulTimeDelUSec);
// Update mouse view
_handleMouse();
}
// Update input handling, camera position/orientation, and render level scene based on current
// camera.
void CApplication::_updateAndRender()
{
// Ignore if a scene has not been loaded yet.
if (m_bSceneLoaded)
{
// Get time delta in uSec
DWORD ulTimeDelUSec = _getTimeDelta();
// Don't bother updating if no significant time has passed.
if (ulTimeDelUSec > 0)
{
_updateInput(ulTimeDelUSec); // Update input time and handle mouse/keyboard actions.
m_pScene->UpdateCamera(ulTimeDelUSec); // Update camera position.
m_pScene->Render(); // Render scene for current camera position/orientation.
}
}
}
void CApplication::_resetBaseTime()
{
if (m_liCountFrequency.QuadPart != 0)
{
// High performance counter supported
QueryPerformanceCounter(&m_liBaseTime);
}
else
{
// Low resolution counter
m_liBaseTime.LowPart = timeGetTime();
}
}
DWORD CApplication::_getTimeDelta(bool bUpdateBase)
{
DWORD dwTimeDel;
if (m_liCountFrequency.QuadPart != 0)
{
// High performance counter supported
LARGE_INTEGER liStopCount;
LARGE_INTEGER liCountDel;
QueryPerformanceCounter(&liStopCount);
liCountDel.QuadPart = liStopCount.QuadPart - m_liBaseTime.QuadPart;
float fRenderTime = (float)liCountDel.QuadPart;
fRenderTime /= (float)m_liCountFrequency.QuadPart; // Time delta in Sec
dwTimeDel = (DWORD)(fRenderTime * 1000000); // Time delta in uSec
if (bUpdateBase)
m_liBaseTime.QuadPart = liStopCount.QuadPart;
}
else
{
// Low resolution counter
DWORD dwTime = timeGetTime();
dwTimeDel = (dwTime - m_liBaseTime.LowPart) * 1000; // Time delta in uSec
if (bUpdateBase)
m_liBaseTime.LowPart = dwTime;
}
return dwTimeDel; // uSec
}
DWORD CApplication::_getTime()
{
DWORD dwTime;
if (m_liCountFrequency.QuadPart != 0)
{
// High performance counter supported
LARGE_INTEGER liTime;
QueryPerformanceCounter(&liTime);
float fTime = (float)liTime.QuadPart;
fTime /= (float)m_liCountFrequency.QuadPart; // Time in Sec
dwTime = (DWORD)(fTime * 1000); // Time in mSec
}
else
{
// Low resolution counter
dwTime = timeGetTime(); // Time in mSec
}
return dwTime; // mSec
}
void CApplication::_initializeKeyMap()
{
// Create Keymap with default settings
delete m_pkeyMap;
m_pkeyMap = new Map<int, int, CompareIntType>(20, CompareKey);
assert(m_pkeyMap);
// @todo - Add UI to allow user to modify key controls.
m_pkeyMap->Add(eMoveForward, sWKey);
m_pkeyMap->Add(eMoveBackward, sSKey);
m_pkeyMap->Add(eMoveLeft, sAKey);
m_pkeyMap->Add(eMoveRight, sDKey);
m_pkeyMap->Add(eLevitate, sXKey);
m_pkeyMap->Add(ePitchUp, sUpKey);
m_pkeyMap->Add(ePitchDown, sDownKey);
m_pkeyMap->Add(eYawLeft, sLeftKey);
m_pkeyMap->Add(eYawRight, sRightKey);
m_pkeyMap->Add(eJump, sSpaceKey);
m_pkeyMap->Add(eCrouch, sLCtlKey);
m_pkeyMap->Add(eRun, sLShftKey);
m_pkeyMap->Add(eQuit, sESCKey);
m_pkeyMap->Add(eToggleScreen, sTABKey);
m_pkeyMap->Add(eFlashlight, sFKey);
}
HMENU CApplication::_createSimpleMenu()
{
// Create menu bar
m_hMenubar = CreateMenu();
// Add simple file and options menus
// File
// ->Open
// ->Exit
//
// Options
// ->Render Options
// ->Full Screen
m_hFileSubMenu = CreateMenu();
AppendMenu(m_hFileSubMenu, MF_STRING, mnuFileOpen, szFileOpen);
AppendMenu(m_hFileSubMenu, MF_SEPARATOR, mnuFileSep1, NULL);
AppendMenu(m_hFileSubMenu, MF_STRING, mnuFileExit, szFileExit);
m_hOptionsSubMenu = CreateMenu();
AppendMenu(m_hOptionsSubMenu, MF_STRING, mnuOptionsDisplay, szOptionsDisplay);
AppendMenu(m_hOptionsSubMenu, MF_STRING, mnuOptionsFullScreen, szOptionsFullScreen);
AppendMenu(m_hMenubar, MF_STRING|MF_POPUP, (UINT_PTR)m_hFileSubMenu, szFileMenu);
AppendMenu(m_hMenubar, MF_STRING|MF_POPUP, (UINT_PTR)m_hOptionsSubMenu, szOptionsMenu);
if (!m_bSceneLoaded)
{
EnableMenuItem(m_hOptionsSubMenu, mnuOptionsDisplay, MF_GRAYED);
EnableMenuItem(m_hOptionsSubMenu, mnuOptionsFullScreen, MF_GRAYED);
}
return m_hMenubar;
}
inline void CApplication::_centerWindowInShell(RECT & windowRect)
{
RECT screenRect;
GetWindowRect(GetShellWindow(), &screenRect);
UINT uWidth = (windowRect.right - windowRect.left);
UINT uHeight = (windowRect.bottom - windowRect.top);
windowRect.left = ((screenRect.right - screenRect.left) - uWidth) / 2;
windowRect.top = ((screenRect.bottom - screenRect.top) - uHeight) / 2;
windowRect.left = (windowRect.left < 0) ? 0 : windowRect.left;
windowRect.top = (windowRect.top < 0) ? 0 : windowRect.top;
windowRect.right = windowRect.left + uWidth;
windowRect.bottom = windowRect.top + uHeight;
}
// Sets the view information of the camera object based on static settings and the aspect
// ratio of the display window.
void CApplication::_setViewInfo()
{
assert(m_pCamera);
assert(m_pRenderer);
// Use the window aspect ratio to set the view aspect correctly.
UINT uHeight;
UINT uWidth;
const CD3DSettings & d3dSettings = m_pRenderer->GetDisplaySettings();
if (d3dSettings.IsWindowed)
{
uHeight = m_nClientHeight;
uWidth = m_nClientWidth;
}
else
{
uHeight = d3dSettings.Fullscreen_DisplayMode.Height;
uWidth = d3dSettings.Fullscreen_DisplayMode.Width;
}
float fAspect = ((float)uHeight) / ((float)uWidth);
m_pCamera->SetView(fAspect, s_fFocalL, s_fNearD, s_fFarD);
}
// Helper method that performs all functions needed to change between windowed mode and
// full screen mode display.
void CApplication::_changeWindowedMode(bool bIsWindowed)
{
m_bWindowedMode = bIsWindowed;
if (m_bWindowedMode)
{
// Set windowed-mode style
SetWindowLong(m_hWnd, GWL_STYLE, s_WinStyle);
HMENU hMenu = _createSimpleMenu();
if (hMenu != NULL)
SetMenu(m_hWnd, hMenu);
RECT windowRect = {0, 0, m_nClientWidth-1, m_nClientHeight-1};
AdjustWindowRect(&windowRect, s_WinStyle, true);
_centerWindowInShell(windowRect);
SetWindowPos(m_hWnd, HWND_NOTOPMOST,
windowRect.left, windowRect.top,
(windowRect.right - windowRect.left ),
(windowRect.bottom - windowRect.top),
SWP_SHOWWINDOW);
}
else
{
// Set fullscreen-mode style
SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE);
SetMenu(m_hWnd, NULL);
}
}
void CApplication::_saveSettings()
{
if (!m_bWindowedMode)
return;
HKEY hKey;
RECT rcClient;
GetClientRect(m_hWnd, &rcClient);
m_nClientWidth = rcClient.right - rcClient.left;
m_nClientHeight = rcClient.bottom - rcClient.top;
if (m_nClientWidth < 640 || m_nClientHeight < 480)
{
m_nClientWidth = 640;
m_nClientHeight = 480;
}
// Save and restore only window client size for now
if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, szAppKey,
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) )
{
RegSetValueEx(hKey, "Width", 0, REG_DWORD, (BYTE *)&m_nClientWidth, sizeof(DWORD));
RegSetValueEx(hKey, "Height", 0, REG_DWORD, (BYTE *)&m_nClientHeight, sizeof(DWORD));
RegCloseKey(hKey);
}
}
void CApplication::_readSettings()
{
HKEY hKey;
// Save and restore only window client size for now
if (ERROR_SUCCESS == RegCreateKeyEx( HKEY_CURRENT_USER, szAppKey,
0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) )
{
DWORD dwType;
DWORD dwLength = sizeof(DWORD);
DWORD dwDest;
if ( ERROR_SUCCESS == RegQueryValueEx(hKey, "Width", 0, &dwType, (BYTE*)(&dwDest), &dwLength) &&
dwType == REG_DWORD )
{
m_nClientWidth = (int)dwDest;
}
else
{
m_nClientWidth = s_nDefClientWidth;
}
if ( ERROR_SUCCESS == RegQueryValueEx(hKey, "Height", 0, &dwType, (BYTE*)(&dwDest), &dwLength) &&
dwType == REG_DWORD )
{
m_nClientHeight = (int)dwDest;
}
else
{
m_nClientHeight = s_nDefClientHeight;
}
RegCloseKey(hKey);
m_nClientWidth = (m_nClientWidth < 640) ? 640 : m_nClientWidth;
m_nClientHeight = (m_nClientHeight < 480) ? 480 : m_nClientHeight;
}
}
//
// CApplication window message proc handler
//
LRESULT CApplication::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
_handleClose();
PostMessage(hWnd, WM_QUIT, 0, 0);
break;
case WM_COMMAND:
if (HIWORD(wParam) == 0) // Indicates a menu item command
{
UINT uMenuID = LOWORD(wParam);
switch (uMenuID)
{
case mnuFileOpen:
_openFile();
break;
case mnuFileExit:
_closeApplication();
break;
case mnuOptionsDisplay:
_displaySettings();
break;
case mnuOptionsFullScreen:
_toggleFullScreen();
break;
default:
break;
}
}
break;
case WM_ENTERMENULOOP:
// Pause game
_pause();
break;
case WM_EXITMENULOOP:
// Resume game
_resume();
break;
case WM_SIZE:
switch (wParam)
{
case SIZE_MINIMIZED:
_pause();
break;
case SIZE_MAXIMIZED:
_resume();
break;
case SIZE_RESTORED:
_resume();
break;
}
break;
case WM_MOUSEMOVE:
// Normally hide cursor in client area, unless user has right button clicked
if (wParam & MK_LBUTTON)
{
if (GetCursor() == NULL)
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
else if (m_bSceneLoaded)
{
if (!_isWindowed())
SetCursor(NULL);
}
break;
case WM_ACTIVATE:
m_Input.AcquireMouse();
m_Input.AcquireKeyboard();
break;
case WM_GETMINMAXINFO:
((MINMAXINFO*)lParam)->ptMinTrackSize.x = s_nDefClientWidth;
((MINMAXINFO*)lParam)->ptMinTrackSize.y = s_nDefClientHeight;
break;
case WM_SYSCOMMAND:
// Prevent moving/sizing and power loss in fullscreen mode
switch (wParam)
{
case SC_MOVE:
case SC_SIZE:
case SC_MAXIMIZE:
case SC_KEYMENU:
case SC_MONITORPOWER:
if (!_isWindowed())
return 1;
break;
}
break;
case WM_SETCURSOR:
// Turn off Windows cursor in fullscreen mode
if (_isActive() && !_isWindowed())
{
SetCursor(NULL);
return true; // prevent Windows from setting cursor to window class cursor
}
break;
}
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//
// Main window call back function
//
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
// Obtain pointer to window application class
CApplication * pApplication = CApplication::GetApplicationInstance();
return pApplication->MsgProc(hWnd, uMsg, wParam, lParam);
}