SimpleTerminalTTYImpl.mesa
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Derived from SimpleTTY.mesa last edited by Forrest January 6, 1981 7:28 PM
Levin on January 17, 1984 11:13:07 am PST
McGregor on April 22, 1982 4:23 pm
Birrell, July 7, 1983 3:20 pm
MBrown, September 17, 1983 8:18 pm
Willie-Sue, June 11, 1985 1:17:50 pm PDT
Russ Atkinson (RRA) February 20, 1985 3:38:56 pm PST
Doug Wyatt, April 12, 1985 5:38:43 pm PST
Tim Diebert: September 11, 1986 5:04:39 pm PDT
DIRECTORY
Ascii USING [BS, SP, CR, TAB],
BasicTime USING [FromPupTime, GMT, Now],
IO USING [card, CharsAvail, CreateStream, CreateStreamProcs, GetChar, PutChar, PutFR, PutRope, STREAM, StreamProcs, time],
Process USING [DisableTimeout, Pause, priorityRealTime, SetPriority, SetTimeout, Ticks],
Rope USING [ROPE],
SimpleTerminal USING [],
SystemVersion USING [bootFileDate, release],
Terminal USING [Virtual],
TTY USING [Create];
SimpleTerminalTTYImpl: MONITOR LOCKS m USING m: POINTER TO MONITORLOCK
IMPORTS BasicTime, IO, Process, SystemVersion, TTY
EXPORTS SimpleTerminal
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
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 "terminalLock"
terminalLock: MONITORLOCK;
state: State ← off;
stateChange: CONDITION ← [timeout: 0];
terminal: Terminal.Virtual ← NIL;
turnOnCount: NAT ← 0;
originalTerminal: Terminal.Virtual;
inStream, outStream: STREAMNIL;
keyboardWatcher: PROCESS;
Exported to SimpleTerminal
TurnOn: PUBLIC SAFE PROC [nameStripe: 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;
originalTerminal ← Terminal.Current[];
state ← turningOn;
EXIT
};
turningOff => {
Shutdown was deferred and is still pending. We simply cancel it.
state ← on;
EXIT
};
on => EXIT;
ENDCASE;
WAIT stateChange;
ENDLOOP;
[] ← terminal.Select[];
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[@terminalLock] THEN {
IF nameStripe = NIL THEN
nameStripe ← 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]]
];
IF ttyStream = NIL THEN {ttyStream ← TTY.Create[]; IO.PutChar[ttyStream, '\n]; charPos ← 1};
InitScreen[nameStripe];
keyboardWatcher ← FORK ProcessKeyboard[];
inStream ← IO.CreateStream[streamProcs: inStreamProcs, streamData: NIL];
outStream ← IO.CreateStream[streamProcs: outStreamProcs, streamData: NIL];
TurnOnEntryB[@terminalLock];
};
RETURN [inStream, outStream]
};
ttyStream: IO.STREAMNIL;
TurnOff: PUBLIC SAFE PROC = 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;
};
};
[] ← TurnOffEntry[@terminalLock];
};
InputTimeout: PUBLIC SAFE SIGNAL = CODE;
SetInputTimeout: PUBLIC SAFE PROC [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];
};
EnableCursorTracking: PUBLIC SAFE PROC = TRUSTED {};
DisableCursorTracking: PUBLIC SAFE PROC = TRUSTED {};
Keyboard Implementation
Global variables protected by "keyboardLock"
keyboardLock: MONITORLOCK;
charactersAvailable: CONDITION;
in, out: NAT;
buffer: PACKED ARRAY NAT[0..50) OF CHARALL['\000];
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[@terminalLock] 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;
charPos: NAT;
InitScreen: PROC [nameStripe: ROPE] = {
IO.PutRope[ttyStream, nameStripe]; IO.PutChar[ttyStream, '\n];
};
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;
};
ClearThisChar: INTERNAL PROC = {
IO.PutChar[ttyStream, Ascii.BS]
};
IO Streams implementation
CreateStreams: PROC = {
};
CharsAvail: SAFE PROC [self: STREAM, wait: BOOL] RETURNS [INT] = TRUSTED {
CharsAvailEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] =
INLINE {RETURN[in ~= out]};
RETURN[IF CharsAvailEntry[@keyboardLock] THEN 1 ELSE 0]
};
EndOf: SAFE PROC [self: STREAM] RETURNS [BOOL] = CHECKED {RETURN[FALSE]};
GetChar: SAFE PROC [self: STREAM] RETURNS [c: CHAR ← '\000] = 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 ← NIL;
outStreamProcs: REF IO.StreamProcs ← NIL;
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
];
terminal ← Terminal.Current[];
terminal.RegisterNotifier[DoShutDown];
};
Initialize[@terminalLock];
END.
ChangeLog
WSO, July 30, 1984: catch BasicTime.TimeNotKnown in call to BasicTime.Now.