IdleImpl.mesa
Copyright Ó 1985, 1986, 1988, 1989, 1991, 1992 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) April 16, 1987 2:08:09 am PDT
Doug Wyatt, December 4, 1986 7:06:23 pm PST
Last tweaked by Mike Spreitzer on October 7, 1987 3:00:44 pm PDT
Willie-Sue (really bj), November 2, 1988 11:33:51 am PST
Bier, March 5, 1991 6:22 pm PST
Last edited by: Mik Lamming - January 26, 1989 11:39:00 am PST
Michael Plass, September 29, 1989 2:03:30 pm PDT
Kenneth A. Pier, March 14, 1991 5:42 pm PST
Willie-s, October 14, 1991 1:00 pm PDT
Christian Jacobi, March 3, 1992 12:33 pm PST
DIRECTORY
Basics, BasicTime, Idle, IdleBackdoor, Imager, ImagerBackdoor, ImagerFont, ImagerSample, InputFocus, KeySymsKB, KeyMapping, KeyTypes, MultiCursors, Process, Real, Rope, SpecialKeySyms, TIPPrivate, UserInput, UserInputOps, ViewerClasses, ViewerOps, ViewerPrivate, ViewersWorld, ViewersWorldInstance, ViewersWorldTypes, ViewersWorldRefType;
IdleImpl: CEDAR MONITOR
IMPORTS Basics, BasicTime, Imager, ImagerBackdoor, ImagerFont, ImagerSample, InputFocus, KeyMapping, MultiCursors, Process, Real, TIPPrivate, UserInputOps, ViewerOps, ViewerPrivate, ViewersWorld, ViewersWorldInstance
EXPORTS Idle, IdleBackdoor, ViewersWorldRefType = BEGIN
Basic Stuff
ViewersWorldRef: TYPE = REF ViewersWorldObj;
ViewersWorldObj: PUBLIC TYPE = ViewersWorldTypes.ViewersWorldObj;
Activity: TYPE ~ IdleBackdoor.Activity;
Animator: TYPE ~ IdleBackdoor.Animator;
AnimatorPrivate: TYPE ~ IdleBackdoor.AnimatorPrivate;
FullIdleHandler: TYPE ~ IdleBackdoor.FullIdleHandler;
KeyFilter: TYPE ~ IdleBackdoor.KeyFilter;
IdleRegistration: TYPE ~ Idle.IdleRegistration;
IdleHandler: TYPE ~ Idle.IdleHandler;
ImageProc: TYPE ~ Idle.ImageProc;
KeyCode: TYPE ~ KeyTypes.KeyCode;
NotifyProc: TYPE ~ ViewerClasses.NotifyProc;
ROPE: TYPE ~ Rope.ROPE;
AnimatorList: TYPE ~ LIST OF Animator;
Bug: ERROR ~ CODE;
Global Variables (not exported)
idleWorld: ViewersWorldTypes.Ref ¬ ViewersWorldInstance.GetWorld[];
Monitored Data
becomeIdle, outOfIdle: CONDITION;
idleState: Activity ¬ unidle;
newState: Activity ¬ unidle; -- just to initialize to something
changeComing: BOOL ¬ FALSE;
enabled: BOOL ¬ TRUE;
fullRegistry: FullIdleHandlerList ¬ NIL;
animatorStack: AnimatorList ¬ NIL;
Exports to IdleBackdoor
FullIdleHandlerList: TYPE ~ LIST OF FullIdleHandlerRecord;
FullIdleHandlerRecord: TYPE ~ RECORD [proc: FullIdleHandler, data: REF ANY];
RegisterFullIdleHandler: PUBLIC ENTRY PROC [handler: FullIdleHandler, data: REF ¬ NIL] RETURNS [IdleRegistration] ~ {
fullRegistry ¬ CONS[[handler, data], fullRegistry];
RETURN [fullRegistry];
};
UnregisterFullIdleHandler: PUBLIC ENTRY PROC [registration: IdleRegistration] = {
ENABLE UNWIND => NULL;
lag: FullIdleHandlerList ¬ NIL;
FOR each: FullIdleHandlerList ¬ fullRegistry, each.rest WHILE each # NIL DO
IF each = registration THEN {
IF lag = NIL THEN fullRegistry ¬ each.rest ELSE lag.rest ¬ each.rest;
EXIT;
};
lag ¬ each;
ENDLOOP;
};
CopyRegistry: INTERNAL PROC RETURNS [head: FullIdleHandlerList ¬ NIL] = {
ENABLE UNWIND => NULL;
tail: FullIdleHandlerList ¬ NIL;
FOR each: FullIdleHandlerList ¬ fullRegistry, each.rest WHILE each # NIL DO
new: FullIdleHandlerList = LIST[each.first];
IF tail = NIL THEN head ¬ new ELSE tail.rest ¬ new;
tail ¬ new;
ENDLOOP;
};
GetAnimator: PUBLIC ENTRY PROC RETURNS [Animator] ~ {
ENABLE UNWIND => NULL;
RETURN [animatorStack.first];
};
SetAnimator: PUBLIC ENTRY PROC [a: Animator] ~ {
ENABLE UNWIND => NULL;
WHILE idleState#unidle DO WAIT outOfIdle ENDLOOP;
animatorStack.first ¬ a;
};
PushAnimator: PUBLIC ENTRY PROC [a: Animator] ~ {
ENABLE UNWIND => NULL;
WHILE idleState#unidle DO WAIT outOfIdle ENDLOOP;
animatorStack ¬ CONS[a, animatorStack];
};
PopAnimator: PUBLIC ENTRY PROC ~ {
ENABLE UNWIND => NULL;
WHILE idleState#unidle DO WAIT outOfIdle ENDLOOP;
IF animatorStack.rest#NIL THEN animatorStack ¬ animatorStack.rest;
};
FinishSet: PROC [changed: BOOL, reg: FullIdleHandlerList] ~ {
FOR each: FullIdleHandlerList ¬ reg, each.rest WHILE each # NIL DO
ENABLE UNWIND => LOOP;
IF each.first.proc#NIL THEN each.first.proc[each.first.data, idleState, newState];
ENDLOOP;
};
IsKeyDown: PROC [watch: KeyFilter] RETURNS [BOOL] = {
IF ViewersWorldInstance.GetWorld[]#idleWorld THEN RETURN [FALSE];
FOR k: KeyCode IN [watch.first .. watch.last] DO
IF UserInputOps.GetLatestKeyState[ViewersWorld.GetInputHandle[idleWorld], k] = down
AND watch.pass[k] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
WaitForLogin: PROC ~ {
SIGNAL NotYetImplemented;
};
NotYetImplemented: SIGNAL = CODE;
DoLogin: PROC RETURNS [wake: BOOL ¬ TRUE] = {
};
defaultKeyFilter: PUBLIC KeyFilter ← [first: 13, last: 111, pass: ALL[TRUE]];
defaultKeyFilter: PUBLIC KeyFilter ¬ [first: VAL[1], last: VAL[125], pass: ALL[TRUE]];
DefaultKeyFilter: PROC RETURNS [KeyFilter] ~ {
FirstKeyCode: PROC [mapping: KeyMapping.Mapping, keySym: KeyTypes.KeySym] RETURNS [keyCode: KeyTypes.KeyCode ¬ keycode0] = {
keyCodes: KeyMapping.KeyCodes ~ KeyMapping.KeyCodesFromKeySym[mapping, keySym];
IF keyCodes.n>0 THEN RETURN [keyCodes[0].keyCode]
};
filter: KeyFilter ¬ [first: VAL[1], last: VAL[125], pass: ALL[TRUE]];
handle: UserInput.Handle ¬ ViewersWorld.GetInputHandle[idleWorld];
mapping: KeyMapping.Mapping ~ UserInputOps.GetMapping[handle];
lockCode1, lockCode2, controlCode, leftShiftCode, rightShiftCode, leftMouseCode, middleMouseCode, rightMouseCode: KeyCode;
lockCode1 ¬ FirstKeyCode[mapping, KeySymsKB.ShiftLock].keyCode;
lockCode2 ¬ FirstKeyCode[mapping, KeySymsKB.CapsLock].keyCode;
controlCode ¬ FirstKeyCode[mapping, KeySymsKB.LeftControl].keyCode;
leftShiftCode ¬ FirstKeyCode[mapping, KeySymsKB.LeftShift].keyCode;
rightShiftCode ¬ FirstKeyCode[mapping, KeySymsKB.RightShift].keyCode;
leftMouseCode ¬ FirstKeyCode[mapping, SpecialKeySyms.Button1].keyCode;
middleMouseCode ¬ FirstKeyCode[mapping, SpecialKeySyms.Button2].keyCode;
rightMouseCode ¬ FirstKeyCode[mapping, SpecialKeySyms.Button3].keyCode;
filter.pass[lockCode1] ¬ FALSE;
filter.pass[lockCode2] ¬ FALSE;
filter.pass[controlCode] ¬ FALSE;
filter.pass[leftShiftCode] ¬ FALSE;
filter.pass[rightShiftCode] ¬ FALSE;
filter.pass[leftMouseCode] ¬ FALSE;
filter.pass[middleMouseCode] ¬ FALSE;
filter.pass[rightMouseCode] ¬ FALSE;
RETURN[filter];
};
Idle Registry (exported to Idle)
SimpleReg: TYPE = REF SimpleRegPrivate;
SimpleRegPrivate: TYPE = RECORD [
handler: IdleHandler,
data: REF ANY
];
IsIdle: PUBLIC PROC RETURNS [BOOL] = {
RETURN [idleState # unidle];
};
RegisterIdleHandler: PUBLIC PROC [handler: IdleHandler, data: REF ¬ NIL] RETURNS [IdleRegistration]= {
sr: SimpleReg ¬ NEW [SimpleRegPrivate ¬ [handler, data]];
RETURN [RegisterFullIdleHandler[Simplify, sr]];
};
Simplify: PROC [data: REF ANY, old, new: Activity] --FullIdleHandler-- ~ {
sr: SimpleReg ~ NARROW[data];
IF old=unidle AND new=idle THEN sr.handler[sr.data, becomingIdle]
ELSE IF old=loggingIn AND new=unidle THEN sr.handler[sr.data, becomingBusy];
};
UnregisterIdleHandler: PUBLIC PROC [registration: IdleRegistration] = {
UnregisterFullIdleHandler[registration];
};
Sleep stuff (exported to Idle)
sleepMillis: [10..2000] ¬ 150;
xVel: INTEGER ¬ 2;
yVel: INTEGER ¬ 1;
sleepEraseOld: BOOL ¬ TRUE;
sleepWaitFactor: [5..100] ¬ 20;
ticksPerSecond: Process.Ticks = Process.SecondsToTicks[1];
WaitForIdle: PUBLIC ENTRY PROC [idle: BOOL] ~ {
WHILE idleState # idle DO WAIT becomeIdle ENDLOOP;
};
DoASleep: PUBLIC PROC ~ {
an: Animator ¬ animatorStack.first;
BecomeIdle[];
an.Animate[idleWorld, IsKeyDown, an.data];
OutOfIdle[];
ViewerOps.PaintEverything[];
};
Sleep: PUBLIC PROC [proc: Idle.ImageProc ¬ DefaultImageProc, powerOff: BasicTime.GMT ¬ BasicTime.nullGMT] = {
IF proc=DefaultImageProc THEN DoASleep[] ELSE {
an: Animator;
TRUSTED {an ¬ NEW [AnimatorPrivate ¬ [Bounce, NEW [Idle.ImageProc ¬ proc]]]};
PushAnimator[an];
DoASleep[];
PopAnimator[];
};
};
StringEscapement: PROC [font: ImagerFont.Font, string: ImagerFont.XStringProc, amplifySpace: REAL] RETURNS [ImagerFont.VEC] = {
sum: ImagerFont.VEC ¬ [0, 0];
charAction: ImagerFont.XCharProc ~ {
width: ImagerFont.VEC;
width ¬ ImagerFont.Escapement[font, char];
IF ImagerFont.Amplified[font, char] THEN {
width.x ¬ width.x*amplifySpace;
width.y ¬ width.y*amplifySpace;
};
sum.x ¬ sum.x+width.x; sum.y ¬ sum.y+width.y;
};
string[charAction];
RETURN[sum];
};
RopeEscapement: PROC [font: ImagerFont.Font, rope: Rope.ROPE, amplifySpace: REAL,
start: INT ¬ 0, len: INT ¬ INT.LAST] RETURNS [ImagerFont.VEC] ~ {
string: ImagerFont.XStringProc ~ { ImagerFont.MapRope[rope, start, len, charAction] };
RETURN[StringEscapement[font, string, amplifySpace]];
};
DoNothing: NotifyProc = {};
NullContextCreator: PROC
[screenServerData: REF, screen: ViewerPrivate.Screen] RETURNS [Imager.Context] = {
sm: ImagerSample.SampleMap = ImagerSample.NewSampleMap[box: [max: [1, 1]]];
RETURN [ImagerBackdoor.BitmapContext[sm]]
};
BecomeIdle: ENTRY PROC = {
idleState ¬ idle;
idleContext ¬ ViewerPrivate.CreateContext[main];
InputFocus.CaptureButtons[DoNothing, TIPPrivate.DefaultTable[FALSE]];
oldContextCreatorProc ¬ ViewerPrivate.SetCreator[NullContextCreator];
BROADCAST becomeIdle;
};
OutOfIdle: ENTRY PROC = {
idleState ¬ unidle;
idleContext ¬ NIL;
InputFocus.ReleaseButtons[];
[] ¬ ViewerPrivate.SetCreator[oldContextCreatorProc];
BROADCAST outOfIdle;
};
idleContext: Imager.Context; -- context for Idle to use
oldContextCreatorProc: ViewerPrivate.ContextCreatorProc;
BecomeIdle will change the Viewers context creator to a Null creator and save the old creator in oldContextCreatorProc. OutOfIdle will restore the Viewers context creator from oldContextCreatorProc.
ColorScreen: PROC [screen: ViewerPrivate.Screen, color: Imager.Color] = {
ignores screen. Uses idle context.
Color the entire screen with the given color.
colorAction: PROC [context: Imager.Context] ~ {
Imager.SetColor[context, color];
Imager.MaskRectangleI[context, 0, 0, 9999, 9999];
};
PaintScreen[screen, colorAction];
};
PaintScreen: PROC [screen: ViewerPrivate.Screen, action: PROC [context: Imager.Context] ] = {
ignores screen. Uses idle context.
IF idleContext=NIL THEN RETURN;
action[idleContext ! UNWIND => CONTINUE;];
};
Bounce: PROC [world: ViewersWorld.Ref, ShouldReturn: PROC [watch: KeyFilter] RETURNS [BOOL], data: REF ANY ¬ NIL] = TRUSTED {
proc: Idle.ImageProc ~ WITH data SELECT FROM
x: REF Idle.ImageProc => x­,
ENDCASE => IF data=NIL THEN DefaultImageProc ELSE ERROR;
width: NAT ¬ ViewersWorld.GetSize[world].width;
height: NAT ¬ ViewersWorld.GetSize[world].height;
x: NAT ¬ (width / 8)*3;
y: NAT ¬ (height / 8)*3;
image: Rope.ROPE ¬ "Type Key";
font: Imager.Font ¬ Imager.FindFontScaled["Xerox/XC1-2-2/Helvetica", 14.0];
vec: ImagerFont.VEC ¬ RopeEscapement[font, image, 1.0];
fontExtents: ImagerFont.Extents ¬ ImagerFont.RopeBoundingBox[font, image];
imageWidth: NAT ¬ Real.Ceiling[vec.x];
descent: INTEGER ¬ Real.Ceiling[fontExtents.descent];
imageHeight: NAT ¬ descent + Real.Ceiling[fontExtents.ascent];
termW: NAT = width;
maxW: NAT = termW - termW/4;
termH: NAT = height;
maxH: NAT = termH - termH/4;
<<ViewerPrivate.>>ColorScreen[main, Imager.black];
DO
mark: BasicTime.Pulses ¬ BasicTime.GetClockPulses[];
w: CARDINAL ¬ 0;
h: CARDINAL ¬ 0;
oldX: INTEGER ¬ x;
oldY: INTEGER ¬ y;
IF image # NIL THEN {
newX: INTEGER ¬ x + xVel;
newY: INTEGER ¬ y + yVel;
xLim: INTEGER ¬ newX + (w ¬ MIN[imageWidth, maxW]);
yLim: INTEGER ¬ newY + (h ¬ MIN[imageHeight, maxH]);
Reflect off of the left or right
SELECT TRUE FROM
newX < 0 => {
newX ¬ 0;
xLim ¬ newX + w;
xVel ¬ - xVel;
};
xLim > termW => {
xLim ¬ termW;
newX ¬ xLim - w;
xVel ¬ - xVel;
};
ENDCASE;
Reflect off of the top or bottom
SELECT TRUE FROM
newY < 0 => {
newY ¬ 0;
yLim ¬ newY + h;
yVel ¬ - yVel;
};
yLim > termH => {
yLim ¬ termH;
newY ¬ yLim - h;
yVel ¬ - yVel;
};
ENDCASE;
x ¬ newX;
y ¬ newY;
};
IF proc # NIL THEN {
image ¬ proc[w: w, h: h];
IF sleepEraseOld THEN {
PaintRectangleBlack: SAFE PROC [context: Imager.Context] = CHECKED {
Imager.SetColor[context, Imager.black];
Imager.MaskRectangleI[context, oldX, oldY-descent, w, h];
};
unpaint old image
<<ViewerPrivate.>>PaintScreen[main, PaintRectangleBlack];
ViewerPrivate.ColorScreen[main, Imager.black];
};
IF image # NIL THEN {
DrawRope: SAFE PROC [context: Imager.Context] = TRUSTED {
Imager.SetXY[context, [x, y]];
Imager.SetFont[context, font];
Imager.SetColor[context, Imager.white];
Imager.ShowRope[context, image];
};
<<ViewerPrivate.>>PaintScreen[main, DrawRope];
};
};
{
This is an adaptive loop intended to keep the overhead of the idle loop small. We don't get out until the elapsed time from the start is sleepWaitFactor times the elapsed time taken to do the screen update.
limit: BasicTime.Pulses ¬ (BasicTime.GetClockPulses[] - mark)*sleepWaitFactor;
DO
IF ShouldReturn[defaultKeyFilter] THEN GO TO done;
Process.Pause[2];
IF (BasicTime.GetClockPulses[] - mark) > limit THEN EXIT;
IF BasicTime.Pulses[BasicTime.GetClockPulses[] - mark] > limit THEN EXIT;
ENDLOOP;
EXITS done => EXIT;
};
ENDLOOP;
};
DefaultImageProc: PUBLIC Idle.ImageProc = {RETURN["Type Key"]};
defaultAnimator: PUBLIC Animator ~ NEW [AnimatorPrivate ¬ [Bounce, NIL]];
nRandoms: CARDINAL = 20;
RandIndex: TYPE = [0..nRandoms);
randTable: ARRAY RandIndex OF CARDINAL ¬ [
30200, 27432, 62096, 39855, 17884, 58726, 55595, 20904, 28164, 27447,
34709, 35231, 33770, 31508, 40689, 1411, 20373, 3422, 62938, 40035];
startTime: BasicTime.GMT;
randIndex: RandIndex ¬ 0; -- initialized by Start trap
Random: PROC RETURNS [r: CARDINAL] = {
This algorithm is stolen from DMT.
i: RandIndex;
randIndex ¬ IF randIndex = LAST[RandIndex] THEN FIRST[RandIndex] ELSE SUCC[randIndex];
i ¬ (randIndex + 3) MOD nRandoms;
r ¬ randTable[i] ¬ randTable[randIndex] + randTable[i];
};
UpdateAndClip: PROC [v: CARDINAL, limit: CARDINAL, scale: CARDINAL ¬ 16]
RETURNS [newV: CARDINAL ¬ 0] = INLINE {
r: CARDINAL = Random[];
x: INTEGER ¬ v;
x ¬ x +
(SELECT TRUE FROM
Basics.BITAND[r, 100000B] ~= 0 => 0,
Basics.BITAND[r, 40000B] ~= 0 => scale,
ENDCASE => -scale);
RETURN [
SELECT x FROM
< 0 => limit - scale,
>= INTEGER[limit - scale] => 0,
ENDCASE => x
]
};
Initialization
StartIdleProcesses: PUBLIC PROC [] = {
idleWorld ¬ ViewersWorldInstance.GetWorld[];
defaultKeyFilter ¬ DefaultKeyFilter[];
startTime ¬ BasicTime.Now[
! BasicTime.TimeNotKnown => { startTime ¬ BasicTime.earliestGMT; CONTINUE } ];
randIndex ¬ Basics.LowHalf[BasicTime.ToPupTime[startTime]] MOD nRandoms;
MultiCursors.SetACursorOffset[-16, -16, TRUE, NIL];
animatorStack ¬ LIST[defaultAnimator];
};
END.