-- N.Wirth June 1, 1977
-- S.Andler August 24, 1977 10:45 PM
-- C.Geschke August 26, 1977 3:54 PM
-- E.Satterthwaite September 12, 1977 3:35 PM
-- R.Johnsson September 19, 1977 9:16 AM
-- J.Sandman October 19, 1977 10:00 AM
-- C.Geschke October 20, 1977 9:14 AM
DIRECTORY
FSPDefs: FROM "FSPDefs",
ImageDefs: FROM "ImageDefs",
InlineDefs: FROM "InlineDefs",
IODefs: FROM "IODefs",
RectangleDefs: FROM "RectangleDefs",
StreamDefs: FROM "StreamDefs",
StringDefs: FROM "StringDefs",
SystemDefs: FROM "SystemDefs",
TeleSilDefs: FROM "TeleSilDefs",
TeleSilProcDefs: FROM "TeleSilProcDefs";

DEFINITIONS FROM
StreamDefs, --
StreamHandle, GetDefaultKey, endof, get
SystemDefs, --
AllocateHeapNode, AllocateHeapString, FreeHeapNode
IODefs; --
DEL, CR, StreamIO

------------------------------------------------------------------

TeleSilMain: PROGRAM
IMPORTS
IODefs, StreamDefs, StringDefs, SystemDefs, TeleSilProcDefs
EXPORTS TeleSilProcDefs =
BEGIN
OPEN
TeleSilProcDefs,
TeleSilDefs;

-- A picture is represented by a single circular linked list.
-- Each element represents either a line, an area, or a text
-- string. It is of type Object. Rover is a pointer to some
-- element of the chain. Objects are allocated from the heap.
-- Unused objects are freed.

-- Global Variables --
boxShade: GrayTone ← 2;
centext: BOOLEAN ← TRUE; -- center text
pictureChanged: BOOLEAN ← FALSE; -- picture changed since last output
rebuildNeeded: BOOLEAN ← FALSE; -- display might have "holes"
etherOn: BOOLEAN ← FALSE;
textFont: FontNumber ← 0;
lineWidth: CARDINAL ← 2;
new, old: Coord; -- Cursor coordinates
tickon: BOOLEAN ← TRUE;

IsLetter: PROCEDURE[ch: CHARACTER] RETURNS[BOOLEAN]=
BEGIN RETURN[ch IN [’a..’z] OR ch IN [’A..’Z]] END;

UpperCase: PROCEDURE[ch: CHARACTER] RETURNS[CHARACTER]=
BEGIN
RETURN[
IF ch IN [’a..’z] THEN LOOPHOLE[InlineDefs.BITAND[LOOPHOLE[ch], 337B]]
ELSE ch]
END;

------------------------------------------------------------------
-- STORAGE ALLOCATION
-- defines
Rover, AllocateObject, FreeObject

rover
: ObjPtr ← NIL; -- Pointer to current object in the circular list

Rover: PUBLIC PROCEDURE RETURNS[POINTER TO ObjPtr] = BEGIN RETURN[@rover] END;

AllocateObject: PUBLIC PROCEDURE[kind: ObjKind]=
BEGIN
previous: ObjPtr ← rover;
rover ← SystemDefs.AllocateHeapNode[
SELECT kind FROM
line => SIZE[line Object],
area => SIZE[area Object],
text => SIZE[text Object],
ENDCASE => SIZE[Object]];
SELECT kind FROM
line => rover↑ ← Object[next: , zone:, state: , body: line[]];
area => rover↑ ← Object[next: , zone:, state: , body: area[]];
text => rover↑ ← Object[next: , zone:, state: , body: text[,]];
ENDCASE;
rover.state ← dead;
IF previous = NIL THEN rover.next ← rover
ELSE BEGIN rover.next ← previous.next; previous.next ← rover END;
pictureChanged ← TRUE
END;

