4645 lines
111 KiB
C++
4645 lines
111 KiB
C++
/* Copyright (c) Microsoft Corporation.
|
|
Licensed under the MIT License. */
|
|
|
|
/***************************************************************************
|
|
|
|
utest.cpp: Socrates main app class
|
|
|
|
Primary Author: ******
|
|
Review Status: REVIEWED - any changes to this file must be reviewed!
|
|
|
|
The APP class handles initialization of the product, and global
|
|
actions such as resolution-switching, switching between the building
|
|
and the studio, and quitting.
|
|
|
|
The KWA (App KidWorld) is the parent of the gob tree in the product.
|
|
It also is used to display a splash screen, and to find AVIs on the CD.
|
|
|
|
***************************************************************************/
|
|
#include "studio.h"
|
|
#include "socres.h"
|
|
#include "mminstal.h"
|
|
|
|
ASSERTNAME
|
|
|
|
// If the following value is defined, 3DMM displays the dlidDesktopResizing
|
|
// and dlidDesktopResized dialogs before and immediately after res-switching.
|
|
// The current thought is that these dialogs are unnecessary since we only
|
|
// res-switch to 640x480, which should always be safe. We still display a
|
|
// dialog if the res-switch failed (dlidDesktopWontResize).
|
|
// #define RES_SWITCH_DIALOGS
|
|
|
|
|
|
// If the following value is defined, 3DMM does a run-time performance test
|
|
// of the graphics, fixed-point math, and copying speed at startup and sets
|
|
// _fSlowCPU to fTrue if it thinks 3DMM is running on a slow computer.
|
|
// The current feeling is that we don't have the resources to tweak the
|
|
// thresholds and verify that this gives us the result we want on all
|
|
// computers.
|
|
//#define PERF_TEST
|
|
|
|
// Duration to display homelogo
|
|
const ulong kdtsHomeLogo = 4 * kdtsSecond;
|
|
|
|
// Duration to display splash screen
|
|
const ulong kdtsSplashScreen = 4 * kdtsSecond;
|
|
|
|
// Duration before res-switch dialog cancels itself
|
|
const ulong kdtsMaxResSwitchDlg = 15 * kdtsSecond;
|
|
|
|
// 2MB cache per source for TAGM
|
|
const ulong kcbCacheTagm = 2048 * 1024;
|
|
|
|
static PSZ kpszAppWndCls = PszLit("3DMOVIE");
|
|
const PSZ kpszOpenFile = PszLit("3DMMOpen.tmp");
|
|
|
|
const long klwOpenDoc = 0x12123434; // arbitrary wParam for WM_USER
|
|
|
|
BEGIN_CMD_MAP(APP, APPB)
|
|
ON_CID_GEN(cidInfo, FCmdInfo, pvNil)
|
|
ON_CID_GEN(cidLoadStudio, FCmdLoadStudio, pvNil)
|
|
ON_CID_GEN(cidLoadBuilding, FCmdLoadBuilding, pvNil)
|
|
ON_CID_GEN(cidTheaterOpen, FCmdTheaterOpen, pvNil)
|
|
ON_CID_GEN(cidTheaterClose, FCmdTheaterClose, pvNil)
|
|
ON_CID_GEN(cidPortfolioOpen, FCmdPortfolioOpen, pvNil)
|
|
ON_CID_GEN(cidPortfolioClear, FCmdPortfolioClear, pvNil)
|
|
ON_CID_GEN(cidDisableAccel, FCmdDisableAccel, pvNil)
|
|
ON_CID_GEN(cidEnableAccel, FCmdEnableAccel, pvNil)
|
|
ON_CID_GEN(cidInvokeSplot, FCmdInvokeSplot, pvNil)
|
|
ON_CID_GEN(cidExitStudio, FCmdExitStudio, pvNil)
|
|
ON_CID_GEN(cidDeactivate, FCmdDeactivate, pvNil)
|
|
END_CMD_MAP_NIL()
|
|
|
|
|
|
APP vapp;
|
|
PTAGM vptagm;
|
|
|
|
RTCLASS(APP)
|
|
RTCLASS(KWA)
|
|
|
|
/***************************************************************************
|
|
Entry point for a Kauai-based app.
|
|
***************************************************************************/
|
|
void FrameMain(void)
|
|
{
|
|
Debug( vcactAV = 2; ) // Speeds up the debug build
|
|
vapp.Run(fappOffscreen, fgobNil, kginMark);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Run
|
|
Overridden APPB::Run method, so that we can attempt to recover
|
|
gracefully from a crash.
|
|
|
|
Arguments:
|
|
ulong grfapp -- app flags
|
|
ulong grfgob -- GOB flags
|
|
long ginDef -- default GOB invalidation
|
|
|
|
************************************************************ PETED ***********/
|
|
void APP::Run(ulong grfapp, ulong grfgob, long ginDef)
|
|
{
|
|
/* Don't bother w/ AssertThis; we'd have to use AssertBaseThis, or
|
|
possibly the parent's AssertValid, which gets done almost right
|
|
away anyway */
|
|
|
|
_CleanupTemp();
|
|
|
|
__try
|
|
{
|
|
APP_PAR::Run(grfapp, grfgob, ginDef);
|
|
}
|
|
__except (UnhandledExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
PDLG pdlg;
|
|
|
|
pdlg = DLG::PdlgNew(dlidAbnormalExit, pvNil, pvNil);
|
|
if (pdlg != pvNil)
|
|
{
|
|
pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
}
|
|
|
|
_fQuit = fTrue;
|
|
MVU::RestoreKeyboardRepeat();
|
|
#ifdef WIN
|
|
ClipCursor(NULL);
|
|
#endif // WIN
|
|
_CleanUp();
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Get the name for the app. Note that the string comes from a resource
|
|
in the exe rather than a chunky file since the app name is needed
|
|
before any chunky files are opened. The app name should not change
|
|
even for different products, i.e., Socrates and Playdo will have
|
|
different _stnProduct, but the same _stnAppName.
|
|
***************************************************************************/
|
|
void APP::GetStnAppName(PSTN pstn)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pstn, 0);
|
|
|
|
if (_stnAppName.Cch() == 0)
|
|
{
|
|
#ifdef WIN
|
|
SZ sz;
|
|
|
|
if (0 != LoadString(vwig.hinst, stidAppName, sz, kcchMaxSz))
|
|
_stnAppName = sz;
|
|
else
|
|
Warn("Couldn't read app name");
|
|
#else //MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
}
|
|
*pstn = _stnAppName;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
struct DBINFO
|
|
{
|
|
long cactAV;
|
|
};
|
|
#endif // DEBUG
|
|
|
|
|
|
/***************************************************************************
|
|
Init the APP
|
|
***************************************************************************/
|
|
bool APP::_FInit(ulong grfapp, ulong grfgob, long ginDef)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
ulong tsHomeLogo;
|
|
ulong tsSplashScreen;
|
|
FNI fniUserDoc;
|
|
long fFirstTimeUser;
|
|
|
|
// Only allow one copy of 3DMM to run at a time:
|
|
if (_FAppAlreadyRunning())
|
|
{
|
|
_TryToActivateWindow();
|
|
_fDontReportInitFailure = fTrue;
|
|
goto LFail;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
DBINFO dbinfo;
|
|
|
|
dbinfo.cactAV = vcactAV;
|
|
if (FGetSetRegKey(PszLit("DebugSettings"), &dbinfo, size(DBINFO),
|
|
fregSetDefault | fregBinary))
|
|
{
|
|
vcactAV = dbinfo.cactAV;
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
if (!_FEnsureOS())
|
|
goto LFail;
|
|
|
|
if (!_FEnsureAudio())
|
|
goto LFail;
|
|
|
|
if (!_FEnsureVideo())
|
|
goto LFail;
|
|
|
|
_ParseCommandLine();
|
|
|
|
// If _ParseCommandLine doesn't set _stnProduct, set to 3dMovieMaker
|
|
if (!_FEnsureProductNames())
|
|
goto LFail;
|
|
|
|
if (!_FFindMsKidsDir())
|
|
goto LFail;
|
|
|
|
// Init product names for tagman & _fniProductDir & potentially _stnProduct
|
|
if (!_FInitProductNames())
|
|
goto LFail;
|
|
|
|
if (!_FOpenResourceFile())
|
|
goto LFail;
|
|
|
|
if (!_FReadStringTables())
|
|
goto LFail;
|
|
|
|
if (!_FEnsureDisplayResolution()) // may call _FInitOS
|
|
goto LFail;
|
|
|
|
if (!_FEnsureColorDepth())
|
|
goto LFail;
|
|
|
|
if (!APP_PAR::_FInit(grfapp, grfgob, ginDef)) // calls _FInitOS
|
|
goto LFail;
|
|
|
|
/* Ensure default font. Do it here just so we get the error reported
|
|
early. */
|
|
OnnDefVariable();
|
|
|
|
if (!_FInitKidworld())
|
|
goto LFail;
|
|
|
|
if (!_FDisplayHomeLogo())
|
|
goto LFail;
|
|
tsHomeLogo = TsCurrent();
|
|
|
|
if (!_FDetermineIfSlowCPU())
|
|
goto LFail;
|
|
|
|
if (!_FInitTdt())
|
|
goto LFail;
|
|
|
|
if (!MTRL::FSetShadeTable(_pcfl, kctgTmap, 0))
|
|
goto LFail;
|
|
|
|
while (TsCurrent() - tsHomeLogo < kdtsHomeLogo)
|
|
; // spin until home logo has been up long enough
|
|
|
|
if (!_FShowSplashScreen())
|
|
goto LFail;
|
|
tsSplashScreen = TsCurrent();
|
|
|
|
if (!_FPlaySplashSound())
|
|
goto LFail;
|
|
|
|
if (!_FGetUserName())
|
|
goto LFail;
|
|
|
|
if (!_FGetUserDirectories())
|
|
goto LFail;
|
|
|
|
if (!_FReadUserData())
|
|
goto LFail;
|
|
|
|
if (!_FInitCrm())
|
|
goto LFail;
|
|
|
|
while (TsCurrent() - tsSplashScreen < kdtsSplashScreen)
|
|
; // spin until splash screen has been up long enough
|
|
Pkwa()->SetMbmp(pvNil); // bring down splash screen
|
|
|
|
// If the user specified a doc on the command line, go straight
|
|
// to the studio. Otherwise, start the building.
|
|
GetPortfolioDoc(&fniUserDoc);
|
|
|
|
if (fniUserDoc.Ftg() == ftgNil)
|
|
{
|
|
// Startup place depends on whether this is the user's first time in.
|
|
if (!FGetProp(kpridFirstTimeUser, &fFirstTimeUser))
|
|
goto LFail;
|
|
|
|
if (!FSetProp(kpridBuildingGob, (fFirstTimeUser ? kgobCloset : kgobLogin)))
|
|
goto LFail;
|
|
if (!_FInitBuilding())
|
|
goto LFail;
|
|
}
|
|
else
|
|
{
|
|
if (!FSetProp(kpridBuildingGob, kgobStudio1))
|
|
goto LFail;
|
|
if (!_FInitStudio(&fniUserDoc, fFalse))
|
|
goto LFail;
|
|
}
|
|
|
|
EnsureInteractive();
|
|
|
|
return fTrue;
|
|
LFail:
|
|
_fQuit = fTrue;
|
|
|
|
if (_fSwitchedResolution)
|
|
{
|
|
if (_FSwitch640480(fFalse)) // try to restore desktop
|
|
_fSwitchedResolution = fFalse;
|
|
}
|
|
|
|
// _fDontReportInitFailure will be true if one of the above functions
|
|
// has already posted an alert explaining why the app is shutting down.
|
|
// If it's false, we put up a OOM or generic error dialog.
|
|
if (!_fDontReportInitFailure)
|
|
{
|
|
PDLG pdlg;
|
|
|
|
if (vpers->FIn(ercOomHq) || vpers->FIn(ercOomPv) || vpers->FIn(ercOomNew))
|
|
pdlg = DLG::PdlgNew(dlidInitFailedOOM, pvNil, pvNil);
|
|
else
|
|
pdlg = DLG::PdlgNew(dlidInitFailed, pvNil, pvNil);
|
|
if (pvNil != pdlg)
|
|
pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
}
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
_CleanupTemp
|
|
Removes any temp files leftover from a previous execution.
|
|
|
|
************************************************************ PETED ***********/
|
|
void APP::_CleanupTemp(void)
|
|
{
|
|
FNI fni;
|
|
|
|
/* Attempt to cleanup any leftovers from a previous bad exit */
|
|
vftgTemp = kftgSocTemp;
|
|
if (fni.FGetTemp())
|
|
{
|
|
FTG ftgTmp = vftgTemp;
|
|
FNE fne;
|
|
|
|
if (fne.FInit(&fni, &ftgTmp, 1))
|
|
{
|
|
bool fFlushErs = fFalse;
|
|
|
|
while (fne.FNextFni(&fni))
|
|
{
|
|
if (!fni.FDelete())
|
|
fFlushErs = fTrue;
|
|
}
|
|
|
|
if (fFlushErs)
|
|
vpers->Flush(ercFniDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determine if another copy of 3DMM is already running by trying to
|
|
create a named semaphore.
|
|
***************************************************************************/
|
|
bool APP::_FAppAlreadyRunning(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
HANDLE hsem;
|
|
STN stn;
|
|
|
|
GetStnAppName(&stn);
|
|
|
|
hsem = CreateSemaphore(NULL, 0, 1, stn.Psz());
|
|
if (hsem != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
CloseHandle(hsem);
|
|
return fTrue;
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Try to find another instance of 3DMM and bring its window to front
|
|
Also, if a document was on the command line, notify the other instance
|
|
***************************************************************************/
|
|
void APP::_TryToActivateWindow(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
HWND hwnd;
|
|
STN stn;
|
|
FNI fniUserDoc;
|
|
|
|
GetStnAppName(&stn);
|
|
hwnd = FindWindow(kpszAppWndCls, stn.Psz());
|
|
if (NULL != hwnd)
|
|
{
|
|
SetForegroundWindow(hwnd);
|
|
ShowWindow(hwnd, SW_RESTORE); // in case it was minimized
|
|
_ParseCommandLine();
|
|
GetPortfolioDoc(&fniUserDoc);
|
|
if (fniUserDoc.Ftg() != ftgNil)
|
|
_FSendOpenDocCmd(hwnd, &fniUserDoc); // ignore failure
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
If we're not running on at least Win95 or NT 3.51, complain and return
|
|
fFalse.
|
|
***************************************************************************/
|
|
bool APP::_FEnsureOS(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
#ifdef WIN
|
|
DWORD dwVersion;
|
|
byte bVersionMajor;
|
|
byte bVersionMinor;
|
|
PDLG pdlg;
|
|
|
|
dwVersion = GetVersion();
|
|
bVersionMinor = (byte)((dwVersion & 0x0000ff00) >> 8);
|
|
bVersionMajor = (byte)(dwVersion & 0x000000ff);
|
|
|
|
if (bVersionMajor >= 4 || (bVersionMajor == 3 && bVersionMinor >= 51))
|
|
return fTrue;
|
|
|
|
// Put up an alert: OS too old
|
|
pdlg = DLG::PdlgNew(dlidBadOS, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fFalse;
|
|
pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
_fDontReportInitFailure = fTrue;
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fFalse; // Bad OS
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Notifies the user if there is no suitable wave-out and/or midi-out
|
|
hardware.
|
|
***************************************************************************/
|
|
bool APP::_FEnsureAudio(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
long cwod; // count of wave-out devices
|
|
long cmod; // count of midi-out devices
|
|
bool fShowMessage;
|
|
PDLG pdlg;
|
|
|
|
|
|
cwod = waveOutGetNumDevs();
|
|
if (cwod <= 0)
|
|
{
|
|
fShowMessage = fTrue;
|
|
if (!FGetSetRegKey(kszWaveOutMsgValue, &fShowMessage,
|
|
size(fShowMessage), fregSetDefault | fregMachine))
|
|
{
|
|
Warn("Registry query failed");
|
|
}
|
|
if (fShowMessage)
|
|
{
|
|
// Put up an alert: no waveout
|
|
pdlg = DLG::PdlgNew(dlidNoWaveOut, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fFalse;
|
|
pdlg->IditDo();
|
|
fShowMessage = !pdlg->FGetCheck(1); // 1 is ID of checkbox
|
|
ReleasePpo(&pdlg);
|
|
if (!fShowMessage)
|
|
{
|
|
// ignore failure
|
|
FGetSetRegKey(kszWaveOutMsgValue, &fShowMessage,
|
|
size(fShowMessage), fregSetKey | fregMachine);
|
|
}
|
|
}
|
|
}
|
|
if (HWD_SUCCESS == wHaveWaveDevice(WAVE_FORMAT_2M08)) // 22kHz, Mono, 8bit is our minimum
|
|
{
|
|
if(wHaveACM())
|
|
{
|
|
// audio compression manager (sound mapper) not installed
|
|
wInstallComp(IC_ACM);
|
|
_fDontReportInitFailure = fTrue;
|
|
return fFalse;
|
|
}
|
|
|
|
if(wHaveACMCodec(WAVE_FORMAT_ADPCM))
|
|
{
|
|
// audio codecs not installed
|
|
wInstallComp(IC_ACM_ADPCM);
|
|
_fDontReportInitFailure = fTrue;
|
|
return fFalse;
|
|
}
|
|
} // have wave device
|
|
|
|
cmod = midiOutGetNumDevs();
|
|
if (cmod <= 0)
|
|
{
|
|
fShowMessage = fTrue;
|
|
if (!FGetSetRegKey(kszMidiOutMsgValue, &fShowMessage,
|
|
size(fShowMessage), fregSetDefault | fregMachine))
|
|
{
|
|
Warn("Registry query failed");
|
|
}
|
|
if (fShowMessage)
|
|
{
|
|
// Put up an alert: no midiout
|
|
pdlg = DLG::PdlgNew(dlidNoMidiOut, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fFalse;
|
|
pdlg->IditDo();
|
|
fShowMessage = !pdlg->FGetCheck(1); // 1 is ID of checkbox
|
|
ReleasePpo(&pdlg);
|
|
if (!fShowMessage)
|
|
{
|
|
// ignore failure
|
|
FGetSetRegKey(kszMidiOutMsgValue, &fShowMessage,
|
|
size(fShowMessage), fregSetKey | fregMachine);
|
|
}
|
|
}
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Notifies the user if there is no suitable video playback devices
|
|
***************************************************************************/
|
|
bool APP::_FEnsureVideo(void)
|
|
{
|
|
if(wHaveMCI("AVIVIDEO"))
|
|
{
|
|
// MCI for video is not installed
|
|
wInstallComp(IC_MCI_VFW);
|
|
_fDontReportInitFailure = fTrue;
|
|
return fFalse;
|
|
}
|
|
|
|
if(HIC_SUCCESS != wHaveICMCodec(MS_VIDEO1))
|
|
{
|
|
// video 1 codec not installed
|
|
wInstallComp(IC_ICM_VIDEO1);
|
|
_fDontReportInitFailure = fTrue;
|
|
return fFalse;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Returns fTrue if color depth is >= 8 bits per pixel. User is alerted
|
|
if depth is less than 8, and function fails. User is warned if depth
|
|
is greater than 8, unless he has clicked the "don't show this messsage
|
|
again" in the dialog before.
|
|
***************************************************************************/
|
|
bool APP::_FEnsureColorDepth(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
HDC hdc;
|
|
long cbitPixel;
|
|
PDLG pdlg;
|
|
bool fShowMessage;
|
|
bool fDontShowAgain = fFalse;
|
|
|
|
hdc = GetDC(NULL);
|
|
if (NULL == hdc)
|
|
return fFalse;
|
|
cbitPixel = GetDeviceCaps(hdc, BITSPIXEL);
|
|
ReleaseDC(NULL, hdc);
|
|
|
|
if (cbitPixel < 8)
|
|
{
|
|
// Put up an alert: Not enough colors
|
|
pdlg = DLG::PdlgNew(dlidNotEnoughColors, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fFalse;
|
|
pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
_fDontReportInitFailure = fTrue;
|
|
return fFalse;
|
|
}
|
|
if (cbitPixel > 8)
|
|
{
|
|
// warn user
|
|
fShowMessage = fTrue;
|
|
if (!FGetSetRegKey(kszGreaterThan8bppMsgValue, &fShowMessage,
|
|
size(fShowMessage), fregSetDefault))
|
|
{
|
|
Warn("Registry query failed");
|
|
}
|
|
if (fShowMessage)
|
|
{
|
|
pdlg = DLG::PdlgNew(dlidTooManyColors, pvNil, pvNil);
|
|
if (pvNil != pdlg)
|
|
{
|
|
pdlg->IditDo();
|
|
fDontShowAgain = pdlg->FGetCheck(1); // 1 is ID of checkbox
|
|
}
|
|
ReleasePpo(&pdlg);
|
|
if (fDontShowAgain)
|
|
{
|
|
fShowMessage = fFalse;
|
|
FGetSetRegKey(kszGreaterThan8bppMsgValue, &fShowMessage,
|
|
size(fShowMessage), fregSetKey); // ignore failure
|
|
}
|
|
}
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Dialog proc for Resolution Switch dialog. Return fTrue (bring down
|
|
the dialog) if kdtsMaxResSwitchDlg has passed since the dialog was
|
|
created.
|
|
***************************************************************************/
|
|
bool _FDlgResSwitch(PDLG pdlg, long *pidit, void *pv)
|
|
{
|
|
AssertPo(pdlg, 0);
|
|
AssertVarMem(pidit);
|
|
AssertPvCb(pv, size(long));
|
|
|
|
long tsResize = *(long *)pv;
|
|
long tsCur;
|
|
|
|
if (*pidit != ivNil)
|
|
{
|
|
return fTrue; // Cancel or OK pressed
|
|
}
|
|
else
|
|
{
|
|
tsCur = TsCurrent();
|
|
if (tsCur - tsResize >= kdtsMaxResSwitchDlg)
|
|
return fTrue;
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Ensure that the screen is at the user's preferred resolution for 3DMM.
|
|
If user has no registry preference, we offer to switch, try to switch,
|
|
and save user's preference. Registry failures are non-fatal, but
|
|
failing to create the main window causes this function to fail.
|
|
|
|
When this function returns, _fRunInWindow is set correctly and the
|
|
main app window *might* be created.
|
|
***************************************************************************/
|
|
bool APP::_FEnsureDisplayResolution(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
PDLG pdlg;
|
|
long idit;
|
|
bool fSwitchRes;
|
|
bool fNoValue;
|
|
long tsResize;
|
|
|
|
if (_FDisplayIs640480())
|
|
{
|
|
// System is already 640x480, so ignore registry and run fullscreen
|
|
_fRunInWindow = fFalse;
|
|
return fTrue;
|
|
}
|
|
|
|
if (!_FDisplaySwitchSupported())
|
|
{
|
|
// System can't switch res, so ignore registry and run in a window
|
|
_fRunInWindow = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
// See if there's a res switch preference in the registry
|
|
if (!FGetSetRegKey(kszSwitchResolutionValue, &fSwitchRes,
|
|
size(fSwitchRes), fregNil, &fNoValue))
|
|
{
|
|
// Registry error...just run in a window
|
|
_fRunInWindow = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
if (!fNoValue)
|
|
{
|
|
// User has a preference
|
|
if (!fSwitchRes)
|
|
{
|
|
_fRunInWindow = fTrue;
|
|
return fTrue;
|
|
}
|
|
else // try to switch res
|
|
{
|
|
_fRunInWindow = fFalse;
|
|
if (!_FInitOS())
|
|
{
|
|
// we're screwed
|
|
return fFalse;
|
|
}
|
|
if (!_FSwitch640480(fTrue))
|
|
{
|
|
_fRunInWindow = fTrue;
|
|
_RebuildMainWindow();
|
|
goto LSwitchFailed;
|
|
}
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
// User doesn't have a preference yet. Do the interactive thing.
|
|
#ifdef RES_SWITCH_DIALOGS
|
|
pdlg = DLG::PdlgNew(dlidDesktopResizing, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fFalse;
|
|
idit = pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
#else //!RES_SWITCH_DIALOGS
|
|
idit = 2; // OK
|
|
#endif //!RES_SWITCH_DIALOGS
|
|
if (idit == 1) // cancel
|
|
{
|
|
_fRunInWindow = fTrue;
|
|
// try to set pref to fFalse
|
|
fSwitchRes = fFalse;
|
|
goto LWriteReg;
|
|
}
|
|
_fRunInWindow = fFalse;
|
|
if (!_FInitOS())
|
|
return fFalse;
|
|
if (!_FSwitch640480(fTrue))
|
|
{
|
|
_fRunInWindow = fTrue;
|
|
_RebuildMainWindow();
|
|
goto LSwitchFailed;
|
|
}
|
|
|
|
tsResize = TsCurrent();
|
|
#ifdef RES_SWITCH_DIALOGS
|
|
pdlg = DLG::PdlgNew(dlidDesktopResized, _FDlgResSwitch, &tsResize);
|
|
idit = ivNil; // if dialog fails to come up, treat like a cancel
|
|
if (pvNil != pdlg)
|
|
idit = pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
#else //!RES_SWITCH_DIALOGS
|
|
idit = 2; // OK
|
|
#endif //!RES_SWITCH_DIALOGS
|
|
if (idit == 1 || idit == ivNil) // cancel or timeout or dialog failure
|
|
{
|
|
_fSwitchedResolution = fFalse;
|
|
_fRunInWindow = fTrue;
|
|
_RebuildMainWindow();
|
|
if (!_FSwitch640480(fFalse)) // restore desktop resolution
|
|
return fFalse;
|
|
goto LSwitchFailed;
|
|
}
|
|
// try to set pref to fTrue
|
|
fSwitchRes = fTrue;
|
|
goto LWriteReg;
|
|
|
|
LSwitchFailed:
|
|
pdlg = DLG::PdlgNew(dlidDesktopWontResize, pvNil, pvNil);
|
|
if (pvNil != pdlg)
|
|
pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
// try to set pref to fFalse
|
|
fSwitchRes = fFalse;
|
|
LWriteReg:
|
|
FGetSetRegKey(kszSwitchResolutionValue, &fSwitchRes, size(fSwitchRes),
|
|
fregSetKey);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return whether display is currently 640x480
|
|
***************************************************************************/
|
|
bool APP::_FDisplayIs640480(void)
|
|
{
|
|
#ifdef WIN
|
|
return (GetSystemMetrics(SM_CXSCREEN) == 640 &&
|
|
GetSystemMetrics(SM_CYSCREEN) == 480);
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
return fFalse;
|
|
#endif //MAC
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Do OS specific initialization.
|
|
***************************************************************************/
|
|
bool APP::_FInitOS(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
long dxpWindow;
|
|
long dypWindow;
|
|
long xpWindow;
|
|
long ypWindow;
|
|
DWORD dwStyle = 0;
|
|
STN stnWindowTitle;
|
|
|
|
if (_fMainWindowCreated) // If someone else called _FInitOS already,
|
|
return fTrue; // we can leave
|
|
|
|
if (!FGetStnApp(idsWindowTitle, &stnWindowTitle))
|
|
return fFalse;
|
|
|
|
// register the window classes
|
|
if (vwig.hinstPrev == hNil)
|
|
{
|
|
WNDCLASS wcs;
|
|
|
|
wcs.style = CS_BYTEALIGNCLIENT | CS_OWNDC;
|
|
wcs.lpfnWndProc = _LuWndProc;
|
|
wcs.cbClsExtra = 0;
|
|
wcs.cbWndExtra = 0;
|
|
wcs.hInstance = vwig.hinst;
|
|
wcs.hIcon = LoadIcon(vwig.hinst, MAKEINTRESOURCE(IDI_APP));
|
|
wcs.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcs.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
|
wcs.lpszMenuName = 0;
|
|
wcs.lpszClassName = kpszAppWndCls;
|
|
if (!RegisterClass(&wcs))
|
|
return fFalse;
|
|
|
|
wcs.lpfnWndProc = _LuMdiWndProc;
|
|
wcs.lpszClassName = PszLit("MDI");
|
|
if (!RegisterClass(&wcs))
|
|
return fFalse;
|
|
}
|
|
|
|
_GetWindowProps(&xpWindow, &ypWindow, &dxpWindow, &dypWindow, &dwStyle);
|
|
|
|
if ((vwig.hwndApp = CreateWindow(kpszAppWndCls, stnWindowTitle.Psz(),
|
|
dwStyle, xpWindow, ypWindow, dxpWindow, dypWindow,
|
|
hNil, hNil, vwig.hinst, pvNil)) == hNil)
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
if (hNil == (vwig.hdcApp = GetDC(vwig.hwndApp)))
|
|
return fFalse;
|
|
|
|
//set a timer, so we can idle regularly.
|
|
if (SetTimer(vwig.hwndApp, 0, 1, pvNil) == 0)
|
|
return fFalse;
|
|
|
|
_haccel = LoadAccelerators(vwig.hinst, MIR(acidMain));
|
|
_haccelGlobal = LoadAccelerators(vwig.hinst, MIR(acidGlobal));
|
|
vwig.haccel = _haccel;
|
|
|
|
ShowWindow(vwig.hwndApp, vwig.wShow);
|
|
_fMainWindowCreated = fTrue;
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Determine window size, position, and window style. Note that you
|
|
should pass in the current dwStyle if the window already exists. If
|
|
the window does not already exist, pass in 0.
|
|
***************************************************************************/
|
|
void APP::_GetWindowProps(long *pxp, long *pyp, long *pdxp, long *pdyp,
|
|
DWORD *pdwStyle)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pxp);
|
|
AssertVarMem(pyp);
|
|
AssertVarMem(pdxp);
|
|
AssertVarMem(pdyp);
|
|
AssertVarMem(pdwStyle);
|
|
|
|
if (!_fRunInWindow)
|
|
{
|
|
*pdwStyle |= (WS_POPUP | WS_CLIPCHILDREN);
|
|
*pdwStyle &= ~(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
|
|
WS_MINIMIZEBOX);
|
|
*pxp = 0;
|
|
*pyp = 0;
|
|
*pdxp = GetSystemMetrics(SM_CXSCREEN);
|
|
*pdyp = GetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
else
|
|
{
|
|
RCS rcs;
|
|
*pdwStyle |= (WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU |
|
|
WS_MINIMIZEBOX);
|
|
*pdwStyle &= ~WS_POPUP;
|
|
rcs.left = 0;
|
|
rcs.top = 0;
|
|
rcs.right = 640;
|
|
rcs.bottom = 480;
|
|
AdjustWindowRect(&rcs, *pdwStyle, fFalse);
|
|
*pdxp = rcs.right - rcs.left;
|
|
*pdyp = rcs.bottom - rcs.top;
|
|
// Center window on screen
|
|
// REVIEW *****: how do you adjust window appropriately for taskbar?
|
|
*pxp = LwMax((GetSystemMetrics(SM_CXSCREEN) - *pdxp) / 2, 0);
|
|
*pyp = LwMax((GetSystemMetrics(SM_CYSCREEN) - *pdyp) / 2, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Change main window properties based on whether we're running in a
|
|
window or fullscreen.
|
|
***************************************************************************/
|
|
void APP::_RebuildMainWindow(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
Assert(_fMainWindowCreated, 0);
|
|
|
|
#ifdef WIN
|
|
long dxpWindow;
|
|
long dypWindow;
|
|
long xpWindow;
|
|
long ypWindow;
|
|
DWORD dwStyle;
|
|
|
|
dwStyle = GetWindowLong(vwig.hwndApp, GWL_STYLE);
|
|
_GetWindowProps(&xpWindow, &ypWindow, &dxpWindow, &dypWindow, &dwStyle);
|
|
SetWindowLong(vwig.hwndApp, GWL_STYLE, dwStyle);
|
|
SetWindowPos(vwig.hwndApp, HWND_TOP, xpWindow, ypWindow, dxpWindow,
|
|
dypWindow, 0);
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Open "3D Movie Maker.chk" or "3DMovie.chk"
|
|
***************************************************************************/
|
|
bool APP::_FOpenResourceFile(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(&_fniProductDir, ffniDir);
|
|
|
|
FNI fni;
|
|
STN stn;
|
|
|
|
fni = _fniProductDir;
|
|
if (!fni.FSetLeaf(&_stnProductLong, kftgChunky) || tYes != fni.TExists())
|
|
{
|
|
fni = _fniProductDir;
|
|
if (!fni.FSetLeaf(&_stnProductShort, kftgChunky) ||
|
|
tYes != fni.TExists())
|
|
{
|
|
fni.GetStnPath(&stn);
|
|
_FCantFindFileDialog(&stn); // ignore failure
|
|
return fFalse;
|
|
}
|
|
}
|
|
_pcfl = CFL::PcflOpen(&fni, fcflNil);
|
|
if (pvNil == _pcfl)
|
|
return fFalse;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Report that 3DMM can't find a file named pstnFile
|
|
***************************************************************************/
|
|
bool APP::_FCantFindFileDialog(PSTN pstnFile)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pstnFile, 0);
|
|
|
|
PDLG pdlg;
|
|
|
|
pdlg = DLG::PdlgNew(dlidCantFindFile, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fFalse;
|
|
|
|
if (!pdlg->FPutStn(1, pstnFile))
|
|
{
|
|
ReleasePpo(&pdlg);
|
|
return fFalse;
|
|
}
|
|
pdlg->IditDo();
|
|
ReleasePpo(&pdlg);
|
|
_fDontReportInitFailure = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
Finds _fniUsersDir, _fniMelanieDir, and _fniUserDir. _fniUserDir is
|
|
set from the registry. This function also determines if this is a first
|
|
time user.
|
|
************************************************************ PETED ***********/
|
|
bool APP::_FGetUserDirectories(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(&_fniMsKidsDir, ffniDir);
|
|
Assert(_stnUser.Cch() > 0, "need valid stnUser!");
|
|
|
|
SZ szDir;
|
|
STN stn;
|
|
STN stnT;
|
|
STN stnUsers;
|
|
bool fFirstTimeUser;
|
|
|
|
// First, find the Users directory
|
|
_fniUsersDir = _fniMsKidsDir;
|
|
if (!FGetStnApp(idsUsersDir, &stnUsers))
|
|
return fFalse;
|
|
if (!_fniUsersDir.FDownDir(&stnUsers, ffniMoveToDir))
|
|
{
|
|
_fniUsersDir.GetStnPath(&stn);
|
|
if (!stn.FAppendStn(&stnUsers))
|
|
return fFalse;
|
|
_FCantFindFileDialog(&stn); // ignore failure
|
|
return fFalse;
|
|
}
|
|
AssertPo(&_fniUsersDir, ffniDir);
|
|
|
|
// Find Melanie's dir
|
|
_fniMelanieDir = _fniUsersDir;
|
|
if (!FGetStnApp(idsMelanie, &stn))
|
|
return fFalse;
|
|
if (!_fniMelanieDir.FDownDir(&stn, ffniMoveToDir))
|
|
{
|
|
_fniMelanieDir.GetStnPath(&stnT);
|
|
if (!stnT.FAppendStn(&stn))
|
|
return fFalse;
|
|
_FCantFindFileDialog(&stnT); // ignore failure
|
|
return fFalse;
|
|
}
|
|
AssertPo(&_fniMelanieDir, ffniDir);
|
|
|
|
fFirstTimeUser = fFalse;
|
|
szDir[0] = chNil;
|
|
if (!FGetSetRegKey(kszHomeDirValue, szDir, size(szDir),
|
|
fregSetDefault | fregString))
|
|
{
|
|
return fFalse;
|
|
}
|
|
stn.SetSz(szDir);
|
|
if (stn.Cch() == 0 || !_fniUserDir.FBuildFromPath(&stn, kftgDir) ||
|
|
tYes != _fniUserDir.TExists())
|
|
{
|
|
// Need to (find or create) and go to user's directory
|
|
fFirstTimeUser = fTrue;
|
|
_fniUserDir = _fniUsersDir;
|
|
|
|
// Ensure that user's root directory exists
|
|
if (!_fniUserDir.FDownDir(&_stnUser, ffniMoveToDir))
|
|
{
|
|
if (!_fniUserDir.FDownDir(&_stnUser, ffniCreateDir | ffniMoveToDir))
|
|
return fFalse;
|
|
}
|
|
|
|
// Try to write path to user dir to the registry
|
|
_fniUserDir.GetStnPath(&stn);
|
|
stn.GetSz(szDir);
|
|
FGetSetRegKey(kszHomeDirValue, szDir, CchSz(szDir) + kcchExtraSz,
|
|
fregSetKey | fregString); // ignore failure
|
|
}
|
|
#ifdef WIN
|
|
if (SetCurrentDirectory(szDir) == FALSE)
|
|
return fFalse;
|
|
#else //!WIN
|
|
RawRtn();
|
|
#endif //!WIN
|
|
AssertPo(&_fniUserDir, ffniDir);
|
|
|
|
if (!FSetProp(kpridFirstTimeUser, fFirstTimeUser))
|
|
return fFalse;
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
_FGetUserName
|
|
Attempts to get the current user name. May return a default value
|
|
if we get a "non-serious" error (as defined in the spec).
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns: fTrue if _stnUser has something usable in it on return
|
|
|
|
************************************************************ PETED ***********/
|
|
bool APP::_FGetUserName(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
bool fRet = fTrue;
|
|
DWORD dwCbUser = size(SZ);
|
|
SZ szT;
|
|
|
|
switch (WNetGetUser(NULL, szT, &dwCbUser))
|
|
{
|
|
case ERROR_EXTENDED_ERROR:
|
|
{
|
|
DWORD dwError;
|
|
SZ szProvider;
|
|
STN stnMessage;
|
|
|
|
if (WNetGetLastError(&dwError, szT, size(SZ), szProvider, size(SZ))
|
|
== NO_ERROR)
|
|
{
|
|
STN stnFormat;
|
|
|
|
if (FGetStnApp(idsWNetError, &stnFormat))
|
|
{
|
|
stnMessage.FFormat(&stnFormat, szProvider, dwError, szT);
|
|
TGiveAlertSz(stnMessage.Psz(), bkOk, cokExclamation);
|
|
}
|
|
}
|
|
else
|
|
Bug("Call to WNetGetLastError failed; this should never happen");
|
|
_stnUser.SetNil();
|
|
fRet = fFalse;
|
|
break;
|
|
}
|
|
|
|
case NO_ERROR:
|
|
_stnUser = szT;
|
|
if (_stnUser.Cch() > 0)
|
|
break; // else fall through...
|
|
|
|
case ERROR_MORE_DATA:
|
|
case ERROR_NO_NETWORK:
|
|
case ERROR_NO_NET_OR_BAD_PATH:
|
|
default:
|
|
if (!FGetStnApp(idsDefaultUser, &_stnUser))
|
|
fRet = fFalse;
|
|
break;
|
|
}
|
|
|
|
Assert(!fRet || _stnUser.Cch() > 0, "Bug in _FGetUserName");
|
|
return fRet;
|
|
#else // WIN
|
|
RawRtn();
|
|
return fFalse;
|
|
#endif // !WIN
|
|
}
|
|
|
|
|
|
/******************************
|
|
The user-data structure
|
|
*******************************/
|
|
struct UDAT
|
|
{
|
|
long rglw[kcpridUserData];
|
|
};
|
|
|
|
/***************************************************************************
|
|
Reads the "user data" from the registry and SetProp's the data onto
|
|
the app
|
|
***************************************************************************/
|
|
bool APP::_FReadUserData(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
UDAT udat;
|
|
long iprid;
|
|
|
|
ClearPb(&udat, size(UDAT));
|
|
|
|
if (!FGetSetRegKey(kszUserDataValue, &udat, size(UDAT),
|
|
fregSetDefault | fregBinary))
|
|
{
|
|
return fFalse;
|
|
}
|
|
for (iprid = 0; iprid < kcpridUserData; iprid++)
|
|
{
|
|
if (!FSetProp(kpridUserDataBase + iprid, udat.rglw[iprid]))
|
|
return fFalse;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Writes the "user data" from the app props to the registry
|
|
***************************************************************************/
|
|
bool APP::_FWriteUserData(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
UDAT udat;
|
|
long iprid;
|
|
|
|
for (iprid = 0; iprid < kcpridUserData; iprid++)
|
|
{
|
|
if (!FGetProp(kpridUserDataBase + iprid, &udat.rglw[iprid]))
|
|
return fFalse;
|
|
}
|
|
if (!FGetSetRegKey(kszUserDataValue, &udat, size(UDAT),
|
|
fregSetKey | fregBinary))
|
|
{
|
|
return fFalse;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
FGetSetRegKey
|
|
Given a reg key (and option sub-key), attempts to either get or set
|
|
the current value of the key. If the reg key is created on a Get
|
|
(fSetKey == fFalse), the data in pvData will be used to set the
|
|
default value for the key.
|
|
|
|
|
|
Arguments:
|
|
PSZ pszValueName -- The value name
|
|
void *pvData -- pointer to buffer to read or write key into or from
|
|
long cbData -- size of the buffer
|
|
ulong grfreg -- flags describing what we should do
|
|
bool *pfNoValue -- optional parameter, takes whether a real registry
|
|
error occurred or not
|
|
|
|
Returns: fTrue if all actions necessary could be performed
|
|
|
|
************************************************************ PETED ***********/
|
|
bool APP::FGetSetRegKey(PSZ pszValueName, void *pvData, long cbData,
|
|
ulong grfreg, bool *pfNoValue)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertSz(pszValueName);
|
|
AssertPvCb(pvData, cbData);
|
|
AssertNilOrVarMem(pfNoValue);
|
|
|
|
bool fRet = fFalse;
|
|
bool fSetKey, fSetDefault, fString, fBinary;
|
|
|
|
fSetKey = grfreg & fregSetKey;
|
|
fSetDefault = grfreg & fregSetDefault;
|
|
fString = grfreg & fregString;
|
|
fBinary = grfreg & fregBinary;
|
|
|
|
#ifdef WIN
|
|
DWORD dwDisposition;
|
|
DWORD dwCbData = cbData;
|
|
DWORD dwType;
|
|
HKEY hkey = 0;
|
|
|
|
if (RegCreateKeyEx((grfreg & fregMachine) ? HKEY_LOCAL_MACHINE :
|
|
HKEY_CURRENT_USER, kszSocratesKey, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS, NULL, &hkey, &dwDisposition) != ERROR_SUCCESS)
|
|
{
|
|
goto LFail;
|
|
}
|
|
|
|
if ((dwDisposition == REG_CREATED_NEW_KEY && fSetDefault) || fSetKey)
|
|
{
|
|
LWriteValue:
|
|
if (fBinary)
|
|
{
|
|
dwType = REG_BINARY;
|
|
}
|
|
else if (fString)
|
|
{
|
|
if (!fSetKey)
|
|
dwCbData = CchSz((PSZ)pvData) + kcchExtraSz;
|
|
else
|
|
Assert(CchSz((PSZ)pvData) < cbData, "Invalid string for reg key");
|
|
dwType = REG_SZ;
|
|
}
|
|
else
|
|
{
|
|
Assert(cbData == size(DWORD), "Unknown reg key type");
|
|
dwType = REG_DWORD;
|
|
}
|
|
|
|
if (RegSetValueEx(hkey, pszValueName, NULL, dwType, (byte *)pvData,
|
|
dwCbData) != ERROR_SUCCESS)
|
|
{
|
|
goto LFail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
long lwRet;
|
|
|
|
if ((lwRet = RegQueryValueEx(hkey, pszValueName, NULL, &dwType,
|
|
(byte *)pvData, &dwCbData)) != ERROR_SUCCESS)
|
|
{
|
|
if (lwRet == ERROR_FILE_NOT_FOUND && fSetDefault)
|
|
goto LWriteValue;
|
|
/* If the caller gave us a way to differentiate a genuine registry
|
|
failure from simply not having set the value yet, do so */
|
|
if (pfNoValue != pvNil)
|
|
fRet = *pfNoValue = (lwRet == ERROR_FILE_NOT_FOUND);
|
|
goto LFail;
|
|
}
|
|
Assert(dwType == (DWORD)(fString ? REG_SZ : (fBinary ? REG_BINARY : REG_DWORD)), "Invalid key type");
|
|
}
|
|
if (pfNoValue != pvNil)
|
|
*pfNoValue = fFalse;
|
|
fRet = fTrue;
|
|
LFail:
|
|
if (hkey != 0)
|
|
RegCloseKey(hkey);
|
|
#else // WIN
|
|
RawRtn();
|
|
#endif // !WIN
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the palette and bring up the Microsoft Home Logo
|
|
***************************************************************************/
|
|
bool APP::_FDisplayHomeLogo(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(_pcfl, 0);
|
|
|
|
BLCK blck;
|
|
PGL pglclr;
|
|
PMBMP pmbmp;
|
|
short bo;
|
|
short osk;
|
|
|
|
if (!_pcfl->FFind(kctgColorTable, kcnoGlcrInit, &blck))
|
|
return fFalse;
|
|
pglclr = GL::PglRead(&blck, &bo, &osk);
|
|
if (pvNil == pglclr)
|
|
return fFalse;
|
|
GPT::SetActiveColors(pglclr, fpalIdentity);
|
|
ReleasePpo(&pglclr);
|
|
|
|
if (!_pcfl->FFind(kctgMbmp, kcnoMbmpHomeLogo, &blck))
|
|
return fFalse;
|
|
pmbmp = MBMP::PmbmpRead(&blck);
|
|
if (pvNil == pmbmp)
|
|
return fFalse;
|
|
_pkwa->SetMbmp(pmbmp);
|
|
ReleasePpo(&pmbmp);
|
|
UpdateMarked();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize tag manager
|
|
***************************************************************************/
|
|
bool APP::_FInitProductNames(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
PGST pgst;
|
|
BLCK blck;
|
|
|
|
// Use kcbCacheTagm of cache per source, don't cache on CD
|
|
vptagm = TAGM::PtagmNew(&_fniMsKidsDir, APP::FInsertCD, kcbCacheTagm);
|
|
if (vptagm == pvNil)
|
|
return fFalse;
|
|
|
|
if (!_FReadTitlesFromReg(&pgst))
|
|
goto LFail;
|
|
|
|
if (!vptagm->FMergeGstSource(pgst, kboCur, koskCur))
|
|
goto LFail;
|
|
|
|
if (!_FFindProductDir(pgst))
|
|
goto LFail;
|
|
|
|
if (!vptagm->FGetSid(&_stnProductLong, &_sidProduct))
|
|
goto LFail;
|
|
|
|
ReleasePpo(&pgst);
|
|
return fTrue;
|
|
|
|
LFail:
|
|
ReleasePpo(&pgst);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read the sids and titles of installed 3DMovie products from the registry
|
|
***************************************************************************/
|
|
bool APP::_FReadTitlesFromReg(PGST *ppgst)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(ppgst);
|
|
|
|
#ifdef WIN
|
|
HKEY hkey = 0;
|
|
DWORD dwDisposition;
|
|
DWORD iValue;
|
|
SZ szSid;
|
|
STN stnSid;
|
|
DWORD cchSid = kcchMaxSz;
|
|
SZ szTitle;
|
|
STN stnTitle;
|
|
DWORD cchTitle = kcchMaxSz;
|
|
PGST pgst;
|
|
long sid;
|
|
|
|
if ((pgst = GST::PgstNew(size(long))) == pvNil)
|
|
goto LFail;
|
|
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, kszProductsKey, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS, NULL, &hkey, &dwDisposition) != ERROR_SUCCESS)
|
|
{
|
|
Warn("Missing InstallDirectory registry entry or registry error");
|
|
goto LFail;
|
|
}
|
|
|
|
for (iValue = 0; RegEnumValue(hkey, iValue, szSid, &cchSid, NULL,
|
|
NULL, (unsigned char *)szTitle, &cchTitle) != ERROR_NO_MORE_ITEMS; iValue++)
|
|
{
|
|
stnSid.SetSz(szSid);
|
|
if (!stnSid.FGetLw(&sid))
|
|
{
|
|
Warn("Invalid registry name for Products key value");
|
|
continue;
|
|
}
|
|
stnTitle.SetSz(&szTitle[0]);
|
|
if (!pgst->FAddStn(&stnTitle, &sid))
|
|
goto LFail;
|
|
cchTitle = kcchMaxSz;
|
|
cchSid = kcchMaxSz;
|
|
}
|
|
|
|
#else //!WIN
|
|
RawRtn();
|
|
#endif //!WIN
|
|
*ppgst = pgst;
|
|
return fTrue;
|
|
|
|
LFail:
|
|
ReleasePpo(&pgst);
|
|
*ppgst = pvNil;
|
|
return fFalse;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Initialize 3-D Text
|
|
***************************************************************************/
|
|
bool APP::_FInitTdt(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(_pcfl, 0);
|
|
|
|
PGST pgst;
|
|
|
|
// set TDT action names
|
|
pgst = _PgstRead(kcnoGstAction);
|
|
if (pvNil == pgst)
|
|
return fFalse;
|
|
Assert(pgst->CbExtra() == size(long), "bad Action string table");
|
|
if (!TDT::FSetActionNames(pgst))
|
|
{
|
|
ReleasePpo(&pgst);
|
|
return fFalse;
|
|
}
|
|
ReleasePpo(&pgst);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read and byte-swap a GST from _pcfl. Assumes that extra data, if any,
|
|
is a long.
|
|
***************************************************************************/
|
|
PGST APP::_PgstRead(CNO cno)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(_pcfl, 0);
|
|
|
|
PGST pgst;
|
|
BLCK blck;
|
|
short bo;
|
|
short osk;
|
|
long istn;
|
|
long lwExtra;
|
|
|
|
if (!_pcfl->FFind(kctgGst, cno, &blck))
|
|
return pvNil;
|
|
pgst = GST::PgstRead(&blck, &bo, &osk);
|
|
if (pvNil == pgst)
|
|
return pvNil;
|
|
Assert(pgst->CbExtra() == 0 || pgst->CbExtra() == size(long),
|
|
"unexpected extra size");
|
|
if (kboCur != bo && 0 != pgst->CbExtra())
|
|
{
|
|
for (istn = 0; istn < pgst->IvMac(); istn++)
|
|
{
|
|
pgst->GetExtra(istn, &lwExtra);
|
|
SwapBytesRglw(&lwExtra, 1);
|
|
pgst->PutExtra(istn, &lwExtra);
|
|
}
|
|
}
|
|
return pgst;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read various string tables from _pcfl
|
|
***************************************************************************/
|
|
bool APP::_FReadStringTables(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(_pcfl, 0);
|
|
|
|
// read studio filename list
|
|
_pgstStudioFiles = _PgstRead(kcnoGstStudioFiles);
|
|
if (pvNil == _pgstStudioFiles)
|
|
return fFalse;
|
|
|
|
// read building filename list
|
|
_pgstBuildingFiles = _PgstRead(kcnoGstBuildingFiles);
|
|
if (pvNil == _pgstBuildingFiles)
|
|
return fFalse;
|
|
|
|
// read shared filename list
|
|
_pgstSharedFiles = _PgstRead(kcnoGstSharedFiles);
|
|
if (pvNil == _pgstSharedFiles)
|
|
return fFalse;
|
|
|
|
// read misc app strings
|
|
_pgstApp = _PgstRead(kcnoGstApp);
|
|
if (pvNil == _pgstApp)
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize KWA (app kidworld)
|
|
***************************************************************************/
|
|
bool APP::_FInitKidworld(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
RC rcRel(0, 0, krelOne, krelOne);
|
|
GCB gcb(CMH::HidUnique(), GOB::PgobScreen(), fgobNil, kginMark, pvNil,
|
|
&rcRel);
|
|
|
|
_pkwa = NewObj KWA(&gcb);
|
|
if (pvNil == _pkwa)
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Show the app splash screen
|
|
***************************************************************************/
|
|
bool APP::_FShowSplashScreen(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
BLCK blck;
|
|
PMBMP pmbmp;
|
|
|
|
if (!_pcfl->FFind(kctgMbmp, kcnoMbmpSplash, &blck))
|
|
return fFalse;
|
|
pmbmp = MBMP::PmbmpRead(&blck);
|
|
if (pvNil == pmbmp)
|
|
return fFalse;
|
|
_pkwa->SetMbmp(pmbmp);
|
|
ReleasePpo(&pmbmp);
|
|
UpdateMarked();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Play the app splash sound
|
|
***************************************************************************/
|
|
bool APP::_FPlaySplashSound(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
PCRF pcrf;
|
|
|
|
pcrf = CRF::PcrfNew(_pcfl, 0);
|
|
if (pvNil == pcrf)
|
|
return fFalse;
|
|
vpsndm->SiiPlay(pcrf, kctgMidi, kcnoMidiSplash);
|
|
ReleasePpo(&pcrf);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Read the chunky files specified by _pgstSharedFiles, _pgstBuildingFiles
|
|
and _pgstStudioFiles and create the global CRM; indices to the Building
|
|
and Studio CRFs are stored in _pglicrfBuilding and _pglicrfStudio.
|
|
***************************************************************************/
|
|
bool APP::_FInitCrm(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(_pkwa, 0);
|
|
AssertPo(_pgstSharedFiles, 0);
|
|
AssertPo(_pgstBuildingFiles, 0);
|
|
AssertPo(_pgstStudioFiles, 0);
|
|
|
|
PSCEG psceg = pvNil;
|
|
PSCPT pscpt = pvNil;
|
|
|
|
_pcrmAll = CRM::PcrmNew(_pgstSharedFiles->IvMac() +
|
|
_pgstBuildingFiles->IvMac() + _pgstStudioFiles->IvMac());
|
|
if (pvNil == _pcrmAll)
|
|
goto LFail;
|
|
|
|
_pglicrfBuilding = GL::PglNew(size(long), _pgstBuildingFiles->IvMac());
|
|
if (pvNil == _pglicrfBuilding)
|
|
goto LFail;
|
|
|
|
_pglicrfStudio = GL::PglNew(size(long), _pgstStudioFiles->IvMac());
|
|
if (pvNil == _pglicrfStudio)
|
|
goto LFail;
|
|
|
|
if (!_FAddToCrm(_pgstSharedFiles, _pcrmAll, pvNil))
|
|
goto LFail;
|
|
|
|
if (!_FAddToCrm(_pgstBuildingFiles, _pcrmAll, _pglicrfBuilding))
|
|
goto LFail;
|
|
|
|
if (!_FAddToCrm(_pgstStudioFiles, _pcrmAll, _pglicrfStudio))
|
|
goto LFail;
|
|
|
|
// Initialize the shared util gob.
|
|
psceg = _pkwa->PscegNew(_pcrmAll, _pkwa);
|
|
if (pvNil == psceg)
|
|
goto LFail;
|
|
|
|
pscpt = (PSCPT)_pcrmAll->PbacoFetch(kctgScript, kcnoInitShared,
|
|
SCPT::FReadScript);
|
|
if (pvNil == pscpt)
|
|
goto LFail;
|
|
|
|
if (!psceg->FRunScript(pscpt))
|
|
goto LFail;
|
|
|
|
ReleasePpo(&psceg);
|
|
ReleasePpo(&pscpt);
|
|
return fTrue;
|
|
LFail:
|
|
ReleasePpo(&psceg);
|
|
ReleasePpo(&pscpt);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Helper function for _FInitCrm. Adds the list of chunky files specified
|
|
in pgstFiles to the CRM pointed to by pcrm. If pglFiles is not pvNil,
|
|
it is filled in with the positions in the CRM of each of the loaded
|
|
crfs.
|
|
***************************************************************************/
|
|
bool APP::_FAddToCrm(PGST pgstFiles, PCRM pcrm, PGL pglFiles)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(&_fniProductDir, ffniDir);
|
|
AssertPo(pgstFiles, 0);
|
|
AssertPo(pcrm, 0);
|
|
AssertNilOrPo(pglFiles, 0);
|
|
|
|
bool fRet = fFalse;
|
|
FNI fni;
|
|
STN stn;
|
|
long istn;
|
|
long cbCache;
|
|
PCFL pcfl = pvNil;
|
|
long icfl;
|
|
|
|
for (istn = 0; istn < pgstFiles->IvMac(); istn++)
|
|
{
|
|
pgstFiles->GetStn(istn, &stn);
|
|
pgstFiles->GetExtra(istn, &cbCache);
|
|
#ifdef DEBUG
|
|
{
|
|
bool fAskForCDSav = Pkwa()->FAskForCD();
|
|
bool fFoundFile;
|
|
|
|
// In debug, we look for "buildingd.chk", "sharedd.chk", etc. and
|
|
// use them instead of the normal files if they exist. If they
|
|
// don't exist, just use the normal files.
|
|
STN stnT = stn;
|
|
stnT.FAppendCh(ChLit('d'));
|
|
stnT.FAppendSz(PszLit(".chk")); // REVIEW *****
|
|
Pkwa()->SetCDPrompt(fFalse);
|
|
fFoundFile = Pkwa()->FFindFile(&stnT, &fni);
|
|
Pkwa()->SetCDPrompt(fAskForCDSav);
|
|
if (fFoundFile)
|
|
{
|
|
pcfl = CFL::PcflOpen(&fni, fcflNil);
|
|
}
|
|
else
|
|
{
|
|
#endif //DEBUG
|
|
stn.FAppendSz(PszLit(".chk")); // REVIEW *****
|
|
if (Pkwa()->FFindFile(&stn, &fni))
|
|
pcfl = CFL::PcflOpen(&fni, fcflNil);
|
|
#ifdef DEBUG
|
|
}
|
|
}
|
|
#endif //DEBUG
|
|
if (pvNil == pcfl)
|
|
{
|
|
if (fni.Ftg() != ftgNil)
|
|
fni.GetStnPath(&stn);
|
|
if (!_fDontReportInitFailure)
|
|
_FCantFindFileDialog(&stn); // ignore failure
|
|
goto LFail;
|
|
}
|
|
if (!pcrm->FAddCfl(pcfl, cbCache, &icfl))
|
|
goto LFail;
|
|
ReleasePpo(&pcfl);
|
|
if (pglFiles != pvNil && !pglFiles->FAdd(&icfl))
|
|
goto LFail;
|
|
}
|
|
|
|
fRet = fTrue;
|
|
LFail:
|
|
if (!fRet)
|
|
ReleasePpo(&pcfl);
|
|
return fRet;
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize and start the building script
|
|
***************************************************************************/
|
|
bool APP::_FInitBuilding(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
bool fRet = fFalse;
|
|
long i;
|
|
long cbCache;
|
|
long iv;
|
|
PCRF pcrfT;
|
|
PSCEG psceg = pvNil;
|
|
PSCPT pscpt = pvNil;
|
|
|
|
BeginLongOp();
|
|
|
|
psceg = _pkwa->PscegNew(_pcrmAll, _pkwa);
|
|
if (pvNil == psceg)
|
|
goto LFail;
|
|
|
|
pscpt = (PSCPT)_pcrmAll->PbacoFetch(kctgScript, kcnoStartApp,
|
|
SCPT::FReadScript);
|
|
if (pvNil == pscpt)
|
|
goto LFail;
|
|
|
|
if (!psceg->FRunScript(pscpt))
|
|
goto LFail;
|
|
|
|
// Up the cache limits of the building crfs in the global crm.
|
|
// Assumption: since the files were added to the crm in the order they
|
|
// appear in _pgstBuilding, I can get the cache amounts from there.
|
|
for (i = 0; i < _pglicrfBuilding->IvMac(); i++)
|
|
{
|
|
_pgstBuildingFiles->GetExtra(i, &cbCache);
|
|
_pglicrfBuilding->Get(i, &iv);
|
|
pcrfT = _pcrmAll->PcrfGet(iv);
|
|
Assert(pcrfT != pvNil, "Main CRM is corrupt.");
|
|
pcrfT->SetCbMax(cbCache);
|
|
}
|
|
|
|
// Zero the cache for the studio crfs in the global crm.
|
|
for (i = 0; i < _pglicrfStudio->IvMac(); i++)
|
|
{
|
|
_pglicrfStudio->Get(i, &iv);
|
|
pcrfT = _pcrmAll->PcrfGet(iv);
|
|
Assert(pcrfT != pvNil, "Main CRM is corrupt.");
|
|
pcrfT->SetCbMax(0);
|
|
}
|
|
|
|
fRet = fTrue;
|
|
LFail:
|
|
if (!fRet)
|
|
EndLongOp();
|
|
ReleasePpo(&psceg);
|
|
ReleasePpo(&pscpt);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Initialize and start the studio script
|
|
***************************************************************************/
|
|
bool APP::_FInitStudio(PFNI pfniUserDoc, bool fFailIfDocOpenFailed)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
long i;
|
|
long cbCache;
|
|
long iv;
|
|
PCRF pcrfT;
|
|
bool fRet = fFalse;
|
|
|
|
_pstdio = STDIO::PstdioNew(khidStudio, _pcrmAll,
|
|
(pfniUserDoc->Ftg() == ftgNil ? pvNil : pfniUserDoc),
|
|
fFailIfDocOpenFailed);
|
|
if (_pstdio == pvNil)
|
|
{
|
|
goto LFail;
|
|
}
|
|
|
|
// Up the cache limits of the studio crfs in the global crm.
|
|
for (i = 0; i < _pglicrfStudio->IvMac(); i++)
|
|
{
|
|
_pgstStudioFiles->GetExtra(i, &cbCache);
|
|
_pglicrfStudio->Get(i, &iv);
|
|
pcrfT = _pcrmAll->PcrfGet(iv);
|
|
Assert(pcrfT != pvNil, "Main CRM is corrupt.");
|
|
pcrfT->SetCbMax(cbCache);
|
|
}
|
|
|
|
// Zero the cache for the building crfs in the global crm.
|
|
for (i = 0; i < _pglicrfBuilding->IvMac(); i++)
|
|
{
|
|
_pglicrfBuilding->Get(i, &iv);
|
|
pcrfT = _pcrmAll->PcrfGet(iv);
|
|
Assert(pcrfT != pvNil, "Main CRM is corrupt.");
|
|
pcrfT->SetCbMax(0);
|
|
}
|
|
|
|
fRet = fTrue;
|
|
|
|
LFail:
|
|
if (!fRet)
|
|
PushErc(ercSocCantInitStudio);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Sets _fSlowCPU if this is a slow CPU. It tests the graphics,
|
|
fixed-point math, and memory copying speed, and if any of them are
|
|
slower than a threshold, _fSlowCPU is set.
|
|
***************************************************************************/
|
|
bool APP::_FDetermineIfSlowCPU(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
bool fSlowCPU;
|
|
|
|
// If user has a saved preference, read and use that
|
|
if (FGetSetRegKey(kszBetterSpeedValue, &fSlowCPU, size(bool), fregNil))
|
|
{
|
|
_fSlowCPU = fSlowCPU;
|
|
return fTrue;
|
|
}
|
|
|
|
_fSlowCPU = fFalse;
|
|
|
|
#ifndef PERF_TEST
|
|
|
|
return fTrue;
|
|
|
|
#else //PERF_TEST
|
|
|
|
PGPT pgptWnd = pvNil;
|
|
PGPT pgptOff = pvNil;
|
|
RC rc1;
|
|
RC rc2;
|
|
ulong ts;
|
|
ulong dts1;
|
|
ulong dts2;
|
|
ulong dts3;
|
|
long i;
|
|
char *pch1 = pvNil;
|
|
char *pch2 = pvNil;
|
|
BRS r1;
|
|
BRS r2;
|
|
BRS r3;
|
|
|
|
// Test 1: Graphics. Copy some graphics to an offscreen buffer,
|
|
// then blit it back to the window 100 times.
|
|
rc1.Set(0, 0, 300, 300);
|
|
rc2.Set(0, 0, 300, 300);
|
|
pgptWnd = GPT::PgptNewHwnd(vwig.hwndApp);
|
|
if (pvNil == pgptWnd)
|
|
goto LFail;
|
|
pgptOff = GPT::PgptNewOffscreen(&rc1, 8); // BWLD RGB buffer is 8-bit
|
|
if (pvNil == pgptOff)
|
|
{
|
|
goto LFail;
|
|
}
|
|
// BLOCK
|
|
{
|
|
GNV gnvWnd(pgptWnd);
|
|
GNV gnvOff(pgptOff);
|
|
gnvOff.CopyPixels(&gnvWnd, &rc1, &rc1);
|
|
ts = TsCurrent();
|
|
for (i = 0; i < 100; i++)
|
|
gnvWnd.CopyPixels(&gnvOff, &rc1, &rc2);
|
|
dts1 = TsCurrent() - ts;
|
|
}
|
|
ReleasePpo(&pgptWnd);
|
|
ReleasePpo(&pgptOff);
|
|
|
|
// Test 2: Math. Do 200,000 fixed-point adds and multiplies
|
|
r1 = BR_SCALAR(1.12); // arbitrary number
|
|
r2 = BR_SCALAR(3.14159); // arbitrary number
|
|
r3 = rZero;
|
|
ts = TsCurrent();
|
|
for (i = 0; i < 200000; i++)
|
|
{
|
|
r2 = BrsMul(r1, r2);
|
|
r3 = BrsAdd(r2, r3);
|
|
}
|
|
dts2 = TsCurrent() - ts;
|
|
|
|
// Test 3: Copying. Copy a 50,000 byte block 200 times.
|
|
if (!FAllocPv((void **)&pch1, 50000, mprNormal, fmemClear))
|
|
goto LFail;
|
|
if (!FAllocPv((void **)&pch2, 50000, mprNormal, fmemClear))
|
|
goto LFail;
|
|
ts = TsCurrent();
|
|
for (i = 0; i < 200; i++)
|
|
CopyPb(pch1, pch2, 50000);
|
|
dts3 = TsCurrent() - ts;
|
|
FreePpv((void **)&pch1);
|
|
FreePpv((void **)&pch2);
|
|
|
|
if (dts1 > 700 || dts2 > 200 || dts3 > 500)
|
|
_fSlowCPU = fTrue;
|
|
|
|
return fTrue;
|
|
LFail:
|
|
ReleasePpo(&pgptWnd);
|
|
ReleasePpo(&pgptOff);
|
|
FreePpv((void **)&pch1);
|
|
FreePpv((void **)&pch2);
|
|
return fFalse;
|
|
|
|
#endif //PERF_TEST
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Reads command line. Also sets _fniExe to path to this executable
|
|
and _fniPortfolioDoc to file specified on command line (if any).
|
|
Also initializes _stnProductLong and _stnProductShort.
|
|
|
|
-f: fast mode
|
|
-s: slow mode
|
|
-p"longname": long productname
|
|
-t"shortname": short productname
|
|
-m: don't minimize window on deactivate
|
|
|
|
***************************************************************************/
|
|
void APP::_ParseCommandLine(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
SZ sz;
|
|
STN stn;
|
|
achar *pch;
|
|
achar *pchT;
|
|
FNI fniT;
|
|
|
|
// Get path to exe
|
|
GetModuleFileName(NULL, sz, kcchMaxSz);
|
|
stn.SetSz(sz);
|
|
if(!_fniExe.FBuildFromPath(&stn))
|
|
Bug("Bad module filename?");
|
|
|
|
pch = vwig.pszCmdLine;
|
|
// first argument (app name) is useless...skip it
|
|
_SkipToSpace(&pch);
|
|
_SkipSpace(&pch);
|
|
|
|
while (*pch != chNil)
|
|
{
|
|
// Look for /options or -options
|
|
if (*pch == ChLit('/') || *pch == ChLit('-'))
|
|
{
|
|
switch(ChUpper(*(pch + 1)))
|
|
{
|
|
case ChLit('M'):
|
|
_fDontMinimize = fTrue;
|
|
break;
|
|
case ChLit('S'):
|
|
_fSlowCPU = fTrue;
|
|
break;
|
|
case ChLit('F'):
|
|
_fSlowCPU = fFalse;
|
|
break;
|
|
case ChLit('P'):
|
|
{
|
|
pch += 2; // skip "-p"
|
|
pchT = pch;
|
|
_SkipToSpace(&pchT);
|
|
// skip quotes
|
|
if (*pch == ChLit('"'))
|
|
_stnProductLong.SetRgch(pch + 1, pchT - pch - 2);
|
|
else
|
|
_stnProductLong.SetRgch(pch, pchT - pch);
|
|
}
|
|
break;
|
|
case ChLit('T'):
|
|
{
|
|
pch += 2; // skip "-t"
|
|
pchT = pch;
|
|
_SkipToSpace(&pchT);
|
|
// skip quotes
|
|
if (*pch == ChLit('"'))
|
|
_stnProductShort.SetRgch(pch + 1, pchT - pch - 2);
|
|
else
|
|
_stnProductShort.SetRgch(pch, pchT - pch);
|
|
}
|
|
break;
|
|
default:
|
|
Warn("Bad command-line switch");
|
|
break;
|
|
}
|
|
_SkipToSpace(&pch);
|
|
_SkipSpace(&pch);
|
|
}
|
|
else // try to parse as fni string
|
|
{
|
|
// get to end of string
|
|
pchT = pch + CchSz(pch);
|
|
|
|
// skip quotes since FBuildFromPath can't deal
|
|
if (*pch == ChLit('"'))
|
|
pch++;
|
|
|
|
// move pchT to begginning of file name itself
|
|
while( (*pchT != '\\') &&
|
|
(pchT != pch))
|
|
pchT--;
|
|
// pchT now points to last'\', move it forward one (only if not pch)
|
|
if (*pchT == '\\')
|
|
pchT++;
|
|
|
|
// set STN to the path, which is the string up to the last '\'
|
|
stn.SetRgch(pch, pchT - pch);
|
|
|
|
// try to map to long file name if we can
|
|
HANDLE hFile = pvNil;
|
|
WIN32_FIND_DATA W32FindData;
|
|
|
|
hFile = FindFirstFile(pch, &W32FindData);
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
// append the longfile name returned...
|
|
stn.FAppendSz(W32FindData.cFileName);
|
|
FindClose(hFile);
|
|
}
|
|
else
|
|
// just use what was passed orginally...
|
|
stn.SetSz(pch);
|
|
|
|
|
|
// remove ending quotes since FBuildFromPath can't deal
|
|
if (stn.Psz()[stn.Cch() - 1] == ChLit('\"'))
|
|
stn.Delete(stn.Cch() - 1, 1);
|
|
if(fniT.FBuildFromPath(&stn))
|
|
{
|
|
SetPortfolioDoc(&fniT);
|
|
}
|
|
// move to end of string, we now assume that everything past options is the document name
|
|
pch += CchSz(pch);
|
|
}
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
}
|
|
|
|
/***************************************************************************
|
|
Make sure that _stnProductLong and _stnProductShort are initialized to
|
|
non-empty strings. If _stnProductLong wasn't initialized from the
|
|
command line, it and _stnProductShort are read from the app resource
|
|
file. If _stnProductLong is valid and _stnProductShort is not,
|
|
_stnProductShort is just set to _stnProductLong.
|
|
***************************************************************************/
|
|
bool APP::_FEnsureProductNames(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (_stnProductLong.Cch() == 0)
|
|
{
|
|
#ifdef WIN
|
|
SZ sz;
|
|
|
|
if (0 == LoadString(vwig.hinst, stid3DMovieNameLong, sz, kcchMaxSz))
|
|
return fFalse;
|
|
_stnProductLong.SetSz(sz);
|
|
|
|
if (0 == LoadString(vwig.hinst, stid3DMovieNameShort, sz, kcchMaxSz))
|
|
return fFalse;
|
|
_stnProductShort.SetSz(sz);
|
|
#else //MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
}
|
|
|
|
if (_stnProductShort.Cch() == 0)
|
|
_stnProductShort = _stnProductLong;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find _fniMsKidsDir
|
|
***************************************************************************/
|
|
bool APP::_FFindMsKidsDir(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
Assert(_stnProductLong.Cch() > 0 && _stnProductShort.Cch() > 0,
|
|
"_stnProductLong and _stnProductShort must exist");
|
|
|
|
FNI fni;
|
|
SZ szMsKidsDir;
|
|
STN stn;
|
|
STN stnUsers;
|
|
|
|
szMsKidsDir[0] = chNil;
|
|
if (!FGetSetRegKey(kszInstallDirValue, szMsKidsDir, size(SZ),
|
|
fregMachine | fregString))
|
|
{
|
|
Warn("Missing InstallDirectory registry entry or registry error");
|
|
}
|
|
stn = szMsKidsDir;
|
|
if (stn.Cch() == 0 || !_fniMsKidsDir.FBuildFromPath(&stn, kftgDir) ||
|
|
tYes != _fniMsKidsDir.TExists())
|
|
{
|
|
// REVIEW *****: this artificial search is temp until we have a
|
|
// real setup program and users have a InstallDirectory registry entry
|
|
_fniMsKidsDir = _fniExe;
|
|
if (!_fniMsKidsDir.FSetLeaf(pvNil, kftgDir))
|
|
return fFalse;
|
|
while (_fniMsKidsDir.FUpDir(pvNil, ffniMoveToDir))
|
|
;
|
|
/* REVIEW ***** (peted): if you check for the MSKIDS dir first, then
|
|
you don't have to reset the dir string before presenting the error
|
|
to the user */
|
|
stn = PszLit("Microsoft Kids"); // REVIEW *****
|
|
if (!_fniMsKidsDir.FDownDir(&stn, ffniMoveToDir))
|
|
{
|
|
stn = PszLit("MSKIDS"); // REVIEW *****
|
|
if (!_fniMsKidsDir.FDownDir(&stn, ffniMoveToDir))
|
|
{
|
|
Warn("Can't find Microsoft Kids or MSKIDS.");
|
|
stn = PszLit("Microsoft Kids");
|
|
_FCantFindFileDialog(&stn); // ignore failure
|
|
return fFalse;
|
|
}
|
|
}
|
|
}
|
|
|
|
AssertPo(&_fniMsKidsDir, ffniDir);
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Find _fniProductDir
|
|
At this point, _stnProduct* is either the command line parameter or
|
|
3D Movie Maker (in the absence of a command line parameter)
|
|
_FFindProductDir() locates the .chk files by checking:
|
|
first, _stnProduct* directories, or
|
|
second, the registry of installed products.
|
|
This routine updates _stnProductLong and _stnProductShort on return.
|
|
***************************************************************************/
|
|
bool APP::_FFindProductDir(PGST pgst)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pgst);
|
|
|
|
STN stnLong;
|
|
STN stnShort;
|
|
STN stn;
|
|
FNI fni;
|
|
long istn;
|
|
|
|
if (_FQueryProductExists(&_stnProductLong, &_stnProductShort, &_fniProductDir))
|
|
return fTrue;
|
|
|
|
for (istn = 0; istn < pgst->IstnMac(); istn++)
|
|
{
|
|
pgst->GetStn(istn, &stn);
|
|
vptagm->SplitString(&stn, &stnLong, &stnShort);
|
|
if (_FQueryProductExists(&stnLong, &stnShort, &fni))
|
|
{
|
|
_stnProductLong = stnLong;
|
|
_stnProductShort = stnShort;
|
|
_fniProductDir = fni;
|
|
return fTrue;
|
|
}
|
|
}
|
|
return fFalse;
|
|
}
|
|
|
|
/***************************************************************************
|
|
See if the product exists.
|
|
Method: See if the directory and chunk file exist.
|
|
***************************************************************************/
|
|
bool APP::_FQueryProductExists(STN *pstnLong, STN *pstnShort, FNI *pfni)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(pfni);
|
|
AssertPo(pstnLong, 0);
|
|
AssertPo(pstnShort, 0);
|
|
|
|
FNI fni;
|
|
STN stn;
|
|
|
|
*pfni = _fniMsKidsDir;
|
|
if (!pfni->FDownDir(pstnLong, ffniMoveToDir) &&
|
|
!pfni->FDownDir(pstnShort, ffniMoveToDir))
|
|
{
|
|
pfni->GetStnPath(&stn);
|
|
if (!stn.FAppendStn(&_stnProductLong))
|
|
goto LFail;
|
|
_FCantFindFileDialog(&stn); // ignore failure
|
|
goto LFail;
|
|
}
|
|
|
|
fni = *pfni;
|
|
if (fni.FSetLeaf(pstnLong, kftgChunky) && (tYes == fni.TExists()))
|
|
return fTrue;
|
|
fni = *pfni;
|
|
if (fni.FSetLeaf(pstnShort, kftgChunky) && (tYes == fni.TExists()))
|
|
return fTrue;
|
|
LFail:
|
|
TrashVar(pfni);
|
|
return fFalse;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Advances *ppch until it points to either the next space character or
|
|
the end of the string. Exception: if the space character is surrounded
|
|
by double quotes, this function skips by it.
|
|
***************************************************************************/
|
|
void APP::_SkipToSpace(char **ppch)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(ppch);
|
|
|
|
bool fQuote = fFalse;
|
|
|
|
while(**ppch != chNil && (fQuote || **ppch != kchSpace))
|
|
{
|
|
if (**ppch == ChLit('"'))
|
|
fQuote = !fQuote;
|
|
(*ppch)++;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Advances *ppch to the next non-space character.
|
|
***************************************************************************/
|
|
void APP::_SkipSpace(char **ppch)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(ppch);
|
|
|
|
while(**ppch == kchSpace)
|
|
(*ppch)++;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Socrates window was activated or deactivated.
|
|
***************************************************************************/
|
|
void APP::_Activate(bool fActive)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
bool fIsIconic;
|
|
|
|
APP_PAR::_Activate(fActive);
|
|
|
|
fIsIconic = IsIconic(vwig.hwndApp);
|
|
|
|
if (!fActive) // app was just deactivated
|
|
{
|
|
if (_FDisplayIs640480() && !_fDontMinimize && !fIsIconic)
|
|
{
|
|
// Note: using SW_MINIMIZE causes a bug where alt-tabbing
|
|
// from this app to a fullscreen DOS window reactivates
|
|
// this app. So use SW_SHOWMINNOACTIVE instead.
|
|
ShowWindow(vwig.hwndApp, SW_SHOWMINNOACTIVE); // minimize app
|
|
_fMinimized = fTrue;
|
|
|
|
// Note that we examine _fMinimized during the WM_DISPLAYCHANGE message
|
|
// received as a result of the following res change call. Therefore the
|
|
// minimize operation MUST precede the res switch.
|
|
if (_fSwitchedResolution)
|
|
_FSwitch640480(fFalse);
|
|
|
|
// When the portfolio is displayed, the main app is automatically disabled.
|
|
// This means all keyboard/mouse input directed at the main app window will
|
|
// be ignored until the portfolio is finished with. If the app is minimized
|
|
// here while the portfolio is displayed, then we will be left with a disabled
|
|
// app window on the win95 task bar. As a result, the app will not appear
|
|
// only the task window invoked by an Alt-tab, nor is it resized when
|
|
// the user clicks on it in the taskbar, (even though win95 tries to
|
|
// activate it). We could do the following...
|
|
// (1) Do not auto-minimize the app window while the portfolio is displayed.
|
|
// This is what happens on NT.
|
|
// (2) Drop the portfolio here, so the app window is enabled on the taskbar.
|
|
// (3) Make sure the app window is enabled now, by doing this...
|
|
EnableWindow(vwig.hwndApp, TRUE);
|
|
|
|
// The concern with doing this, is that when the app is later restored,
|
|
// it is then enabled when it shouldn't be, as the portfolio is still
|
|
// up in front of it. As it happens, this doesn't matter because the
|
|
// portfolio is full screen. This means that the user can't direct any
|
|
// mouse input to the main app window, and the portfolio will eat up any
|
|
// keyboard input.
|
|
|
|
}
|
|
}
|
|
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
|
|
/* Don't do this stuff unless we've got the CEX set up */
|
|
if (vpcex != pvNil)
|
|
{
|
|
|
|
if (!fActive)
|
|
{
|
|
if (!vpcex->FCidIn(cidDeactivate))
|
|
{
|
|
vpcex->EnqueueCid(cidDeactivate);
|
|
_fDown = fTrue;
|
|
_cactToggle = 0;
|
|
}
|
|
}
|
|
else if (_pcex != pvNil)
|
|
{
|
|
//
|
|
// End the modal wait
|
|
//
|
|
Assert(CactModal() > 0, "AAAAAAAAAhhhhh! - P.Floyd (Encore performance)");
|
|
|
|
// If there is no cidEndModal currently waiting to be processed,
|
|
// enqueue one now. This ensures we don't get multiple cidEndModal's
|
|
// processed. Note that we don't want to set _pcex null here as it
|
|
// is later examined in the wndproc before we ultimately return
|
|
// from FModalTopic.
|
|
if (!_pcex->FCidIn(cidEndModal))
|
|
_pcex->EnqueueCid(cidEndModal);
|
|
|
|
}
|
|
else
|
|
{
|
|
vpcex->FlushCid(cidDeactivate);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Deactivate the app
|
|
***************************************************************************/
|
|
bool APP::FCmdDeactivate(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
GCB gcb;
|
|
PWOKS pwoksModal;
|
|
GTE gte;
|
|
PGOB pgob;
|
|
ulong grfgte;
|
|
long lwRet;
|
|
CMD_MOUSE cmd;
|
|
PT pt;
|
|
bool fDoQuit;
|
|
|
|
if (_pcex != pvNil)
|
|
{
|
|
Assert(CactModal() > 0, "AAAAAAAAAhhhhh! - P.Floyd");
|
|
return(fTrue);
|
|
}
|
|
|
|
pgob = vpcex->PgobTracking();
|
|
if ((pgob != pvNil) && (_cactToggle < 500))
|
|
{
|
|
//
|
|
// Toggle the mouse button
|
|
//
|
|
TrackMouse(pgob, &pt);
|
|
|
|
ClearPb(&cmd, size(CMD_MOUSE));
|
|
|
|
cmd.pcmh = pgob;
|
|
cmd.cid = cidTrackMouse;
|
|
cmd.xp = pt.xp;
|
|
cmd.yp = pt.yp;
|
|
cmd.grfcust = GrfcustCur();
|
|
if (_fDown)
|
|
{
|
|
cmd.grfcust |= fcustMouse;
|
|
}
|
|
else
|
|
{
|
|
cmd.grfcust &= ~fcustMouse;
|
|
}
|
|
vpcex->EnqueueCmd((PCMD)&cmd);
|
|
vpcex->EnqueueCid(cidDeactivate);
|
|
_fDown = !_fDown;
|
|
_cactToggle++;
|
|
return fTrue;
|
|
}
|
|
|
|
gte.Init(Pkwa(), fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (!(grfgte & fgtePre) || !pgob->FIs(kclsGOK))
|
|
continue;
|
|
|
|
((PGOK)pgob)->Suspend();
|
|
}
|
|
|
|
if (FPushModal())
|
|
{
|
|
gcb.Set(CMH::HidUnique(), Pkwa(), fgobNil, kginMark);
|
|
gcb._rcRel.Set(0, 0, krelOne, krelOne);
|
|
|
|
_pcex = vpcex;
|
|
|
|
if (pvNil != (pwoksModal = NewObj WOKS(&gcb, Pkwa()->Pstrg())))
|
|
{
|
|
vpcex->SetModalGob(pwoksModal);
|
|
FModalLoop(&lwRet); // If we cannot enter modal mode, then we just won't suspend.
|
|
vpcex->SetModalGob(pvNil);
|
|
}
|
|
|
|
ReleasePpo(&pwoksModal);
|
|
|
|
_pcex = pvNil;
|
|
|
|
// The user may have selected Close the app system menu while the
|
|
// app was minimized on the taskbar. Depending on how Windows sent
|
|
// the messages to us, (ie the processing order of activate and
|
|
// close messages are not predictable it seems), we may or may not
|
|
// have a cidQuit message queued for the app. If PopModal destroys
|
|
// queued messages, then we would loose any Waiting cidQuit.
|
|
// Therefore check if we have a queued cidQuit, and if so, requeue
|
|
// it after the call to PopModal.
|
|
fDoQuit = vpcex->FCidIn(cidQuit);
|
|
|
|
PopModal();
|
|
|
|
if(fDoQuit)
|
|
vpcex->EnqueueCid(cidQuit);
|
|
}
|
|
|
|
|
|
gte.Init(Pkwa(), fgteNil);
|
|
while (gte.FNextGob(&pgob, &grfgte, fgteNil))
|
|
{
|
|
if (!(grfgte & fgtePre) || !pgob->FIs(kclsGOK))
|
|
continue;
|
|
|
|
((PGOK)pgob)->Resume();
|
|
}
|
|
|
|
return(fTrue);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Copy pixels from an offscreen buffer (pgnvSrc, prcSrc) to the screen
|
|
(pgnvDst, prcDst). This is called to move bits from an offscreen
|
|
buffer to the screen during a _FastUpdate cycle. This gives us
|
|
a chance to do a transition.
|
|
***************************************************************************/
|
|
void APP::_CopyPixels(PGNV pgnvSrc, RC *prcSrc, PGNV pgnvDst, RC *prcDst)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertPo(pgnvSrc, 0);
|
|
AssertVarMem(prcSrc);
|
|
AssertPo(pgnvDst, 0);
|
|
AssertVarMem(prcDst);
|
|
|
|
PMVIE pmvie = _Pmvie(); // Get the current movie, if any
|
|
PGOB pgob;
|
|
RC rcDst, rcSrc, rcWorkspace;
|
|
|
|
if (pmvie == pvNil || pmvie->Trans() == transNil)
|
|
{
|
|
APP_PAR::_CopyPixels(pgnvSrc, prcSrc, pgnvDst, prcDst);
|
|
return;
|
|
}
|
|
|
|
Assert(prcSrc->Dyp() == prcDst->Dyp() && prcSrc->Dxp() == prcDst->Dxp(),
|
|
"rc's are scaled");
|
|
|
|
// Need to do a transition, but if it's a slow transition (not a cut),
|
|
// we want to do a regular copy on all the areas around the workspace,
|
|
// then the slow transition on just the workspace.
|
|
|
|
pgob = Pkwa()->PgobFromHid(kidWorkspace);
|
|
|
|
if (pgob == pvNil || pmvie->Trans() == transCut)
|
|
{
|
|
pmvie->DoTrans(pgnvDst, pgnvSrc, prcDst, prcSrc);
|
|
return;
|
|
}
|
|
|
|
// NOTE: This code assumes that the following will base rcWorkspace
|
|
// in the same coordinate system as prcDst, which is always true
|
|
// according to ShonK.
|
|
pgob->GetRc(&rcWorkspace, cooHwnd);
|
|
|
|
// Do the areas around the workspace without the transition
|
|
if (prcDst->ypTop < rcWorkspace.ypTop)
|
|
{
|
|
rcSrc = *prcSrc;
|
|
rcSrc.ypBottom = rcWorkspace.ypTop + (rcSrc.ypTop - prcDst->ypTop);
|
|
rcDst = *prcDst;
|
|
rcDst.ypBottom = rcWorkspace.ypTop;
|
|
APP_PAR::_CopyPixels(pgnvSrc, &rcSrc, pgnvDst, &rcDst);
|
|
}
|
|
|
|
if (prcDst->ypBottom > rcWorkspace.ypBottom)
|
|
{
|
|
rcSrc = *prcSrc;
|
|
rcSrc.ypTop = rcWorkspace.ypBottom + (rcSrc.ypTop - prcDst->ypTop);
|
|
rcDst = *prcDst;
|
|
rcDst.ypTop = rcWorkspace.ypBottom;
|
|
APP_PAR::_CopyPixels(pgnvSrc, &rcSrc, pgnvDst, &rcDst);
|
|
}
|
|
if (prcDst->xpLeft < rcWorkspace.xpLeft)
|
|
{
|
|
rcSrc.ypTop = rcWorkspace.ypTop + (prcSrc->ypTop - prcDst->ypTop);
|
|
rcSrc.ypBottom = rcWorkspace.ypBottom + (prcSrc->ypTop - prcDst->ypTop);
|
|
rcSrc.xpLeft = prcSrc->xpLeft;
|
|
rcSrc.xpRight = rcWorkspace.xpLeft + (prcSrc->xpLeft - prcDst->xpLeft);
|
|
rcDst = *prcDst;
|
|
rcDst.xpRight = rcWorkspace.xpLeft;
|
|
rcDst.ypTop = rcWorkspace.ypTop;
|
|
rcDst.ypBottom = rcWorkspace.ypBottom;
|
|
APP_PAR::_CopyPixels(pgnvSrc, &rcSrc, pgnvDst, &rcDst);
|
|
}
|
|
|
|
if (prcDst->xpRight > rcWorkspace.xpRight)
|
|
{
|
|
rcSrc.ypTop = rcWorkspace.ypTop + (prcSrc->ypTop - prcDst->ypTop);
|
|
rcSrc.ypBottom = rcWorkspace.ypBottom + (prcSrc->ypTop - prcDst->ypTop);
|
|
rcSrc.xpLeft = rcWorkspace.xpRight + (prcSrc->xpLeft - prcDst->xpLeft);
|
|
rcSrc.xpRight = prcSrc->xpRight;
|
|
rcDst = *prcDst;
|
|
rcDst.xpLeft = rcWorkspace.xpRight;
|
|
rcDst.ypTop = rcWorkspace.ypTop;
|
|
rcDst.ypBottom = rcWorkspace.ypBottom;
|
|
APP_PAR::_CopyPixels(pgnvSrc, &rcSrc, pgnvDst, &rcDst);
|
|
}
|
|
|
|
//
|
|
// Now do the workspace copy, with the transition
|
|
//
|
|
if (rcWorkspace.FIntersect(prcDst))
|
|
{
|
|
rcSrc = rcWorkspace;
|
|
rcSrc.Offset(prcSrc->xpLeft - prcDst->xpLeft, prcSrc->ypTop - prcDst->ypTop);
|
|
pmvie->DoTrans(pgnvDst, pgnvSrc, &rcWorkspace, &rcSrc);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
Load the Studio
|
|
***************************************************************************/
|
|
bool APP::FCmdLoadStudio(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
FNI fniUserDoc;
|
|
CHID chidProject;
|
|
long kidBuilding;
|
|
PGOB pgob;
|
|
PGOK pgokBackground;
|
|
|
|
kidBuilding = pcmd->rglw[0];
|
|
chidProject = pcmd->rglw[1];
|
|
|
|
GetPortfolioDoc(&fniUserDoc); // might be ftgNil
|
|
|
|
if (_FInitStudio(&fniUserDoc))
|
|
{
|
|
// Nuke the building gob
|
|
pgob = Pkwa()->PgobFromHid(kidBuilding);
|
|
if (pvNil != pgob)
|
|
pgob->Release();
|
|
|
|
// Start a project, if requested
|
|
if (chidProject != chidNil)
|
|
{
|
|
pgokBackground = (PGOK)Pkwa()->PgobFromHid(kidBackground);
|
|
if ((pgokBackground != pvNil) && pgokBackground->FIs(kclsGOK))
|
|
{
|
|
AssertPo(pgokBackground, 0);
|
|
// REVIEW *****: if this fails, what happens?
|
|
pgokBackground->FRunScript((kstDefault << 16) | chidProject);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assert(_pstdio == pvNil, "_FInitStudio didn't clean up");
|
|
vpcex->EnqueueCid(cidLoadStudioFailed);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Load the Building
|
|
***************************************************************************/
|
|
bool APP::FCmdLoadBuilding(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
if ((_pstdio != pvNil) && !_pstdio->FShutdown())
|
|
{
|
|
long kgobReturn;
|
|
|
|
// This attempt may have destroyed the contents of kpridBuildingGob;
|
|
// Try to reset it from kpridBuildingGobT.
|
|
if (FGetProp(kpridBuildingGobT, &kgobReturn))
|
|
FSetProp(kpridBuildingGob, kgobReturn);
|
|
if (FGetProp(kpridBuildingStateT, &kgobReturn))
|
|
FSetProp(kpridBuildingState, kgobReturn);
|
|
Warn("Shutting down Studio failed");
|
|
return fTrue;
|
|
}
|
|
|
|
if(_FInitBuilding())
|
|
ReleasePpo(&_pstdio);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Exit the studio
|
|
***************************************************************************/
|
|
bool APP::FCmdExitStudio(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
bool tRet;
|
|
STN stnBackup;
|
|
|
|
// Now query the user to find whether they want to go to the
|
|
// building or to exit the app completely.
|
|
|
|
if (!FGetStnApp(idsExitStudio, &stnBackup))
|
|
return fTrue;
|
|
|
|
tRet = TModal(vpapp->PcrmAll(), ktpcQueryExitStudio, &stnBackup, bkYesNoCancel);
|
|
|
|
// Take more action if the user did not hit cancel.
|
|
if(tRet != tMaybe)
|
|
{
|
|
// Save the current movie if necessary.
|
|
if ((_pstdio != pvNil) && !_pstdio->FShutdown(tRet == tYes))
|
|
return fTrue;
|
|
|
|
// Either go to the building, or leave the app.
|
|
if (tRet == tYes)
|
|
{
|
|
// Go to the building.
|
|
if(_FInitBuilding())
|
|
ReleasePpo(&_pstdio);
|
|
}
|
|
else if (tRet == tNo)
|
|
{
|
|
// User wants to quit. We have have any dirty doc already by now.
|
|
// Note that APP::Quit() doesn't release _pstdio when we quit so
|
|
// we don't need to do it here either.
|
|
_fQuit = fTrue;
|
|
}
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Load the Theater
|
|
***************************************************************************/
|
|
bool APP::FCmdTheaterOpen(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
long kidParent;
|
|
|
|
kidParent = pcmd->rglw[0];
|
|
|
|
if (pvNil != _ptatr)
|
|
{
|
|
Bug("You forgot to close the last TATR!");
|
|
AssertPo(_ptatr, 0);
|
|
return fTrue;
|
|
}
|
|
_ptatr = TATR::PtatrNew(kidParent);
|
|
// Let the script know whether the open succeeded or failed
|
|
vpcex->EnqueueCid(cidTheaterOpenCompleted, pvNil, pvNil, (pvNil != _ptatr));
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Close the Theater
|
|
***************************************************************************/
|
|
bool APP::FCmdTheaterClose(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
AssertPo(_ptatr, 0);
|
|
ReleasePpo(&_ptatr);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clear the portfolio doc
|
|
***************************************************************************/
|
|
bool APP::FCmdPortfolioClear(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
_fniPortfolioDoc.SetNil();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Display the customized open file common dlg.
|
|
***************************************************************************/
|
|
bool APP::FCmdPortfolioOpen(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
FNI fni;
|
|
bool fOKed;
|
|
long idsTitle, idsFilterLabel, idsFilterExt;
|
|
FNI fniUsersDir;
|
|
PFNI pfni;
|
|
ulong grfPrevType;
|
|
CNO cnoWave = cnoNil;
|
|
|
|
// Set up strings specific to this use of the portfolio.
|
|
|
|
switch (pcmd->rglw[0])
|
|
{
|
|
case kpfPortOpenMovie:
|
|
idsTitle = idsPortfOpenMovieTitle;
|
|
idsFilterLabel = idsPortfMovieFilterLabel;
|
|
idsFilterExt = idsPortfMovieFilterExt;
|
|
|
|
grfPrevType = fpfPortPrevMovie;
|
|
cnoWave = kwavPortOpenMovie;
|
|
|
|
break;
|
|
|
|
case kpfPortOpenSound:
|
|
// Only display extensions appropriate to the type of sound being imported.
|
|
idsTitle = idsPortfOpenSoundTitle;
|
|
idsFilterLabel = idsPortfSoundFilterLabel;
|
|
if(pcmd->rglw[1] == kidMidiGlass)
|
|
idsFilterExt = idsPortfSoundMidiFilterExt;
|
|
else
|
|
idsFilterExt = idsPortfSoundWaveFilterExt;
|
|
|
|
grfPrevType = fpfPortPrevMovie | fpfPortPrevSound;
|
|
cnoWave = kwavPortOpenSound;
|
|
|
|
break;
|
|
|
|
case kpfPortOpenTexture:
|
|
idsTitle = idsPortfOpenTextureTitle;
|
|
idsFilterLabel = idsPortfTextureFilterLabel;
|
|
idsFilterExt = idsPortfTextureFilterExt;
|
|
|
|
grfPrevType = fpfPortPrevTexture;
|
|
// Currently no audio for open texture, because we don't open texture yet.
|
|
|
|
break;
|
|
|
|
default:
|
|
Bug("Unrecognized portfolio open type.");
|
|
return fFalse;
|
|
}
|
|
|
|
// Prepare to set the initial directory for the portfolio if necessary.
|
|
|
|
switch (pcmd->rglw[2])
|
|
{
|
|
case kpfPortDirUsers:
|
|
|
|
// Initial directory will be the 'Users' directory.
|
|
|
|
vapp.GetFniUsers(&fniUsersDir);
|
|
pfni = &fniUsersDir;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Initial directory will be current directory.
|
|
|
|
pfni = pvNil;
|
|
|
|
break;
|
|
}
|
|
|
|
// Now display the open dlg. Script is informed of the outcome
|
|
// of the portfolio from beneath FPortDisplayWithIds.
|
|
fOKed = FPortDisplayWithIds(&fni, fTrue, idsFilterLabel, idsFilterExt,
|
|
idsTitle, pvNil, pvNil, pfni, grfPrevType, cnoWave);
|
|
if (fOKed)
|
|
{
|
|
// User selected a file, so store fni for later use.
|
|
SetPortfolioDoc(&fni);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
OnnDefVariable
|
|
Retrieves the default onn for this app. Gets the name from the app's
|
|
string table.
|
|
************************************************************ PETED ***********/
|
|
long APP::OnnDefVariable(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (_onnDefVariable == onnNil)
|
|
{
|
|
STN stn;
|
|
|
|
if (!FGetStnApp(idsDefaultFont, &stn) ||
|
|
!FGetOnn(&stn, &_onnDefVariable))
|
|
{
|
|
_onnDefVariable = APP_PAR::OnnDefVariable();
|
|
}
|
|
}
|
|
return _onnDefVariable;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
FGetOnn
|
|
APP version of FGetOnn. Mainly used so that we can easily report
|
|
failure to the user if the font isn't around. And no, I don't care
|
|
that we'll call FGetOnn twice on failure...it's a failure case, and
|
|
can stand to be slow. :) Maps the font on failure so that there's
|
|
still a usable onn if the user doesn't want to mess with alternate
|
|
solutions.
|
|
|
|
Arguments:
|
|
PSTN pstn -- The font name
|
|
long *ponn -- takes the result
|
|
|
|
Returns: fTrue if the original font could be found.
|
|
|
|
************************************************************ PETED ***********/
|
|
bool APP::FGetOnn(PSTN pstn, long *ponn)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (!vntl.FGetOnn(pstn, ponn))
|
|
{
|
|
if (!_fFontError)
|
|
{
|
|
PushErc(ercSocNoDefaultFont);
|
|
_fFontError = fTrue;
|
|
}
|
|
*ponn = vntl.OnnMapStn(pstn);
|
|
return fFalse;
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return the size of memory
|
|
***************************************************************************/
|
|
void APP::MemStat(long *pdwTotalPhys, long *pdwAvailPhys)
|
|
{
|
|
#ifdef WIN
|
|
MEMORYSTATUS ms;
|
|
ms.dwLength = size(MEMORYSTATUS);
|
|
GlobalMemoryStatus(&ms);
|
|
if (pvNil != pdwTotalPhys)
|
|
*pdwTotalPhys = ms.dwTotalPhys;
|
|
if (pvNil != pdwAvailPhys)
|
|
*pdwAvailPhys = ms.dwAvailPhys;
|
|
#else
|
|
RawRtn();
|
|
#endif //!WIN
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
DypTextDef
|
|
Retrieves the default dypFont for this app. Gets the font size as
|
|
a string from the app's string table, then converts it to the number
|
|
value.
|
|
************************************************************ PETED ***********/
|
|
long APP::DypTextDef(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
if (_dypTextDef == 0)
|
|
{
|
|
STN stn;
|
|
|
|
if (pvNil == _pgstApp || !FGetStnApp(idsDefaultDypFont, &stn) ||
|
|
!stn.FGetLw(&_dypTextDef) || _dypTextDef <= 0)
|
|
{
|
|
Warn("DypTextDef failed");
|
|
_dypTextDef = APP_PAR::DypTextDef();
|
|
}
|
|
}
|
|
return _dypTextDef;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Ask the user if they want to save changes to the given doc.
|
|
***************************************************************************/
|
|
bool APP::TQuerySaveDoc(PDOCB pdocb, bool fForce)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pdocb, 0);
|
|
|
|
STN stnName;
|
|
long tpc;
|
|
STN stnBackup;
|
|
long bk;
|
|
bool tResult;
|
|
|
|
pdocb->GetName(&stnName);
|
|
tpc = fForce ? ktpcQuerySave : ktpcQuerySaveWithCancel;
|
|
if (!FGetStnApp(idsSaveChangesBkp, &stnBackup))
|
|
stnBackup.SetNil();
|
|
bk = fForce? bkYesNo : bkYesNoCancel;
|
|
|
|
tResult = TModal(vpapp->PcrmAll(), tpc, &stnBackup, bk, kstidQuerySave,
|
|
&stnName);
|
|
vpcex->EnqueueCid(cidQuerySaveDocResult, pvNil, pvNil, tResult);
|
|
|
|
return tResult;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Quit routine. May or may not initiate the quit sequence (depending
|
|
on user input).
|
|
***************************************************************************/
|
|
void APP::Quit(bool fForce)
|
|
{
|
|
AssertThis(0);
|
|
|
|
bool tRet;
|
|
STN stnBackup;
|
|
|
|
// If we already know we have to quit, or a modal topic is already
|
|
// being displayed, then do not query the user to quit here.
|
|
if (_fQuit || vpappb->CactModal() > (_pcex != pvNil? 1 : 0)
|
|
|| FInPortfolio())
|
|
{
|
|
// Make sure the app is visible to the user. Otherwise if this
|
|
// return is preventing a system shutdown and we're minimized
|
|
// on the taskbar, then the user won't know why the shutdown failed.
|
|
if (!_fQuit)
|
|
EnsureInteractive();
|
|
return;
|
|
}
|
|
|
|
if (fForce)
|
|
{
|
|
// Force quit, don't ask the user if they want to quit. But
|
|
// do ask if they want to save their documents.
|
|
DOCB::FQueryCloseAll(fdocForceClose);
|
|
_fQuit = fTrue;
|
|
|
|
return;
|
|
}
|
|
|
|
// If we're minimized, user is closing app from the taskbar. Quit
|
|
// without confirmation (we'll still confirm movie save if user has
|
|
// a dirty doc)
|
|
if (_fMinimized)
|
|
{
|
|
tRet = tYes;
|
|
}
|
|
else
|
|
{
|
|
if (!FGetStnApp(idsConfirmExitBkp, &stnBackup))
|
|
stnBackup.SetNil();
|
|
tRet = TModal(vpapp->PcrmAll(), ktpcQueryQuit, &stnBackup, bkYesNo);
|
|
}
|
|
|
|
if (tRet == tYes)
|
|
{
|
|
// User wants to quit, so shut down studio if necessary
|
|
if (_pstdio == pvNil || _pstdio->FShutdown(fFalse))
|
|
_fQuit = fTrue;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Return a pointer to the current movie, if any. The movie could be in
|
|
the studio, theater, or splot machine.
|
|
***************************************************************************/
|
|
PMVIE APP::_Pmvie(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
PMVIE pmvie = pvNil;
|
|
PSPLOT psplot;
|
|
|
|
if (_pstdio != pvNil && _pstdio->Pmvie() != pvNil)
|
|
pmvie = _pstdio->Pmvie();
|
|
else if (_ptatr != pvNil && _ptatr->Pmvie() != pvNil)
|
|
pmvie = _ptatr->Pmvie();
|
|
else if (Pkwa() != pvNil)
|
|
{
|
|
psplot = (PSPLOT)Pkwa()->PgobFromCls(kclsSPLOT);
|
|
if (psplot != pvNil)
|
|
pmvie = psplot->Pmvie();
|
|
}
|
|
return pmvie;
|
|
}
|
|
|
|
|
|
/****************************************
|
|
Info dialog items (idits)
|
|
****************************************/
|
|
enum
|
|
{
|
|
iditOkInfo,
|
|
iditProductId,
|
|
iditWindowModeInfo,
|
|
#ifdef DEBUG
|
|
iditCactAV,
|
|
#endif // DEBUG
|
|
iditProductNameInfo,
|
|
iditSaveChanges,
|
|
iditRenderModeInfo,
|
|
iditLimInfo
|
|
};
|
|
|
|
|
|
#ifdef WIN
|
|
|
|
/***************************************************************************
|
|
Useful function
|
|
***************************************************************************/
|
|
char *LoadGenResource(HINSTANCE hInst, LPCSTR lpResource, LPCSTR lpType)
|
|
{
|
|
HRSRC hResource;
|
|
HGLOBAL hGbl;
|
|
|
|
hResource = FindResource(hInst, lpResource, lpType);
|
|
|
|
if (hResource == NULL)
|
|
return(NULL);
|
|
|
|
hGbl = LoadResource(hInst, hResource);
|
|
|
|
return (char *)LockResource(hGbl);
|
|
}
|
|
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
Put up info dialog
|
|
***************************************************************************/
|
|
bool APP::FCmdInfo(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
PMVIE pmvie = pvNil;
|
|
PDLG pdlg;
|
|
long idit;
|
|
bool fRunInWindowNew;
|
|
STN stn;
|
|
STN stnT;
|
|
bool fSaveChanges;
|
|
|
|
pmvie = _Pmvie();
|
|
|
|
pdlg = DLG::PdlgNew(dlidInfo, pvNil, pvNil);
|
|
if (pvNil == pdlg)
|
|
return fTrue;
|
|
pdlg->PutRadio(iditRenderModeInfo, _fSlowCPU ? 1 : 0);
|
|
|
|
GetStnAppName(&stn);
|
|
#ifdef DEBUG
|
|
stn.FAppendSz(PszLit(" (Debug)"));
|
|
#endif //DEBUG
|
|
stnT.FFormatSz(PszLit(" %d.%d.%04d"), rmj, rmm, rup);
|
|
stn.FAppendStn(&stnT);
|
|
pdlg->FPutStn(iditProductNameInfo, &stn);
|
|
|
|
#ifdef WIN
|
|
LPSTR lpszPid;
|
|
char rgchPid[kcchMaxStn + 1];
|
|
|
|
lpszPid = (LPSTR)LoadGenResource(vwig.hinst, MAKEINTRESOURCE(RC_PID_NUMBER), RT_RCDATA);
|
|
|
|
if(lpszPid != pvNil)
|
|
{
|
|
|
|
#define PID_RPC 128
|
|
#define PID_SITE (PID_RPC + 5)
|
|
#define PID_SERIAL (PID_SITE + 3)
|
|
#define PID_RANDOM (PID_SERIAL + 7)
|
|
|
|
wsprintf(rgchPid,
|
|
"%5.5s-%3.3s-%7.7s-%5.5s",
|
|
lpszPid+PID_RPC,
|
|
lpszPid+PID_SITE,
|
|
lpszPid+PID_SERIAL,
|
|
lpszPid+PID_RANDOM);
|
|
|
|
stn.SetSz(rgchPid);
|
|
pdlg->FPutStn(iditProductId, &stn);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
pdlg->FPutLwInEdit(iditCactAV, vcactAV);
|
|
#endif // DEBUG
|
|
pdlg->PutRadio(iditWindowModeInfo, _fRunInWindow ? 1 : 0);
|
|
|
|
idit = pdlg->IditDo();
|
|
|
|
fSaveChanges = pdlg->FGetCheck(iditSaveChanges);
|
|
if (FPure(_fSlowCPU) != FPure(pdlg->LwGetRadio(iditRenderModeInfo)))
|
|
{
|
|
PMVIE pmvie;
|
|
|
|
_fSlowCPU = !_fSlowCPU;
|
|
pmvie = _Pmvie();
|
|
if (pvNil != pmvie)
|
|
{
|
|
pmvie->Pbwld()->FSetHalfMode(fFalse, _fSlowCPU);
|
|
pmvie->InvalViews();
|
|
}
|
|
}
|
|
if (fSaveChanges)
|
|
{
|
|
bool fSlowCPU = _fSlowCPU;
|
|
FGetSetRegKey(kszBetterSpeedValue, &fSlowCPU,
|
|
size(bool), fregSetKey);
|
|
}
|
|
|
|
fRunInWindowNew = pdlg->LwGetRadio(iditWindowModeInfo);
|
|
if (FPure(_fRunInWindow) != FPure(fRunInWindowNew))
|
|
{
|
|
if (!fRunInWindowNew)
|
|
{
|
|
// user wants to be fullscreen
|
|
if (_FDisplaySwitchSupported())
|
|
{
|
|
_fRunInWindow = fFalse;
|
|
_RebuildMainWindow();
|
|
if (!_FSwitch640480(fTrue))
|
|
{
|
|
_fRunInWindow = fTrue;
|
|
_RebuildMainWindow();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// user wants to run in a window.
|
|
// Don't allow user to run in a window at 640x480 resolution.
|
|
if (!_FDisplayIs640480() || _fSwitchedResolution)
|
|
{
|
|
_fRunInWindow = fTrue;
|
|
_RebuildMainWindow();
|
|
if (_FSwitch640480(fFalse))
|
|
{
|
|
_fSwitchedResolution = fFalse;
|
|
}
|
|
else
|
|
{
|
|
// back to fullscreen
|
|
_fRunInWindow = fFalse;
|
|
_RebuildMainWindow();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (fSaveChanges)
|
|
{
|
|
bool fSwitchRes = !_fRunInWindow;
|
|
|
|
FGetSetRegKey(kszSwitchResolutionValue, &fSwitchRes,
|
|
size(fSwitchRes), fregSetKey);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
bool fEmpty;
|
|
long lwT;
|
|
|
|
if (pdlg->FGetLwFromEdit(iditCactAV, &lwT, &fEmpty) && !fEmpty)
|
|
{
|
|
if (lwT < 0)
|
|
{
|
|
Debugger();
|
|
}
|
|
else
|
|
{
|
|
vcactAV = lwT;
|
|
if (fSaveChanges)
|
|
{
|
|
DBINFO dbinfo;
|
|
|
|
dbinfo.cactAV = vcactAV;
|
|
AssertDo(FGetSetRegKey(PszLit("DebugSettings"), &dbinfo, size(DBINFO),
|
|
fregSetKey | fregBinary),
|
|
"Couldn't save current debug settings in registry");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
ReleasePpo(&pdlg);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef WIN
|
|
#ifdef UNICODE
|
|
typedef LONG (WINAPI *PFNCHDS) (LPDEVMODEW lpDevMode, DWORD dwFlags);
|
|
const PSZ kpszChds = PszLit("ChangeDisplaySettingsW");
|
|
#else
|
|
typedef LONG (WINAPI *PFNCHDS) (LPDEVMODEA lpDevMode, DWORD dwFlags);
|
|
const PSZ kpszChds = PszLit("ChangeDisplaySettingsA");
|
|
#endif // !UNICODE
|
|
|
|
#ifdef BUG1920
|
|
#ifdef UNICODE
|
|
typedef BOOL (WINAPI *PFNENUM) (LPCWSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEW lpDevMode);
|
|
const PSZ kpszEnum = PszLit("EnumDisplaySettingsW");
|
|
#else
|
|
typedef BOOL (WINAPI *PFNENUM) (LPCSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEA lpDevMode);
|
|
const PSZ kpszEnum = PszLit("EnumDisplaySettingsA");
|
|
#endif // !UNICODE
|
|
#endif // BUG1920
|
|
#endif //WIN
|
|
|
|
#ifndef DM_BITSPERPEL
|
|
#define DM_BITSPERPEL 0x00040000L // from wingdi.h
|
|
#define DM_PELSWIDTH 0x00080000L
|
|
#define DM_PELSHEIGHT 0x00100000L
|
|
#endif //!DM_BITSPERPEL
|
|
|
|
#ifndef CDS_FULLSCREEN
|
|
#define CDS_FULLSCREEN 4
|
|
#endif //!CDS_FULLSCREEN
|
|
|
|
#ifndef DISP_CHANGE_SUCCESSFUL
|
|
#define DISP_CHANGE_SUCCESSFUL 0
|
|
#endif //!DISP_CHANGE_SUCCESSFUL
|
|
|
|
/***************************************************************************
|
|
Determine if display resolution switching is supported
|
|
***************************************************************************/
|
|
bool APP::_FDisplaySwitchSupported(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
DWORD dwVersion;
|
|
byte bVersionMajor;
|
|
byte bVersionMinor;
|
|
bool fNT;
|
|
|
|
dwVersion = GetVersion();
|
|
bVersionMinor = (byte)((dwVersion & 0x0000ff00) >> 8);
|
|
bVersionMajor = (byte)(dwVersion & 0x000000ff);
|
|
fNT = ((dwVersion & 0x80000000) == 0);
|
|
if (fNT)
|
|
{
|
|
// Note: At this time, no version of NT supports res-switching. If
|
|
// this changes in the future, do the version-check here. We
|
|
// can't just try ChangeDisplaySettings to see if it works,
|
|
// because on NT 3.51, the API exists, but hangs when we call
|
|
// it.
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
if (bVersionMajor >= 4)
|
|
return fTrue;
|
|
}
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fFalse; // OS doesn't support res-switching
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Switch to/from 640x480x8bit video mode. It uses GetProcAddress so it
|
|
can fail gracefully on systems that don't support
|
|
ChangeDisplaySettings().
|
|
***************************************************************************/
|
|
bool APP::_FSwitch640480(bool fTo640480)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
#ifdef WIN
|
|
#ifdef BUG1920
|
|
bool fSetMode = fFalse, fSetBbp = fTrue;
|
|
DWORD iModeNum;
|
|
PFNENUM pfnEnum;
|
|
#endif // BUG1920
|
|
HINSTANCE hLibrary;
|
|
PFNCHDS pfnChds;
|
|
DEVMODE devmode;
|
|
long lwResult;
|
|
|
|
hLibrary = LoadLibrary(PszLit("USER32.DLL"));
|
|
if (0 == hLibrary)
|
|
goto LFail;
|
|
|
|
pfnChds = (PFNCHDS)GetProcAddress(hLibrary, kpszChds);
|
|
if (pvNil == pfnChds)
|
|
goto LFail;
|
|
|
|
#ifdef BUG1920
|
|
pfnEnum = (PFNENUM)GetProcAddress(hLibrary, kpszEnum);
|
|
if (pvNil == pfnEnum)
|
|
goto LFail;
|
|
#endif // BUG1920
|
|
|
|
if (fTo640480)
|
|
{
|
|
// Try to switch to 640x480
|
|
#ifdef BUG1920
|
|
LRetry:
|
|
for (iModeNum = 0; pfnEnum(NULL, iModeNum, &devmode); iModeNum++)
|
|
{
|
|
if ((fSetBbp ? devmode.dmBitsPerPel != 8 :
|
|
devmode.dmBitsPerPel < 8) || devmode.dmPelsWidth != 640 ||
|
|
devmode.dmPelsHeight != 480)
|
|
{
|
|
continue;
|
|
}
|
|
lwResult = pfnChds(&devmode, CDS_FULLSCREEN);
|
|
if (lwResult == DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
fSetMode = fTrue;
|
|
break;
|
|
}
|
|
}
|
|
if (!fSetMode && fSetBbp)
|
|
{
|
|
fSetBbp = fFalse;
|
|
goto LRetry;
|
|
}
|
|
|
|
if (fSetMode && _FDisplayIs640480())
|
|
#else // BUG1920
|
|
devmode.dmSize = size(DEVMODE);
|
|
devmode.dmBitsPerPel = 8;
|
|
devmode.dmPelsWidth = 640;
|
|
devmode.dmPelsHeight = 480;
|
|
devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
lwResult = pfnChds(&devmode, CDS_FULLSCREEN);
|
|
if (DISP_CHANGE_SUCCESSFUL != lwResult)
|
|
{
|
|
// try without setting the bpp
|
|
devmode.dmFields &= ~DM_BITSPERPEL;
|
|
lwResult = pfnChds(&devmode, CDS_FULLSCREEN);
|
|
}
|
|
|
|
if (DISP_CHANGE_SUCCESSFUL == lwResult && _FDisplayIs640480())
|
|
#endif // !BUG1920
|
|
{
|
|
_fSwitchedResolution = fTrue;
|
|
SetWindowPos(vwig.hwndApp, HWND_TOP, 0, 0, 640, 480, 0);
|
|
}
|
|
else
|
|
{
|
|
goto LFail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Try to restore user's previous resolution
|
|
lwResult = pfnChds(NULL, CDS_FULLSCREEN);
|
|
if (DISP_CHANGE_SUCCESSFUL != lwResult)
|
|
goto LFail;
|
|
}
|
|
FreeLibrary(hLibrary);
|
|
return fTrue;
|
|
LFail:
|
|
if (0 != hLibrary)
|
|
FreeLibrary(hLibrary);
|
|
#endif //WIN
|
|
#ifdef MAC
|
|
RawRtn();
|
|
#endif //MAC
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Clean up routine - app is shutting down
|
|
***************************************************************************/
|
|
void APP::_CleanUp(void)
|
|
{
|
|
_FWriteUserData();
|
|
ReleasePpo(&_pstdio);
|
|
ReleasePpo(&_ptatr);
|
|
ReleasePpo(&_pcfl);
|
|
ReleasePpo(&_pmvieHandoff);
|
|
ReleasePpo(&_pcrmAll);
|
|
ReleasePpo(&_pglicrfBuilding);
|
|
ReleasePpo(&_pglicrfStudio);
|
|
ReleasePpo(&_pgstBuildingFiles);
|
|
ReleasePpo(&_pgstStudioFiles);
|
|
ReleasePpo(&_pgstSharedFiles);
|
|
ReleasePpo(&_pgstApp);
|
|
ReleasePpo(&_pkwa);
|
|
BWLD::CloseBRender();
|
|
APP_PAR::_CleanUp();
|
|
if (_fSwitchedResolution)
|
|
_FSwitch640480(fFalse); // try to restore desktop
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Put up a modal help balloon
|
|
***************************************************************************/
|
|
bool APP::TModal(PRCA prca, long tpc, PSTN pstnBackup, long bkBackup,
|
|
long stidSubst, PSTN pstnSubst)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(prca, 0);
|
|
|
|
long lwSelect;
|
|
bool tRet;
|
|
STN stn;
|
|
|
|
// If app is minimized, restore it so user can see the dialog
|
|
EnsureInteractive();
|
|
|
|
if (ivNil != stidSubst)
|
|
{
|
|
AssertPo(pstnSubst, 0);
|
|
if (!Pkwa()->Pstrg()->FPut(stidSubst, pstnSubst))
|
|
return tMaybe;
|
|
}
|
|
|
|
if (pvNil == prca || !Pkwa()->FModalTopic(prca, tpc, &lwSelect))
|
|
{
|
|
// Backup plan: use old TGiveAlertSz.
|
|
if (pvNil != pstnSubst)
|
|
stn.FFormat(pstnBackup, pstnSubst);
|
|
else
|
|
stn = *pstnBackup;
|
|
tRet = TGiveAlertSz(stn.Psz(), bkBackup, cokExclamation);
|
|
}
|
|
else
|
|
{
|
|
// Help balloon appeared ok, so translate returned value into
|
|
// something we can use here. lwSelect is returned as the
|
|
// (1 based) index of the btn clicked, out of Yes/No/Cancel.
|
|
switch (lwSelect)
|
|
{
|
|
case 1:
|
|
tRet = tYes;
|
|
break;
|
|
case 2:
|
|
tRet = tNo;
|
|
break;
|
|
case 3:
|
|
tRet = tMaybe;
|
|
break;
|
|
default:
|
|
Bug("TModal() balloon returned unrecognized selection");
|
|
tRet = tMaybe;
|
|
}
|
|
}
|
|
|
|
if (ivNil != stidSubst)
|
|
Pkwa()->Pstrg()->Delete(stidSubst);
|
|
UpdateMarked(); // make sure alert is drawn over
|
|
return tRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Static function to prompt the user to insert the CD named pstnTitle
|
|
***************************************************************************/
|
|
bool APP::FInsertCD(PSTN pstnTitle)
|
|
{
|
|
AssertPo(pstnTitle, 0);
|
|
|
|
STN stnBackup;
|
|
bool tRet;
|
|
|
|
stnBackup = PszLit("I can't find the CD '%s' Please insert it. Should I look again?");
|
|
tRet = vpapp->TModal(vpapp->PcrmAll(), ktpcQueryCD, &stnBackup, bkYesNo,
|
|
kstidQueryCD, pstnTitle);
|
|
|
|
/* Don't tell the user that they told us not to try again; they know
|
|
that already */
|
|
if (tRet == tNo)
|
|
vapp._fDontReportInitFailure = fTrue;
|
|
|
|
return (tYes == tRet);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
DisplayErrors
|
|
Displays any errors that happen to be on the error stack. Call this
|
|
when you don't want to wait until idle time to show errors.
|
|
************************************************************ PETED ***********/
|
|
void APP::DisplayErrors(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
long erc;
|
|
STN stnMessage;
|
|
|
|
if (vpers->FPop(&erc))
|
|
{
|
|
STN stnErr;
|
|
|
|
vpers->Clear();
|
|
|
|
//
|
|
// Convert to help topic number
|
|
//
|
|
switch (erc)
|
|
{
|
|
case ercOomHq:
|
|
erc = ktpcercOomHq;
|
|
break;
|
|
|
|
case ercOomPv:
|
|
erc = ktpcercOomPv;
|
|
break;
|
|
|
|
case ercOomNew:
|
|
erc = ktpcercOomNew;
|
|
break;
|
|
|
|
/* We don't have any real specific information to present the user in
|
|
these cases, plus they generally shouldn't come up anyway (a more
|
|
informational error should have been pushed farther up the chain
|
|
of error reporters) */
|
|
case ercFilePerm:
|
|
case ercFileOpen:
|
|
case ercFileCreate:
|
|
case ercFileSwapNames:
|
|
case ercFileRename:
|
|
case ercFniGeneral:
|
|
case ercFniDelete:
|
|
case ercFniRename:
|
|
case ercFniMismatch:
|
|
case ercFniDirCreate:
|
|
case ercFneGeneral:
|
|
case ercCflCreate:
|
|
case ercCflSaveCopy:
|
|
case ercSndmCantInit:
|
|
case ercSndmPartialInit:
|
|
case ercGfxCantDraw:
|
|
case ercGfxCantSetFont:
|
|
case ercGfxNoFontList:
|
|
case ercGfxCantSetPalette:
|
|
case ercDlgCantGetArgs:
|
|
case ercDlgCantFind:
|
|
case ercRtxdTooMuchText:
|
|
case ercRtxdReadFailed:
|
|
case ercRtxdSaveFailed:
|
|
case ercCantOpenVideo:
|
|
case ercMbmpCantOpenBitmap:
|
|
|
|
/* In theory, these are obsolete. */
|
|
case ercSocTdtTooLong:
|
|
case ercSocWaveInProblems:
|
|
case ercSocCreatedUserDir:
|
|
|
|
/* Display a generic error message, with the error code in it */
|
|
stnErr.FFormatSz(PszLit("%d"), erc);
|
|
if (!vapp.Pkwa()->Pstrg()->FPut(kstidGenericError, &stnErr))
|
|
stnErr.SetNil();
|
|
erc = ktpcercSocGenericError;
|
|
break;
|
|
|
|
case ercFileGeneral:
|
|
erc = ktpcercFileGeneral;
|
|
break;
|
|
|
|
case ercCflOpen:
|
|
erc = ktpcercCflOpen;
|
|
break;
|
|
|
|
case ercCflSave:
|
|
erc = ktpcercCflSave;
|
|
break;
|
|
|
|
case ercCrfCantLoad:
|
|
erc = ktpcercCrfCantLoad;
|
|
break;
|
|
|
|
case ercFniHidden:
|
|
erc = ktpcercFniHidden;
|
|
break;
|
|
|
|
case ercOomGdi:
|
|
erc = ktpcercOomGdi;
|
|
break;
|
|
|
|
case ercDlgOom:
|
|
erc = ktpcercDlgOom;
|
|
break;
|
|
|
|
case ercCantSave:
|
|
erc = ktpcercCantSave;
|
|
break;
|
|
|
|
case ercSocSaveFailure:
|
|
erc = ktpcercSocSaveFailure;
|
|
break;
|
|
|
|
case ercSocSceneSwitch:
|
|
erc = ktpcercSocSceneSwitch;
|
|
break;
|
|
|
|
case ercSocSceneChop:
|
|
erc = ktpcercSocSceneChop;
|
|
break;
|
|
|
|
case ercSocBadFile:
|
|
erc = ktpcercSocBadFile;
|
|
break;
|
|
|
|
case ercSocNoTboxSelected:
|
|
erc = ktpcercSocNoTboxSelected;
|
|
break;
|
|
|
|
case ercSocNoActrSelected:
|
|
erc = ktpcercSocNoActrSelected;
|
|
break;
|
|
|
|
case ercSocNotUndoable:
|
|
erc = ktpcercSocNotUndoable;
|
|
break;
|
|
|
|
case ercSocNoScene:
|
|
erc = ktpcercSocNoScene;
|
|
break;
|
|
|
|
case ercSocBadVersion:
|
|
erc = ktpcercSocBadVersion;
|
|
break;
|
|
|
|
case ercSocNothingToPaste:
|
|
erc = ktpcercSocNothingToPaste;
|
|
break;
|
|
|
|
case ercSocBadFrameSlider:
|
|
erc = ktpcercSocBadFrameSlider;
|
|
break;
|
|
|
|
case ercSocGotoFrameFailure:
|
|
erc = ktpcercSocGotoFrameFailure;
|
|
break;
|
|
|
|
case ercSocDeleteBackFailure:
|
|
erc = ktpcercSocDeleteBackFailure;
|
|
break;
|
|
|
|
case ercSocActionNotApplicable:
|
|
erc = ktpcercSocActionNotApplicable;
|
|
break;
|
|
|
|
case ercSocCannotPasteThatHere:
|
|
erc = ktpcercSocCannotPasteThatHere;
|
|
break;
|
|
|
|
case ercSocNoModlForChar:
|
|
erc = ktpcercSocNoModlForChar;
|
|
break;
|
|
|
|
case ercSocNameTooLong:
|
|
erc = ktpcercSocNameTooLong;
|
|
break;
|
|
|
|
case ercSocTboxTooSmall:
|
|
erc = ktpcercSocTboxTooSmall;
|
|
break;
|
|
|
|
case ercSocNoThumbnails:
|
|
erc = ktpcercSocNoThumbnails;
|
|
break;
|
|
|
|
case ercSocBadTdf:
|
|
erc = ktpcercSocBadTdf;
|
|
break;
|
|
|
|
case ercSocNoActrMidi:
|
|
erc = ktpcercSocNoActrMidi;
|
|
break;
|
|
|
|
case ercSocNoImportRollCall:
|
|
erc = ktpcercSocNoImportRollCall;
|
|
break;
|
|
|
|
case ercSocNoNukeRollCall:
|
|
erc = ktpcercSocNoNukeRollCall;
|
|
break;
|
|
|
|
case ercSocSceneSortError:
|
|
erc = ktpcercSocSceneSortError;
|
|
break;
|
|
|
|
case ercSocCantInitSceneSort:
|
|
erc = ktpcercSocCantInitSceneSort;
|
|
break;
|
|
|
|
case ercSocCantInitSplot:
|
|
erc = ktpcercSocCantInitSplot;
|
|
break;
|
|
|
|
case ercSocNoWaveIn:
|
|
erc = ktpcercSocNoWaveIn;
|
|
break;
|
|
|
|
case ercSocPortfolioFailed:
|
|
erc = ktpcercSocPortfolioFailed;
|
|
break;
|
|
|
|
case ercSocCantInitStudio:
|
|
erc = ktpcercSocCantInitStudio;
|
|
break;
|
|
|
|
case ercSoc3DWordCreate:
|
|
erc = ktpcercSoc3DWordCreate;
|
|
break;
|
|
|
|
case ercSoc3DWordChange:
|
|
erc = ktpcercSoc3DWordChange;
|
|
break;
|
|
|
|
case ercSocWaveSaveFailure:
|
|
erc = ktpcercSocWaveSaveFailure;
|
|
break;
|
|
|
|
case ercSocNoSoundName:
|
|
erc = ktpcercSocNoSoundName;
|
|
break;
|
|
|
|
case ercSndamWaveDeviceBusy:
|
|
erc = ktpcercSndamWaveDeviceBusy;
|
|
break;
|
|
|
|
case ercSocNoKidSndsInMovie:
|
|
erc = ktpcercSocNoKidSndsInMovie;
|
|
break;
|
|
|
|
case ercSocMissingMelanieDoc:
|
|
erc = ktpcercSocMissingMelanieDoc;
|
|
break;
|
|
|
|
case ercSocCantLoadMelanieDoc:
|
|
erc = ktpcercSocCantLoadMelanieDoc;
|
|
break;
|
|
|
|
case ercSocBadSoundFile:
|
|
erc = ktpcercSocBadSoundFile;
|
|
break;
|
|
|
|
case ercSocBadSceneSound:
|
|
erc = ktpcercSocBadSceneSound;
|
|
break;
|
|
|
|
case ercSocNoDefaultFont:
|
|
erc = ktpcercSocNoDefaultFont;
|
|
break;
|
|
|
|
case ercSocCantCacheTag:
|
|
erc = ktpcercSocCantCacheTag;
|
|
break;
|
|
|
|
case ercSocInvalidFilename:
|
|
erc = ktpcercSocInvalidFilename;
|
|
break;
|
|
|
|
case ercSndMidiDeviceBusy:
|
|
erc = ktpcercSndMidiDeviceBusy;
|
|
break;
|
|
|
|
case ercSocCantCopyMsnd:
|
|
erc = ktpcercSocCantCopyMsnd;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
FGetStnApp(idsOOM, &stnMessage);
|
|
TModal(PcrmAll(), erc, &stnMessage, bkOk);
|
|
if (stnErr.Cch() != 0)
|
|
vapp.Pkwa()->Pstrg()->Delete(kstidGenericError);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Idle routine. Do APPB idle stuff, then report any runtime errors.
|
|
***************************************************************************/
|
|
bool APP::FCmdIdle(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
bool fFileError = fFalse;
|
|
PCFL pcfl;
|
|
|
|
APP_PAR::FCmdIdle(pcmd);
|
|
|
|
/* Check all open chunky files for errors */
|
|
for (pcfl = CFL::PcflFirst(); pcfl != pvNil; pcfl = (PCFL)pcfl->PbllNext())
|
|
{
|
|
if (pcfl->ElError() != elNil)
|
|
{
|
|
fFileError = fTrue;
|
|
pcfl->ResetEl();
|
|
}
|
|
}
|
|
/* Ensure that we report *something* if there was a file error */
|
|
if (fFileError && vpers->Cerc() == 0)
|
|
PushErc(ercFileGeneral);
|
|
|
|
DisplayErrors();
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
#ifdef WIN
|
|
/***************************************************************************
|
|
Tell another instance of the app to open a document.
|
|
***************************************************************************/
|
|
bool APP::_FSendOpenDocCmd(HWND hwnd, PFNI pfniUserDoc)
|
|
{
|
|
AssertBaseThis(0);
|
|
Assert(pvNil != hwnd, "bad hwnd");
|
|
AssertPo(pfniUserDoc, ffniFile);
|
|
|
|
STN stnUserDoc;
|
|
STN stn;
|
|
FNI fniTemp;
|
|
PFIL pfil = pvNil;
|
|
BLCK blck;
|
|
DWORD dwProcId;
|
|
|
|
// Write filename to 3DMMOpen.tmp in the temp dir
|
|
pfniUserDoc->GetStnPath(&stnUserDoc);
|
|
if (!fniTemp.FGetTemp())
|
|
goto LFail;
|
|
stn = kpszOpenFile;
|
|
if (!fniTemp.FSetLeaf(&stn, kftgTemp))
|
|
goto LFail;
|
|
if (tYes == fniTemp.TExists())
|
|
{
|
|
// replace any existing open-doc request
|
|
if (!fniTemp.FDelete())
|
|
goto LFail;
|
|
}
|
|
pfil = FIL::PfilCreate(&fniTemp);
|
|
if (pvNil == pfil)
|
|
goto LFail;
|
|
if (!pfil->FSetFpMac(stnUserDoc.CbData()))
|
|
goto LFail;
|
|
blck.Set(pfil, 0, stnUserDoc.CbData());
|
|
if (!stnUserDoc.FWrite(&blck, 0))
|
|
goto LFail;
|
|
blck.Free(); // so it doesn't reference pfil anymore
|
|
ReleasePpo(&pfil);
|
|
#ifdef WIN
|
|
dwProcId = GetWindowThreadProcessId(hwnd, NULL);
|
|
PostThreadMessage(dwProcId, WM_USER, klwOpenDoc, 0);
|
|
#else
|
|
RawRtn();
|
|
#endif //!WIN
|
|
return fTrue;
|
|
LFail:
|
|
blck.Free(); // so it doesn't reference pfil anymore
|
|
ReleasePpo(&pfil);
|
|
return fFalse;
|
|
}
|
|
#endif //WIN
|
|
|
|
|
|
#ifdef WIN
|
|
/***************************************************************************
|
|
Process a request (from another instance of the app) to open a document
|
|
***************************************************************************/
|
|
bool APP::_FProcessOpenDocCmd(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
STN stnUserDoc;
|
|
STN stn;
|
|
FNI fniTemp;
|
|
PFIL pfil = pvNil;
|
|
FNI fniUserDoc;
|
|
BLCK blck;
|
|
|
|
// Find the temp file
|
|
if (!fniTemp.FGetTemp())
|
|
return fFalse;
|
|
stn = kpszOpenFile;
|
|
if (!fniTemp.FSetLeaf(&stn, kftgTemp))
|
|
return fFalse;
|
|
if (tYes != fniTemp.TExists())
|
|
{
|
|
Bug("Got a ProcessOpenDocCmd but there's no temp file!");
|
|
return fFalse;
|
|
}
|
|
|
|
// See if we can accept open document commands now: If accelerators
|
|
// are enabled, then ctrl-o is enabled, so open-document commands are
|
|
// acceptable.
|
|
if (_cactDisable > 0 || CactModal() > 0)
|
|
goto LFail;
|
|
|
|
// Read the document filename from temp file
|
|
pfil = FIL::PfilOpen(&fniTemp);
|
|
if (pvNil == pfil)
|
|
goto LFail;
|
|
blck.Set(pfil, 0, pfil->FpMac());
|
|
if (!stnUserDoc.FRead(&blck, 0))
|
|
goto LFail;
|
|
blck.Free(); // so it doesn't reference pfil anymore
|
|
ReleasePpo(&pfil);
|
|
|
|
if (!fniUserDoc.FBuildFromPath(&stnUserDoc))
|
|
goto LFail;
|
|
if (pvNil == _pstdio)
|
|
{
|
|
SetPortfolioDoc(&fniUserDoc);
|
|
vpcex->EnqueueCid(cidLoadStudioDoc); // this will load the portfolio doc
|
|
}
|
|
else
|
|
{
|
|
// ignore failure
|
|
_pstdio->FLoadMovie(&fniUserDoc);
|
|
}
|
|
|
|
fniTemp.FDelete(); // ignore failure
|
|
return fTrue;
|
|
LFail:
|
|
blck.Free(); // so it doesn't reference pfil anymore
|
|
ReleasePpo(&pfil);
|
|
fniTemp.FDelete(); // ignore failure
|
|
return fFalse;
|
|
}
|
|
#endif //WIN
|
|
|
|
|
|
#ifdef WIN
|
|
/***************************************************************************
|
|
Override standard _FGetNextEvt to catch WM_USER event. Otherwise
|
|
the event will get thrown away, because the event's hwnd is nil.
|
|
***************************************************************************/
|
|
bool APP::_FGetNextEvt(PEVT pevt)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pevt);
|
|
|
|
if (!APP_PAR::_FGetNextEvt(pevt))
|
|
return fFalse;
|
|
if (pevt->message != WM_USER || pevt->wParam != klwOpenDoc)
|
|
return fTrue;
|
|
_FProcessOpenDocCmd(); // ignore failure
|
|
return fFalse; // we've handled the WM_USER event
|
|
}
|
|
#endif //WIN
|
|
|
|
|
|
/***************************************************************************
|
|
Override default _FastUpdate to optionally skip offscreen buffer
|
|
***************************************************************************/
|
|
void APP::_FastUpdate(PGOB pgob, PREGN pregnClip, ulong grfapp, PGPT pgpt)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
PMVIE pmvie;
|
|
|
|
pmvie = _Pmvie();
|
|
|
|
if (_fOnscreenDrawing && pvNil != pmvie && pmvie->FPlaying() &&
|
|
pmvie->Pscen()->Nfrm() != pmvie->Pscen()->NfrmFirst())
|
|
{
|
|
APP_PAR::_FastUpdate(pgob, pregnClip, grfapp | fappOnscreen, pgpt);
|
|
}
|
|
else
|
|
{
|
|
APP_PAR::_FastUpdate(pgob, pregnClip, grfapp, pgpt);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef WIN
|
|
/***************************************************************************
|
|
Override default UpdateHwnd to optionally skip offscreen buffer
|
|
***************************************************************************/
|
|
void APP::UpdateHwnd(HWND hwnd, RC *prc, ulong grfapp)
|
|
{
|
|
AssertBaseThis(0); // APP may not be completely valid
|
|
|
|
PMVIE pmvie;
|
|
|
|
pmvie = _Pmvie();
|
|
|
|
if (_fOnscreenDrawing && pvNil != pmvie && pmvie->FPlaying() &&
|
|
pmvie->Pscen()->Nfrm() != pmvie->Pscen()->NfrmFirst())
|
|
{
|
|
APP_PAR::UpdateHwnd(hwnd, prc, grfapp | fappOnscreen);
|
|
}
|
|
else
|
|
{
|
|
APP_PAR::UpdateHwnd(hwnd, prc, grfapp);
|
|
}
|
|
}
|
|
#endif //WIN
|
|
|
|
|
|
#ifdef WIN
|
|
|
|
#ifndef WM_DISPLAYCHANGE
|
|
#define WM_DISPLAYCHANGE 0x007E // from winuser.h
|
|
#endif // !WM_DISPLAYCHANGE
|
|
|
|
/***************************************************************************
|
|
Handle Windows messages for the main app window. Return true iff the
|
|
default window proc should _NOT_ be called.
|
|
***************************************************************************/
|
|
bool APP::_FFrameWndProc(HWND hwnd, uint wm, WPARAM wParam, LPARAM lw,
|
|
long *plwRet)
|
|
{
|
|
AssertBaseThis(0);
|
|
AssertVarMem(plwRet);
|
|
|
|
switch(wm)
|
|
{
|
|
case WM_ERASEBKGND:
|
|
// Tell windows that we handled the Erase so it doesn't do one.
|
|
// In general we don't want to erase our background ahead of time.
|
|
// This prevents AVIs from flashing.
|
|
*plwRet = fTrue;
|
|
return fTrue;
|
|
|
|
case WM_SIZE:
|
|
{
|
|
bool fRet;
|
|
long lwStyle;
|
|
|
|
fRet = APP_PAR::_FFrameWndProc(hwnd, wm, wParam, lw, plwRet);
|
|
lwStyle = GetWindowLong(hwnd, GWL_STYLE);
|
|
lwStyle &= ~WS_MAXIMIZEBOX;
|
|
if (wParam == SIZE_MINIMIZED)
|
|
{
|
|
_fMinimized = fTrue;
|
|
if (vpcex != pvNil)
|
|
lwStyle |= WS_SYSMENU;
|
|
else
|
|
lwStyle &= ~WS_SYSMENU;
|
|
}
|
|
else if (!_fRunInWindow)
|
|
lwStyle &= ~WS_SYSMENU;
|
|
SetWindowLong(hwnd, GWL_STYLE, lwStyle);
|
|
if (wParam == SIZE_RESTORED)
|
|
{
|
|
if (_fMainWindowCreated)
|
|
_RebuildMainWindow();
|
|
if (_fSwitchedResolution && _fMinimized)
|
|
{
|
|
if (!_FDisplayIs640480())
|
|
_FSwitch640480(fTrue);
|
|
}
|
|
ShowWindow(vwig.hwndApp, SW_RESTORE); // restore app window
|
|
_fMinimized = fFalse;
|
|
}
|
|
return fRet;
|
|
}
|
|
case WM_DISPLAYCHANGE:
|
|
// Note that we don't need to do any of this if we're closing down
|
|
if (_fQuit)
|
|
break;
|
|
|
|
if (_FDisplayIs640480())
|
|
{
|
|
_fRunInWindow = fFalse;
|
|
_RebuildMainWindow();
|
|
}
|
|
else
|
|
{
|
|
// We're not running at 640x480 resolution now. Current design is that
|
|
// if we switch from 640x480 to higher while the app is minimized, the
|
|
// app it to still be full screen when restored. Therefore we don't
|
|
// need to change _fRunInWindow here, as that we remain the same as
|
|
// before the res switch, (as will the Windows properties for the app).
|
|
// All we need to is make a note that we're no longer running in the
|
|
// current windows settings resolution if we're running in full screen.
|
|
|
|
if (!_fRunInWindow)
|
|
{
|
|
_fSwitchedResolution = fTrue;
|
|
|
|
// If we're not minimized then we must switch to 640x480 resolution.
|
|
// Don't switch res unless we're the active app window
|
|
|
|
if (!_fMinimized && GetForegroundWindow() == vwig.hwndApp)
|
|
{
|
|
if (!_FSwitch640480(fTrue))
|
|
_fSwitchedResolution = fFalse;
|
|
}
|
|
}
|
|
|
|
// Call rebuild now to make sure the app window gets positioned
|
|
// at the centre of the screen. Note that none of the other
|
|
// window attributes will change beneath _RebuildMainWindow.
|
|
if (!_fMinimized)
|
|
_RebuildMainWindow();
|
|
}
|
|
return fTrue;
|
|
|
|
case WM_INITMENUPOPUP:
|
|
{
|
|
// Disable the Close menu item if we are displaying a modal topic.
|
|
// The user can't exit until a modal topic is dismissed.
|
|
|
|
bool fDisableClose = (vpappb->CactModal() > (_pcex != pvNil ? 1 : 0));
|
|
|
|
EnableMenuItem((HMENU)wParam, SC_CLOSE, MF_BYCOMMAND |
|
|
(fDisableClose ? (MF_DISABLED | MF_GRAYED) : MF_ENABLED));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((_pstdio == pvNil) || (_pstdio->Pmvie() == pvNil))
|
|
{
|
|
return APP_PAR::_FFrameWndProc(hwnd, wm, wParam, lw, plwRet);
|
|
}
|
|
|
|
switch (wm)
|
|
{
|
|
case WM_QUERY_EXISTS:
|
|
*plwRet = _pstdio->Pmvie()->LwQueryExists(wParam, lw);
|
|
return(fTrue);
|
|
|
|
case WM_QUERY_LOCATION:
|
|
*plwRet = _pstdio->Pmvie()->LwQueryLocation(wParam, lw);
|
|
return(fTrue);
|
|
|
|
case WM_SET_MOVIE_POS:
|
|
*plwRet = _pstdio->Pmvie()->LwSetMoviePos(wParam, lw);
|
|
return(fTrue);
|
|
|
|
default:
|
|
return APP_PAR::_FFrameWndProc(hwnd, wm, wParam, lw, plwRet);
|
|
}
|
|
|
|
}
|
|
#endif //WIN
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Returns whether or not screen savers should be allowed.
|
|
*
|
|
* Parameters:
|
|
* None
|
|
*
|
|
* Returns:
|
|
* fTrue - Screen savers should be allowed
|
|
* fFalse - Screen savers should be blocked
|
|
*
|
|
**************************************************************************/
|
|
bool APP::FAllowScreenSaver(void)
|
|
{
|
|
AssertBaseThis(0);
|
|
|
|
// Disable the screen saver if...
|
|
// 1. We're going to autominimize if a screen saver starts. Otherwise
|
|
// the user would be confused when they get back to the machine.
|
|
// 2. We've switched resolutions, (ie we're full screen in a > 640x480 mode).
|
|
// Otherwise the screen save only acts on a portion of the screen.
|
|
|
|
return !_FDisplayIs640480() && !_fSwitchedResolution;
|
|
}
|
|
|
|
/***************************************************************************
|
|
Disable the application accelerators
|
|
***************************************************************************/
|
|
void APP::DisableAccel(void)
|
|
{
|
|
AssertBaseThis(0); // Gets called from destructors
|
|
|
|
if (_cactDisable == 0)
|
|
{
|
|
#ifdef WIN
|
|
_haccel = vwig.haccel;
|
|
vwig.haccel = _haccelGlobal;
|
|
#else
|
|
RawRtn();
|
|
#endif
|
|
}
|
|
|
|
_cactDisable++;
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
Enable the application accelerators
|
|
***************************************************************************/
|
|
void APP::EnableAccel(void)
|
|
{
|
|
AssertBaseThis(0); // Gets called from destructors
|
|
Assert(_cactDisable > 0, "Enable called w/o a disable");
|
|
|
|
_cactDisable--;
|
|
|
|
if (_cactDisable == 0)
|
|
{
|
|
#ifdef WIN
|
|
vwig.haccel = _haccel;
|
|
#else
|
|
RawRtn();
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Handle disable accelerator command
|
|
*
|
|
* Parameters:
|
|
* pcmd - Pointer to the command to process.
|
|
*
|
|
* Returns:
|
|
* fTrue if it handled the command, else fFalse.
|
|
*
|
|
**************************************************************************/
|
|
bool APP::FCmdDisableAccel(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
DisableAccel();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Handle enable accelerator command
|
|
*
|
|
* Parameters:
|
|
* pcmd - Pointer to the command to process.
|
|
*
|
|
* Returns:
|
|
* fTrue if it handled the command, else fFalse.
|
|
*
|
|
**************************************************************************/
|
|
bool APP::FCmdEnableAccel(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
EnableAccel();
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
FCmdInvokeSplot
|
|
Invokes the splot machine.
|
|
|
|
Arguments:
|
|
PCMD pcmd
|
|
rglw[0] -- contains the GOB id of the parent of the Splot machine
|
|
rglw[1] -- contains the GOB id of the Splot machine itself
|
|
|
|
Returns: fTrue always
|
|
|
|
************************************************************ PETED ***********/
|
|
bool APP::FCmdInvokeSplot(PCMD pcmd)
|
|
{
|
|
AssertThis(0);
|
|
AssertVarMem(pcmd);
|
|
|
|
PSPLOT psplot;
|
|
|
|
psplot = SPLOT::PsplotNew(pcmd->rglw[0], pcmd->rglw[1], _pcrmAll);
|
|
if (psplot == pvNil)
|
|
PushErc(ercSocCantInitSplot);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Handoff a movie to the app so it can pass it on to the studio
|
|
***************************************************************************/
|
|
void APP::HandoffMovie(PMVIE pmvie)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pmvie, 0);
|
|
|
|
ReleasePpo(&_pmvieHandoff);
|
|
_pmvieHandoff = pmvie;
|
|
_pmvieHandoff->AddRef();
|
|
}
|
|
|
|
/***************************************************************************
|
|
Grab the APP movie
|
|
***************************************************************************/
|
|
PMVIE APP::PmvieRetrieve(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
PMVIE pmvie = _pmvieHandoff;
|
|
|
|
_pmvieHandoff = pvNil; // Caller now owns this pointer
|
|
return pmvie;
|
|
}
|
|
|
|
|
|
#ifdef BUG1085
|
|
/******************************************************************************
|
|
HideCurs
|
|
ShowCurs
|
|
PushCurs
|
|
PopCurs
|
|
|
|
Some simple cursor restoration functionality, for use when a modal
|
|
topic comes up. Assumes that you won't try to mess with the cursor
|
|
state while you've got the old cursor state pushed, and that you won't
|
|
try to push the cursor state while you've already got the old cursor
|
|
state pushed.
|
|
|
|
************************************************************ PETED ***********/
|
|
void APP::HideCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
Assert(_cactCursHide != ivNil, "Can't hide cursor in Push/PopCurs pair");
|
|
_cactCursHide++;
|
|
APP_PAR::HideCurs();
|
|
}
|
|
|
|
void APP::ShowCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
Assert(_cactCursHide > 0, "Unbalanced ShowCurs call");
|
|
_cactCursHide--;
|
|
APP_PAR::ShowCurs();
|
|
}
|
|
|
|
void APP::PushCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
Assert(_cactCursHide != ivNil, "Can't nest cursor restoration");
|
|
_cactCursSav = _cactCursHide;
|
|
while (_cactCursHide)
|
|
ShowCurs();
|
|
_cactCursHide = ivNil;
|
|
}
|
|
|
|
void APP::PopCurs(void)
|
|
{
|
|
AssertThis(0);
|
|
|
|
Assert(_cactCursHide == ivNil, "Unbalanced cursor restoration");
|
|
_cactCursHide = 0;
|
|
while (_cactCursHide < _cactCursSav)
|
|
HideCurs();
|
|
}
|
|
#endif // BUG1085
|
|
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the APP
|
|
***************************************************************************/
|
|
void APP::AssertValid(ulong grf)
|
|
{
|
|
APP_PAR::AssertValid(0);
|
|
AssertNilOrPo(_pstdio, 0);
|
|
AssertNilOrPo(_ptatr, 0);
|
|
AssertNilOrPo(_pmvieHandoff, 0);
|
|
AssertPo(_pcfl, 0);
|
|
AssertPo(_pgstStudioFiles, 0);
|
|
AssertPo(_pgstBuildingFiles, 0);
|
|
AssertPo(_pgstSharedFiles, 0);
|
|
AssertPo(_pgstApp, 0);
|
|
AssertPo(_pkwa, 0);
|
|
AssertPo(_pgstApp, 0);
|
|
AssertPo(_pcrmAll, 0);
|
|
AssertPo(_pglicrfBuilding, 0);
|
|
AssertPo(_pglicrfStudio, 0);
|
|
AssertNilOrPo(_pcex, 0);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the APP
|
|
***************************************************************************/
|
|
void APP::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
APP_PAR::MarkMem();
|
|
MarkMemObj(vptagm);
|
|
MTRL::MarkShadeTable();
|
|
TDT::MarkActionNames();
|
|
MarkMemObj(_pstdio);
|
|
MarkMemObj(_ptatr);
|
|
MarkMemObj(_pmvieHandoff);
|
|
MarkMemObj(_pcfl);
|
|
MarkMemObj(_pgstStudioFiles);
|
|
MarkMemObj(_pgstBuildingFiles);
|
|
MarkMemObj(_pgstSharedFiles);
|
|
MarkMemObj(_pgstApp);
|
|
MarkMemObj(_pkwa);
|
|
MarkMemObj(_pgstApp);
|
|
MarkMemObj(_pcrmAll);
|
|
MarkMemObj(_pglicrfBuilding);
|
|
MarkMemObj(_pglicrfStudio);
|
|
MarkMemObj(_pcex);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
// KWA (KidWorld for App) stuff begins here
|
|
//
|
|
//
|
|
//
|
|
|
|
|
|
/***************************************************************************
|
|
KWA destructor
|
|
***************************************************************************/
|
|
KWA::~KWA(void)
|
|
{
|
|
ReleasePpo(&_pmbmp);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Set the KWA's MBMP (for splash screen)
|
|
***************************************************************************/
|
|
void KWA::SetMbmp(PMBMP pmbmp)
|
|
{
|
|
AssertThis(0);
|
|
AssertNilOrPo(pmbmp, 0);
|
|
|
|
RC rc;
|
|
|
|
if (pvNil != pmbmp)
|
|
pmbmp->AddRef();
|
|
ReleasePpo(&_pmbmp);
|
|
_pmbmp = pmbmp;
|
|
GetRcVis(&rc, cooLocal);
|
|
vpappb->MarkRc(&rc, this);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Draw the KWA's MBMP, if any (for splash screen)
|
|
***************************************************************************/
|
|
void KWA::Draw(PGNV pgnv, RC *prcClip)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pgnv, 0);
|
|
AssertVarMem(prcClip);
|
|
|
|
if (pvNil != _pmbmp)
|
|
pgnv->DrawMbmp(_pmbmp, 0, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Find a file given a string.
|
|
***************************************************************************/
|
|
bool KWA::FFindFile(PSTN pstnSrc, PFNI pfni)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(pstnSrc, 0);
|
|
AssertPo(pfni, 0);
|
|
|
|
return vptagm->FFindFile(vapp.SidProduct(), pstnSrc, pfni, FAskForCD());
|
|
}
|
|
|
|
/***************************************************************************
|
|
Do a modal help topic.
|
|
***************************************************************************/
|
|
bool KWA::FModalTopic(PRCA prca, CNO cnoTopic, long *plwRet)
|
|
{
|
|
AssertThis(0);
|
|
AssertPo(prca, 0);
|
|
AssertVarMem(plwRet);
|
|
|
|
bool fRet;
|
|
|
|
// Take any special action here if necessary, before the
|
|
// modal help topic is displayed. (Eg, disable help keys).
|
|
|
|
// Now take the default action.
|
|
#ifdef BUG1085
|
|
vapp.PushCurs();
|
|
fRet = KWA_PAR::FModalTopic(prca, cnoTopic, plwRet);
|
|
vapp.PopCurs();
|
|
#else
|
|
fRet = KWA_PAR::FModalTopic(prca, cnoTopic, plwRet);
|
|
#endif // !BUG1085
|
|
|
|
// Let script know that the modal topic has been dismissed.
|
|
// This is required for the projects.
|
|
vpcex->EnqueueCid(cidModalTopicClosed, 0, 0, *plwRet);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/***************************************************************************
|
|
Assert the validity of the KWA
|
|
***************************************************************************/
|
|
void KWA::AssertValid(ulong grf)
|
|
{
|
|
KWA_PAR::AssertValid(0);
|
|
AssertNilOrPo(_pmbmp, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Mark memory used by the KWA
|
|
***************************************************************************/
|
|
void KWA::MarkMem(void)
|
|
{
|
|
AssertThis(0);
|
|
KWA_PAR::MarkMem();
|
|
MarkMemObj(_pmbmp);
|
|
}
|
|
#endif // DEBUG
|