JPEGView/Source/C/JPEGView.c
Aaron Giles 92bdb55672 JPEGView 3.3 for Macintosh
These are the sources for the final official release of JPEGView for the
Mac, back in 1994.
2015-02-05 00:18:10 -08:00

1 line
25 KiB
C
Raw Permalink Blame History

/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information. */
/* Contact email: mac@aarongiles.com */
/*********************************************************/
//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================
#if THINK_C
#include "THINK.Header"
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif
//=====================================================================================
// VBLRecord: structure containing information about cursors that we set during VBL
//=====================================================================================
typedef struct VBLRecord
{
VBLTask theTask;
CursHandle spin[kNumCursors];
ulong curSpin, lastSpin;
Boolean spinning;
} VBLRecord, *VBLRecordPtr;
//=====================================================================================
// Global variables local to this module
//=====================================================================================
static VBLRecord gVBLTask;
static VBLUPP gSpinCursor;
//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================
static void MemoryInit(void);
static void UPPInit(void);
static void ToolBoxInit(void);
static void CheckSystem(void);
static Boolean TrapAvailable(short theTrap);
static void PaletteInit(void);
static void GeneralInit(void);
#if USESROUTINEDESCRIPTORS
static pascal void SpinCursor(VBLTaskPtr theTask);
#else
static pascal void SpinCursor(void);
#endif
static void ErrorHandler(short theErr, short resID, StringPtr addString);
//=====================================================================================
// Set compiler up to turn off 68020 code generation for these first few functions,
// until we're sure we're in a safe environment.
//=====================================================================================
#if THINK_C
#pragma options(!mc68020)
#elif applec
#pragma processor 68000
#elif __MWERKS__
#pragma code68020 off
#endif
//=====================================================================================
// void main(void)
//=====================================================================================
// Initialize the application, open any previously open floating windows, then start
// the main event loop and wait until it's done before cleaning up.
//=====================================================================================
// NOTE: This needs to be compiled compatible for 68000-based machines, since we can't
// be sure yet that we're on a 68020.
//=====================================================================================
extern void main(void)
{
Boolean statsOpen, commentsOpen, colorsOpen;
// initialize everything
gTitleBarHeight = kTitleBarHeight;
MemoryInit();
AppleEventInit();
MonitorInit();
MenuBarInit();
DrawingInit();
PaletteInit();
GeneralInit();
gThePrefs.reminderCount++;
// open any windoids that were left open from last run
if (gThePrefs.statsOpen)
if (OpenStats() == noErr && !gInBackground) ChangeActive(GetStatWindow());
if (gThePrefs.commentsOpen) {
gCommentsOpen = true;
if (OpenComments() == noErr && !gInBackground) ChangeActive(GetCommentsWindow());
}
if (gThePrefs.colorsOpen)
if (OpenColors() == noErr && !gInBackground) ChangeActive(GetColorsWindow());
// set up the menus and take our events
AdjustMenus();
MainEventLoop();
// close all the open windows, one at a time
statsOpen = gThePrefs.statsOpen;
commentsOpen = gThePrefs.commentsOpen;
colorsOpen = gThePrefs.colorsOpen;
while (FrontWindow()) DoCloseWindow(FrontWindow(), false);
// save the preferences, reset devices, and exit
gThePrefs.statsOpen = statsOpen;
gThePrefs.commentsOpen = commentsOpen;
gThePrefs.colorsOpen = colorsOpen;
SaveGeneralPrefs();
ResetDevices();
if (GetFirstWindow()) CalcVisBehind((WindowPeek)GetFirstWindow(), GetGrayRgn());
RemoveQDxDispatchPatch();
MySetPort(nil);
HiliteMenu(0);
ExitToShell();
}
//=====================================================================================
// void MemoryInit(void)
//=====================================================================================
// Set up the application, call the main event loop, then clean up when done.
//=====================================================================================
// NOTE: This needs to be compiled compatible for 68000-based machines, since we can't
// be sure yet that we're on a 68020.
//=====================================================================================
static void MemoryInit(void)
{
Handle tempHandle;
Str255 theString;
short i;
// increase the stack size
SetApplLimit(GetApplLimit() - kStackSize);
MaxApplZone();
for (i = 0; i < 24; i++) MoreMasters();
// set up all our callback function UPP's
UPPInit();
// start up the Toolbox now
ToolBoxInit();
// verify the system configuration
CheckSystem();
// set the grow zone procedure
SetGrowZone(NewGrowZoneProc((ProcPtr)GrowZone));
// allocate memory for windows & ports
if (!(tempHandle = NewHandleClear(kMaxPorts * sizeof(DialogRecord))))
FatalError(errNoMemory);
MoveHHi(tempHandle), HLock(tempHandle);
gPortPool = (DialogRecord *)StripAddress(*tempHandle);
// load up all the JPEGView strings
for (i = 1; i <= strLast; i++) {
GetIndString(theString, rStrings, i);
if (!theString[0]) FatalError(errNoLoadStrings);
if (!(gString[i - 1] = (StringPtr)NewPtr(theString[0] + 1))) FatalError(errNoMemory);
BlockMove(theString, gString[i - 1], theString[0] + 1);
}
for (i = 1; i <= kFileFormats; i++) {
GetIndString(gFormat[i - 1]->formatName, rFormatNames, i);
if (!gFormat[i - 1]->formatName) FatalError(errNoLoadStrings);
}
// load in all our custom cursors
for (i = 0; i < kNumCursors; i++) {
if (!(gVBLTask.spin[i] = GetCursor(rFirstSpin + i))) FatalError(errNoLoadCursor);
MoveHHi((Handle)gVBLTask.spin[i]), HLock((Handle)gVBLTask.spin[i]);
}
StartSpinning();
if (!(gCross = GetCursor(crossCursor))) FatalError(errNoLoadCursor);
MoveHHi((Handle)gCross), HLock((Handle)gCross);
if (!(gHand = GetCursor(rHandCursor))) FatalError(errNoLoadCursor);
MoveHHi((Handle)gCross), HLock((Handle)gHand);
if (!(gSize = GetCursor(rSizeCursor))) FatalError(errNoLoadCursor);
MoveHHi((Handle)gCross), HLock((Handle)gSize);
}
//=====================================================================================
// void UPPInit(void)
//=====================================================================================
// Initializes all the global Universal Proc Pointers, for PowerPC compatibility.
//=====================================================================================
// NOTE: This needs to be compiled compatible for 68000-based machines, since we can't
// be sure yet that we're on a 68020.
//=====================================================================================
static void UPPInit(void)
{
// from AppleEventUtils.c
gAEIdleProc = NewAEIdleProc((ProcPtr)AEIdleProc);
// from DialogUtils.c
gGenericFilter = NewModalFilterProc((ProcPtr)GenericFilter);
gOutlineOK = NewUserItemProc((ProcPtr)OutlineOK);
gPopUpMenuBox = NewUserItemProc((ProcPtr)PopUpMenuBox);
gLineItem = NewUserItemProc((ProcPtr)LineItem);
// from FileMenu.c
gOpenSaveModalFilter = NewModalFilterYDProc((ProcPtr)OpenSaveModalFilter);
// from JPEGView.c
gSpinCursor = NewVBLProc((ProcPtr)SpinCursor);
// from ProgressProcs.c
gDummyProg.prog.progressProc = NewICMProgressProc((ProcPtr)DummyProgress);
gDummyProg.prog.progressRefCon = (long)&gDummyProg;
gGenericProgress = NewICMProgressProc((ProcPtr)GenericProgress);
gSlideProgress = NewICMProgressProc((ProcPtr)SlideProgress);
gDrawProgress = NewICMProgressProc((ProcPtr)DrawProgress);
}
//=====================================================================================
// void ToolBoxInit(void)
//=====================================================================================
// Initialize the toolbox manager and get the cursors.
//=====================================================================================
// NOTE: This needs to be compiled compatible for 68000-based machines, since we can't
// be sure yet that we're on a 68020.
//=====================================================================================
static void ToolBoxInit(void)
{
// initialize the Toolbox here
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
#if !defined(powerc) && !defined(__powerc)
{
Handle h;
short i;
SetResLoad(false);
for (i = Count1Resources('CODE'); i > 0; i--) {
h = Get1IndResource('CODE', i);
if (*h) HLock(h);
else {
LoadResource(h);
if (ResError()) {
SetResLoad(1);
FatalError(errNoMemory);
} else {
HUnlock(h);
MoveHHi(h);
HLock(h);
}
}
}
SetResLoad (true) ;
}
#endif
}
//=====================================================================================
// Boolean TrapAvailable(short theTrap)
//=====================================================================================
// Verifies if a trap is available to be called; swiped from THINK Reference with minor
// changes.
//=====================================================================================
// NOTE: This needs to be compiled compatible for 68000-based machines, since we can't
// be sure yet that we're on a 68020.
//=====================================================================================
static Boolean TrapAvailable(short theTrap)
{
short numToolboxTraps = 0x400;
TrapType tType;
tType = ((theTrap & 0x800) > 0) ? ToolTrap : OSTrap;
if (tType == ToolTrap) theTrap &= 0x7ff;
if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xaa6e, ToolTrap))
numToolboxTraps = 0x200;
if (theTrap >= numToolboxTraps) return false;
return (NGetTrapAddress(theTrap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
}
//=====================================================================================
// void CheckSystem(void)
//=====================================================================================
// Check gestalt to be sure we have everything we need.
//=====================================================================================
// NOTE: This needs to be compiled compatible for 68000-based machines, since we can't
// be sure yet that we're on a 68020.
//=====================================================================================
static void CheckSystem(void)
{
OSErr theErr;
long resp;
// die a quick death if not on 68020
if (!TrapAvailable(_Gestalt) ||
Gestalt(gestaltProcessorType, &resp) != noErr || resp < 3) {
StopAlert(rNeeds68020Alert, nil);
ExitToShell();
}
// do the same if we're not running System 7
if (Gestalt(gestaltSystemVersion, &resp) != noErr || resp < 0x700) {
StopAlert(rNeedsSystem7Alert, nil);
ExitToShell();
}
// verify QuickTime and image compression manager versions
if (Gestalt(gestaltCompressionMgr, &resp) == noErr && resp >= 15) {
if (Gestalt(gestaltQuickTime, &resp) == noErr) {
if ((gQTVersion = resp >> 16) < 0x100) MinorError(errFunnyQTVersion);
if (GetCodecInfo(&gJPEGInfo, kJPEGCompression, anyCodec) != noErr) gQTVersion = 0;
#if defined(powerc) || defined(__powerc)
if ((Ptr)DecompressImage == kUnresolvedSymbolAddress) gQTVersion = 0;
#endif
}
}
KeepSpinning();
MySetPort(nil);
// see if the Drag Manager is present
if (Gestalt(gestaltDragMgrAttr, &resp) == noErr && (resp & (1L << gestaltDragMgrPresent))) {
gDragMgrPresent = true;
#if defined(powerc) || defined(__powerc)
if ((Ptr)InstallTrackingHandler == kUnresolvedSymbolAddress) gDragMgrPresent = false;
#endif
}
// load the JPEGView icon family
theErr = GetIconSuite(&gIconSuite, rAPPLType, svAllAvailableData);
if (theErr != noErr) FatalError(errNoMemory);
HNoPurge(gIconSuite);
// set up our notification mgr item
gTheNotification.qType = nmType;
gTheNotification.nmMark = 1; // put diamond in application menu
gTheNotification.nmIcon = gIconSuite; // flashing icon handle
gTheNotification.nmSound = (Handle)-1; // system beep sound
gTheNotification.nmStr = nil; // no alert box
gTheNotification.nmResp = nil; // no response procedure
gTheNotification.nmRefCon = 0; // no ref con
}
//=====================================================================================
// Reset compiler to generate 68020 code again.
//=====================================================================================
#if THINK_C
#pragma options(mc68020)
#elif applec
#pragma processor 68020
#elif __MWERKS__
#pragma code68020 off
#endif
//=====================================================================================
// void PaletteInit(void)
//=====================================================================================
// Load up all the system color tables and convert them to palettes.
//=====================================================================================
static void PaletteInit(void)
{
CTabHandle theColors;
short i;
for (i = 1; i <= 8; i <<= 1) {
if (theColors = GetCTable(64 + i)) {
if (gColorPalette[i] = NewPalette(1 << i, theColors, pmTolerant, 0))
DisposCTable(theColors);
else FatalError(errNoMemory);
} else FatalError(errNoLoadCLUT);
if (theColors = GetCTable(32 + i)) {
if (gGreyPalette[i] = NewPalette(1 << i, theColors, pmTolerant, 0))
DisposCTable(theColors);
else FatalError(errNoMemory);
} else FatalError(errNoLoadCLUT);
}
KeepSpinning();
}
//=====================================================================================
// void GeneralInit(void)
//=====================================================================================
// Do the rest of the initialization.
//=====================================================================================
static void GeneralInit(void)
{
Rect prevRect = { 0, 0, 80, 80 }, iconRect = { 0, 0, 32, 32 };
ProcessSerialNumber thePSN, selfPSN = { 0, kCurrentProcess };
short refNum, oldRefNum = CurResFile();
ProcessInfoRec theInfo;
EventRecord theEvent;
Intl0Hndl intlData;
FSSpec theSpec;
OSErr theErr;
ushort i;
// find the JPEGView help file and open its resource fork
theInfo.processAppSpec = &theSpec;
theInfo.processInfoLength = sizeof(ProcessInfoRec);
theInfo.processName = nil;
theErr = GetProcessInformation(&selfPSN, &theInfo);
if (theErr == noErr) {
theErr = FSMakeFSSpec(theSpec.vRefNum, theSpec.parID, gString[strHelpFile], &theSpec);
if (theErr == noErr) {
refNum = FSpOpenResFile(&theSpec, fsRdPerm);
UseResFile(oldRefNum);
gHelpFound = true;
}
}
// install and remove our QDxDispatch patch to force it to load
InstallQDxDispatchPatch(false);
RemoveQDxDispatchPatch();
// take a few events to let our background status become known
for (i = 0; i < 4; i++)
WaitNextEvent(mDownMask + keyDownMask + keyUpMask + autoKeyMask +
updateMask + activMask, &theEvent, kSleepTime, nil);
// determine our background status
GetFrontProcess(&thePSN);
SameProcess(&selfPSN, &thePSN, &gInBackground);
gInBackground = !gInBackground;
// initialize the slide show options
if (LoadPrefsHandle(kPrefsSlideType, 0, (Handle *)&gSlideOptions) != noErr)
if (PtrToHand((Ptr)&gDefaultSlideOptions, (Handle *)&gSlideOptions, sizeof(SlideOptions)) != noErr)
FatalError(errNoMemory);
// load the preferences
LoadGeneralPrefs();
// create the preview and icon offscreen worlds
if (!(gGenericGWorld = gPreviewGWorld = MyNewGWorld(&prevRect, 32, nil, nil, false, false)) ||
!(gIconGWorld = MyNewGWorld(&iconRect, 32, nil, nil, false, false)))
FatalError(errNoMemory);
// seed the random number generator
qd.randSeed = TickCount();
KeepSpinning();
// get some international number info
if (intlData = (Intl0Hndl)IUGetIntl(0)) {
gThousandsSep = (*intlData)->thousSep;
gDecimal = (*intlData)->decimalPt;
gListSep = (*intlData)->listSep;
}
}
//=====================================================================================
// void StartSpinning(void)
//=====================================================================================
// Initialize the VBL task to begin spinning the cursor.
//=====================================================================================
extern void StartSpinning(void)
{
if (gVBLTask.spinning) return;
gVBLTask.spinning = true;
gVBLTask.curSpin %= kNumCursors;
KeepSpinning();
gVBLTask.theTask.qType = vType;
gVBLTask.theTask.vblAddr = gSpinCursor;
gVBLTask.theTask.vblCount = kSpinContinue;
gVBLTask.theTask.vblPhase = 0;
SetCursor(*gVBLTask.spin[gVBLTask.curSpin]);
VInstall((QElemPtr)&gVBLTask);
}
//=====================================================================================
// void StopSpinning(CursPtr newCursor)
//=====================================================================================
// Remove the spinning task from the VBL queue.
//=====================================================================================
extern void StopSpinning(CursPtr newCursor)
{
if (gVBLTask.spinning) VRemove((QElemPtr)&gVBLTask);
gVBLTask.spinning = false;
SetCursor(newCursor);
}
//=====================================================================================
// void KeepSpinning(void)
//=====================================================================================
// Keep the cursor spinning for another two cycles.
//=====================================================================================
extern void KeepSpinning(void)
{
gVBLTask.lastSpin = ((gVBLTask.curSpin / kNumCursors) + 2) * kNumCursors;
}
//=====================================================================================
// void SpinIndef(void)
//=====================================================================================
// Keep the cursor spinning indefinitely.
//=====================================================================================
extern void SpinIndef(void)
{
gVBLTask.lastSpin = 0x7fffffff;
}
//=====================================================================================
// pascal long GetVBLRec(void)
//=====================================================================================
// Some inline assembly to return the value in A0, which is a pointer to the VBLTask
// record.
//=====================================================================================
// NOTE: This is only needed for the 680x0 version of the application; PowerPC versions
// get the address of the VBLTask passed as a parameter, thanks to Apple's trickery.
//=====================================================================================
#if !USESROUTINEDESCRIPTORS
static pascal long GetVBLRec(void) = 0x2e88;
#endif
//=====================================================================================
// pascal void SpinCursor(void)
//=====================================================================================
// Actually spin the cursor; this is the VBL task function we point to. Note that we
// absolutely must not move memory here.
//=====================================================================================
// NOTE: We use some conditional macros here to accomodate the nicer PowerPC version of
// this function.
//=====================================================================================
#if USESROUTINEDESCRIPTORS
static pascal void SpinCursor(VBLTaskPtr theTask)
{
VBLRecordPtr theData = (VBLRecordPtr)theTask;
#else
static pascal void SpinCursor(void)
{
VBLRecordPtr theData = (VBLRecordPtr)GetVBLRec();
#endif
if (!LMGetCrsrBusy() && theData->curSpin < theData->lastSpin)
SetCursor(*theData->spin[++theData->curSpin % kNumCursors]);
theData->theTask.vblCount = kSpinFrequency;
}
//=====================================================================================
// void HandleAEError(OSErr theErr, short memErr, StringPtr theString)
//=====================================================================================
// Handle an error returned from an apple event.
//=====================================================================================
extern void HandleAEError(OSErr theErr, short memErr, StringPtr theString)
{
if (gIntError) StringError(gIntError, theString);
else switch (theErr) {
case noErr:
break;
case errNoHelpFound:
MinorError(theErr);
break;
case mFulErr:
case memFullErr:
if (memErr) StringError(memErr, theString);
else MinorError(errNoMemory);
break;
case codecAbortErr:
break;
case codecBadDataErr:
StringError(errCorruptImage, theString);
break;
case -4101:
MinorError(errNoPrinterSelected);
break;
case iPrSavPFil:
StringError(errCantPrint, theString);
break;
default:
ErrorHandler(theErr, 0, gNullString);
break;
}
gIntError = 0;
}
//=====================================================================================
// void MinorError(short theErr)
//=====================================================================================
// Display an alert for a minor error.
//=====================================================================================
extern void MinorError(short theErr)
{
ErrorHandler(theErr, rMinorErrors, nil);
}
//=====================================================================================
// void FatalError(short theErr)
//=====================================================================================
// Display an alert for a fatal error.
//=====================================================================================
extern void FatalError(short theErr)
{
ErrorHandler(theErr, rFatalErrors, nil);
}
//=====================================================================================
// void StringError(short theErr, StringPtr origString)
//=====================================================================================
// Display an alert for a minor error that includes an inserted string.
//=====================================================================================
extern void StringError(short theErr, StringPtr origString)
{
Str255 theString;
if (origString) {
BlockMove(origString, theString, *origString + 1);
ErrorHandler(theErr, rStringErrors, theString);
} else ErrorHandler(theErr, rGenericStringErrors, nil);
}
//=====================================================================================
// void ErrorHandler(short theErr, short resID, StringPtr addString)
//=====================================================================================
// Generic error handler. Note that we should post a notification manager event if
// we're in the background.
//=====================================================================================
static void ErrorHandler(short theErr, short resID, StringPtr addString)
{
Boolean isFatal = (resID == rFatalErrors);
Str255 theString;
OSErr AEErr;
// non-fatal errors in slide shows might need to be ignored
if (!isFatal && gSlideShow && (*gSlideOptions)->noErrors) return;
// any error during a screen saver slide show should return and abort
if (gScreenSaver) {
SendQuitApplication();
return;
}
// if we're in an Apple event, be polite and request to move to the front
if (gInAppleEvent) {
AEErr = AEInteractWithUser(kAEDefaultTimeout, &gTheNotification, gAEIdleProc);
if (AEErr == errAENoUserInteraction) {
if (isFatal) ExitToShell();
else return;
}
}
// if we're in the background and it's not fatal, post a notification manager event
if (gInBackground) RequestForeground();
// otherwise, display the appropriate alert with the appropriate text, if any
MySetPort(nil);
StopSpinning(&qd.arrow);
if (resID) {
GetIndString(theString, resID, theErr);
if (theString[0]) {
if (addString) ParamText(theString, addString, gNullString, gNullString);
else ParamText(theString, gNullString, gNullString, gNullString);
} else {
ParamText((StringPtr)"\pCouldn<EFBFBD>t load the error messages!", gNullString, gNullString, gNullString);
isFatal = true;
}
if (isFatal) {
FWStopAlert(CenterAlert(rFatalAlert), nil);
ExitToShell();
} else FWNoteAlert(CenterAlert(rMinorAlert), gGenericFilter);
} else {
NumToString(theErr, theString);
ParamText(theString, gNullString, gNullString, gNullString);
FWNoteAlert(CenterAlert(rUnknownAlert), gGenericFilter);
}
}
//=====================================================================================
// void RequestForeground(void)
//=====================================================================================
// Post a notifcation then sit in a loop, similar to the AppleEvent idle proc, and wait
// until we get bumped to the foreground.
//=====================================================================================
extern void RequestForeground(void)
{
if (!gInBackground) return;
NMInstall(&gTheNotification);
while (gInBackground) {
RgnHandle mouseRgn = nil;
long sleep = kSleepTime;
EventRecord theEvent;
MySetPort((CGrafPtr)FWFrontWindow());
WaitNextEvent(activMask + updateMask + osMask + diskMask, &theEvent, sleep,
mouseRgn);
AEIdleProc(&theEvent, &sleep, &mouseRgn);
}
NMRemove(&gTheNotification);
}