FreeObject: PUBLIC PROCEDURE[pObj: ObjPtr]=
BEGIN
WITH pObj SELECT FROM text => SystemDefs.FreeHeapString[str]; ENDCASE;
SystemDefs.FreeHeapNode[pObj];
pictureChanged ← TRUE
END;

------------------------------------------------------------------
MakeObject: PROCEDURE[kind: ObjKind, z: Zone]=
BEGIN AllocateObject[kind]; rover.state ← selected; rover.zone ← z END;

Display: PROCEDURE[pObj: ObjPtr]=
BEGIN
z: Zone ← pObj.zone;
SELECT pObj.state FROM
dead => BEGIN EraseRectangle[z]; rebuildNeeded ← TRUE END;
active =>
WITH pObj SELECT FROM
line => PaintRectangle[12, z, black];
area => PaintRectangle[13, z, shade];
text => [] ← PaintString[str, z, fontno];
ENDCASE;
selected =>
WITH pObj SELECT FROM
line => PaintRectangle[12, z, gray];
area => PaintRectangle[13, z, shade];
text =>
BEGIN [] ← PaintString[str, z, fontno]; PaintRectangle[13, z, 1] END;
ENDCASE;
ENDCASE
END;

MakeLine: PROCEDURE=
BEGIN -- draw line from old to new
w: INTEGER ← old.x - new.x;
h: INTEGER ← old.y - new.y;
IF old = new THEN RETURN;
IF ABS[w] < ABS[h] THEN
BEGIN -- line is vertical
w ← lineWidth; new.x ← old.x;
IF h < 0 THEN -- top down -- MakeObject[line, [old,w,-h]]
ELSE -- bottom up -- MakeObject[line, [[new.x, new.y+lineWidth],w,h]];
new.x ← old.x
END
ELSE
BEGIN -- line is horizontal
h ← lineWidth; new.y ← old.y;
IF w < 0 THEN -- left to right -- MakeObject[line, [old,-w,h]]
ELSE -- right to left -- MakeObject[line, [[new.x+lineWidth, new.y],w,h]];
new.y ← old.y
END;
Display[rover];
SetMousePosition[new] -- indicate where we assumed the cursor to be
END;

MakeArea: PROCEDURE=
BEGIN
IF old.x = new.x OR old.y = new.y THEN RETURN;
MakeObject[area, [[MIN[old.x, new.x], MIN[old.y, new.y]],
ABS[new.x-old.x], ABS[new.y-old.y]]];
WITH rover SELECT FROM area => shade ← boxShade; ENDCASE;
Display[rover]
END;

MakeBox: PROCEDURE=
BEGIN
x0: CARDINAL ← MIN[old.x, new.x];
y0: CARDINAL ← MIN[old.y, new.y];
x1: CARDINAL ← MAX[old.x, new.x];
y1: CARDINAL ← MAX[old.y, new.y];
IF x0 = x1 OR y0 = y1 THEN RETURN;
MakeObject[line, [[x0,y0], x1-x0, lineWidth]];
Display[rover];
MakeObject[line, [[x1,y0], lineWidth, y1-y0]];
Display[rover];
MakeObject[line, [[x0+lineWidth,y1], x1-x0, lineWidth]];
Display[rover];
MakeObject[line, [[x0,y0+lineWidth], lineWidth, y1-y0]];
Display[rover];
END;

ClearSelections: PROCEDURE=
BEGIN
stop: ObjPtr ← rover;
IF rover # NIL THEN
DO
IF rover.state = selected THEN
BEGIN
WITH rover SELECT FROM
text => BEGIN state ← dead; Display[rover] END;
ENDCASE;
rover.state ← active; Display[rover]
END;
IF (rover ← rover.next) = stop THEN EXIT
ENDLOOP
END;

NewPicture: PUBLIC PROCEDURE=
BEGIN -- This routine relies on somebody else clearing the display
stop: ObjPtr ← rover; nextObj: ObjPtr;
IF rover#NIL THEN
DO
nextObj ← rover.next; FreeObject[rover];
IF (rover ← nextObj)=stop THEN BEGIN rover ← NIL; EXIT END
ENDLOOP;
rebuildNeeded ← TRUE
END;

