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: STREAM ← NIL;
keyboardWatcher: PROCESS;
Exported to SimpleTerminal
TurnOn:
PUBLIC
SAFE
PROC [nameStripe:
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;
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.STREAM ← NIL;
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 CHAR ← ALL['\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: 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;
};
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.