APP.CPP

//--------------------------------------------------------------------------- 
// App.cpp
//---------------------------------------------------------------------------
// Shell for sample spr program
//---------------------------------------------------------------------------
// (C) Copyright 1992-1997 by Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//---------------------------------------------------------------------------

#include "Main.h"
#pragma hdrstop
#include "App.h"
#include "MsgLoop.h"
#include "Game.h"
#include <stdio.h>


//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE


//---------------------------------------------------------------------------
// Prototypes
//---------------------------------------------------------------------------
BOOL CALLBACK AppDlgProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
LONG CALLBACK PSWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);


//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------
HINSTANCE g_hinst = NULL;
ITypeLib *g_ptlMain = NULL; // Cache of TypeLib
CApp *g_papp = NULL;
IUnknown *g_punkApplicationNA = NULL; // Not AddRef()'d
char *g_pszCodeFile = NULL;
char *g_pszRecFile = NULL;
FILE *g_pfileRec = NULL;
int g_mode = MODE_NORMAL;
CMGRRINFO App_crinfo =
{
sizeof(CMGRRINFO), // size of CMGRRINFO structure in bytes.
0, // Don't need idle time
cmgrrfPreTranslateAll, // Need pretranslate
cmgradvfModal // Need modal notifications
};
HRESULT CApp::s_hr = E_FAIL;


//---------------------------------------------------------------------------
// Helpers for little vs. bit endian
//---------------------------------------------------------------------------

#undef BIGENDIAN // change for big-endian systems

typedef union
{
DWORD i4;
struct
{
char b1;
char b2;
char b3;
char b4;
};
} I4_SW;

#ifndef BIGENDIAN
#define NORMALIZE_I4(i4) (i4)
#else
#define NORMALIZE_I4(i4) \
{ \
I4_SW l=i4; \
(i4).b1 = l.b4; \
(i4).b2 = l.b3; \
(i4).b3 = l.b2; \
(i4).b4 = l.b1; \
}
#endif

typedef struct
{
char win;
char msg;
I4_SW wp;
I4_SW lp;
} REC;


//---------------------------------------------------------------------------
// Write out one message.
//---------------------------------------------------------------------------
#define RECORD(win, msg, wp, lp) {if (g_mode == MODE_RECORD) Record(win,msg,wp,lp);}
#define WIN_DLG ((char)0xdd) // Dlg
#define WIN_PS ((char)0xee) // playsurfacE
#define WIN_ABOUT ((char)0xaa) // About