AddPicture: PROCEDURE=
BEGIN pictureChanged ← TRUE; rebuildNeeded ← TRUE END;

SelectObject: PROCEDURE=
BEGIN -- the one at the cursor’s position
stop: ObjPtr ← rover;
selObj: ObjPtr ← NIL;
z: Zone;
p: CARDINAL ← maxCARDINAL; -- perimeter
x: INTEGER ← new.x;
y: INTEGER ← new.y;

IF rover#NIL THEN
DO
IF rover.state=active THEN
BEGIN
z ← rover.zone;
IF (x IN [z.point.x .. z.point.x+LOOPHOLE[z.w,INTEGER]]) AND
(y IN [z.point.y .. z.point.y+LOOPHOLE[z.h,INTEGER]]) AND
(z.w+z.h < p)
THEN BEGIN p ← z.w+z.h; selObj ← rover END
END;
IF (rover ← rover.next)=stop THEN EXIT
ENDLOOP;
IF selObj#NIL THEN BEGIN selObj.state ← selected; Display[selObj] END
END;

SelectZone: PROCEDURE=
BEGIN
stop: ObjPtr ← rover;
x0: INTEGER ← MIN[old.x, new.x];
y0: INTEGER ← MIN[old.y, new.y];
x1: INTEGER ← MAX[old.x, new.x];
y1: INTEGER ← MAX[old.y, new.y];
z: Zone;

IF rover#NIL THEN
DO
IF rover.state=active THEN
BEGIN
z ← rover.zone;
IF (z.point.x IN [x0..x1])
AND (z.point.x+LOOPHOLE[z.w,INTEGER] IN [x0..x1])
AND (z.point.y IN [y0..y1])
AND (z.point.y+LOOPHOLE[z.h,INTEGER] IN [y0..y1])
THEN BEGIN rover.state ← selected; Display[rover] END
END;
IF (rover ← rover.next)=stop THEN EXIT
ENDLOOP
END;

SelectEverything: PROCEDURE=
BEGIN
stop: ObjPtr ← rover;
IF rover#NIL THEN
DO
IF rover.state=active THEN
BEGIN rover.state ← selected; Display[rover] END;
IF (rover ← rover.next)=stop THEN EXIT
ENDLOOP
END;

MoveSelectedObjects: PROCEDURE=
BEGIN
stop: ObjPtr ← rover;
dx: INTEGER ← new.x - old.x;
dy: INTEGER ← new.y - old.y;

IF rover#NIL THEN
DO
IF rover.state=selected THEN
BEGIN
rover.state ← dead; Display[rover];
rover.zone.point.x ← rover.zone.point.x+dx;
rover.zone.point.y ← rover.zone.point.y+dy;
rover.state ← selected; Display[rover];
pictureChanged ← TRUE
END;
IF (rover ← rover.next)=stop THEN EXIT
ENDLOOP
END;

DeleteSelectedObjects: PROCEDURE=
BEGIN
stop: ObjPtr ← rover;
pObj: ObjPtr;
IF rover#NIL THEN
DO
pObj ← rover.next;
IF pObj.state=selected THEN
BEGIN
pObj.state ← dead; Display[pObj];
IF pObj=rover THEN rover ← NIL ELSE rover.next ← pObj.next;
FreeObject[pObj]
END
ELSE rover ← pObj;
IF pObj=stop THEN EXIT
ENDLOOP
END;

