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;
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];
};
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;
};
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
]
};
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];
};