void Record
(
char win,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
REC rec;

if (!g_pfileRec)
{
g_pfileRec = fopen(g_pszRecFile, "wb");
if (!g_pfileRec)
{
MessageBox(NULL, "Could not open record file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
exit(1);
}
}

switch (msg)
{
default:
return;

case WM_COMMAND:
msg = 1;
break;
case WM_SYSCOMMAND:
msg = 2;
break;
case WM_TIMER:
msg = 3;
break;
case WM_CHAR:
msg = 4;
break;
case WM_KEYDOWN:
msg = 5;
break;
case WM_KEYUP:
msg = 6;
break;
case WM_MOUSEMOVE:
msg = 7;
break;
case WM_LBUTTONUP:
msg = 8;
break;
case WM_RBUTTONUP:
msg = 9;
break;
case WM_MBUTTONUP:
msg = 10;
break;
case WM_LBUTTONDOWN:
msg = 11;
break;
case WM_RBUTTONDOWN:
msg = 12;
break;
case WM_MBUTTONDOWN:
msg = 13;
break;
}

rec.win = win;
rec.msg = (char)msg;
rec.wp.i4 = wp;
NORMALIZE_I4(rec.wp); // Handle byte-swapping for little vs. big endian.
rec.lp.i4 = lp;
NORMALIZE_I4(rec.lp); // Handle byte-swapping for little vs. big endian.
fwrite(&rec, sizeof(rec), 1, g_pfileRec);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT CApp::Playback
(
void
)
{
REC rec;
HWND hwnd;
UINT msg;

if (!g_pfileRec)
{
g_pfileRec = fopen(g_pszRecFile, "rb");
if (!g_pfileRec)
{
MessageBox(NULL, "Could not open playback file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
return E_FAIL;
}
}

while (TRUE)
{
size_t cch = fread(&rec, sizeof(rec), 1, g_pfileRec);
if (cch != 1)
{
if (feof(g_pfileRec))
{
fclose(g_pfileRec);
g_pfileRec = NULL;
g_mode = MODE_NORMAL;
m_fQuit = TRUE;
return S_OK;
}
MessageBox(NULL, "Unexpected read error in playback file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
return E_FAIL;
}

switch (rec.win)
{
case WIN_DLG:
hwnd = m_hwndDlg;
break;

case WIN_PS:
hwnd = m_hwndPS;
break;

case WIN_ABOUT:
hwnd = m_hwndAbout;
break;
}

UINT mpimsg[] = {0, WM_COMMAND, WM_SYSCOMMAND, WM_TIMER, WM_CHAR, WM_KEYDOWN, WM_KEYUP,
WM_MOUSEMOVE, WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP, WM_LBUTTONDOWN,
WM_RBUTTONDOWN, WM_MBUTTONDOWN};
if (rec.msg <= 0 || rec.msg >= sizeof(mpimsg)/sizeof(mpimsg[0]))
{
MessageBox(NULL, "Bad msg # in playback file", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
return E_FAIL;
}
msg = mpimsg[rec.msg];
NORMALIZE_I4(rec.wp); // Handle byte-swapping for little vs. big endian.
NORMALIZE_I4(rec.lp); // Handle byte-swapping for little vs. big endian.

SendMessage(hwnd, msg, (WPARAM)rec.wp.i4, (LPARAM)rec.lp.i4);
if (!m_pmsgloop->FPushMessageLoop(g_papp->m_idcomp, cmgrloopDoEvents, NULL))
return E_FAIL;
}

return S_OK;
}


//---------------------------------------------------------------------------
// Helper to parse command-line parameters
//---------------------------------------------------------------------------
HRESULT ParseCmdLine
(
char *pszCmdLine
)
{
if (!pszCmdLine)
goto DoError;

if (pszCmdLine[0] == '/')
{
if (pszCmdLine[1] == 'P')
{
g_mode = MODE_PLAYBACK;
goto GetFile;
}
else if (pszCmdLine[1] == 'R')
{
g_mode = MODE_RECORD;
GetFile:
if (pszCmdLine[2] <= ' ')
goto DoError;
pszCmdLine += 2;
g_pszRecFile = pszCmdLine;
while (*pszCmdLine > ' ') // Skip non-white space
pszCmdLine++;
if (!*pszCmdLine) // End Of String
goto DoError; // Needed <fileCode>
*pszCmdLine = 0;
pszCmdLine++;
srand(1); // Seed random # generator
}
else
goto DoError;
}

// Skip white space
while (*pszCmdLine && *pszCmdLine <= ' ')
pszCmdLine++;
if (!*pszCmdLine)
{
DoError:
MessageBox(NULL, "Usage: Spruuids [/P<filePlay> | /R<fileRec>] <fileCode>, where <fileCode> is a VB Script", "Spruuids", MB_OK | MB_ICONEXCLAMATION);
return E_FAIL;
}

// Rest of Cmd Line should be source file
g_pszCodeFile = pszCmdLine;
return S_OK;
}


//---------------------------------------------------------------------------
// Main program
//---------------------------------------------------------------------------
int WINAPI WinMain
(
HINSTANCE hinst, // Instance handle of current instance
HINSTANCE hinstPrev, // Instance handle of previous instance
LPSTR lpszCmdLine, // Null-terminated command line
int iCmdShow // How window should be initially displayed
)
{
int ret = 1; // Assume non-normal exit
BOOL fInit = FALSE;
HRESULT hr;

// Stuff hinst in global for all to see
g_hinst = hinst;
hr = ParseCmdLine(lpszCmdLine);
if (hr)
goto CleanUp;

// Initialize OLE
hr = CoInitialize(NULL);
if (hr)
goto CleanUp;
fInit = TRUE;

// Create the CApp object, since it runs the show, note it inits g_papp
hr = CApp::CreateApp(hinst);
if (hr)
goto CleanUp;
ASSERT(g_papp, "!hr but g_papp==NULL");

// Start a new game
g_papp->CausePause(0);
AppEvt_NewGame();

// Either Playback or push the main msg loop
if (g_mode == MODE_PLAYBACK)
hr = g_papp->Playback();
else
hr = g_papp->MainMsgLoop();
if (hr)
goto CleanUp;

// Normal exit
ret = 0;

// Cleanup, if we haven't already done so
CleanUp:
if (g_pfileRec)
{
fclose(g_pfileRec);
g_pfileRec = NULL;
}
if (g_papp)
delete g_papp;
if (fInit)
CoUninitialize();
return ret;
}


//***************************************************************************
// Constructor and Destructor support for CApp
//***************************************************************************

//---------------------------------------------------------------------------
// Creates an instance of CApp. Use this instead of "new", as this returns
// errors, etc.
//---------------------------------------------------------------------------
HRESULT CApp::CreateApp
(
HINSTANCE hinst
)
{
if (g_papp)
return E_UNEXPECTED;

CApp *papp = new CApp(hinst);
if (!papp)
return E_OUTOFMEMORY;
if (papp->s_hr)
return papp->s_hr;

return S_OK;
}


//---------------------------------------------------------------------------
// Constructor
//---------------------------------------------------------------------------
CApp::CApp
(
HINSTANCE hinst
)
{
WNDCLASS cls;
RECT rect;
int cx, cy;

INIT_SIGNATURE(SIG_App);

// Init globals
g_papp = this;
g_punkApplicationNA = this->GetUnknown(); // GetUnknown() doesn't AddRef, but we don't want to anyway.
s_hr = E_FAIL; // Assume failure

// Init members
m_pmsgloop = NULL;
m_pgame = NULL;
m_hwndDlg = NULL;
m_hwndPS = NULL;
m_hwndStat = NULL;
m_hwndAbout = NULL;
m_hinst = hinst;
m_idcomp = 0xffffffff;
m_fRegisteredComp = FALSE;
m_cmodal = 0;
m_fQuit = FALSE;
m_cref = 1;
m_ptinfoCls = NULL;
m_ptinfoInt = NULL;
m_pdispBaseObject = NULL;

// Add new class for dialog which is our application:
cls.style = 0;
cls.lpfnWndProc = DefDlgProc;
cls.cbClsExtra = 0;
cls.cbWndExtra = 0;
cls.hInstance = hinst;
cls.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(ID_ICO_APP));
cls.hCursor = NULL;
cls.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
cls.lpszMenuName = NULL;
cls.lpszClassName = "DlgClass";
if (!RegisterClass(&cls))
return;

// Add new class for play surface:
cls.style = CS_OWNDC;
cls.lpfnWndProc = PSWndProc;
cls.cbClsExtra = 0;
cls.cbWndExtra = 0;
cls.hInstance = hinst;
cls.hIcon = NULL;
cls.hCursor = LoadCursor(hinst, MAKEINTRESOURCE(ID_CUR_PLAYSURF));
cls.hbrBackground = NULL;
cls.lpszMenuName = NULL;
cls.lpszClassName = "PlaySurface";
if (!RegisterClass(&cls))
return;

// Now that we've registerd the window classes, create the main window
m_hwndDlg = CreateDialog(hinst, MAKEINTRESOURCE(ID_DLG_APP), NULL, AppDlgProc);
if (!m_hwndDlg)
{
s_hr = E_OUTOFMEMORY;
return;
}

// Center Dialog:
GetWindowRect(m_hwndDlg, &rect);
rect.right -= rect.left;
rect.bottom -= rect.top;
cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);
MoveWindow(m_hwndDlg, (cx - rect.right) >> 1, (cy - rect.bottom) >> 1, rect.right, rect.bottom, TRUE);

// Create the Component Manager
m_pmsgloop = new CMsgLoop(m_hwndDlg);
if (!m_pmsgloop)
{
s_hr = E_OUTOFMEMORY;
return;
}

m_fRegisteredComp = m_pmsgloop->FRegisterComponent(this, &App_crinfo, &m_idcomp);
if (!m_fRegisteredComp)
return;

HRESULT hr = this->LoadGame();
if (hr)
{
s_hr = hr;
return;
}

// Success
s_hr = S_OK;
ShowWindow(m_hwndDlg, SW_NORMAL);
return;
}


//---------------------------------------------------------------------------
// Destructor
//---------------------------------------------------------------------------
CApp::~CApp
(
void
)
{
CHECK_SIGNATURE(SIG_App);

g_papp = NULL;
g_punkApplicationNA = NULL; // Wasn't AddRef()'d, so don't Release().

if (m_hwndDlg)
{
ShowWindow(m_hwndDlg, SW_HIDE);
ReleaseCapture();
DestroyWindow(m_hwndDlg);
m_hwndDlg = NULL;
}

if (m_pgame)
{
delete m_pgame;
m_pgame = NULL;
}

if (m_fRegisteredComp)
{
ASSERT(m_pmsgloop, "m_idcomp, but !m_pmsgloop");
BOOL f = m_pmsgloop->FRevokeComponent(m_idcomp);
ASSERT(!f, "!f");
m_fRegisteredComp = FALSE;
}

if (m_pmsgloop)
{
m_pmsgloop->Release();
m_pmsgloop = NULL;
}

// Free dynamically created TypeInfo / TypeLib stuff
if (g_ptinfoClsGame)
g_ptinfoClsGame->Release();
if (g_ptinfoIntGame)
g_ptinfoIntGame->Release();

if (g_ptlGameSubObj)
g_ptlGameSubObj->Release();

// Free TypeInfo / TypeLib loaded from our resources
if (g_ptinfoClsGameOA)
g_ptinfoClsGameOA->Release();
if (g_ptinfoIntGameOA)
g_ptinfoIntGameOA->Release();

if (g_ptinfoClsSpriteClass)
g_ptinfoClsSpriteClass->Release();
if (g_ptinfoIntSpriteClass)
g_ptinfoIntSpriteClass->Release();

if (g_ptinfoClsSprite)
g_ptinfoClsSprite->Release();
if (g_ptinfoIntSprite)
g_ptinfoIntSprite->Release();

if (m_ptinfoCls)
m_ptinfoCls->Release();
if (m_ptinfoInt)
m_ptinfoInt->Release();

if (g_ptlMain)
g_ptlMain->Release();

DESTROY_SIGNATURE(SIG_App);
}


//***************************************************************************
// General methods
//***************************************************************************

//---------------------------------------------------------------------------
// Enter main message loop for application
//---------------------------------------------------------------------------
HRESULT CApp::MainMsgLoop
(
void
)
{
if (!m_pmsgloop->FPushMessageLoop(g_papp->m_idcomp, cmgrloopDoEvents, NULL))
return E_FAIL;

return S_OK;
}


//---------------------------------------------------------------------------
// Load a game from the given file.
//---------------------------------------------------------------------------
HRESULT CApp::LoadGame
(
void
)
{
if (m_pgame)
return E_UNEXPECTED;

return CGame::CreateGame(g_hinst, m_hwndDlg, m_hwndPS, m_hwndStat, m_pmsgloop, &m_pgame);
}


//---------------------------------------------------------------------------
// Get rid of an existing game, if present.
//---------------------------------------------------------------------------
void CApp::CloseGame
(
void
)
{
if (m_pgame)
{
m_pgame->Close();
m_pgame->Release();
m_pgame = NULL;
}
}


//---------------------------------------------------------------------------
// Called when pausing or unpausing
//---------------------------------------------------------------------------
void CApp::CausePause
(
int p // -1=Toggle, 0=UnPause, 1=Pause
)
{
// Invoke game.cpp's Pause event. Returns whether or not app is paused
p = AppEvt_Pause(p);
}


//***************************************************************************
// DlgProcs, WndProcs
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CALLBACK AppDlgProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
switch (msg)
{
case WM_INITDIALOG:
{
g_papp->m_hwndDlg = hwnd;
g_papp->m_hwndPS = GetDlgItem(hwnd, ID_CTL_PLAYSURF);
g_papp->m_hwndStat = GetDlgItem(hwnd, ID_CTL_STATUS);
break;
}

case WM_ACTIVATE:
if (LOWORD(wp) != WA_INACTIVE)
{
g_papp->m_pmsgloop->OnComponentActivate(g_papp->m_idcomp);
SetFocus(g_papp->m_hwndPS);
return TRUE;
}
break;

case WM_SETFOCUS:
SetFocus(g_papp->m_hwndPS);
return TRUE;

case WM_QUERYDRAGICON:
return (BOOL)LoadIcon(g_papp->m_hinst, MAKEINTRESOURCE(ID_ICO_APP));

case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;

if (!IsIconic(hwnd))
return FALSE;
hdc = wp ? (HDC)wp : BeginPaint(hwnd, &ps);
DrawIcon(hdc, 0, 0, LoadIcon(g_papp->m_hinst, MAKEINTRESOURCE(ID_ICO_APP)));
if (!wp)
EndPaint(hwnd, &ps);
return TRUE;
}

case WM_ERASEBKGND:
{
if (IsIconic(hwnd))
{
DefWindowProc(hwnd, msg, wp, lp);
return TRUE;
}

// Draw fancy boarders
RECT rect;

GetClientRect(hwnd, &rect);
DrawEdge((HDC)wp, &rect, 0, BF_RECT | BF_MIDDLE);

GetWindowRect(g_papp->m_hwndPS, &rect);
ScreenToClient(hwnd, (LPPOINT)&rect);
ScreenToClient(hwnd, (LPPOINT)&rect.right);
InflateRect(&rect, 2, 2);
DrawEdge((HDC)wp, &rect, EDGE_SUNKEN, BF_RECT);

GetWindowRect(g_papp->m_hwndStat, &rect);
InflateRect(&rect, 2, 2);
ScreenToClient(hwnd, (LPPOINT)&rect);
ScreenToClient(hwnd, (LPPOINT)&rect.right);
DrawEdge((HDC)wp, &rect, BDR_SUNKENOUTER, BF_RECT);
break;
}

case WM_SYSCOMMAND:
RECORD(WIN_DLG, msg, wp, lp);
switch (wp & 0xfff0)
{
case SC_CLOSE:
if (g_papp->FQueryTerminate(TRUE))
g_papp->m_fQuit = TRUE;
break;
}
return FALSE;

case WM_SIZE:
if (IsIconic(hwnd))
g_papp->CausePause(TRUE);
break;

case WM_COMMAND:
RECORD(WIN_DLG, msg, wp, lp);
if (!LOWORD(lp) || HIWORD(lp) == 1)
switch (wp)
{
case ID_MENU_NEWGAME:
g_papp->CausePause(1);
if (!g_papp->m_pgame->m_fGameOver && g_mode != MODE_PLAYBACK)
{
int ret;
g_papp->m_pmsgloop->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
ret = MessageBox(g_papp->m_hwndDlg, "Do you really want to start a new game?", "Spruuids", MB_YESNO | MB_ICONQUESTION);
g_papp->m_pmsgloop->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
SetFocus(g_papp->m_hwndPS);
if (ret == IDYES)
goto NewGame;
}
else
{
NewGame: g_papp->CausePause(0);
AppEvt_NewGame();
}
return TRUE;

case ID_MENU_EDIT:
{
char sz[255];
// Shell notepad to edit file we loaded.
g_papp->CausePause(1);
wsprintf(sz, "Notepad.exe %s", g_pszCodeFile);
WinExec(sz, SW_SHOWNORMAL);
return TRUE;
}

case ID_MENU_RELOAD:
g_papp->CausePause(1);
if (g_papp->m_pgame && !g_papp->m_pgame->m_fGameOver)
{
int ret;
g_papp->m_pmsgloop->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
ret = MessageBox(g_papp->m_hwndDlg, "Do you really want to start a new game?", "Spruuids", MB_YESNO | MB_ICONQUESTION);
g_papp->m_pmsgloop->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
SetFocus(g_papp->m_hwndPS);
if (ret == IDYES)
goto OpenGame;
}
else
{
OpenGame: g_papp->CloseGame();
g_papp->LoadGame();
g_papp->CausePause(0);
AppEvt_NewGame(); // Start a new game
}
return TRUE;

case ID_MENU_EXIT:
if (g_papp->FQueryTerminate(TRUE))
g_papp->m_fQuit = TRUE;
return TRUE;

case ID_MENU_PAUSE:
g_papp->CausePause(-1);
break;

case ID_MENU_ABOUT:
g_papp->CausePause(1);
g_papp->m_pmsgloop->OnComponentEnterState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL, NULL);
DialogBox(g_papp->m_hinst, MAKEINTRESOURCE(ID_DLG_ABOUT), g_papp->m_hwndDlg, AboutDlgProc);
g_papp->m_pmsgloop->FOnComponentExitState(g_papp->m_idcomp, cmgrstateModal, cmgrcontextAll, 0, NULL);
SetFocus(g_papp->m_hwndPS);
return TRUE;

case ID_MENU_HIGHSCORES:
case ID_MENU_HELPINDEX:
case ID_MENU_HELPPLAY:
case ID_MENU_HELPCOMMANDS:
case ID_MENU_HELPHELP:
return TRUE;
}
break;
}

return AppEvt_DlgProc(hwnd, msg, wp, lp);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CALLBACK AboutDlgProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
switch (msg)
{
case WM_INITDIALOG:
{
RECT rect;
RECT rect2;

g_papp->m_hwndAbout = hwnd;
GetWindowRect(g_papp->m_hwndDlg, &rect);
GetWindowRect(hwnd, &rect2);
MoveWindow(hwnd, (rect.right+rect.left)/2-(rect2.right-rect2.left)/2,
(rect.bottom+rect.top)/2-(rect2.bottom-rect2.top)/2,
rect2.right-rect2.left,
rect2.bottom-rect2.top,
TRUE);
return TRUE;
}

case WM_COMMAND:
RECORD(WIN_ABOUT, msg, wp, lp);
g_papp->m_hwndAbout = NULL;
EndDialog(hwnd, TRUE);
return TRUE;
}
return FALSE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
LRESULT CALLBACK PSWndProc
(
HWND hwnd,
UINT msg,
WPARAM wp,
LPARAM lp
)
{
switch (msg)
{
case WM_TIMER:
case WM_CHAR:
case WM_KEYUP:
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
RECORD(WIN_PS, msg, wp, lp);
break;


case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
RECORD(WIN_PS, msg, wp, lp);
SetFocus(hwnd);
break;

case WM_GETDLGCODE:
return DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTMESSAGE | DLGC_WANTTAB;

case WM_KEYDOWN:
RECORD(WIN_PS, msg, wp, lp);
switch (wp)
{
case VK_ESCAPE:
ShowWindow(g_papp->m_hwndDlg, SW_MINIMIZE);
break;

case VK_F1:
SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_HELPINDEX, 0L);
break;

case VK_F2:
SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_NEWGAME, 0L);
break;

case VK_PAUSE:
case VK_F3:
SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_PAUSE, 0L);
break;

case VK_F5:
SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_RELOAD, 0L);
break;