CopySelectedObjects: PROCEDURE=
BEGIN
oldObj: ObjPtr;
stop: ObjPtr ← rover;
dx: INTEGER ← new.x - old.x;
dy: INTEGER ← new.y - old.y;
z: Zone;
IF rover#NIL THEN
DO
IF rover.state=selected THEN
BEGIN
WITH rover SELECT FROM text =>
BEGIN state ← dead; Display[rover] END;
ENDCASE;
rover.state ← marked
END;
IF (rover ← rover.next)=stop THEN EXIT
ENDLOOP;
IF rover#NIL THEN
DO
IF rover.state=marked THEN
BEGIN
rover.state ← active; Display[rover];
z ← rover.zone;
z.point.x ← z.point.x + dx;
z.point.y ← z.point.y + dy;
oldObj ← rover;
WITH old: oldObj SELECT FROM
line => MakeObject[line, z];
area =>
BEGIN
MakeObject[area, z];
WITH new: rover SELECT FROM area => new.shade ← old.shade; ENDCASE;
END;
text =>
BEGIN
MakeObject[text, z];
WITH new: rover SELECT FROM
text =>
BEGIN
new.fontno ← old.fontno;
new.str ← AllocateHeapString[old.str.length];
StringDefs.AppendString[new.str, old.str]
END;
ENDCASE;
END;
ENDCASE;
Display[rover]
END;
IF (rover ← rover.next)=stop THEN EXIT
ENDLOOP
END;

Rebuild: PUBLIC PROCEDURE=
BEGIN
counter: CARDINAL ← 0;
stop: ObjPtr ← rover;
oldIcon: CursorIcon ← SetCursorIcon[rebuild];

IF rover#NIL THEN
DO
Display[rover];
IF (rover ← rover.next)=stop OR (counter ← counter+1)=rebuildChunk THEN
BEGIN
IF MouseValue[]#0 OR NOT keys.endof[keys] THEN GOTO interrupt;
IF etherOn THEN
BEGIN
Yield; -- let Pup Package do its thing
IF CmdWaiting[] THEN GOTO interrupt
END;
IF rover=stop THEN EXIT;
counter ← 0
END
REPEAT
interrupt =>
BEGIN
[] ← SetCursorIcon[oldIcon];
rebuildNeeded ← TRUE;
RETURN
END
ENDLOOP;
IF tickon THEN DisplayTicks;
rebuildNeeded ← FALSE;
[] ← SetCursorIcon[oldIcon]
END;

Ticks: PROCEDURE[newTick: BOOLEAN]=
BEGIN
IF newTick # tickon THEN
BEGIN
tickon ← newTick;
IF tickon THEN DisplayTicks
ELSE BEGIN EraseTicks; rebuildNeeded ← TRUE END
END
END;

NextDigit: PROCEDURE [prompt: STRING, max: CARDINAL] RETURNS [CARDINAL]=
BEGIN
d: CARDINAL;
ch: CHARACTER;
headOrg: Coord = [32,16]; -- origin for text display
strHeight: CARDINAL ← FontHeight[fontno: 0];
strWidth: CARDINAL ← PaintString[prompt, [point: headOrg, w: BMBitsPerLine-headOrg.x, h: strHeight], 0];

ch ← keys.get[keys];
EraseRectangle[[point: headOrg, w: strWidth, h: strHeight]];
rebuildNeeded ← TRUE;
d ← LOOPHOLE[ch, CARDINAL] - 60B;
IF d IN [0..max] THEN RETURN[d] ELSE RETURN[0]
END;

MouseAction: PROCEDURE[mouseEvent: CARDINAL]=
BEGIN
SetMousePosition[new]; -- indicate mouseposition at entry to command
SELECT mouseEvent FROM
0 => -- Blue button -- -- select
BEGIN
ClearSelections;
IF ABS[new.x-old.x]+ABS[new.y-old.y]>16 THEN SelectZone ELSE SelectObject
END;
1 => -- Blue button + LeftShift key -- -- multiple select
IF ABS[new.x-old.x]+ABS[new.y-old.y]>16 THEN SelectZone ELSE SelectObject;
2 => -- Blue button + Ctrl key -- NULL;
3 => -- Blue button + LeftShift + Ctrl keys -- NULL;
4 => -- Yellow button -- -- draw line
BEGIN ClearSelections; MakeLine; END;
5 => -- Yellow button + LeftShift key -- -- draw box
BEGIN ClearSelections; MakeBox; END;
6 => -- Yellow button + Ctrl key -- -- draw area
BEGIN ClearSelections; MakeArea; END;
7 => -- Yellow button + LeftShift + Ctrl keys -- NULL;
8 => -- Red button -- -- move
IF new#old THEN MoveSelectedObjects;
9 => -- Red button + LeftShift key -- -- copy
IF new#old THEN CopySelectedObjects;
10 => -- Red button + Ctrl key -- NULL;
11 => -- Red button + LeftShift + Ctrl keys -- NULL;
ENDCASE;
END;

