<> <> <> <> <> <> <> 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 { <<[] _ terminal.Select[];>> }; 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.