case VK_F9:
SendMessage(g_papp->m_hwndDlg, WM_COMMAND, ID_MENU_EDIT, 0L);
break;
}
break;
}

return AppEvt_PSWndProc(hwnd, msg, wp, lp);
}


//***************************************************************************
// IUnknown Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::QueryInterface
(
REFIID iid,
void **ppvObjOut
)
{
if (!ppvObjOut)
return E_INVALIDARG;

*ppvObjOut = NULL;

if (iid == IID_IUnknown)
*ppvObjOut = this->GetUnknown();
else if (iid == IID_IDispatch)
*ppvObjOut = this->GetDispatch();
else if (iid == IID_ISpruuidsApp)
*ppvObjOut = (ISpruuidsApp *)this;
else if (iid == IID_IOleComponent)
*ppvObjOut = (IOleComponent *)this;

if (*ppvObjOut)
{
this->AddRef();
return S_OK;
}

return E_NOINTERFACE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CApp::AddRef
(
void
)
{
ASSERT(m_cref, "bad m_cref");
return ++m_cref;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CApp::Release
(
void
)
{
ASSERT(m_cref, "bad m_cref");
m_cref--;
if (!m_cref)
{
delete this;
return 0;
}
return m_cref;
}


//***************************************************************************
// IOleComponent Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CApp::FPreTranslateMessage
(
MSG *pmsg
)
{
return IsDialogMessage(m_hwndDlg, pmsg);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::OnEnterState
(
ULONG state,
BOOL fEnter
)
{
// TODO: We only care about Modal notifications. You application will likely
// TODO: need to support some of the others.

if (state == cmgrstateModal)
{
if (fEnter)
{
if (!m_cmodal)
{
this->CausePause(1); // Pause for the user, since we're modal
EnableWindow(m_hwndDlg, FALSE); // Entering modal state, so disable window
}
m_cmodal++;
}
else if (m_cmodal) // Don't decrement if zero!
{
m_cmodal--;
if (!m_cmodal)
EnableWindow(m_hwndDlg, TRUE); // Leaving modal state, so re-enable window
}
}
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::OnAppActivate
(
BOOL fActive,
DWORD dwOtherThreadID
)
{
// TODO: If you have floating palettes, etc., show them here.
SetFocus(m_hwndPS);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::OnLoseActivation
(
void
)
{
// TODO: If you have floating palettes, etc., hide them here.
this->CausePause(1);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------

STDMETHODIMP_(BOOL) CApp::FDoIdle 
(
DWORD grfidlef
)
{
// TODO: If you have need idle-time processing, do so here
return FALSE;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CApp::FContinueMessageLoop
(
ULONG uReason,
void *pvLoopData
)
{
MSG msg;

// If we're playing back a recording, return from msg loop once there are
// no more msgs.
if (g_mode == MODE_PLAYBACK && !PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
return FALSE;

// Since the only time we push a message loop is for the Main message loop,
// we don't want that one popped until we decide to exit the application.
return !m_fQuit;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(BOOL) CApp::FQueryTerminate
(
BOOL fPromptUser
)
{
// Disallow termination if modal dlg is up
if (m_cmodal)
{
if (fPromptUser)
MessageBox(NULL, "Please terminate Dlg before exiting", "Spruuids",
MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OK);
return FALSE;
}

return AppEvt_FQueryTerminate(fPromptUser);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP_(void) CApp::Terminate
(
void
)
{
// Unregister component & release all refs on Component Manager
if (m_fRegisteredComp)
{
ASSERT(m_pmsgloop, "m_idcomp, but !m_pmsgloop");
BOOL f = m_pmsgloop->FRevokeComponent(m_idcomp);
ASSERT(!f, "!f");
m_fRegisteredComp = FALSE;
}

if (m_pmsgloop)
{
m_pmsgloop->Release();
m_pmsgloop = NULL;
}

m_fQuit = TRUE;
}


//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// Method needed by COleAuto, so it can implement IDispatch for us.
//---------------------------------------------------------------------------
HRESULT CApp::GetTypeLibInfo
(
HINSTANCE *phinstOut,
const GUID **pplibidOut,
SHORT *pwMajLib,
SHORT *pwMinLib,
const CLSID **ppclsidOut,
const IID **ppiidOut,
ITypeLib ***ppptlOut
)
{
*phinstOut = g_hinst;
*pplibidOut = &LIBID_SPRUUIDS;
*pwMajLib = 1;
*pwMinLib = 0;
*ppclsidOut = &CLSID_SpruuidsApp;
*ppiidOut = &IID_ISpruuidsApp;
*ppptlOut = &g_ptlMain;
return S_OK;
}


//***************************************************************************
// SpruuidsApp Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::get_Application
(
ISpruuidsApp** lppaReturn
)
{
return this->QueryInterface(IID_ISpruuidsApp, (void **)lppaReturn);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::get_Parent
(
ISpruuidsApp** lppaReturn
)
{
return this->QueryInterface(IID_ISpruuidsApp, (void **)lppaReturn);
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::Quit
(
void
)
{
m_fQuit = TRUE;

return S_OK;
}


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP CApp::get_Game
(
IGame** lppaReturn
)
{
return m_pgame->QueryInterface(IID_IGame, (void **)lppaReturn);
}


//--- EOF -------------------------------------------------------------------