mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-29 05:49:19 +00:00
1132 lines
34 KiB
C
1132 lines
34 KiB
C
|
/*
|
||
|
File: ScrapCoercion.c
|
||
|
|
||
|
Contains: Routines that implement scrap coercion on foreground process switches.
|
||
|
|
||
|
Written by: Phil Goldman and Erich Ringewald
|
||
|
|
||
|
Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved.
|
||
|
|
||
|
Change History (most recent first):
|
||
|
|
||
|
<17> 4/9/92 KST #1027029,<JH>: Inform TSM when a non-TSM aware application is
|
||
|
getting suspended so TSM can pull down input method's menu.
|
||
|
<16> 3/23/92 JSM OSEvents.h is obsolete, use Events.h.
|
||
|
<15> 2/25/91 DFH DC,WS#DFH-910225a: Fixed so GetScrap works even if called before
|
||
|
first event call. IsScrapOwnedByCurrentProcess did not allow for
|
||
|
case where MoveScrap() has not been called on the current app.
|
||
|
<14> 1/28/91 DFH JSM,#81718:Fixed OrphanScrapIntoPartition to deal with case
|
||
|
where SCRAPHANDLE is nil because scrap is on the disk.
|
||
|
<12> 1/8/91 DFH (rdd) Changed StartForegroundSwitch to allow for case where
|
||
|
current front process is suspended for high-level debugging.
|
||
|
<11> 12/21/90 DFH Fixed SetFirstDstCoercionState to set srcMyScrapInfo.pScrapOwner
|
||
|
= pCurrentProcess when dst scrap is already up to date.
|
||
|
<10> 12/18/90 DFH Added GetScrap patch support routine.
|
||
|
<9> 12/18/90 DFH Removed fgAppsUnexecuted mechanism, since front switch queue
|
||
|
works better.
|
||
|
<8> 12/17/90 DFH Fixed scrap retention devices.
|
||
|
<7> 12/15/90 DFH Propagate correct scrap, even if some destination process
|
||
|
doesn't have enough memory for it. True even if real scrap owner
|
||
|
ExitToShell's.
|
||
|
<6> 12/11/90 DFH CancelQuitAll now called only when FG switching out of intended
|
||
|
victim. Other switches can be normal (e.g. when intended victim
|
||
|
is a BG-only application).
|
||
|
<5> 12/10/90 DFH Compiler bug workaround in MoveScrap. Use SCRAPCOUNT += 1
|
||
|
instead of SCRAPCOUNT++.
|
||
|
<4> 11/14/90 DFH Change ScrapIO to use PBOpenDF instead of PBOpen.
|
||
|
<3> 11/6/90 DFH Use structure defs for menu bar access in CS_INIT.
|
||
|
<0> x/xx/86 PYG New Today.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <types.h>
|
||
|
#include <memory.h>
|
||
|
#include <desk.h>
|
||
|
#include <menus.h>
|
||
|
#include <quickdraw.h>
|
||
|
#include <palette.h>
|
||
|
#include <osutils.h>
|
||
|
#include <events.h>
|
||
|
#include <windows.h>
|
||
|
#include <files.h>
|
||
|
#include <errors.h>
|
||
|
|
||
|
#include <TSMprivate.h>
|
||
|
#include <TextServices.h>
|
||
|
|
||
|
|
||
|
#include "Glue.h"
|
||
|
#include "Lomem.h"
|
||
|
#include "Data.h"
|
||
|
#include "SysMisc.h"
|
||
|
#include "Patches.h"
|
||
|
#include "Puppet.h"
|
||
|
#include "AppleEventExtensions.h"
|
||
|
|
||
|
|
||
|
|
||
|
#pragma segment kernel_segment
|
||
|
|
||
|
static long resume_message; /* the message field of the resume event */
|
||
|
static short eatMouseTimeout; /* counter of gne tries to eat mouse-down in CS_EATMOUSE */
|
||
|
static short clickDAWindTimeout; /* counter of gne tries to force click in fake DA window */
|
||
|
|
||
|
|
||
|
/* Function prototypes that shoule be in (yet another) header file */
|
||
|
void BringProcessToFront(PEntryPtr);
|
||
|
|
||
|
/* Function prototypes defined and used in this file */
|
||
|
void SetThirdSrcCoercionState(void);
|
||
|
void SetFirstDstCoercionState(void);
|
||
|
short FindCutCopyItems(MenuHandle);
|
||
|
void MoveScrap(void);
|
||
|
void ScrapIO(short, StringPtr, Ptr, u_size);
|
||
|
void ActivateTopWindow(short);
|
||
|
void DisposeOrphanedScrap(void);
|
||
|
|
||
|
/* ActivateTopWindow. Invert the hiliting the front window when the current application
|
||
|
* is the kind that does the rest of the deactivate/activate itself.
|
||
|
* NOTE: Assumes A5 = PROCESSMGRGLOBALS
|
||
|
*/
|
||
|
void
|
||
|
ActivateTopWindow(short activateMods)
|
||
|
{
|
||
|
WindowPtr frontWindow;
|
||
|
EventRecord dummyEvent;
|
||
|
|
||
|
assert(pCurrentProcess->p_layer != nil);
|
||
|
|
||
|
if ((frontWindow = FrontWindowIn(pCurrentProcess->p_layer)) != nil)
|
||
|
{
|
||
|
/* Highlight the window and make sure the palette agrees. */
|
||
|
HiliteWindow(frontWindow, (activateMods != 0));
|
||
|
if (Colorized && ((activateMods & activeFlag) != 0))
|
||
|
ActivatePalette(frontWindow);
|
||
|
|
||
|
/* If it's a DA window and activate events are allowed, give the DA an activate event */
|
||
|
if (((WindowPeek) frontWindow)->windowKind < 0 && SEVTENB != 0)
|
||
|
{
|
||
|
dummyEvent.what = activateEvt;
|
||
|
dummyEvent.message = (unsigned long)frontWindow;
|
||
|
dummyEvent.modifiers = activateMods;
|
||
|
(void) SystemEvent(&dummyEvent);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* PutUpFakeWindow. Puts up a window way offscreen to generate de-activate events.
|
||
|
* NOTE: Have to have at least one character in name string just in case someone
|
||
|
* tries to stick this name in a menu (null strings are not allowed there).
|
||
|
* An app that does this: Insight Accounts Receivable
|
||
|
* Assumes A5 == PROCESSMGRGLOBALS
|
||
|
*/
|
||
|
void
|
||
|
PutUpFakeWindow(void)
|
||
|
{
|
||
|
WindowPtr fakeWindowPtr;
|
||
|
Rect fakeWindowRect;
|
||
|
short nullski = TWO_CHARS_TO_SHORT('\01',' ');
|
||
|
|
||
|
/* NOTE: Should use global here */
|
||
|
SET_RECT(&fakeWindowRect,16000,16000,16004,16004);
|
||
|
|
||
|
fakeWindowPtr = NewWindow(NULL, &fakeWindowRect,
|
||
|
&nullski, true, (short) 0, (WindowPtr) -1, true, 0L);
|
||
|
|
||
|
/* Tag it as a fake window by sticking a funny value in the windowKind field */
|
||
|
((WindowPeek) fakeWindowPtr)->windowKind = SCRAP_COERCION_FAKE_DA_WINDOW_ID;
|
||
|
pCurrentProcess->p_wptr = fakeWindowPtr;
|
||
|
}
|
||
|
|
||
|
/* TakeDownFakeWindow. Takes down the fake DA window we put up earlier. Nothing fancy.
|
||
|
* Assumes A5 == PROCESSMGRGLOBALS
|
||
|
*/
|
||
|
void
|
||
|
TakeDownFakeWindow(void)
|
||
|
{
|
||
|
register WindowPeek fakeWindowPtr;
|
||
|
register PEntryPtr pCurrProc;
|
||
|
|
||
|
/* Does the window exist? If so, get rid of it. */
|
||
|
pCurrProc = pCurrentProcess;
|
||
|
if ((fakeWindowPtr = pCurrProc->p_wptr) != nil)
|
||
|
{
|
||
|
DisposeWindow(fakeWindowPtr);
|
||
|
pCurrProc->p_wptr = nil;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define SCRAP_TRANSFER_SIZE 1024
|
||
|
#define DO_SCRAP_READ ((short) 0)
|
||
|
#define DO_SCRAP_WRITE ((short) 1)
|
||
|
|
||
|
/* MoveScrap. Moves the scrap from the source task to the destination task. The scrap
|
||
|
* of the source task is described by srcMyScrapInfo. The scrap of the destination
|
||
|
* task is described in lomem.
|
||
|
*/
|
||
|
void
|
||
|
MoveScrap(void)
|
||
|
{
|
||
|
u_size size;
|
||
|
|
||
|
/* Say we've tried at least once to give this process a scrap. It means that the
|
||
|
* scrap copied over during LaunchApplication is no longer to be used.
|
||
|
*/
|
||
|
pCurrentProcess->p_scrapIsNotFromLaunch = true;
|
||
|
|
||
|
/* Get/Set scrap size */
|
||
|
size = SCRAPINFO = srcMyScrapInfo.size;
|
||
|
|
||
|
/* See if the dst task scrap is in memory */
|
||
|
if (SCRAPHANDLE != nil)
|
||
|
{
|
||
|
/* Check whether the src task scrap is in mem */
|
||
|
if (srcMyScrapInfo.memHandle != nil)
|
||
|
{
|
||
|
if (PtrToXHand(*srcMyScrapInfo.memHandle, SCRAPHANDLE, size) != noErr)
|
||
|
{
|
||
|
SCRAPINFO = 0;
|
||
|
SetHandleSize(SCRAPHANDLE, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* The src task scrap is on disk */
|
||
|
else
|
||
|
{
|
||
|
SetHandleSize(SCRAPHANDLE, size);
|
||
|
if (MEMERROR == noErr)
|
||
|
ScrapIO(DO_SCRAP_READ, srcMyScrapInfo.fileName, *SCRAPHANDLE, size);
|
||
|
else
|
||
|
{
|
||
|
SCRAPINFO = 0;
|
||
|
SetHandleSize(SCRAPHANDLE, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* The dst task scrap is on disk */
|
||
|
else
|
||
|
{
|
||
|
/* See whether the src task scrap is in memory */
|
||
|
if (srcMyScrapInfo.memHandle != nil)
|
||
|
ScrapIO(DO_SCRAP_WRITE, SCRAPNAME, *srcMyScrapInfo.memHandle, size);
|
||
|
#ifdef DISK_TO_DISK_SCRAP
|
||
|
|
||
|
/* The src task scrap is on disk. I give up!
|
||
|
* NOTE: srcMyScrapInfo.vRefNum is never set. ScrapCopyOverVolumes doesn't exist.
|
||
|
*/
|
||
|
else
|
||
|
{
|
||
|
short vRefNumDst = BOOTVOL;
|
||
|
|
||
|
/* Do I/O if on separate volumes, otherwise just change size */
|
||
|
if (srcMyScrapInfo.vRefNum != vRefNumDst)
|
||
|
{
|
||
|
/* Files on different volumes */
|
||
|
ScrapCopyOverVolumes(srcMyScrapInfo.vRefNum,vRefNumDst);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
#endif DISK_TO_DISK_SCRAP
|
||
|
}
|
||
|
|
||
|
/* If it worked, indicate that the current process' scrap is good, and get rid
|
||
|
* of the orphaned scrap (its job is done!).
|
||
|
*/
|
||
|
if (size == SCRAPINFO)
|
||
|
{
|
||
|
srcMyScrapInfo.pScrapOwner = pCurrentProcess;
|
||
|
DisposeOrphanedScrap();
|
||
|
}
|
||
|
|
||
|
/* Invalidate the scrap, and sync up local counter with global. */
|
||
|
SCRAPCOUNT += 1;
|
||
|
pCurrentProcess->p_cutcopycount = cutCopyCount;
|
||
|
}
|
||
|
|
||
|
/* IsScrapOwnedByCurrentProcess. Return whether we think that the current SCRAPINFO
|
||
|
* is valid. Essentially, the check is whether the most recent MoveScrap() failed
|
||
|
* and no new scrap has been made.
|
||
|
*/
|
||
|
Boolean
|
||
|
IsScrapOwnedByCurrentProcess(void)
|
||
|
{
|
||
|
PEntryPtr pCurrProc;
|
||
|
|
||
|
pCurrProc = pCurrentProcess;
|
||
|
return ((pCurrProc == srcMyScrapInfo.pScrapOwner) ||
|
||
|
(pCurrProc->p_cutcopycount != cutCopyCount) ||
|
||
|
(pCurrProc->p_scrapIsNotFromLaunch == false));
|
||
|
}
|
||
|
|
||
|
/* ScrapIO. Generic scrap file I/O based on the first parameter passed in */
|
||
|
void
|
||
|
ScrapIO(short ioMode, StringPtr scrapFileName, Ptr scrapPtr, u_size size)
|
||
|
{
|
||
|
IOParam IOParamLocal;
|
||
|
|
||
|
/* Do the open */
|
||
|
IOParamLocal.ioNamePtr = scrapFileName;
|
||
|
IOParamLocal.ioVRefNum = BOOTVOL; /* scrap is always on boot volume */
|
||
|
IOParamLocal.ioVersNum = (char) 0;
|
||
|
IOParamLocal.ioMisc = NULL;
|
||
|
IOParamLocal.ioPermssn = (ioMode == DO_SCRAP_READ) ? fsRdPerm : fsWrPerm;
|
||
|
(void) PBOpenDF((ParmBlkPtr) &IOParamLocal, SyncHFS);
|
||
|
|
||
|
/* Do the I/O */
|
||
|
IOParamLocal.ioBuffer = scrapPtr;
|
||
|
IOParamLocal.ioReqCount = size;
|
||
|
IOParamLocal.ioPosMode = fsFromStart;
|
||
|
IOParamLocal.ioPosOffset = 0L;
|
||
|
if (ioMode == DO_SCRAP_READ)
|
||
|
(void) PBRead((ParmBlkPtr) &IOParamLocal, SyncHFS);
|
||
|
else
|
||
|
(void) PBWrite((ParmBlkPtr) &IOParamLocal, SyncHFS);
|
||
|
|
||
|
/* Do the close */
|
||
|
(void) PBClose((ParmBlkPtr) &IOParamLocal, SyncHFS);
|
||
|
}
|
||
|
|
||
|
/* Coercion_State_Engine. This is the engine that drives the scrap coercion that
|
||
|
* brackets user-initiated switches between a source task (src) and a destination task
|
||
|
* (dst). It is procedurally driven, rather than table driven, based on the variable
|
||
|
* coercionState. The sequence of values that coercionState will have is a function of
|
||
|
* the PEntry of the src and dst tasks, in specific the modeNeedSuspendResume (in
|
||
|
* p_taskMode) and the p_cutcopycount field. These two values are used to determine new
|
||
|
* values of coercionState at certain key points in the scrap coercion. In between
|
||
|
* these key points new coercionState values are predetermined from the old ones.
|
||
|
*
|
||
|
* See the flowchart in file 'Scrap Coercion Flowchart' for a more visual explanation.
|
||
|
*
|
||
|
* The return value of the function is a word-sized boolean indicating whether the
|
||
|
* given EventRecord has been filled in with a non-nullEvent (i.e. like an event call).
|
||
|
*
|
||
|
* NOTE: Assumes A5 = PROCESSMGRGLOBALS
|
||
|
*/
|
||
|
|
||
|
#define APP_EVENTS_ONLY_MASK (app1Mask+app2Mask+app3Mask)
|
||
|
|
||
|
short
|
||
|
Coercion_State_Engine(unsigned short eventmask, EventRecord *theevent, Ptr oldtrap, Boolean pullevent)
|
||
|
{
|
||
|
short retval;
|
||
|
|
||
|
switch(coercionState)
|
||
|
{
|
||
|
/* First state seen by the incoming process */
|
||
|
case CS_ENTERING:
|
||
|
{
|
||
|
SetFirstDstCoercionState();
|
||
|
retval = wFalse;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Outgoing app has finished coercion. We're ready to switch */
|
||
|
case CS_EXITING:
|
||
|
{
|
||
|
PEntryPtr pProc;
|
||
|
|
||
|
src_scrap_setup();
|
||
|
coercionState = CS_ENTERING;
|
||
|
pProc = pNewFrontProcess;
|
||
|
pNewFrontProcess = nil;
|
||
|
|
||
|
/* Make pProc frontmost, and switch to it */
|
||
|
BringProcessToFront(pProc);
|
||
|
|
||
|
retval = wFalse;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Generate the deactivate, activate events caused by putting up window */
|
||
|
case CS_GENERATE_DEACTIVATE1:
|
||
|
case CS_GENERATE_DEACTIVATE2:
|
||
|
{
|
||
|
if (pullevent)
|
||
|
SetThirdSrcCoercionState();
|
||
|
retval = CALL_FNC_PTR(Eventcommon_ptr,oldtrap,(eventmask & activMask, theevent));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_MOVE_SCRAP:
|
||
|
{
|
||
|
MoveScrap();
|
||
|
|
||
|
nullTimeout = NULL_TIMEOUT_INIT;
|
||
|
coercionState = ((pCurrentProcess->p_taskmode & modeNeedSuspendResume) != 0)
|
||
|
? CS_RESUME : CS_GENERATE_NULLS_THEN_CMD_C;
|
||
|
|
||
|
/* sync up next time thru */
|
||
|
retval = wFalse;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_GENERATE_NULLS_THEN_CMD_V:
|
||
|
case CS_GENERATE_NULLS_THEN_CMD_C:
|
||
|
{
|
||
|
#define CMD_V (0x976)
|
||
|
#define CMD_C (0x863)
|
||
|
retval = wFalse;
|
||
|
|
||
|
/* Generating a few null events to make apps like Microsoft File happy */
|
||
|
if (--nullTimeout > NULL_TIMEOUT_CUTOFF)
|
||
|
break;
|
||
|
|
||
|
/* If it's OK to, convert the event to a cmd-C or cmd-V. First off,
|
||
|
* system events aren't allowed inside GNE if SEVTENB != 0.
|
||
|
*/
|
||
|
if ((pullevent == false) || (SEVTENB == (char) 0 && nullTimeout > 0))
|
||
|
{
|
||
|
/* Must have caller that wants key-downs */
|
||
|
if ((eventmask & keyDownMask) != 0)
|
||
|
{
|
||
|
/* Try to get outgoing app to post its scrap */
|
||
|
if (coercionState == CS_GENERATE_NULLS_THEN_CMD_V)
|
||
|
{
|
||
|
if (pullevent)
|
||
|
coercionState = CS_EXITING;
|
||
|
theevent->message = CMD_V;
|
||
|
}
|
||
|
|
||
|
/* Try to get new app to load the scrap. */
|
||
|
else
|
||
|
{
|
||
|
if (pullevent)
|
||
|
{
|
||
|
clickDAWindTimeout = CLICK_DA_WIND_TIMEOUT_INIT;
|
||
|
coercionState = CS_CLICK_IN_WINDOW;
|
||
|
}
|
||
|
|
||
|
theevent->message = CMD_C;
|
||
|
}
|
||
|
|
||
|
/* force a cmd-<something>
|
||
|
* NOTE: Why do we also say the mouse button is down?
|
||
|
*/
|
||
|
theevent->what = keyDown;
|
||
|
theevent->modifiers = cmdKey+btnState;
|
||
|
retval = wTrue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* We timed out waiting for SEVTENB to be clear. Abandon the attempt. */
|
||
|
else
|
||
|
{
|
||
|
/* See if we were in destination trying to load the scrap. */
|
||
|
if (coercionState == CS_GENERATE_NULLS_THEN_CMD_C)
|
||
|
{
|
||
|
coercionState = CS_CLICK_IN_WINDOW;
|
||
|
clickDAWindTimeout = CLICK_DA_WIND_TIMEOUT_INIT;
|
||
|
}
|
||
|
|
||
|
/* We were trying to get the outgoing app to post the scrap. */
|
||
|
else
|
||
|
coercionState = CS_EXITING;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Fabricate a mouse-down in the fake DA window so we can close the window when
|
||
|
* the app calls _SystemClick (i.e. as if the user clicked the close box).
|
||
|
*/
|
||
|
case CS_CLICK_IN_WINDOW:
|
||
|
{
|
||
|
WindowPtr fakeDAWindowPtr;
|
||
|
|
||
|
retval = wFalse;
|
||
|
|
||
|
/* Must have caller that wants mousedown. Timeout if necessary. */
|
||
|
if ((eventmask & mDownMask) == 0)
|
||
|
{
|
||
|
if (--clickDAWindTimeout == 0)
|
||
|
coercionState = CS_TAKE_DOWN_WINDOW;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Return the event */
|
||
|
coercionState = CS_TAKE_DOWN_WINDOW;
|
||
|
if ((fakeDAWindowPtr = pCurrentProcess->p_wptr) != nil)
|
||
|
{
|
||
|
theevent->what = mouseDown;
|
||
|
PT_TO_PT(&(*(((WindowPeek) fakeDAWindowPtr)->strucRgn))->rgnBBox.top, &theevent->where);
|
||
|
theevent->modifiers = 0;
|
||
|
retval = wTrue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* We just gave a mousedown in the DA window. Either, the app called SystemClick
|
||
|
* and we closed the window, or he has made another event call and we can take
|
||
|
* down the window now. Look for an activate event on another window.
|
||
|
* NOTE: If we DO give back an activate, we'll come through here again, since we
|
||
|
* don't change the coercionState.
|
||
|
*/
|
||
|
case CS_TAKE_DOWN_WINDOW:
|
||
|
{
|
||
|
/* Just in case the app never calls _SystemClick */
|
||
|
TakeDownFakeWindow();
|
||
|
|
||
|
/* Try and force thru the activate event that should be generated since we just
|
||
|
* took down the fake window
|
||
|
*/
|
||
|
retval = CALL_FNC_PTR(Eventcommon_ptr,oldtrap,(eventmask & activMask, theevent));
|
||
|
|
||
|
/* Either there was no activate to get (e.g. no windows left) or we got it.
|
||
|
* In both cases forge ahead.
|
||
|
* NOTE: The 32-bit dirty check of the CURACTIVATE high bit checks whether the
|
||
|
* window update was canceled (i.e. <= 0 means no window update in progress).
|
||
|
*/
|
||
|
if ((long)CURACTIVATE <= 0)
|
||
|
{
|
||
|
eatMouseTimeout = EAT_MOUSE_TIMEOUT;
|
||
|
coercionState = CS_EATMOUSE;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_PRESUSPEND_DEACTIVATE:
|
||
|
/* Deactivate the top window and return a suspend event. This also
|
||
|
* results in a deactivate event later on.
|
||
|
*/
|
||
|
ActivateTopWindow(0);
|
||
|
|
||
|
/* and fall thru */
|
||
|
|
||
|
case CS_SUSPEND:
|
||
|
{
|
||
|
retval = wFalse;
|
||
|
|
||
|
/* fabricate a suspend event */
|
||
|
if ((eventmask & app4Mask) != 0)
|
||
|
{
|
||
|
theevent->what = app4Evt;
|
||
|
theevent->message = SUSPEND_MESSAGE;
|
||
|
retval = wTrue;
|
||
|
}
|
||
|
|
||
|
/* actually switch next time */
|
||
|
if (pullevent)
|
||
|
coercionState = CS_EXITING;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_RESUME:
|
||
|
{
|
||
|
/* Make transition to next state */
|
||
|
if (pullevent)
|
||
|
{
|
||
|
if ((pCurrentProcess->p_taskmode & modeDoesActivateOnFGSwitch) != 0)
|
||
|
{
|
||
|
/* treat resume as a activate evt too */
|
||
|
ActivateTopWindow(activeFlag);
|
||
|
|
||
|
eatMouseTimeout = EAT_MOUSE_TIMEOUT;
|
||
|
coercionState = CS_EATMOUSE;
|
||
|
}
|
||
|
else
|
||
|
coercionState = CS_TAKE_DOWN_WINDOW;
|
||
|
}
|
||
|
|
||
|
/* Generate a resume event for the app. Added bonus: Throw in scrap
|
||
|
* coercion flags and puppet string results into the .message field.
|
||
|
*/
|
||
|
retval = wFalse;
|
||
|
if ((eventmask & app4Mask) != 0)
|
||
|
{
|
||
|
theevent->what = app4Evt;
|
||
|
theevent->message = resume_message;
|
||
|
|
||
|
retval = wTrue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_EATMOUSE:
|
||
|
{
|
||
|
/* About to leave scrap coercion so check if a mouse event
|
||
|
* should be flushed. Flush if mouse down in content rgn,
|
||
|
* close box, or zoom box of front window. This is most
|
||
|
* likely the mouse click that caused the FG switch so long ago,
|
||
|
* but not necessarily (since the FG switch might be programmatic
|
||
|
* or via Apple menu process list item).
|
||
|
* NOTE: Technically, w->hilited check should be w == FrontWindow(),
|
||
|
* but since many apps (e.g. SuperPaint) have tool windows, and
|
||
|
* highlight a menu farther along in the list, we do this hack.
|
||
|
* Question: What does this do in PageMaker, which has 2
|
||
|
* active windows?
|
||
|
*/
|
||
|
|
||
|
short windowPart;
|
||
|
WindowPtr pWindow;
|
||
|
Boolean allDone = false;
|
||
|
|
||
|
/* If there is a mousedown, process it */
|
||
|
if (retval = CALL_FNC_PTR(Eventcommon_ptr, oldtrap,(eventmask & mDownMask, theevent)))
|
||
|
{
|
||
|
windowPart = FindWindow(theevent->where, &pWindow);
|
||
|
if ((pWindow != nil)
|
||
|
&& (windowPart != inDrag)
|
||
|
&& (windowPart != inDesk)
|
||
|
&& (((WindowPeek) pWindow)->hilited)
|
||
|
&& (((windowPart == inGoAway) || (windowPart == inZoomIn) || (windowPart == inZoomOut))
|
||
|
|| (pCurrentProcess->p_taskmode & modeGetFrontClicks) == 0) )
|
||
|
{
|
||
|
if (pullevent == false)
|
||
|
FlushEvents(mDownMask, mUpMask);
|
||
|
FlushEvents(mUpMask, everyEvent-mUpMask);
|
||
|
retval = wFalse;
|
||
|
}
|
||
|
|
||
|
/* Indicate that this click brought the app to the front */
|
||
|
else
|
||
|
theevent->modifiers |= activeFlag;
|
||
|
|
||
|
/* That's all folks! */
|
||
|
allDone = pullevent;
|
||
|
}
|
||
|
|
||
|
/* We're done if this was GetNextEvent, or we timed out with EventAvails */
|
||
|
else
|
||
|
allDone = (pullevent || ((eventmask & mDownMask) != 0) || (--eatMouseTimeout == 0));
|
||
|
|
||
|
/* High-level switch is complete. If we've switched into a scratchpad task,
|
||
|
* now is a good time to set its state to receive the puppet strings.
|
||
|
*/
|
||
|
if (allDone)
|
||
|
{
|
||
|
/* Let process see real events for a change */
|
||
|
coercionState = CS_DONE;
|
||
|
|
||
|
/* Well… maybe a few more fake ones! */
|
||
|
if (pCurrentProcess->p_puppetvars.puppetMasterProc != nil)
|
||
|
pCurrentProcess->p_state = PRPUPPET;
|
||
|
|
||
|
/* Are we in the middle of a system QuitAll? */
|
||
|
if (pSystemQuitAllMsg != nil)
|
||
|
{
|
||
|
PEntryPtr pTargetProc;
|
||
|
|
||
|
/* Can continue the quitall if we are switching back to the
|
||
|
* originator after a victim has died. Allow initial switch to
|
||
|
* intended victim.
|
||
|
*/
|
||
|
pTargetProc = PEntryFromPSN(&(pSystemQuitAllMsg->targetmfid.localPSN));
|
||
|
if ( (pTargetProc == nil) && EqualPSN(&(pSystemQuitAllMsg->sendermfid.localPSN), &pCurrentProcess->p_serialNumber) )
|
||
|
ContinueQuitAll();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Initial coercion state of a process. This code should only be reached the
|
||
|
* very first time an app makes an event call in the foreground.
|
||
|
*/
|
||
|
case CS_INIT:
|
||
|
{
|
||
|
register MenuPair *pThisMenuPair;
|
||
|
register Ptr limitPtr;
|
||
|
MenuHandle menuHdl;
|
||
|
|
||
|
|
||
|
/* Check if process can do cut or copies, but only if there is a menu list. */
|
||
|
pCurrentProcess->p_haseditcmdkeys = 0;
|
||
|
if ((MENULIST != nil) && (*MENULIST != nil))
|
||
|
{
|
||
|
/* Check likely edit menu first. Scan remainder only on no-find. */
|
||
|
if ((menuHdl = GetMHandle(3)) && FindCutCopyItems(menuHdl))
|
||
|
pCurrentProcess->p_haseditcmdkeys = 1;
|
||
|
else
|
||
|
{
|
||
|
pThisMenuPair = &((MenuListPtr) (*MENULIST))->menuPairs[0];
|
||
|
limitPtr = *MENULIST + ((MenuListPtr) (*MENULIST))->lastMenu;
|
||
|
while (pThisMenuPair <= limitPtr)
|
||
|
{
|
||
|
/* Check this menu */
|
||
|
if (FindCutCopyItems(pThisMenuPair->menuHdl))
|
||
|
{
|
||
|
pCurrentProcess->p_haseditcmdkeys = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Look at next menu */
|
||
|
pThisMenuPair += 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Set up process with the current scrap. */
|
||
|
MoveScrap();
|
||
|
|
||
|
/* Put process in state to receive scratchpad events if the lad has
|
||
|
* already acquired a master. Poor guy.
|
||
|
*/
|
||
|
coercionState = CS_DONE;
|
||
|
if (pCurrentProcess->p_puppetvars.puppetMasterProc != nil)
|
||
|
pCurrentProcess->p_state = PRPUPPET;
|
||
|
retval = CALL_FNC_PTR(Eventcommon_ptr,oldtrap,(eventmask, theevent));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(retval);
|
||
|
}
|
||
|
|
||
|
#pragma segment Main
|
||
|
|
||
|
/* AddToFrontProcessQueue. Puts pProc at the end of the waiting list for being switched
|
||
|
* to the foreground. Uses a pretty crude queue, on the assumption that we're not going
|
||
|
* to have a very long list.
|
||
|
* NOTE: This could be made more VM friendly, by a) skipping RemoveFromFrontProcessQueue
|
||
|
* if we could tell that pProc wasn't in the queue, and b) using a tail pointer.
|
||
|
*/
|
||
|
void
|
||
|
AddToFrontProcessQueue(PEntryPtr pProc)
|
||
|
{
|
||
|
PEntryPtr pCurr, pPrev;
|
||
|
|
||
|
/* Remove pProc if it is already queued */
|
||
|
RemoveFromFrontProcessQueue(pProc);
|
||
|
|
||
|
/* Scan the list to find the end */
|
||
|
pPrev = nil;
|
||
|
pCurr = pFrontProcessQueue;
|
||
|
while (pCurr != nil)
|
||
|
{
|
||
|
pPrev = pCurr;
|
||
|
pCurr = pCurr->p_nextQueuedFrontProcess;
|
||
|
}
|
||
|
|
||
|
/* Now, append to the list */
|
||
|
pProc->p_nextQueuedFrontProcess = nil;
|
||
|
if (pPrev == nil)
|
||
|
pFrontProcessQueue = pProc;
|
||
|
else
|
||
|
pPrev->p_nextQueuedFrontProcess = pProc;
|
||
|
}
|
||
|
|
||
|
/* PopFrontProcessQueue. Extract and return the first process to bring to the front. */
|
||
|
PEntryPtr
|
||
|
PopFrontProcessQueue(void)
|
||
|
{
|
||
|
PEntryPtr pProc;
|
||
|
|
||
|
/* Look at the queue */
|
||
|
if ((pProc = pFrontProcessQueue) != nil)
|
||
|
{
|
||
|
pFrontProcessQueue = pProc->p_nextQueuedFrontProcess;
|
||
|
pProc->p_nextQueuedFrontProcess = nil;
|
||
|
}
|
||
|
|
||
|
return(pProc);
|
||
|
}
|
||
|
|
||
|
/* RemoveFromFrontProcessQueue. "Cancels" pending foreground switch to pProc. */
|
||
|
void
|
||
|
RemoveFromFrontProcessQueue(PEntryPtr pProc)
|
||
|
{
|
||
|
PEntryPtr pCurr, pPrev;
|
||
|
|
||
|
pPrev = nil;
|
||
|
pCurr = pFrontProcessQueue;
|
||
|
while (pCurr != nil)
|
||
|
{
|
||
|
if (pCurr == pProc)
|
||
|
{
|
||
|
PEntryPtr pNext;
|
||
|
|
||
|
pNext = pProc->p_nextQueuedFrontProcess;
|
||
|
if (pPrev == nil)
|
||
|
pFrontProcessQueue = pNext;
|
||
|
else
|
||
|
pPrev->p_nextQueuedFrontProcess = pNext;
|
||
|
pProc->p_nextQueuedFrontProcess = nil;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pPrev = pCurr;
|
||
|
pCurr = pCurr->p_nextQueuedFrontProcess;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* OrphanScrapIntoPartition. This is a great one! When the APPLZONE is about to be
|
||
|
* nuked, we make sure that the SCRAPHANDLE is not taken with it, if that scrap is one
|
||
|
* that we'll want to pass on to other processes (i.e. realScrapOwner is true). We save
|
||
|
* it off by copying it to the beginning of the application partition, then resizing the
|
||
|
* partition to the correct length. This avoids excessive stack or heap use. We
|
||
|
* indicate that SCRAPHANDLE is an orphan, so that it gets disposed when the copying
|
||
|
* (MoveScrap()) is complete.
|
||
|
* If realScrapOwner is false, we make sure the scrap gets disposed even if it is not
|
||
|
* inside the partition that is going away.
|
||
|
* NOTE: Requires valid APPLZONE, but then invalidates it! Yea!
|
||
|
*/
|
||
|
void
|
||
|
OrphanScrapIntoPartition(void)
|
||
|
{
|
||
|
register Handle scrapHdl, procHdl;
|
||
|
unsigned long scrapSize;
|
||
|
Boolean scrapInZone;
|
||
|
|
||
|
/* Nothing to do if scrap is purged.
|
||
|
* NOTE: This has side-effect that a purged scrap handle from heap other than
|
||
|
* APPLZONE or one of its subzones will not get disposed. Too risky to try, tho!
|
||
|
*/
|
||
|
if ((scrapHdl = SCRAPHANDLE) != nil)
|
||
|
if (*scrapHdl == nil)
|
||
|
return;
|
||
|
|
||
|
/* Remember whether handle is from APPLZONE.
|
||
|
* NOTE: Will be “false” for nil handle, which is what we want.
|
||
|
*/
|
||
|
scrapInZone = ( (scrapHdl >= APPLZONE) && (scrapHdl < APPLZONE->bkLim) );
|
||
|
|
||
|
/* Don't save/copy the scrap if dying process doesn't have the scrap of interest.
|
||
|
* Dispose the scrap if it is not going to be implicitly disposed when the
|
||
|
* partition is. E.g. The DA Handler's scrap is in the system heap.
|
||
|
*/
|
||
|
if ((pCurrentProcess != srcMyScrapInfo.pScrapOwner) && (pCurrentProcess->p_cutcopycount == cutCopyCount))
|
||
|
{
|
||
|
if ((scrapInZone == false) && (scrapHdl != nil))
|
||
|
DisposHandle(scrapHdl);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Copy scrap to beginning of partition, then size down the partition handle.
|
||
|
* We nil out APPLZONE since we are putting it out of commission.
|
||
|
* NOTE: We know that scrapSize < GetHandleSize(procHdl) since the SCRAPHANDLE
|
||
|
* is fully contained by a heap within the partition.
|
||
|
*/
|
||
|
if (scrapInZone)
|
||
|
{
|
||
|
procHdl = pCurrentProcess->p_zoneh;
|
||
|
scrapSize = GetHandleSize(scrapHdl);
|
||
|
APPLZONE = nil;
|
||
|
BlockMove(*scrapHdl, *procHdl, scrapSize);
|
||
|
SetHandleSize(procHdl, scrapSize);
|
||
|
|
||
|
/* Now replace the lomem, and forget that handle was the partition */
|
||
|
pCurrentProcess->p_zoneh = nil;
|
||
|
HUnlock(procHdl);
|
||
|
SCRAPHANDLE = procHdl;
|
||
|
}
|
||
|
|
||
|
/* Set up coercion info, and remember to chuck the scrap when we're done. This needs to
|
||
|
* be done even if scrapHdl is nil, since that just means the scrap is on disk.
|
||
|
*/
|
||
|
src_scrap_setup();
|
||
|
srcMyScrapInfo.pScrapOwner = nil;
|
||
|
srcMyScrapInfo.orphanedScrap = true;
|
||
|
}
|
||
|
|
||
|
/* DisposeOrphanedScrap. Dispose the source scrap if it was meant to be. Zero
|
||
|
* memHandle field for safety.
|
||
|
* NOTE: The orphaned scrap exists until a new scrap is made. A new scrap is made
|
||
|
* (or at least committed to) when a) an application calls ZeroScrap, PutScrap,
|
||
|
* SysEdit (cut or copy), or MenuSelect (cut or copy), or b) we call MoveScrap()
|
||
|
* toward the end of a foreground switch. At these times, DisposeOrphanedScrap is
|
||
|
* called because we'll no longer need the orphan. In (a) cases, the orphan was
|
||
|
* never needed (oh well). In (b), the orphan was used to give the new front app
|
||
|
* a scrap, so the orphan's job is complete. Give 'im an extra lump of coal!
|
||
|
*/
|
||
|
void
|
||
|
DisposeOrphanedScrap(void)
|
||
|
{
|
||
|
if (srcMyScrapInfo.orphanedScrap)
|
||
|
{
|
||
|
srcMyScrapInfo.orphanedScrap = false;
|
||
|
if (srcMyScrapInfo.memHandle != nil)
|
||
|
{
|
||
|
assert(*(srcMyScrapInfo.memHandle) != 0);
|
||
|
DisposHandle(srcMyScrapInfo.memHandle);
|
||
|
srcMyScrapInfo.memHandle = nil;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* src_scrap_setup. Save the outgoing app's scrap state information. It will be
|
||
|
* used later when MoveScrap is called in the context of the new foreground
|
||
|
* application.
|
||
|
*/
|
||
|
void
|
||
|
src_scrap_setup(void)
|
||
|
{
|
||
|
assert (pCurrentProcess != nil);
|
||
|
|
||
|
/* Don't change scrap info if the current process has an out-of-date scrap.
|
||
|
* Decrement the p_cutcopycount so we'll try again to copy the scrap when
|
||
|
* twitching this process back in.
|
||
|
*/
|
||
|
if ((pCurrentProcess != srcMyScrapInfo.pScrapOwner) && (pCurrentProcess->p_cutcopycount == cutCopyCount))
|
||
|
{
|
||
|
pCurrentProcess->p_cutcopycount--;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Dump orphan, if he's around */
|
||
|
DisposeOrphanedScrap();
|
||
|
|
||
|
/* Set up the src scrap record */
|
||
|
srcMyScrapInfo.size = SCRAPINFO;
|
||
|
|
||
|
/* if SCRAPHANDLE == nil, src scrap is on disk */
|
||
|
if ((srcMyScrapInfo.memHandle = SCRAPHANDLE) == nil)
|
||
|
srcMyScrapInfo.fileName = SCRAPNAME;
|
||
|
|
||
|
/* Assume this scrap is attached still. Caller can change this, if he wants. */
|
||
|
srcMyScrapInfo.orphanedScrap = false;
|
||
|
|
||
|
/* Claim scrap and synch the cutcopycount since global was just taken from the app */
|
||
|
srcMyScrapInfo.pScrapOwner = pCurrentProcess;
|
||
|
pCurrentProcess->p_cutcopycount = cutCopyCount;
|
||
|
}
|
||
|
|
||
|
/* StartForegroundSwitch. Initiate foreground switch. */
|
||
|
void
|
||
|
StartForegroundSwitch(PEntryPtr pProc)
|
||
|
{
|
||
|
register PEntryPtr pFront, pTargetProc;
|
||
|
|
||
|
assert(coercionState == CS_DONE);
|
||
|
pNewFrontProcess = pProc;
|
||
|
pFront = pFrontProcess;
|
||
|
|
||
|
/* Are we in the middle of a system QuitAll? Cancel it if we're switching out of
|
||
|
* the currently intended victim.
|
||
|
* NOTE: This assumes that StartForegroundSwitch is not called when switching to
|
||
|
* a new front process after the current front process has ExitToShell'ed. Otherwise,
|
||
|
* we would be canceling the aeQuitAll just because we got someone to quit! Pretty
|
||
|
* safe assumption, though, since StartForegroundSwitch deals with pCurrentProcess,
|
||
|
* which is completely invalid after ExitToShell.
|
||
|
*/
|
||
|
if ( (pSystemQuitAllMsg != nil)
|
||
|
&& (pFront == (pTargetProc = PEntryFromPSN(&(pSystemQuitAllMsg->targetmfid.localPSN)))) )
|
||
|
CancelQuitAll(&pTargetProc->p_serialNumber);
|
||
|
|
||
|
/* Check whether FG switching out of a process that has been suspended for
|
||
|
* high-level debugging. If so, we must forego the source's half of the state
|
||
|
* machine. We go straight to the new FG process.
|
||
|
*/
|
||
|
if (pFront->p_state == PRNULL)
|
||
|
{
|
||
|
coercionState = CS_ENTERING;
|
||
|
BringProcessToFront(pProc);
|
||
|
|
||
|
// Inform TSM that a non-TSM aware app got swapped to the front ...
|
||
|
//if ( !pFront->p_inlineAware && (tsmLayerOwner != nil))
|
||
|
// InformTSM(kMsgResumeApp, nil);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Start the ball rolling… */
|
||
|
if ((pFront->p_taskmode & (modeNeedSuspendResume | modeDoesActivateOnFGSwitch))
|
||
|
== (modeNeedSuspendResume | modeDoesActivateOnFGSwitch))
|
||
|
{
|
||
|
coercionState = CS_PRESUSPEND_DEACTIVATE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
coercionState = (WINDOWLIST != nil) ? CS_GENERATE_DEACTIVATE1 : CS_GENERATE_DEACTIVATE2;
|
||
|
PutUpFakeWindow();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Inform TSM that a non-TSM aware app got suspended ...
|
||
|
|
||
|
if ( !pFront->p_inlineAware && (tsmLayerOwner != nil))
|
||
|
InformTSM(kMsgSuspendApp, nil);
|
||
|
}
|
||
|
|
||
|
/* SetThirdSrcCoercionState. Decide where to go after CS_GENERATE_DEACTIVATE1
|
||
|
* or CS_GENERATE_DEACTIVATE2. Being in CS_GENERATE_DEACTIVATE2 means that
|
||
|
* we're expecting to see an activate event for the fake DA window. It's time
|
||
|
* to put the outgoing app on ice.
|
||
|
*/
|
||
|
void
|
||
|
SetThirdSrcCoercionState(void)
|
||
|
{
|
||
|
/* See if we're expecting deactivate-then-activate (i.e. whether the fake window
|
||
|
* was not the only window in the WINDOWLIST).
|
||
|
*/
|
||
|
if (coercionState == CS_GENERATE_DEACTIVATE1)
|
||
|
{
|
||
|
coercionState = CS_GENERATE_DEACTIVATE2;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* If we can't see cut/copies, assume it happened */
|
||
|
if (pCurrentProcess->p_haseditcmdkeys == false)
|
||
|
cutCopyCount++;
|
||
|
|
||
|
/* Set the next state. This will determine how the outgoing app will be told/forced
|
||
|
* to post its scrap and get switched out.
|
||
|
*/
|
||
|
|
||
|
/* Try to use modern suspend event that tells app to do it all */
|
||
|
if ((pCurrentProcess->p_taskmode & modeNeedSuspendResume) != 0)
|
||
|
coercionState = CS_SUSPEND;
|
||
|
|
||
|
/* We have dumb app that doesn't understand suspend events. */
|
||
|
else
|
||
|
{
|
||
|
/* Force scrap coercion if necessary */
|
||
|
if (pCurrentProcess->p_cutcopycount != cutCopyCount)
|
||
|
{
|
||
|
nullTimeout = NULL_TIMEOUT_INIT;
|
||
|
coercionState = CS_GENERATE_NULLS_THEN_CMD_V;
|
||
|
}
|
||
|
|
||
|
/* Otherwise, start the move to the new app. */
|
||
|
else
|
||
|
coercionState = CS_EXITING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* SetFirstDstCoercionState. Start the the track that the dst task will take through
|
||
|
* coercion state space. Called only by engine in CS_ENTERING state, this can be
|
||
|
* pulled back inline for optimization.
|
||
|
*/
|
||
|
void
|
||
|
SetFirstDstCoercionState(void)
|
||
|
{
|
||
|
PEntryPtr pCurrProc = pCurrentProcess;
|
||
|
|
||
|
if (pCurrProc->p_slices == 1)
|
||
|
{
|
||
|
coercionState = CS_INIT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* NOTE: Have to set bit 1 dynamically */
|
||
|
resume_message = RESUME_MESSAGE;
|
||
|
|
||
|
/* Check whether returning from scratchpad switch */
|
||
|
if (pCurrProc->p_puppetvars.masterPuppetProc != nil)
|
||
|
{
|
||
|
resume_message |= (1 << bitResumeScratch);
|
||
|
*((char *) &resume_message + 1) = pCurrProc->p_puppetvars.masterResumeMsg;
|
||
|
pCurrProc->p_puppetvars.masterResumeMsg = resumeFlag;
|
||
|
pCurrProc->p_puppetvars.masterPuppetProc = nil;
|
||
|
}
|
||
|
|
||
|
/* Check whether we need to copy clipboard. If so, the process needs also to
|
||
|
* coerce the clipboard into its scrap when it gets its resume event.
|
||
|
*/
|
||
|
if (pCurrProc->p_cutcopycount != cutCopyCount)
|
||
|
{
|
||
|
resume_message |= convertClipboardFlag;
|
||
|
coercionState = CS_MOVE_SCRAP;
|
||
|
}
|
||
|
|
||
|
/* No clipboard copying needed. Take the easy way out. */
|
||
|
else
|
||
|
{
|
||
|
/* Pass the honor of scrap ownership to the new front process */
|
||
|
srcMyScrapInfo.pScrapOwner = pCurrentProcess;
|
||
|
|
||
|
/* Remove orphaned scrap, if that's the case */
|
||
|
DisposeOrphanedScrap();
|
||
|
|
||
|
/* Shortcut to resume event for the bright, young apps */
|
||
|
if ((pCurrProc->p_taskmode & modeNeedSuspendResume) != 0)
|
||
|
coercionState = CS_RESUME;
|
||
|
else
|
||
|
{
|
||
|
coercionState = CS_CLICK_IN_WINDOW;
|
||
|
clickDAWindTimeout = CLICK_DA_WIND_TIMEOUT_INIT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* CheckForCutCopySysEditCmd. Given the edit command passed to _SysEdit, determine whether
|
||
|
* it is a "cut" or "copy" type of command. If it is, increment the "cutcopycount"
|
||
|
* addressed by pCutCopyCount. If said count is the global cutCopyCount, call
|
||
|
* DisposeOrphanScrap to free the memory now that we won't need the orphan.
|
||
|
*/
|
||
|
void
|
||
|
CheckForCutCopySysEditCmd(short theEditCmd, short *pCutCopyCount)
|
||
|
{
|
||
|
/* Edit command passed to _SysEdit has not yet been biased by accUndo */
|
||
|
if ( (theEditCmd != (accCut-accUndo)) && (theEditCmd != (accCopy-accUndo)) )
|
||
|
return;
|
||
|
|
||
|
(*pCutCopyCount)++;
|
||
|
if (pCutCopyCount == &cutCopyCount)
|
||
|
DisposeOrphanedScrap();
|
||
|
}
|
||
|
|
||
|
/* CheckForCutCopyMenuKey. Given the character that is the key equivalent for a menu
|
||
|
* item, determine whether the item is a "cut" or "copy" type of command.
|
||
|
* If it is, increment the "cutcopycount" addressed by pCutCopyCount. If said count is
|
||
|
* the global cutCopyCount, call DisposeOrphanScrap to free the memory now that we won't
|
||
|
* need the orphan.
|
||
|
*/
|
||
|
void
|
||
|
CheckForCutCopyMenuKey(unsigned char ch, short *pCutCopyCount)
|
||
|
{
|
||
|
/* Is it a cut or copy char? */
|
||
|
switch(ch)
|
||
|
{
|
||
|
case (unsigned char)'X':
|
||
|
case (unsigned char)'x':
|
||
|
case (unsigned char)'≈': /* Option-x */
|
||
|
case (unsigned char)'C':
|
||
|
case (unsigned char)'c':
|
||
|
case (unsigned char)'ç': /* Option-c */
|
||
|
case (unsigned char)'Z':
|
||
|
case (unsigned char)'z':
|
||
|
case (unsigned char)'Ω': /* Option-z */
|
||
|
{
|
||
|
(*pCutCopyCount)++;
|
||
|
if (pCutCopyCount == &cutCopyCount)
|
||
|
DisposeOrphanedScrap();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* FindCutCopyItems. Check the menu with Handle menuHdl to see if it has any cut
|
||
|
* or copy items that we can find. Return result of search. This does nothing to
|
||
|
* relocate menuHdl so it needn't be locked.
|
||
|
* NOTE: Uses speedy but structure-dependant search through menu data.
|
||
|
*/
|
||
|
short
|
||
|
FindCutCopyItems(MenuHandle menuHdl)
|
||
|
{
|
||
|
MenuPtr menuPtr;
|
||
|
register Ptr chPtr;
|
||
|
char nameLen;
|
||
|
|
||
|
/* Assume menu is useless if no menu info available */
|
||
|
if (menuHdl == nil || (menuPtr = *menuHdl) == nil)
|
||
|
return(wFalse);
|
||
|
|
||
|
/* Jump over menu title */
|
||
|
chPtr = &(menuPtr->menuData);
|
||
|
nameLen = *chPtr++;
|
||
|
chPtr += nameLen;
|
||
|
if (*chPtr == (char) 0)
|
||
|
return(wFalse);
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
nameLen = *chPtr++; /* get length of item name */
|
||
|
if (nameLen == (char) 0)
|
||
|
return(wFalse); /* ran off end of list */
|
||
|
chPtr += nameLen + 4; /* skip over name and flags */
|
||
|
|
||
|
/* Is it a cut or copy char? */
|
||
|
switch(chPtr[-3])
|
||
|
{
|
||
|
case 'X':
|
||
|
case 'x':
|
||
|
case 'C':
|
||
|
case 'c':
|
||
|
return(wTrue);
|
||
|
}
|
||
|
}
|
||
|
}
|