MouseCmd: PROCEDURE[mouseEvent: CARDINAL]=
BEGIN
cmd: CmdObj;
MouseAction[mouseEvent];
IF etherOn THEN
BEGIN cmd ← [mouse[mouseEvent,old,new]]; SendCmd[@cmd] END;
END;

KeyAction: PROCEDURE RETURNS[{goOn, quit}]=
BEGIN
ch: CHARACTER ← keys.get[keys];
[] ← SetCursorIcon[input];
IF ch=DEL OR ch=ControlD THEN
BEGIN DeleteSelectedObjects; KeyCmd[DEL,0] END ELSE
IF ch>’ THEN StringCmd[ch]
ELSE
SELECT ch FROM
ControlA=> -- ↑Add file
BEGIN
TypeScriptWindow;
WriteString["Add "];
IF (IF pictureChanged THEN Confirm[] ELSE TRUE) THEN
BEGIN AddPicture; Input; IF etherOn THEN Refresh END;
BitMapDisplay
END;
ControlC=> -- set ↑Color
BEGIN boxShade ← NextDigit["Color", black]; KeyCmd[ControlC,boxShade];
END;
034C => -- ↑\ (Debug)
CallDebugger[];
ControlE=> -- ↑Ethernet action
BEGIN
TypeScriptWindow;
WriteString["EtherNet command: Listen/Connect/Refresh/Disconnect: "];
SELECT UpperCase[keys.get[keys]] FROM
’L => -- Listen
BEGIN
WriteLine["Listen"];
IF etherOn THEN Error["Already listening"] ELSE Listen
END;
’C => -- Connect
BEGIN
WriteLine["Connect"];
IF etherOn THEN Error["Disconnect first"] ELSE Connect
END;
’R => -- Refresh
BEGIN
WriteLine["Refresh"];
IF NOT etherOn THEN Error["No connections"]
ELSE Refresh
END;
’D => -- Disconnect
BEGIN
WriteLine["Disconnect"];
IF NOT etherOn THEN Error["No connections"] ELSE Disconnect
END;
ENDCASE => WriteLine["XXX"];
BitMapDisplay
END;
ControlF=> -- set ↑Font number
BEGIN
textFont ← NextDigit["Font number", LAST[FontNumber]];
IF NOT FontLoaded[textFont] THEN textFont ← 0;
KeyCmd[ControlF,textFont]
END;
ControlG=> -- set ↑Grid spacing
BEGIN
SetGridSpacing[gridSpacing ← NextDigit["Gridspacing", LAST[GridSpacing]]];
KeyCmd[ControlG, gridSpacing];
END;
ControlH=> -- ↑Hardcopy
BEGIN TypeScriptWindow; WriteLine["Hardcopy"]; HardCopy; BitMapDisplay
END;
ControlI=> -- ↑Input file
BEGIN
TypeScriptWindow; WriteString["Input "];
IF (IF pictureChanged THEN Confirm[] ELSE TRUE) THEN
BEGIN NewPicture; Input; IF etherOn THEN Refresh END;
BitMapDisplay
END;
ControlO=> -- ↑Output
BEGIN
TypeScriptWindow; WriteLine["Output"]; Output;
pictureChanged ← FALSE; BitMapDisplay
END;
ControlQ=> -- ↑Quit
BEGIN
TypeScriptWindow; WriteString["Quit "];
IF (IF pictureChanged THEN Confirm[] ELSE TRUE) THEN
BEGIN IF etherOn THEN Disconnect; RETURN[quit] END;
BitMapDisplay
END;
ControlR=> -- ↑Restart
BEGIN
TypeScriptWindow; WriteString["Restart "];
IF (IF pictureChanged THEN Confirm[] ELSE TRUE) THEN
BEGIN NewPicture; ChangeFonts; KeyCmd[ControlR,0] END;
BitMapDisplay
END;
ControlS=> -- ↑Shrink bitmap
BEGIN ShrinkBM; KeyCmd[ControlS,0] END;
ControlT=> -- on/off ↑Ticks
BEGIN Ticks[NOT tickon]; KeyCmd[ControlT,tickon] END;
ControlW=> -- set line↑Width
BEGIN
lineWidth ← NextDigit["line Width", 9];
IF lineWidth=0 THEN lineWidth ← 16;
KeyCmd[ControlW,lineWidth];
END;
ControlX=> -- centered/not centered te↑Xt
BEGIN centext ← NOT centext; KeyCmd[ControlX,centext] END;
ControlY=> -- select ever↑Ything
BEGIN SelectEverything; KeyCmd[ControlY,0] END;
ENDCASE;
[] ← SetCursorIcon[arrow];
RETURN[goOn]
END;

