<<>> <> <> <> <> <> <> <> <> <> <> <> <> 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 <> 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; <> idleWorld: ViewersWorldTypes.Ref ¬ ViewersWorldInstance.GetWorld[]; <> 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; <> 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: 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]; }; <> 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]; }; <> 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; <> ColorScreen: PROC [screen: ViewerPrivate.Screen, color: Imager.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] ] = { <> 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; <>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]); <> SELECT TRUE FROM newX < 0 => { newX ¬ 0; xLim ¬ newX + w; xVel ¬ - xVel; }; xLim > termW => { xLim ¬ termW; newX ¬ xLim - w; xVel ¬ - xVel; }; ENDCASE; <> 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]; }; <> <>PaintScreen[main, PaintRectangleBlack]; <> }; 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]; }; <>PaintScreen[main, DrawRope]; }; }; { <> limit: BasicTime.Pulses ¬ (BasicTime.GetClockPulses[] - mark)*sleepWaitFactor; DO IF ShouldReturn[defaultKeyFilter] THEN GO TO done; Process.Pause[2]; < 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] = { <> 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]; }; END.