-- SpecialTerminalImpl.mesa
-- Derived from SimpleTTY.mesa last edited by Forrest January 6, 1981 7:28 PM
-- last edited by Levin on November 12, 1982 5:26 pm
-- last edited by McGregor on April 22, 1982 4:23 pm
DIRECTORY
Ascii USING [
BS, ControlA, ControlR, ControlQ, ControlV, ControlW, ControlX, CR, DEL,
ESC, FF, SP, TAB],
BitBlt USING [AlignedBBTable, BBptr, BBTableSpace, BITBLT],
Environment USING [BitAddress],
Format USING [
Date, Decimal, LongDecimal, LongNumber, LongOctal, LongSubStringItem,
Number, Octal, SubString],
Inline USING [BITAND],
Process USING [DisableTimeout, Pause, SecondsToTicks, SetPriority, SetTimeout],
Runtime USING [GetTableBase],
SpecialSpace USING [
MakeGlobalFrameResident, --MakeGlobalFrameSwappable,--
MakeProcedureResident, MakeProcedureSwappable],
SpecialTerminal USING [GetProc, PutProc],
Streams USING [Destroy, Handle, NewStream, PutChar, Write],
String USING [
AppendChar, AppendLongNumber, AppendNumber,
StringToLongNumber, StringToNumber, SubString],
System USING [GetGreenwichMeanTime, gmtEpoch, GreenwichMeanTime],
TerminalMultiplex USING [CurrentTerminal, PermitSwaps, PreventSwaps, SelectTerminal, Terminal],
Time USING [Append, Packed, Unpack],
TTY USING [DateFormat, Handle, LongSubString, NumberFormat],
UserTerminal USING [
Coordinate, cursor, GetBitBltTable, keyboard, mouse, screenHeight,
screenWidth, SetMousePosition, SetState, SetBackground,
WaitForScanLine],
Volume USING [GetLabelString, maxNameLength, systemID];
SpecialTerminalImpl: MONITOR
LOCKS m USING m: POINTER TO MONITORLOCK
IMPORTS
BitBlt, Format, Inline, Process, Runtime, SpecialSpace,
Streams, String, System, TerminalMultiplex, Time, UserTerminal, Volume
EXPORTS TTY, SpecialTerminal =
BEGIN OPEN Ascii, BitBlt;
screenLock: MONITORLOCK;
-- These variables are protected by "screenLock"
typescript: Streams.Handle;
typescriptEnabled: BOOLEAN ← FALSE;
noTrack: CARDINAL ← 0;
keyboardLock: MONITORLOCK;
-- These variables are protected by "keyboardLock"
state: {off, turningOn, on, turningOff} ← off;
stateChange: CONDITION ← [timeout: 0];
terminalLock: MONITORLOCK;
-- These variables are protected by "terminalLock"
originalTerminal: TerminalMultiplex.Terminal;
originalLockCount: CARDINAL;
keyboardWatcher, timePainter: PROCESS;
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Exports to SpecialTerminal
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
InputTimeout: PUBLIC SIGNAL = CODE;
SetInputTimeout: PUBLIC PROC [ticks: CARDINAL] = {
SetInputTimeoutInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
IF ticks = 0 THEN Process.DisableTimeout[@charactersAvailable]
ELSE Process.SetTimeout[@charactersAvailable, ticks]};
SetInputTimeoutInternal[@keyboardLock]};
TurnOn: PUBLIC PROC RETURNS [SpecialTerminal.GetProc, SpecialTerminal.PutProc] = {
EnsureSpecialVirtualTerminalInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
OPEN TerminalMultiplex;
terminal: TerminalMultiplex.Terminal;
lockCount: CARDINAL;
[terminal, lockCount] ← CurrentTerminal[];
IF terminal ~= special THEN {
originalTerminal ← terminal; originalLockCount ← lockCount;
THROUGH [0..lockCount) DO PermitSwaps[] ENDLOOP;
IF ~SelectTerminal[terminal: special, lock: TRUE] THEN ERROR}
ELSE PreventSwaps[]
};
EnsureSpecialVirtualTerminalInternal[@terminalLock];
RETURN [getChar, putChar]
};
getChar: PROC RETURNS [CHAR] = {RETURN[GetChar[tty]]};
putChar: PROC [c: CHAR] = {PutChar[tty, c]};
TurnOff: PUBLIC PROC = {
EnsureOriginalVirtualTerminalInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
OPEN TerminalMultiplex;
PermitSwaps[];
IF CurrentTerminal[].lockCount = 0 THEN {
IF ~SelectTerminal[terminal: originalTerminal, lock: originalLockCount > 0] THEN ERROR;
THROUGH [1..originalLockCount) DO PreventSwaps[] ENDLOOP}
};
EnsureOriginalVirtualTerminalInternal[@terminalLock];
};
EnableCursorTracking: PUBLIC PROC = {
EnableInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
IF (noTrack ← noTrack - 1) = 0 THEN doBlink ← TRUE};
EnableInternal[@screenLock]};
DisableCursorTracking: PUBLIC PROC = {
DisableInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
IF (noTrack ← noTrack + 1) ~= 0 THEN doBlink ← FALSE};
DisableInternal[@screenLock]};
EnableTypescriptFile: PUBLIC PROC = {
EnableTypescriptInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
IF typescriptEnabled THEN RETURN;
typescript ← Streams.NewStream["Init.log"L, Streams.Write];
typescriptEnabled ← TRUE};
EnableTypescriptInternal[@screenLock]};
DisableTypescriptFile: PUBLIC PROC = {
DisableTypescriptInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
IF ~typescriptEnabled THEN RETURN;
Streams.Destroy[typescript];
typescriptEnabled ← FALSE};
DisableTypescriptInternal[@screenLock]};
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Stuff for special clients
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tty: PUBLIC TTY.Handle ← LOOPHOLE[123456B]; -- value is never used
TerminalOn: PUBLIC PROC RETURNS [BOOLEAN] = {
TerminalOnInternal: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE {
ENABLE UNWIND => NULL;
WHILE state ~= off AND state ~= on DO WAIT stateChange ENDLOOP;
RETURN [state = on]};
RETURN[TerminalOnInternal[@keyboardLock]]
};
TurnOnInternal: PUBLIC PROC [nameStripe: LONG STRING] = {
TurnOnInternalA: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
ENABLE UNWIND => NULL;
WHILE state ~= off DO WAIT stateChange ENDLOOP;
state ← turningOn; BROADCAST stateChange};
TurnOnInternalB: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
state ← on; BROADCAST stateChange};
IF ~font.newStyle OR font.indexed OR ~(font.min IN [0C..177C])
OR ~(font.max+1 IN [0C..177C]) THEN ERROR;
TurnOnInternalA[@keyboardLock];
SpecialSpace.MakeProcedureResident[ProcessKeyboard];
SpecialSpace.MakeGlobalFrameResident[SpecialTerminalImpl];
CDT ← FALSE;
echo ← TRUE;
in ← out ← 0;
InitScreen[nameStripe];
keyboardWatcher ← FORK ProcessKeyboard[];
TurnOnInternalB[@keyboardLock];
};
TurnOffInternal: PUBLIC PROC = {
TurnOffInternalA: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
ENABLE UNWIND => NULL;
WHILE state ~= on DO WAIT stateChange ENDLOOP;
state ← turningOff; BROADCAST stateChange};
TurnOffInternalB: ENTRY PROC [m: POINTER TO MONITORLOCK] = {
state ← off; BROADCAST stateChange};
TurnOffInternalA[@keyboardLock];
SpecialSpace.MakeProcedureSwappable[ProcessKeyboard];
-- Pilot doesn't do this right: SpecialSpace.MakeGlobalFrameSwappable[SpecialTTY];
[] ← UserTerminal.SetState[disconnected];
JOIN keyboardWatcher;
JOIN timePainter;
TurnOffInternalB[@keyboardLock];};
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- FONT Definitions and variables
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
font: LONG POINTER TO MACHINE DEPENDENT RECORD [
newStyle(0:0..0): BOOLEAN,
indexed(0:1..1): BOOLEAN,
fixed(0:2..2): BOOLEAN,
kerned(0:3..3): BOOLEAN,
pad(0:4..15): [0..7777B],
min(1): CHARACTER, -- limits of chars in font
max(2): CHARACTER, -- limits of chars in font
maxwidth(3): CARDINAL,
length(4): CARDINAL,
ascent(5): CARDINAL,
descent(6): CARDINAL,
xoffset(7): CARDINAL,
raster(8): CARDINAL,
chars(9:0..63): SELECT OVERLAID * FROM
hasBoundingBox => [
boundingBox(9:0..63): RECORD [FontBBox, FontBBoy, FontBBdx, FontBBDy: INTEGER],
BBBitmap(13): ARRAY [0..0) OF WORD],
noBoundingBox => [bitmap(9): ARRAY [0..0) OF WORD],
ENDCASE] = GetFont[];
bitmap: LONG POINTER = IF font.kerned THEN @font.BBBitmap ELSE @font.bitmap;
xInSegment: LONG POINTER TO ARRAY CHARACTER [0C..0C) OF CARDINAL =
bitmap + font.raster*FontHeight[] - (font.min-0C);
height: INTEGER[0..LAST[INTEGER]] = FontHeight[];
CharWidth: PROC [char: CHARACTER] RETURNS [[0..LAST[INTEGER]]] =
INLINE BEGIN
IF ~(char IN [font.min..font.max]) THEN char ← font.max+1;
RETURN[xInSegment[char+1] - xInSegment[char]]
END;
FontHeight: PROC RETURNS [[0..LAST[INTEGER]]] = INLINE {RETURN[font.ascent+font.descent]};
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Keyboard Definitions and Constants
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
downUp: TYPE = {down, up};
keycount: CARDINAL = 80; -- must be 0 mod 16
Keyarray: TYPE = MACHINE DEPENDENT RECORD [SELECT OVERLAID * FROM
b => [bits: PACKED ARRAY [0..keycount) OF downUp],
wds => [wds: ARRAY [0..keycount/16) OF WORD],
ENDCASE];
KeyItem: TYPE = RECORD [
Letter: BOOLEAN, ShiftCode: CHARACTER [0C..177C], NormalCode: CHARACTER [0C..377C]];
-- Keyboard Info
ctrl: CARDINAL = 52;
leftShift: CARDINAL = 57;
shiftLock: CARDINAL = 72;
rightShift: CARDINAL = 76;
spare3: CARDINAL = 77;
KeyTable: ARRAY [16..keycount) OF KeyItem = [
-- Index [0..15] mouse, etc
-- Index [16..31]
[FALSE, 45C, 65C], -- %,5
[FALSE, 44C, 64C], -- $,4
[FALSE, 176C, 66C], -- ~,6
[TRUE, 105C, 145C], -- E
[FALSE, 46C, 67C], -- &,7
[TRUE, 104C, 144C], -- D
[TRUE, 125C, 165C], -- U
[TRUE, 126C, 166C], -- V
[FALSE, 51C, 60C], -- ),0
[TRUE, 113C, 153C], -- K
[FALSE, 30C, 55C], -- `,-
[TRUE, 120C, 160C], -- P
[FALSE, 77C, 57C], -- ?,/
[FALSE, 174C, 134C], -- |,\
[FALSE, 12C, 12C], -- LF
[FALSE, 10C, 10C], -- BS
-- Index [32..47]
[FALSE, 43C, 63C], -- #,3
[FALSE, 100C, 62C], -- @,2
[TRUE, 127C, 167C], -- W
[TRUE, 121C, 161C], -- Q
[TRUE, 123C, 163C], -- S
[TRUE, 101C, 141C], -- A
[FALSE, 50C, 71C], -- (,9
[TRUE, 111C, 151C], -- I
[TRUE, 130C, 170C], -- X
[TRUE, 117C, 157C], -- O
[TRUE, 114C, 154C], -- L
[FALSE, 74C, 54C], -- <,,
[FALSE, 42C, 47C], -- ",'
[FALSE, 175C, 135C], --},]
[FALSE, 0C, 0C], -- SPARE2
[FALSE, 0C, 0C], -- SPARE1
-- Index [48..63]
[FALSE, 41C, 61C], -- !,1
[FALSE, 33C, 33C], -- ESCAPE
[FALSE, 11C, 11C], -- TAB
[TRUE, 106C, 146C], -- F
[FALSE, 0C, 0C], -- CONTROL
[TRUE, 103C, 143C], -- C
[TRUE, 112C, 152C], -- J
[TRUE, 102C, 142C], -- B
[TRUE, 132C, 172C], -- Z
[FALSE, 0C, 0C], -- SHIFT
[FALSE, 76C, 56C], -- >,.
[FALSE, 72C, 73C], -- :,;
[FALSE, 15C, 15C], -- CR
[FALSE, 136C, 137C], -- ↑,←
[FALSE, 177C, 177C], -- DEL
[FALSE, 0C, 0C], -- NOT USED (FL3)
-- Index [64..79]
[TRUE, 122C, 162C], -- R
[TRUE, 124C, 164C], -- T
[TRUE, 107C, 147C], -- G
[TRUE, 131C, 171C], -- Y
[TRUE, 110C, 150C], -- H
[FALSE, 52C, 70C], -- *,8
[TRUE, 116C, 156C], -- N
[TRUE, 115C, 155C], -- M
[FALSE, 0C, 0C], -- LOCK
[FALSE, 40C, 40C], -- SPACE
[FALSE, 173C, 133C], -- {,[
[FALSE, 53C, 75C], -- +,=
[FALSE, 0C, 0C], -- Shift
[FALSE, 0C, 0C], -- Spare3
[FALSE, 0C, 0C], -- not user (FR4)
[FALSE, 0C, 0C]]; -- not user (FR5)
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Keyboard Implementation
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- These variables are protected by "keyboardLock"
CDT: BOOLEAN;
charactersAvailable: CONDITION;
echo: BOOLEAN;
in, out: CARDINAL;
buffer: PACKED ARRAY [0..50) OF CHARACTER;
-- EXTERNAL PROCS
CtrlChar: PROC [c: CHARACTER] RETURNS [CHARACTER] = INLINE {RETURN[LOOPHOLE[Inline.BITAND[c, 37B]]]};
-- ENTRY PROCS
ProcessKeyboard: PROC =
BEGIN
old, new: Keyarray;
kp: LONG POINTER TO Keyarray = LOOPHOLE[UserTerminal.keyboard];
blinkCount: CARDINAL ← 33;
GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE {
RETURN[state = turningOff]};
Process.SetPriority[6];
new ← kp↑;
UNTIL GoAway[@keyboardLock] DO
Brdcst: ENTRY PROC [m: POINTER TO MONITORLOCK] =
INLINE {BROADCAST charactersAvailable};
charsSeen: BOOLEAN ← FALSE;
old ← new;
UserTerminal.WaitForScanLine[0];
new ← kp↑;
TrackCursor[@screenLock];
IF (blinkCount←blinkCount-1) = 0 THEN {BlinkCursor[]; blinkCount ← 34};
FOR i: CARDINAL IN [1..keycount/16) DO
IF old.wds[i]#new.wds[i] THEN
FOR j: CARDINAL IN [i*16..(i+1)*16) DO
char: CHARACTER;
entry: KeyItem;
IF new.bits[j]=up OR old.bits[j]=down THEN LOOP;
IF (char ← (entry←KeyTable[j]).NormalCode)#0C THEN
BEGIN
SELECT TRUE FROM
new.bits[ctrl]=down =>
IF char=177C THEN {CDT ← TRUE; LOOP} ELSE char ← CtrlChar[char];
new.bits[leftShift]=down, new.bits[rightShift]=down =>
char ← entry.ShiftCode;
new.bits[shiftLock]=down AND entry.Letter =>
char ← entry.ShiftCode;
ENDCASE;
StuffBuffer[char, @keyboardLock];
charsSeen ← TRUE
END;
ENDLOOP
ENDLOOP;
IF charsSeen THEN Brdcst[@keyboardLock];
ENDLOOP
END;
TrackCursor: ENTRY PROC [m: POINTER TO MONITORLOCK] =
INLINE BEGIN
mouse: UserTerminal.Coordinate ← UserTerminal.mouse↑;
IF noTrack > 0 THEN RETURN;
mouse.x ← MIN[MAX[0, mouse.x], UserTerminal.screenWidth];
mouse.y ← MIN[MAX[0, mouse.y], UserTerminal.screenHeight];
UserTerminal.cursor↑ ← mouse;
UserTerminal.SetMousePosition[mouse]
END;
StuffBuffer: ENTRY PROC [c: CHARACTER, m: POINTER TO MONITORLOCK] =
INLINE BEGIN
newin: CARDINAL;
IF (newin←in+1) = LENGTH[buffer] THEN newin ← 0;
IF newin#out THEN {buffer[in] ← c; in ← newin};
END;
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- DISPLAY
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- These variables are protected by "screenLock"
BBTable: BBTableSpace;
bbPtr: BBptr = AlignedBBTable[@BBTable];
charPos, line, nCharPos, nLines: CARDINAL;
usableHeight, usableWidth: CARDINAL;
firstLine, thisLine: Environment.BitAddress;
bitsPerTextLine: CARDINAL; -- = screenWidth*font.height
doBlink: BOOLEAN ← FALSE;
nameStripeOrg: Environment.BitAddress;
nameStripeWidth: CARDINAL;
lastTime: System.GreenwichMeanTime;
GetBitAddress: PROC [p: LONG POINTER, o: LONG CARDINAL]
RETURNS [Environment.BitAddress] =
{RETURN[[p+o/16, 0, o MOD 16]]};
BlinkCursor: PROC =
BEGIN
BlinkCursorEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] =
BEGIN
blinker: CARDINAL ← 60000B;
IF doBlink THEN
BEGIN
bbPtr.src ← [@blinker, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE, dstFunc: xor];
BITBLT[bbPtr];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.flags ← [];
END;
END;
IF doBlink THEN BlinkCursorEntry[@screenLock];
END;
Backup: INTERNAL PROC =
BEGIN
t: CARDINAL = bbPtr.dst.bit+16-font.maxwidth;
IF charPos=0 THEN RETURN;
charPos ← charPos - 1;
bbPtr.dst.word ← bbPtr.dst.word + t/16 - 1;
bbPtr.dst.bit ← t MOD 16;
END;
InitBitmap: PROC =
BEGIN
zero: CARDINAL ← 0;
bbPtr↑ ← UserTerminal.GetBitBltTable[];
-- bbPtr.dst, bbPtr.height, and bbPtr.width are set up
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
BITBLT[bbPtr];
END;
InitScreen: PROC [nameStripe: LONG STRING] =
BEGIN
borderWidth: CARDINAL = 16;
boxWidth: CARDINAL = 1;
marginWidth: CARDINAL = 8;
org: Environment.BitAddress;
InitBitmap: PROC = {
zero: CARDINAL ← 0;
-- bbPtr.dst, bbPtr.height, and bbPtr.width are set up
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
BITBLT[bbPtr]};
MakeBorder: PROC [w: CARDINAL] = {
stipple: ARRAY [0..3] OF CARDINAL ← [104210B,104210B,021042B,021042B];
-- bbPtr.dstBpl never changes
bbPtr.src ← [@stipple, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 3]]];
bbPtr.flags ← [gray: TRUE];
-- top stripe
bbPtr.dst ← org;
bbPtr.width ← usableWidth;
bbPtr.height ← w;
BITBLT[bbPtr];
-- bottom stripe
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*(usableHeight-w)];
BITBLT[bbPtr];
-- left side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w];
bbPtr.width ← w;
bbPtr.height ← usableHeight-w-w;
BITBLT[bbPtr];
-- right side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+usableWidth-w];
BITBLT[bbPtr];
org ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+w];
usableHeight ← usableHeight - w - w;
usableWidth ← usableWidth - w - w;
};
MakeBox: PROC [w: CARDINAL] = {
black: CARDINAL ← 177777B;
bbPtr.src ← [@black, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: w-1]]];
bbPtr.flags ← [gray: TRUE];
-- top stripe
bbPtr.dst ← org;
bbPtr.width ← usableWidth;
bbPtr.height ← w;
BITBLT[bbPtr];
-- bottom stripe
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*(usableHeight-w)];
BITBLT[bbPtr];
-- left side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w];
bbPtr.width ← w;
bbPtr.height ← usableHeight-w-w;
BITBLT[bbPtr];
-- right side
bbPtr.dst ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+usableWidth-w];
BITBLT[bbPtr];
org ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+w];
usableHeight ← usableHeight - w - w;
usableWidth ← usableWidth - w - w;
};
MakeNameStripe: PROC [nameStripe: LONG STRING] = {
black: CARDINAL ← 177777B;
volumeLabel: STRING ← [Volume.maxNameLength];
Volume.GetLabelString[Volume.systemID, volumeLabel];
nameStripeOrg ← org;
nameStripeWidth ← usableWidth;
-- paint version text
bbPtr.dst ← GetBitAddress[org.word, org.bit+marginWidth];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.height ← height;
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [];
FOR i: CARDINAL IN [0..nameStripe.length) DO
bbPtr.src ← GetBitAddress[bitmap, xInSegment[nameStripe[i]]];
BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
ENDLOOP;
-- paint volume name
bbPtr.dst ← GetBitAddress[org.word,
org.bit+usableWidth-marginWidth-volumeLabel.length*font.maxwidth];
FOR i: CARDINAL IN [0..volumeLabel.length) DO
bbPtr.src ← GetBitAddress[bitmap, xInSegment[volumeLabel[i]]];
BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
ENDLOOP;
-- overlay with stripe
bbPtr.src ← [@black, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]];
bbPtr.flags ← [gray: TRUE, dstFunc: xor];
bbPtr.dst ← org;
bbPtr.width ← usableWidth;
bbPtr.height ← height;
BITBLT[bbPtr];
org ← GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*height];
usableHeight ← usableHeight - height;
lastTime ← System.gmtEpoch;
timePainter ← FORK MaintainTime[];
};
[] ← UserTerminal.SetBackground[white];
[] ← UserTerminal.SetState[off]; -- allocate the bitmap, but don't display it yet.
bbPtr↑ ← UserTerminal.GetBitBltTable[];
org ← bbPtr.dst;
usableHeight ← bbPtr.height;
usableWidth ← bbPtr.width;
InitBitmap[];
MakeBorder[borderWidth];
MakeBox[boxWidth];
MakeNameStripe[nameStripe];
-- now display the bitmap
[] ← UserTerminal.SetState[on];
usableHeight ← usableHeight - 2*marginWidth;
usableWidth ← usableWidth - 2*marginWidth;
bitsPerTextLine ← bbPtr.dstBpl*height;
firstLine ←
GetBitAddress[org.word, org.bit+LONG[marginWidth]*bbPtr.dstBpl+marginWidth];
nCharPos ← usableWidth/font.maxwidth;
nLines ← usableHeight/height;
InitPainting[];
END;
MaintainTime: PROC = {
GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE {
RETURN[state = turningOff]};
BBTable: BBTableSpace;
bbPtr: BBptr = AlignedBBTable[@BBTable];
secondsOrg: Environment.BitAddress;
date: STRING ← [25];
black: CARDINAL ← 177777B;
SetToBlacken: PROC = {
bbPtr.src ← [@black, 0, 0];
bbPtr.srcDesc ← [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]];
bbPtr.flags ← [gray: TRUE]};
PaintTime: PROC = {
current: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
IF lastTime/60 = current/60 THEN {
seconds: [0..59] ← current MOD 60;
SetToBlacken[];
bbPtr.dst ← secondsOrg;
bbPtr.width ← 2*font.maxwidth;
bbPtr.height ← height;
BITBLT[bbPtr];
-- bbPtr.dst & bbPtr.height still OK
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [srcFunc: complement];
bbPtr.src ← GetBitAddress[bitmap, xInSegment['0+seconds/10]];
BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
bbPtr.src ← GetBitAddress[bitmap, xInSegment['0+seconds MOD 10]];
BITBLT[bbPtr]}
ELSE {
date.length ← 0;
Time.Append[date, Time.Unpack[current]];
SetToBlacken[];
bbPtr.dst ← GetBitAddress[nameStripeOrg.word,
nameStripeOrg.bit+(nameStripeWidth-date.length*font.maxwidth)/2];
secondsOrg ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+(date.length-2)*font.maxwidth];
bbPtr.width ← date.length*font.maxwidth;
bbPtr.height ← height;
BITBLT[bbPtr];
-- bbPtr.dst & bbPtr.height still OK
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [srcFunc: complement];
FOR i: CARDINAL IN [0..date.length) DO
bbPtr.src ← GetBitAddress[bitmap, xInSegment[date[i]]];
BITBLT[bbPtr];
bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
ENDLOOP};
lastTime ← current};
bbPtr↑ ← UserTerminal.GetBitBltTable[];
UNTIL GoAway[@keyboardLock] DO
PaintTime[];
Process.Pause[Process.SecondsToTicks[1]];
ENDLOOP};
ClearScreen: PROC =
BEGIN
zero: CARDINAL ← 0;
bbPtr.dst ← firstLine;
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
bbPtr.height ← usableHeight;
bbPtr.width ← usableWidth;
BITBLT[bbPtr];
InitPainting[];
END;
InitPainting: PROC =
BEGIN
-- set up standard arguments for character painting
charPos ← 0; line ← 1;
--bbPtr.dstBpl already set up
--bbPtr.src set when proc called
bbPtr.dst ← thisLine ← firstLine;
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.height ← height;
bbPtr.width ← font.maxwidth;
bbPtr.flags ← [];
END;
ClearThisChar: INTERNAL PROC =
BEGIN
zero: CARDINAL ← 0;
bbPtr.src ← [@zero, 0, 0];
bbPtr.srcDesc ← [gray[[0, 0, 0, 0]]];
bbPtr.flags ← [gray: TRUE];
BITBLT[bbPtr];
bbPtr.srcDesc ← [srcBpl[font.raster*16]];
bbPtr.flags ← [];
END;
DisplayChar: INTERNAL PROC [c: CHARACTER] =
BEGIN
Newline: INTERNAL PROC =
BEGIN
IF line<nLines THEN
{thisLine ← GetBitAddress[thisLine.word, thisLine.bit+bitsPerTextLine]; line ← line+1}
ELSE
BEGIN
zero: CARDINAL ← 0;
sBBTable: BBTableSpace;
sbbPtr: BBptr = AlignedBBTable[@sBBTable];
sbbPtr↑ ← [
dst: firstLine, dstBpl: bbPtr.dstBpl,
src: GetBitAddress[firstLine.word, firstLine.bit+bitsPerTextLine],
srcDesc: [srcBpl[bbPtr.dstBpl]], flags: [direction: forward],
width: nCharPos*font.maxwidth, height: height*(nLines-1)];
BITBLT[sbbPtr];
sbbPtr↑ ← [
dst: thisLine, src: [@zero, 0, 0],
dstBpl: bbPtr.dstBpl, srcDesc: [gray[[0, 0, 0, 0]]],
width: nCharPos*font.maxwidth, height: height,
flags: [gray: TRUE]];
BITBLT[sbbPtr];
END;
bbPtr.dst ← thisLine;
charPos ← 0;
END;
SELECT c FROM
IN (SP..'~] =>
BEGIN
IF ~(c IN [font.min..font.max]) THEN c ← font.max+1;
bbPtr.src ← GetBitAddress[bitmap, xInSegment[c]];
BITBLT[bbPtr];
END;
SP => NULL;
CR => {Newline[]; RETURN};
BS => {Backup[]; ClearThisChar[]; RETURN};
TAB => {UNTIL (charPos MOD 8)=0 DO DisplayChar[SP] ENDLOOP; RETURN};
FF => {ClearScreen[]; RETURN};
IN [0C..SP) => {DisplayChar['↑]; DisplayChar[c+('A-1C)]; RETURN};
ENDCASE => RETURN;
IF (charPos←charPos+1)>=nCharPos THEN Newline[]
ELSE bbPtr.dst ← GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth];
END;
GetFont: PROC RETURNS [LONG POINTER] =
BEGIN
-- Note: Even though it doesn't say so in its strike header, this is a fixed-width
-- font. Bitter experience has shown that not all Gacha10.strike font files are, in
-- fact, fixed width. You need to painstakingly examine the xInSegment table for
-- the truth.
Gacha10Strike: ARRAY [0..1142B) OF WORD = [
-- 0-- 100000B, 000040B, 000176B, 000007B, 001136B, 000010B, 000004B, 000000B,
-- 10-- 000052B, 000000B, 000001B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 20-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 30-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 40-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 50-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 60-- 000000B, 000000B, 017000B, 000040B, 120243B, 106204B, 004010B, 040100B,
-- 70-- 000000B, 000002B, 034040B, 160701B, 107616B, 037070B, 070000B, 000200B,
-- 100-- 010034B, 034041B, 140707B, 107637B, 016104B, 070162B, 022010B, 110434B,
-- 110-- 074161B, 160707B, 144221B, 021104B, 104761B, 141007B, 000000B, 000200B,
-- 120-- 000040B, 001600B, 020010B, 010401B, 140000B, 000000B, 000000B, 000000B,
-- 130-- 000000B, 000000B, 000060B, 103000B, 017000B, 000040B, 120245B, 052412B,
-- 140-- 004020B, 020520B, 000000B, 000002B, 042141B, 011042B, 104021B, 001104B,
-- 150-- 104000B, 000400B, 004042B, 042121B, 021044B, 044020B, 021104B, 020022B,
-- 160-- 042015B, 114442B, 042211B, 011041B, 004221B, 021104B, 104021B, 001001B,
-- 170-- 000000B, 000200B, 000040B, 002000B, 020000B, 000400B, 040000B, 000000B,
-- 200-- 000000B, 000400B, 000000B, 000000B, 000100B, 100400B, 017000B, 000040B,
-- 210-- 121765B, 054412B, 004020B, 020340B, 100000B, 000004B, 046241B, 010042B,
-- 220-- 104020B, 002104B, 104100B, 101000B, 002042B, 056121B, 021044B, 044020B,
-- 230-- 021104B, 020022B, 102015B, 114442B, 042211B, 011041B, 004221B, 025050B,
-- 240-- 050041B, 000401B, 002010B, 034260B, 160643B, 107415B, 026070B, 070440B,
-- 250-- 043313B, 007054B, 032260B, 161704B, 044221B, 021104B, 174100B, 100400B,
-- 260-- 017000B, 000040B, 000503B, 001004B, 000040B, 010520B, 100000B, 000004B,
-- 270-- 052040B, 020704B, 107436B, 002070B, 104100B, 102017B, 101004B, 052121B,
-- 300-- 161004B, 047436B, 020174B, 020023B, 102012B, 112442B, 042211B, 160601B,
-- 310-- 004212B, 025020B, 050101B, 000401B, 007020B, 002311B, 011144B, 042023B,
-- 320-- 031010B, 010500B, 042514B, 110462B, 046311B, 010404B, 044221B, 012104B,
-- 330-- 010100B, 100406B, 057000B, 000040B, 000501B, 101012B, 100040B, 010103B,
-- 340-- 160017B, 100010B, 052040B, 040044B, 104221B, 004104B, 074000B, 002000B,
-- 350-- 001010B, 052211B, 011004B, 044020B, 027104B, 020022B, 042012B, 112442B,
-- 360-- 074211B, 040101B, 004212B, 025020B, 020101B, 000201B, 012476B, 036211B,
-- 370-- 001047B, 142021B, 021010B, 010600B, 042510B, 110442B, 042200B, 140404B,
-- 400-- 042425B, 004104B, 020100B, 100411B, 117000B, 000000B, 003745B, 042321B,
-- 410-- 000040B, 010000B, 100000B, 000010B, 062040B, 100047B, 140221B, 004104B,
-- 420-- 004000B, 001017B, 102000B, 056371B, 011044B, 044020B, 021104B, 020422B,
-- 430-- 042012B, 111442B, 040211B, 021041B, 004212B, 012050B, 020201B, 000201B,
-- 440-- 002020B, 042211B, 001044B, 002021B, 021010B, 010500B, 042510B, 110442B,
-- 450-- 042200B, 020404B, 042425B, 004050B, 020600B, 100300B, 017000B, 000040B,
-- 460-- 001205B, 042521B, 000040B, 010000B, 101400B, 002020B, 042041B, 001040B,
-- 470-- 104221B, 010104B, 104101B, 100400B, 004010B, 040211B, 011044B, 044020B,
-- 500-- 021104B, 020422B, 022012B, 111442B, 040211B, 011041B, 004204B, 012104B,
-- 510-- 020401B, 000101B, 002010B, 042311B, 011144B, 042023B, 021010B, 010440B,
-- 520-- 042510B, 110462B, 046201B, 010444B, 141012B, 012050B, 040100B, 100400B,
-- 530-- 017000B, 000040B, 001203B, 104616B, 100040B, 010000B, 000400B, 002020B,
-- 540-- 034371B, 170700B, 103416B, 010070B, 070100B, 100200B, 010010B, 036211B,
-- 550-- 160707B, 107620B, 016104B, 070342B, 023710B, 110434B, 040161B, 010701B,
-- 560-- 003404B, 012104B, 020761B, 000101B, 002000B, 036260B, 160643B, 102015B,
-- 570-- 021010B, 010420B, 042510B, 107054B, 032200B, 160303B, 041012B, 021020B,
-- 600-- 174100B, 100400B, 000000B, 000000B, 000001B, 000000B, 000020B, 020000B,
-- 610-- 000400B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 100000B,
-- 620-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 630-- 000040B, 000000B, 000000B, 000000B, 000001B, 000001B, 000000B, 000000B,
-- 640-- 000000B, 000001B, 000000B, 110000B, 000000B, 000040B, 002000B, 000000B,
-- 650-- 000000B, 000020B, 000100B, 100400B, 000000B, 000000B, 000000B, 000000B,
-- 660-- 000020B, 020000B, 001000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 670-- 000001B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 700-- 000000B, 000000B, 000030B, 000000B, 000000B, 000000B, 000001B, 000001B,
-- 710-- 000000B, 000000B, 000000B, 000016B, 000000B, 060000B, 000000B, 000040B,
-- 720-- 002000B, 000000B, 000000B, 000140B, 000100B, 100400B, 000000B, 000000B,
-- 730-- 000000B, 000000B, 000010B, 040000B, 000000B, 000000B, 000000B, 000000B,
-- 740-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 750-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 760-- 000001B, 140007B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B,
-- 770-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000060B, 103000B,
--1000-- 012602B, 000000B, 000007B, 000016B, 000025B, 000034B, 000043B, 000052B,
--1010-- 000061B, 000070B, 000077B, 000106B, 000115B, 000124B, 000133B, 000142B,
--1020-- 000151B, 000160B, 000167B, 000176B, 000205B, 000214B, 000223B, 000232B,
--1030-- 000241B, 000250B, 000257B, 000266B, 000275B, 000304B, 000313B, 000322B,
--1040-- 000331B, 000340B, 000347B, 000356B, 000365B, 000374B, 000403B, 000412B,
--1050-- 000421B, 000430B, 000437B, 000446B, 000455B, 000464B, 000473B, 000502B,
--1060-- 000511B, 000520B, 000527B, 000536B, 000545B, 000554B, 000563B, 000572B,
--1070-- 000601B, 000610B, 000617B, 000626B, 000635B, 000644B, 000653B, 000662B,
--1100-- 000671B, 000700B, 000700B, 000707B, 000716B, 000725B, 000734B, 000743B,
--1110-- 000752B, 000761B, 000770B, 000777B, 001006B, 001015B, 001024B, 001033B,
--1120-- 001042B, 001051B, 001060B, 001067B, 001076B, 001105B, 001114B, 001123B,
--1130-- 001132B, 001141B, 001150B, 001157B, 001166B, 001175B, 001204B, 001213B,
--1140-- 001222B, 001230B];
p: LONG POINTER TO ARRAY [0..1142B) OF WORD ← Runtime.GetTableBase[LOOPHOLE[SpecialTerminalImpl]];
DO
IF p[0] = Gacha10Strike[0] AND p↑ = Gacha10Strike THEN RETURN[p];
p ← p+1;
ENDLOOP;
END;
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- TTY Implementation
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create: PUBLIC PROC [STRING] RETURNS [TTY.Handle] = {ERROR};
Destroy: PUBLIC PROC [TTY.Handle] = {ERROR};
BackingStream: PUBLIC PROC [TTY.Handle] RETURNS [never: UNSPECIFIED] = {ERROR};
UserAbort: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN[CDT]};
ResetUserAbort: PUBLIC PROC =
BEGIN
CDResetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {CDT ← FALSE};
CDResetEntry[@keyboardLock]
END;
-- input procedures
CharsAvailable: PUBLIC PROC [h: TTY.Handle] RETURNS [CARDINAL] =
BEGIN
P: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [CARDINAL] =
INLINE {RETURN[IF in>=out THEN in-out ELSE in+LENGTH[buffer]-out]};
RETURN[P[@keyboardLock]]
END;
GetChar: PUBLIC PROC [h: TTY.Handle] RETURNS [c: CHARACTER] =
BEGIN
GetInternal: 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) = LENGTH[buffer] THEN out ← 0;
RETURN[TRUE]
};
UNTIL GetInternal[@keyboardLock] DO SIGNAL InputTimeout; ENDLOOP;
END;
GetDecimal: PUBLIC PROC [h: TTY.Handle] RETURNS [INTEGER] =
BEGIN
s: STRING ← [10];
[] ← GetEditedString[h, s, IsAtom, TRUE];
RETURN[String.StringToNumber[s, 10]]
END;
GetID: PUBLIC PROC [h: TTY.Handle, s: STRING] =
{[] ← GetEditedString[h, s, IsAtom, TRUE]};
GetLine: PUBLIC PROC [h: TTY.Handle, s: STRING] =
{[] ← GetEditedString[h, s, IsCR, TRUE]; PutChar[h, CR]};
GetLongDecimal: PUBLIC PROC [h: TTY.Handle] RETURNS [n: LONG INTEGER] =
BEGIN
s: STRING ← [32];
[] ← GetEditedString[h, s, IsAtom, TRUE];
RETURN[String.StringToLongNumber[s, 10]]
END;
GetLongNumber: PUBLIC PROC [
h: TTY.Handle, default: LONG UNSPECIFIED, radix: CARDINAL]
RETURNS [n: LONG UNSPECIFIED] =
BEGIN
s: STRING ← [32];
IF radix = 10 AND LOOPHOLE[default, LONG INTEGER] < 0 THEN
{s[0] ← '-; s.length ← 1; default ← -default};
String.AppendLongNumber[s, default, radix];
IF radix = 8 THEN String.AppendChar[s, 'B];
[] ← GetEditedString[h, s, IsAtom, TRUE];
RETURN[String.StringToLongNumber[s, radix]];
END;
GetLongOctal: PUBLIC PROC [h: TTY.Handle] RETURNS [n: LONG UNSPECIFIED] =
BEGIN
s: STRING ← [32];
[] ← GetEditedString[h, s, IsAtom, TRUE];
RETURN[String.StringToLongNumber[s, 8]]
END;
GetNumber: PUBLIC PROC [h: TTY.Handle, default: UNSPECIFIED, radix: CARDINAL]
RETURNS [n: UNSPECIFIED] =
BEGIN
s: STRING ← [10];
IF radix = 10 AND LOOPHOLE[default, INTEGER] < 0 THEN
{default ← -default; s[0] ← '-; s.length ← 1};
String.AppendNumber[s, default, radix];
IF radix = 8 THEN String.AppendChar[s, 'B];
[] ← GetEditedString[h, s, IsAtom, TRUE];
RETURN[String.StringToNumber[s, radix]];
END;
GetOctal: PUBLIC PROC [h: TTY.Handle] RETURNS [n: UNSPECIFIED] =
BEGIN
s: STRING ← [10];
[] ← GetEditedString[h, s, IsAtom, TRUE];
RETURN[String.StringToNumber[s, 8]]
END;
GetString: PUBLIC PROC [
h: TTY.Handle, s: STRING, t: PROC [c: CHARACTER] RETURNS [yes: BOOLEAN]] =
{PutChar[h, GetEditedString[h, s, t, TRUE]]};
LineOverflow: PUBLIC SIGNAL [s: STRING] RETURNS [ns: STRING] = CODE;
Rubout: PUBLIC SIGNAL = CODE;
GetEditedString: PUBLIC PROC [
h: TTY.Handle, s: STRING,
t: PROC [c: CHARACTER] RETURNS [yes: BOOLEAN],
newstring: BOOLEAN]
RETURNS [c: CHARACTER] =
BEGIN
c ← GetChar[h];
IF newstring THEN
IF c = ESC THEN {PutString[h, s]; c ← GetChar[h]} ELSE s.length ← 0;
UNTIL t[c] DO
SELECT c FROM
DEL => SIGNAL Rubout;
ControlA, BS => -- backspace
IF s.length > 0 THEN {IF echo THEN PutChar[h, BS]; s.length ← s.length - 1};
ControlW, ControlQ => -- backword
BEGIN
-- text to be backed up is of the form ...<li><v><ti>, the <v> and <ti> are to
-- be removed.
state: {ti, v, li} ← ti;
FOR i: CARDINAL DECREASING IN [0..s.length) DO
SELECT s[i] FROM
IN ['A..'Z], IN ['a..'z], IN ['0..'9] => IF state = ti THEN state ← v;
ENDCASE => IF state = v THEN state ← li;
IF state = li THEN GO TO Done;
IF echo THEN PutChar[h, BS];
REPEAT
Done => s.length ← i + 1;
FINISHED => s.length ← 0;
ENDLOOP;
END;
ControlX => -- back everything
BEGIN
IF echo THEN FOR i: CARDINAL IN [0..s.length) DO PutChar[h, BS] ENDLOOP;
s.length ← 0;
END;
ControlR => -- refresh-- IF echo THEN {PutChar[h, CR]; PutString[h, s]};
ControlV => -- dont parse next char
BEGIN
WHILE s.length >= s.maxlength DO s ← SIGNAL LineOverflow[s] ENDLOOP;
s[s.length] ← c ← GetChar[h];
s.length ← s.length + 1;
IF echo THEN PutChar[h, c]
END;
ENDCASE =>
BEGIN
WHILE s.length >= s.maxlength DO s ← SIGNAL LineOverflow[s] ENDLOOP;
s[s.length] ← c;
s.length ← s.length + 1;
IF echo THEN PutChar[h, c];
END;
c ← GetChar[h];
ENDLOOP;
END;
IsAtom: PROC [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = SP OR c = CR]};
IsCR: PROC [c: CHARACTER] RETURNS [BOOLEAN] = {RETURN[c = CR]};
PutBackChar: PUBLIC PROC [h: TTY.Handle, c: CHARACTER] =
BEGIN
P: ENTRY PROC [m: POINTER TO MONITORLOCK] =
INLINE BEGIN
newout: CARDINAL = IF out=0 THEN LENGTH[buffer]-1 ELSE out-1;
IF newout#in THEN {buffer[out ← newout] ← c; BROADCAST charactersAvailable};
END;
P[@keyboardLock]
END;
SetEcho: PUBLIC PROC [h: TTY.Handle, new: BOOLEAN] RETURNS [old: BOOLEAN] =
BEGIN
SetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {old ← echo; echo ← new};
SetEntry[@keyboardLock]
END;
-- output procedures
NewLine: PUBLIC PROC [TTY.Handle] RETURNS [BOOLEAN] = {RETURN[charPos=0]};
PutChar: PUBLIC PROC [h: TTY.Handle, c: CHARACTER] =
BEGIN
PutCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE {
doBlink ← FALSE; ClearThisChar[]; DisplayChar[c]; doBlink ← TRUE;
IF typescriptEnabled THEN Streams.PutChar[typescript, c]};
PutCharEntry[@screenLock];
END;
PutLongString: PUBLIC PROC [h: TTY.Handle, s: LONG STRING] =
{IF s#NIL THEN FOR i: CARDINAL IN [0..s.length) DO PutChar[h, s[i]] ENDLOOP};
PutString: PUBLIC PROC [h: TTY.Handle, s: STRING] = {PutLongString[h, s]};
OutString: PROC [s: STRING] = {PutLongString[LOOPHOLE[0], s]};
PutBlanks: PUBLIC PROC [h: TTY.Handle, n: CARDINAL] =
{THROUGH [0..n) DO PutChar[h, ' ] ENDLOOP};
PutDate: PUBLIC PROC [h: TTY.Handle, gmt: Time.Packed, format: TTY.DateFormat] =
{Format.Date[gmt, format, OutString]};
PutDecimal: PUBLIC PROC [h: TTY.Handle, n: INTEGER] = {Format.Decimal[n, OutString]};
PutLongDecimal: PUBLIC PROC [h: TTY.Handle, n: LONG INTEGER] =
{Format.LongDecimal[n, OutString]};
PutLongNumber: PUBLIC PROC [h: TTY.Handle, n: LONG UNSPECIFIED, format: TTY.NumberFormat] =
{Format.LongNumber[n, format, OutString]};
PutLongOctal: PUBLIC PROC [h: TTY.Handle, n: LONG UNSPECIFIED] =
{Format.LongOctal[n, OutString]};
PutNumber: PUBLIC PROC [h: TTY.Handle, n: UNSPECIFIED, format: TTY.NumberFormat] =
{Format.Number[n, format, OutString]};
PutOctal: PUBLIC PROC [h: TTY.Handle, n: UNSPECIFIED] = {Format.Octal[n, OutString]};
PutLongSubString: PUBLIC PROC [h: TTY.Handle, ss: TTY.LongSubString] =
{Format.LongSubStringItem[ss, OutString]};
PutSubString: PUBLIC PROC [h: TTY.Handle, ss: String.SubString] =
{Format.SubString[ss, OutString]};
END.