CallDebugger: MACHINE CODE = INLINE[347B, 54B]; -- KFCB, sCallDebugger

KeyCmd: PROCEDURE[ch: CHARACTER, word: UNSPECIFIED]=
BEGIN
cmd: CmdObj;
IF etherOn THEN BEGIN cmd ← [keyboard[ch,word]]; SendCmd[@cmd] END
END;

StringCmd: PROCEDURE[ch: CHARACTER]=
BEGIN
cmd: CmdObj;
tmp: STRING;
w, h: CARDINAL;
header: STRING ← [80]; -- buffer for text input
pos: Coord;
s: STRING;

[w,h] ← GetString[header, textFont, ch]; rebuildNeeded ← TRUE;
IF w > 0 THEN
BEGIN
ClearSelections;
s ← AllocateHeapString[header.length];
StringDefs.AppendString[s, header];
pos ← CursorPosition[];
IF centext THEN pos ← [pos.x - w/2, pos.y - h/2];
MakeObject[text, Zone[pos, w, h]];
WITH rover SELECT FROM text => BEGIN fontno ← textFont; str ← s; END; ENDCASE;
Display[rover];
IF etherOn THEN
BEGIN
tmp ← AllocateHeapString[s.length];
StringDefs.AppendString[tmp, s];
cmd ← [text[Zone[pos, w, h], tmp]];
SendCmd[@cmd]
END
END
END;

MarkCmd: PROCEDURE[point: Coord, icon: CursorIcon]=
BEGIN
cmd: CmdObj;
Mark[point, icon];
IF etherOn THEN BEGIN cmd ← [mark[point, icon]]; SendCmd[@cmd] END
END;

CursorCmd: PROCEDURE[point: Coord]=
BEGIN
cmd: CmdObj;
IF etherOn THEN BEGIN cmd ← [cursor[point]]; SendCmd[@cmd] END
END;

SetCursorIconCmd: PROCEDURE[icon: CursorIcon] RETURNS[oldIcon: CursorIcon]=
BEGIN
cmd: CmdObj;
oldIcon ← SetCursorIcon[icon];
IF etherOn THEN BEGIN cmd ← [setCursorIcon[icon]]; SendCmd[@cmd] END
END;

InputStatus: PUBLIC PROCEDURE[GetWord: PROCEDURE RETURNS[WORD]]=
BEGIN
boxShade ← GetWord[];
textFont ← GetWord[];
SetGridSpacing[gridSpacing ← GetWord[]];
Ticks[LOOPHOLE[GetWord[]]];
lineWidth ← GetWord[];
centext ← LOOPHOLE[GetWord[]]
END;

