DIRECTORY Ascii USING [BS, CR, SP, TAB], BasicTime USING [FromPupTime, GMT, Now], File USING [GetVolumeName, SystemVolume, Volume], IO USING [card, CharsAvail, CreateStream, CreateStreamProcs, GetChar, PutChar, PutFR, PutRope, STREAM, StreamProcs, time], Process USING [DisableTimeout, Pause, priorityRealTime, SetPriority, SetTimeout, Ticks], Rope USING [Cat, ROPE], SimpleTerminal USING [], SimpleTerminalBackdoor USING [Impl, ImplRep], SystemVersion USING [bootFileDate, release], ThisMachine USING [Address, Name, ProcessorID], TerminalDefs USING [KeyName, KeyState], TTYStream USING [CreateStream]; SimpleTerminalImpl: MONITOR LOCKS m USING m: POINTER TO MONITORLOCK IMPORTS BasicTime, File, IO, Process, Rope, SystemVersion, ThisMachine, TTYStream EXPORTS SimpleTerminal, SimpleTerminalBackdoor = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Impl: TYPE = SimpleTerminalBackdoor.Impl; ImplRep: TYPE = SimpleTerminalBackdoor.ImplRep; State: TYPE = {off, turningOn, on, turningOff, joining}; simpleTerminalLock: MONITORLOCK; curImpl: Impl; state: State _ off; stateChange: CONDITION _ [timeout: 0]; turnOnCount: NAT _ 0; inStream, outStream: STREAM _ NIL; SetImpl: PUBLIC SAFE PROC [impl: Impl] RETURNS [ok: BOOL] ~ TRUSTED { SetImplEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] ~ { SELECT state FROM off => IF (ok _ curImpl=defaultImpl) THEN curImpl _ impl; turningOn, on, turningOff, joining => ok _ FALSE; ENDCASE => ERROR; }; SetImplEntry[@simpleTerminalLock]; }; UnsetImpl: PUBLIC SAFE PROC [impl: Impl] ~ TRUSTED { UnsetImplEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] ~ { UNTIL state = off DO WAIT stateChange ENDLOOP; IF curImpl # impl THEN RETURN WITH ERROR CallerError[]; curImpl _ defaultImpl; }; UnsetImplEntry[@simpleTerminalLock]; }; CallerError: ERROR = CODE; TurnOn: PUBLIC SAFE PROC [nameStripe: ROPE _ NIL] RETURNS [in, out: STREAM] = CHECKED {[in, out] _ FullTurnOn[nameStripe, NIL]}; FullTurnOn: PUBLIC SAFE PROC [bannerLeft, bannerRight: ROPE _ NIL] RETURNS [in, out: STREAM] = TRUSTED { TurnOnEntryA: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [turnOnNeeded: BOOL] = -- INLINE -- { oldState: State = state; DO SELECT state FROM off => { IF turnOnCount ~= 0 THEN ERROR; curImpl.CaptureOriginalState[curImpl]; state _ turningOn; EXIT }; turningOff => { state _ on; EXIT }; on => EXIT; ENDCASE; WAIT stateChange; ENDLOOP; curImpl.ReStart[curImpl]; turnOnCount _ turnOnCount.SUCC; IF oldState ~= state THEN BROADCAST stateChange; RETURN[state ~= on] }; TurnOnEntryB: ENTRY PROC [m: POINTER TO MONITORLOCK] = -- INLINE -- { state _ on; BROADCAST stateChange; }; IF TurnOnEntryA[@simpleTerminalLock] THEN { IF bannerLeft = NIL THEN bannerLeft _ DefaultBannerLeft[]; IF bannerRight = NIL THEN bannerRight _ DefaultBannerRight[]; [inStream, outStream] _ curImpl.Start[curImpl, bannerLeft, bannerRight]; TurnOnEntryB[@simpleTerminalLock]; }; RETURN [inStream, outStream] }; DefaultBannerLeft: PUBLIC SAFE PROC RETURNS [ROPE] ~ CHECKED { RETURN [IO.PutFR["Cedar %g.%g.%g of %t", IO.card[SystemVersion.release.major], IO.card[SystemVersion.release.minor], IO.card[SystemVersion.release.patch], IO.time[BasicTime.FromPupTime[SystemVersion.bootFileDate]] ]]; }; DefaultBannerRight: PUBLIC SAFE PROC RETURNS [ROPE] ~ CHECKED { systemVolume: File.Volume = File.SystemVolume[]; volumeLabel: ROPE _ IF systemVolume = NIL THEN "No System Volume" ELSE File.GetVolumeName[systemVolume]; name: ROPE; name _ Rope.Cat[name, ThisMachine.Name[]]; name _ Rope.Cat[name, " (", ThisMachine.Address[]]; name _ Rope.Cat[name, ", ", ThisMachine.ProcessorID[$ProductSoftware], ")"]; RETURN [volumeLabel.Cat[" on ", name]]; }; TurnOff: PUBLIC SAFE PROC = TRUSTED { ShutdownNeeded: SAFE PROC RETURNS [BOOL] ~ TRUSTED { TurnOffEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [shutdown: BOOL] = -- INLINE -- { DO SELECT state FROM off => RETURN[FALSE]; on => EXIT; ENDCASE => WAIT stateChange; ENDLOOP; IF (shutdown _ ((turnOnCount _ turnOnCount.PRED) = 0)) THEN { state _ turningOff; BROADCAST stateChange; }; }; RETURN [TurnOffEntry[@simpleTerminalLock]]}; curImpl.TurnOff[curImpl, ShutdownNeeded]; }; StartShutdown: PUBLIC SAFE PROC RETURNS [needed: BOOL] ~ TRUSTED { DoShutDownEntryA: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [needed: BOOL] = INLINE { IF (needed _ state = turningOff) THEN { state _ joining; BROADCAST stateChange; }; }; needed _ DoShutDownEntryA[@simpleTerminalLock]; }; ShuttingDown: PUBLIC SAFE PROC RETURNS [BOOL] ~ TRUSTED { GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE { RETURN[state = joining]}; RETURN [GoAway[@simpleTerminalLock]]; }; FinishShutdown: PUBLIC SAFE PROC ~ TRUSTED { DoShutDownEntryB: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { state _ off; BROADCAST stateChange; }; inStream _ outStream _ NIL; DoShutDownEntryB[@simpleTerminalLock]; }; InputTimeout: PUBLIC SAFE SIGNAL = CODE; SetInputTimeout: PUBLIC SAFE PROC [ticks: Process.Ticks] = CHECKED { curImpl.SetInputTimeout[curImpl, ticks]; }; EnableCursorTracking: PUBLIC SAFE PROC = TRUSTED { curImpl.EnableCursorTracking[curImpl]; }; DisableCursorTracking: PUBLIC SAFE PROC = TRUSTED { curImpl.DisableCursorTracking[curImpl]; }; keyboardWatcher: PROCESS; ttyStream: IO.STREAM _ NIL; charPos: NAT; defaultImpl: PUBLIC Impl _ NEW [ImplRep _ [ CaptureOriginalState: CaptureOriginalTerminalState, Start: TerminalStart, ReStart: TerminalReStart, TurnOff: TerminalTurnOff, SetInputTimeout: SetTerminalInputTimeout, DisableCursorTracking: DisableTerminalCursorTracking, EnableCursorTracking: EnableTerminalCursorTracking, data: NIL]]; CaptureOriginalTerminalState: SAFE PROC [impl: Impl] ~ CHECKED { }; TerminalStart: SAFE PROC [impl: Impl, bannerLeft, bannerRight: ROPE] RETURNS [in, out: STREAM] ~ TRUSTED { IF ttyStream = NIL THEN {ttyStream _ TTYStream.CreateStream[]; IO.PutRope[ttyStream, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"]; charPos _ 1}; InitScreen[bannerLeft, bannerRight]; keyboardWatcher _ FORK ProcessKeyboard[]; in _ IO.CreateStream[inStreamProcs, NIL]; out _ IO.CreateStream[outStreamProcs, NIL]; }; TerminalReStart: SAFE PROC [impl: Impl] ~ CHECKED { }; TerminalTurnOff: SAFE PROC [impl: Impl, ShutdownNeeded: SAFE PROC RETURNS [BOOL]] ~ CHECKED { }; SetTerminalInputTimeout: SAFE PROC [impl: Impl, ticks: Process.Ticks] ~ TRUSTED { SetInputTimeoutEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { IF ticks = 0 THEN Process.DisableTimeout[@charactersAvailable] ELSE Process.SetTimeout[@charactersAvailable, ticks]; }; SetInputTimeoutEntry[@keyboardLock]; }; DisableTerminalCursorTracking: SAFE PROC [impl: Impl] ~ TRUSTED {}; EnableTerminalCursorTracking: SAFE PROC [impl: Impl] ~ TRUSTED {}; KeyState: TYPE = TerminalDefs.KeyState; KeyName: TYPE ~ TerminalDefs.KeyName; KeyItem: TYPE ~ RECORD[normal, shift: CHAR] _ [0C, 0C]; keyboardLock: MONITORLOCK; charactersAvailable: CONDITION; in, out: NAT; buffer: PACKED ARRAY NAT[0..50) OF CHAR _ ALL[' ]; ProcessKeyboard: PROC = { GoAway: ENTRY PROC[m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE { RETURN[state = joining]; }; AddToBuffer: ENTRY PROC[m: POINTER TO MONITORLOCK, c: CHAR] = INLINE { newin: NAT _ in + 1; IF newin = buffer.LENGTH THEN newin _ 0; IF newin ~= out THEN {buffer[in] _ c; in _ newin}; }; NotifyCharsAvailable: ENTRY PROC[m: POINTER TO MONITORLOCK] = INLINE { BROADCAST charactersAvailable; }; in _ out _ 0; Process.SetPriority[Process.priorityRealTime]; UNTIL GoAway[@simpleTerminalLock] DO charsSeen: BOOL _ FALSE; numChars: INT _ 0; Process.Pause[1]; -- let the other guys run. numChars _ IO.CharsAvail[ttyStream]; IF numChars # 0 THEN BEGIN char: CHAR _ IO.GetChar[ttyStream]; AddToBuffer[@keyboardLock, char]; NotifyCharsAvailable[@keyboardLock]; END; ENDLOOP; }; screenLock: MONITORLOCK; noTrack: CARDINAL _ 0; InitScreen: PROC [bannerLeft, bannerRight: ROPE] = { IO.PutRope[ttyStream, bannerLeft]; IO.PutChar[ttyStream, '\n]; IO.PutChar[ttyStream, '\n]; IO.PutRope[ttyStream, bannerRight]; IO.PutChar[ttyStream, '\n]; }; ClearThisChar: INTERNAL PROC = {}; DisplayChar: INTERNAL PROC [c: CHAR] = { SELECT c FROM IN [Ascii.SP..'~] => {IO.PutChar[ttyStream, c]; charPos _ charPos + 1}; Ascii.CR => {IO.PutChar[ttyStream, c]; charPos _ 0}; Ascii.BS => {IO.PutChar[ttyStream, c]; charPos _ charPos - 1; IF charPos < 1 THEN charPos _ 1}; Ascii.TAB => {IO.PutChar[ttyStream, c]; charPos _ charPos + 1; UNTIL charPos MOD 8 = 0 DO IO.PutChar[ttyStream, ' ]; charPos _ charPos + 1; ENDLOOP; RETURN}; IN [0C..Ascii.SP) => {IO.PutChar[ttyStream, c]}; ENDCASE => RETURN; }; CreateStreams: PROC = { }; CharsAvail: SAFE PROC [self: STREAM, wait: BOOL] RETURNS [n: INT] = TRUSTED { CharsAvailEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = { WHILE wait AND in = out DO WAIT charactersAvailable ENDLOOP; n _ in - out; IF n < 0 THEN n _ n + buffer.LENGTH; }; CharsAvailEntry[@keyboardLock]; }; EndOf: SAFE PROC [self: STREAM] RETURNS [BOOL] = CHECKED {RETURN[FALSE]}; GetChar: SAFE PROC [self: STREAM] RETURNS [c: CHAR] = TRUSTED { GetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE { ENABLE UNWIND => NULL; WHILE in = out DO WAIT charactersAvailable; IF in = out THEN RETURN[FALSE]; ENDLOOP; c _ buffer[out]; IF (out _ out + 1) = buffer.LENGTH THEN out _ 0; RETURN[TRUE] }; UNTIL GetEntry[@keyboardLock] DO SIGNAL InputTimeout; ENDLOOP; }; PutChar: SAFE PROC [self: STREAM, char: CHAR] = TRUSTED { PutCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { DisplayChar[char]; }; PutCharEntry[@screenLock]; }; EraseChar: SAFE PROC [self: STREAM, char: CHAR] = TRUSTED { EraseCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { ClearThisChar[]; -- to get rid of possible blinker DisplayChar[Ascii.BS]; }; EraseCharEntry[@screenLock]; }; inStreamProcs: REF IO.StreamProcs; outStreamProcs: REF IO.StreamProcs; Initialize: ENTRY PROC [m: POINTER TO MONITORLOCK] = { inStreamProcs _ IO.CreateStreamProcs[ variety: $input, class: $SimpleTerminalTTY, getChar: GetChar, endOf: EndOf, charsAvail: CharsAvail ]; outStreamProcs _ IO.CreateStreamProcs[ variety: $output, class: $SimpleTerminalTTY, putChar: PutChar, eraseChar: EraseChar ]; curImpl _ defaultImpl; }; Initialize[@simpleTerminalLock]; END. ChangeLog WSO, July 30, 1984: catch BasicTime.TimeNotKnown in call to BasicTime.Now. τSimpleTerminalImpl.mesa Copyright Σ 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) February 20, 1985 3:38:56 pm PST Willie-Sue, June 11, 1985 1:17:50 pm PDT Doug Wyatt, January 17, 1987 4:50:01 pm PST Mike Spreitzer March 4, 1987 12:05:10 pm PST Tim Diebert: March 5, 1987 9:20:32 am PST Constraints to satisfy externally: A statevector is needed at priority 6 for the keyboard watching process, at least until facilities exist to let it allocate one itself. If keyboard watcher is to avoid faults, its code and global frame should be pinned. Global variables protected by "simpleTerminalLock" Exported to SimpleTerminal and SimpleTerminalBackdoor Shutdown was deferred and is still pending. We simply cancel it. Default Implementation [] _ terminal.Select[]; Keyboard Definitions Keyboard Implementation Global variables protected by "keyboardLock" Display Implementation Global variables protected by "screenLock" IO Streams implementation Initialization ΚΞ˜codešœ™KšœN™NJ™4K™(K™+K™,K™)—K˜šΟk ˜ Kš œœœœœœ˜Kšœ œœ˜(Kšœœ'˜1KšœœWœ˜zKšœœK˜XKšœœœ˜Kšœœ˜Kšœœ˜-Kšœœ˜,Kšœ œ˜/Kšœ œ˜'Kšœ œ˜—K˜Kš Πblœ œœœœ ˜CKšœœ6˜QKšœ'˜.Kšœ˜˜Kšœœœ˜Kšœœ œ˜Kšœœ˜)Kšœ œ"˜/—˜šœ"™"Kšœ‡™‡K™S—K˜Kšœœ-˜8K˜Kšœ2™2K˜Kšœ œ˜ K˜K˜Kšœ˜Kšœ  œ˜&Kšœ œ˜Kšœœœ˜"K˜K™Kšœ5™5K˜šΟnœœœœœœœ˜Eš Ÿ œœœœœ œ˜8šœ˜Kšœœœ˜9Kšœ+œ˜1Kšœœ˜—K˜—Kšœ"˜"K˜—K˜š Ÿ œœœœœ˜4š Ÿœœœœœ œ˜:Kšœ œœ œ˜.Kš œœœœœ˜7Kšœ˜K˜—Kšœ$˜$K˜KšŸ œœœ˜—K˜KšŸœœœœœœœ œœ%œ˜€K˜šŸ œœœœœœœ œœ˜hš Ÿ œœœœœ œ˜4KšœœΟc œ˜-K˜š˜šœ˜šœ˜Kšœœœ˜Kšœ&˜&Kšœ˜Kš˜K˜—šœ˜KšœA™AK˜ Kš˜Kšœ˜—Kšœœ˜ Kšœ˜—Kšœ ˜Kšœ˜—Kšœ˜Kšœœ˜Kšœœ œ ˜0Kšœ ˜Kšœ˜—šŸ œœœœœ œ  œ˜EKšœ ˜ Kš œ ˜Kšœ˜—šœ#œ˜+Kšœœœ"˜:Kšœœœ$˜=KšœH˜HKšœ"˜"K˜—Kšœ˜Kšœ˜—K˜šŸœœœœœœœ˜>šœœ˜(Kšœ#˜%Kšœ#˜%Kšœ#˜%Kšœ8˜:K˜—K˜—K˜šŸœœœœœœœ˜?Kšœ0˜0šœ œ˜Kšœœœ˜-Kšœ"˜&—Kšœœ˜ Kšœ*˜*Kšœ3˜3KšœL˜LKšœ!˜'K˜—K˜š Ÿœœœœœ˜%š Ÿœœœœœœ˜4š Ÿ œœœœœ œ˜4Kšœ œ  œ˜)š˜šœ˜Kšœœœ˜Kšœœ˜ Kšœœ ˜—Kšœ˜—šœ)œœ˜=Kšœ˜Kš œ ˜Kšœ˜—K˜—Kšœ&˜,—Kšœ)˜)K˜K˜—šŸ œœœœœ œœ˜Bš Ÿœœœœœ œ˜8Kšœ œœ˜!šœœ˜'Kšœ˜Kš œ ˜K˜—Kšœ˜—Kšœ/˜/K˜—K˜šŸ œœœœœœœ˜9šŸœœœœœ œœœœ˜KKšœ˜—Kšœ˜%K˜—K˜š Ÿœœœœœ˜,šŸœœœœœ œœ˜CKšœ ˜ Kš œ ˜Kšœ˜—Kšœœ˜Kšœ&˜&K˜—K˜K˜Kšœœ œœ˜(K˜š Ÿœœœœœ˜DKšœ(˜(K˜K˜—š Ÿœœœœœ˜2Kšœ&˜&K˜K˜—š Ÿœœœœœ˜3Kšœ'˜'K˜K˜—K™K™K˜Kšœœ˜K˜Kšœ œœœ˜Kšœ œ˜ K˜šœ œœ ˜+KšŸœ˜3KšŸœ˜KšŸœ˜KšŸœ˜KšŸœ˜)KšŸœ ˜5KšŸœ˜3Kšœœ˜ —K˜šŸœœœœ˜@K˜—K˜šŸ œœœ'œœ œœ˜jšœ œœ'˜>KšœJ˜LKšœ ˜ —Kšœ$˜$Kšœœ˜)Kšœœœ˜)Kšœœœ˜+K˜—K˜šŸœœœœ˜3Kšœ™K˜—K˜KšŸœœœŸœœœœœœ˜`K˜K˜šŸœœœ&œ˜QšŸœœœœœ œœ˜GKšœ œ-˜>Kšœ1˜5Kšœ˜—K˜$K˜—K˜KšŸœœœœ˜CK˜KšŸœœœœ˜BK˜Kšœ™K˜Kšœ œ˜'Kšœ œ˜%šœ œœœ ˜7K˜—K˜K™Kšœ™K˜K™Kšœ,™,K˜Kšœ œ˜K˜Kšœ œ˜K˜Kšœ œ˜ Kš œœœœœœœ˜2K˜šŸœœ˜šŸœœœœœ œœœœ˜GKšœ˜Kšœ˜—šŸ œœœœœ œœœ˜FKšœœ˜Kšœœœ ˜(Kšœœ˜2Kšœ˜—šŸœœœœœ œœ˜FKš œ˜Kšœ˜—K˜ K˜.šœ˜$Kšœ œœ˜Kšœ œ˜Kšœ ˜,Kšœ œ˜$šœœ˜Kšœœœ˜#Kšœ!˜!Kšœ$˜$Kšœ˜—Kšœ˜—Kšœ˜—K™K™K˜K˜šœ*™*K˜Kšœ  œ˜K˜Kšœ œ˜—K˜šŸ œœœ˜4Jšœ!œœ˜ZJšœ"œ˜?Kšœ˜K˜—šŸ œœœ˜"K˜—šŸ œœœœ˜(šœ˜ Kšœœ œ/˜GKšœœœ%˜4šœœœ.˜=Kšœ œ˜!—šœœœ-˜>Kš œ œœœ0œ˜UKšœ˜—Kšœ œœ˜0Kšœœ˜—Kšœ˜—K™K™K˜šŸ œœ˜K˜—K˜šŸ œœœœœœœœ˜Mš Ÿœœœœœ œ˜;Kš œœ œœœ˜Kšœ˜K˜—š Ÿœœœœœœ˜9šŸ œœœœœ œœ˜?Kšœ˜Kšœ˜—K˜Kšœ˜K˜—š Ÿ œœœœœœ˜;šŸœœœœœ œœ˜AKšœ !˜3Kšœœ˜Kšœ˜—K˜Kšœ˜K˜—K˜™K™Kšœœœ ˜"Kšœœœ ˜#K˜š Ÿ œœœœœ œ˜6šœœ˜%Kšœ+˜+Kšœ˜K˜ K˜K˜—šœœ˜&Kšœ,˜,K˜K˜K˜—K˜Kšœ˜K˜—K˜ —K™—Kšœ˜K˜K˜ K˜K˜KK˜K˜—…—(V=