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
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;
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.
State: TYPE = {off, turningOn, on, turningOff, joining};
Global variables protected by "simpleTerminalLock"
simpleTerminalLock: MONITORLOCK;
curImpl: Impl;
state: State ← off;
stateChange: CONDITION ← [timeout: 0];
turnOnCount: NAT ← 0;
inStream, outStream: STREAMNIL;
Exported to SimpleTerminal and SimpleTerminalBackdoor
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: ROPENIL] RETURNS [in, out: STREAM] = CHECKED {[in, out] ← FullTurnOn[nameStripe, NIL]};
FullTurnOn: PUBLIC SAFE PROC [bannerLeft, bannerRight: ROPENIL] 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 => {
Shutdown was deferred and is still pending. We simply cancel it.
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];
};
Default Implementation
keyboardWatcher: PROCESS;
ttyStream: IO.STREAMNIL;
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 {};
Keyboard Definitions
KeyState: TYPE = TerminalDefs.KeyState;
KeyName: TYPE ~ TerminalDefs.KeyName;
KeyItem: TYPE ~ RECORD[normal, shift: CHAR] ← [0C, 0C];
Keyboard Implementation
Global variables protected by "keyboardLock"
keyboardLock: MONITORLOCK;
charactersAvailable: CONDITION;
in, out: NAT;
buffer: PACKED ARRAY NAT[0..50) OF CHARALL[' ];
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: BOOLFALSE;
numChars: INT ← 0;
Process.Pause[1]; -- let the other guys run.
numChars ← IO.CharsAvail[ttyStream];
IF numChars # 0 THEN BEGIN
char: CHARIO.GetChar[ttyStream];
AddToBuffer[@keyboardLock, char];
NotifyCharsAvailable[@keyboardLock];
END;
ENDLOOP;
};
Display Implementation
Global variables protected by "screenLock"
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;
};
IO Streams implementation
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];
};
Initialization
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.