OutputStatus: PUBLIC PROCEDURE[PutWord: PROCEDURE[word: WORD]]=
BEGIN
PutWord[boxShade];
PutWord[textFont];
PutWord[gridSpacing];
PutWord[LOOPHOLE[tickon]];
PutWord[lineWidth];
PutWord[LOOPHOLE[centext]]
END;

ZoneChecking: PROCEDURE RETURNS[BOOLEAN]=
BEGIN
z: FSPDefs.ZonePointer ← SystemDefs.HeapZone[];
RETURN[z.checking ← NOT z.checking]
END;

SetEtherOn: PUBLIC PROCEDURE[newValue: BOOLEAN] = BEGIN etherOn ← newValue END;

--------------------------------------------------------------------
-- MAIN PROGRAM


keys: StreamHandle;
mouseValue, mouseEvent: CARDINAL; -- mouse buttons
pCmd: CmdPtr;
gridSpacing: GridSpacing;
tmp: Coord;
typeScriptDCB: RectangleDefs.DCBptr;

typeScriptDCB ← MEMORY[420B];
MEMORY[420B] ← RectangleDefs.DCBnil; -- Make display come up white

[] ← SetCursorIcon[arrow];
SetGridSpacing[gridSpacing ← 3];
keys ← GetDefaultKey[];
IF zoneChecking THEN [] ← ZoneChecking[];
InitDisplay;

--------------------------------------------------------------------
-- Surround the following loop with this enable-clause in TeleSilResident
-- ENABLE SegmentDefs.InsufficientVM => BEGIN ShrinkBM; RESUME END;
--------------------------------------------------------------------

DO ENABLE IODefs.Rubout => RESUME;
IF (mouseValue ← MouseValue[])#0 AND
(IF etherOn THEN RequestControl[] ELSE TRUE) THEN
BEGIN -- Mouse Action!
new ← old ← CursorPosition[];
mouseEvent ← MouseEvent[mouseValue];
CursorCmd[tmp ← old];
IF mouseEvent=2 THEN [] ← SetCursorIconCmd[blackArrow]
ELSE MarkCmd[old, greyArrow];
WHILE MouseValue[] # 0 DO -- Mouse button down
IF etherOn THEN Yield; -- Let Pup Package do its thing
new ← CursorPosition[];
IF new # tmp THEN CursorCmd[tmp ← new]
ENDLOOP;
IF mouseEvent = 2 THEN [] ← SetCursorIconCmd[arrow]
ELSE BEGIN MarkCmd[old, greyArrow]; MouseCmd[MouseEvent[mouseValue]] END
END;

IF NOT keys.endof[keys] AND
(IF etherOn THEN RequestControl[] ELSE TRUE) AND
KeyAction[]=quit THEN EXIT;

IF etherOn THEN
BEGIN
Yield; -- Let PUP Package do its thing

WHILE CmdWaiting[] DO
pCmd ← NextCmd[];
IF pCmd # NIL THEN
BEGIN
WITH pCmd SELECT FROM
mouse =>
BEGIN old ← oldCoord; new ← newCoord; MouseAction[mouseEvent] END;
keyboard =>
SELECT char FROM
DEL => DeleteSelectedObjects;
ControlC => boxShade ← info;
ControlF => textFont ← info;
ControlG => SetGridSpacing[gridSpacing ← info];
ControlR =>
BEGIN
TypeScriptWindow; Error["Restart not implemented"];
BitMapDisplay
END;
ControlS => ShrinkBM;
ControlT => Ticks[info];
ControlW => lineWidth ← info;
ControlX => centext ← info;
ControlY => SelectEverything;
ENDCASE;
text =>
BEGIN
ClearSelections; MakeObject[text, z];
WITH rover SELECT FROM text =>
BEGIN fontno ← textFont; str ← s END;
ENDCASE;
Display[rover]
END;
ENDCASE;
FreeCmd[pCmd];
Yield; -- Let PUP Package do its thing - again
END
ENDLOOP
END;

IF rebuildNeeded THEN Rebuild
ENDLOOP;

ImageDefs.StopMesa[]
END.