--Author: John Maxwell
--last modified: January 16, 1982 12:21 PM

DIRECTORY
CursorDefs,
DisplayDefs USING [DisplayOff],
FrameDefs USING [MakeCodeResident,GlobalFrame],
Graphics USING [black, CharData, CopyContext, DestroyPath, DisplayChar, DisplayContext, DisplayString, DrawRectangle, DrawScreenArea, EnterPoint, GetStringBox, grey, InitContext, MakeFont, Map, MoveTo, NewContext, PaintingFunction, ScreenToUser, SetClipArea, SetFont, SetLineWidth, SetPaint, SetTexture, StartAreaPath, Texture, Translate, Vec, white],
ImageDefs USING [StopMesa],
IODefs USING [ReadChar,Rubout,SetEcho,WriteLine],
MiscDefs USING [CurrentTime],
MusicDefs USING [AnyBug, BlueBug, Control, MouseX, MouseY, RedBug, Shift, YellowBug],
MusicProcess,
InterfaceImplA,
Real USING [FixI],
Score USING [InitializeSynthesizer, StopListening, StopPlaying, Test],
Screen USING [CommandProcs],
StreamDefs USING [DiskHandle, FileNameError, GetDefaultKey, KeyboardHandle, NewByteStream, Read],
StringDefs USING [AppendChar, AppendString, EqualString, InvalidNumber],
SystemDefs USING [AllocateHeapString, AllocateSegment, FreeHeapString],
Utility USING [InitStorage];


ScreenImpl: PROGRAM
IMPORTS DisplayDefs, FrameDefs, Graphics, ImageDefs, IODefs, MiscDefs, MusicDefs, Music:MusicProcess, Interface:InterfaceImplA, Real, Score, StreamDefs, StringDefs, SystemDefs, Utility
EXPORTS Screen =
BEGIN
OPEN Graphics, CursorDefs, MusicDefs, Screen;
keyboard:StreamDefs.KeyboardHandle;

--******************************************************************
--User Commands
--******************************************************************

Main: PROCEDURE =
BEGIN
Initialize[];
DrawScreen[];
ReadCommandLine[];
DisplayMessage["Mockingbird of January 16, 1982"];
DO IF badCommand THEN BEGIN
WHILE IODefs.ReadChar[]#177C DO NULL; ENDLOOP;
EraseMessage[]; badCommand←FALSE; END;
badCommand←FALSE;
SELECT TRUE FROM
RedBug[] => HandleRed[];
BlueBug[] => HandleBlue[];
YellowBug[] => HandleYellow[];
NOT keyboard.endof[keyboard] => ReadKeyboard[];
ENDCASE => ChangeCursor[];
FOR w:CARDINAL IN [1..noWindows) DO
IF windows[w].command.count>50 THEN BackUp[w];
ENDLOOP;
ENDLOOP;
END;

badCommand:BOOLEAN←FALSE;

Command:PROCEDURE RETURNS[BOOLEAN] =
BEGIN
image:Image;
EraseMessage[];
MousePoint[];
ScreenP ← ScreenToUser[screen,MouseP];
image ← FindCommand[ScreenP];
IF image.command # NullProc THEN {
image.command[image.rect];
WHILE AnyBug[] DO NULL; ENDLOOP;
RETURN[TRUE]};
RETURN[FALSE];
END;

HandleRed:PROCEDURE =
BEGIN
w:CARDINAL;
top:REAL;
IF Command[] THEN RETURN;
w ← FindWindow[ScreenP.y];
IF w = 0 THEN RETURN;
Select[w];
top ← windows[w].top-header-40;
IF ScreenP.x> 606 THEN RETURN;
SELECT TRUE FROM
Control[] => windows[w].command.redbug[];
Shift[] => windows[w].command.redbug[];
ScreenP.x<leftMargin => Scroll[w,Real.FixI[top-ScreenP.y]];
ScreenP.x > rightMargin => NULL; --MoveWindow[ScreenP.y];
ENDCASE => windows[w].command.redbug[];
END;

HandleBlue:PROCEDURE =
BEGIN
w:CARDINAL;
top:REAL;
IF Command[] THEN RETURN;
w ← FindWindow[ScreenP.y];
IF w = 0 THEN RETURN;
Select[w];
IF ScreenP.x> 606 THEN RETURN;
top ← windows[w].top-header-40;
SELECT TRUE FROM
Control[] => windows[w].command.bluebug[];
Shift[] => windows[w].command.bluebug[];
ScreenP.x<leftMargin => Scroll[w,Real.FixI[ScreenP.y-top]];
ScreenP.x > rightMargin => NULL; --NewWindow[w,ScreenP.y];
ENDCASE => windows[w].command.bluebug[];
END;

HandleYellow:PROCEDURE =
BEGIN
w:CARDINAL;
IF Command[] THEN RETURN;
w ← FindWindow[ScreenP.y];
IF w = 0 THEN RETURN;
Select[w];
IF ScreenP.x> 606 THEN RETURN;
SELECT TRUE FROM
Control[] => windows[w].command.yellowbug[];
Shift[] => windows[w].command.yellowbug[];
ScreenP.x < leftMargin => windows[w].command.thumb[];
ScreenP.x > rightMargin => NULL; --SubWindow[w,ScreenP.y];
ENDCASE => windows[w].command.yellowbug[];
END;

ReadKeyboard:PROCEDURE =
BEGIN
ENABLE BEGIN
StringDefs.InvalidNumber => BEGIN
DisplayMessage["illegal number- type DEL to continue"];
badCommand ← TRUE;
CONTINUE; END;
IODefs.Rubout => BEGIN
DisplayMessage["command terminated"];
CONTINUE; END;
END;
EraseMessage[];
windows[selectedWindow].command.keyboard;
END;

ChangeCursor:PROCEDURE =
BEGIN
newState:CursorState;
MousePoint[];
ScreenP ← ScreenToUser[screen,MouseP];
IF ScreenP.x> 606 THEN RETURN;
SELECT TRUE FROM
FindCommand[ScreenP].command#NullProc => newState ← command;
ScreenP.y > windows[0].bottom => newState ← commandwindow;
ScreenP.x > rightMargin => newState ← window;
ScreenP.x < leftMargin => newState ← scroll;
ENDCASE => newState ← screen;
IF newState=currentState THEN RETURN;
IF currentState=screen THEN oldCursor ← cursor↑;
SELECT newState FROM
window => cursor↑ ← windowCursor;
command => cursor↑ ← bullseyeCursor;
scroll => cursor↑ ← scrollUpDownCursor;
commandwindow => cursor↑ ← textCursor;
ENDCASE => cursor↑ ← oldCursor;
currentState ← newState;
END;

oldCursor:Cursor ← textCursor;
CursorState:TYPE = {window,scroll,command,screen,commandwindow};
currentState:CursorState ← screen;

Scroll:PROCEDURE[w:CARDINAL,y:INTEGER] =
BEGIN
IF y > 0 THEN cursor↑ ← scrollUpCursor;
IF y < 0 THEN cursor↑ ← scrollDownCursor;
windows[w].command.scroll[y];
cursor↑ ← scrollUpDownCursor;
END;

Initialize:PROCEDURE =
BEGIN
DisplayDefs.DisplayOff[white];
--DisplayDefs.SetSystemDisplaySize[0,0];
[] ← IODefs.SetEcho[FALSE];
Utility.InitStorage[];
keyboard ← StreamDefs.GetDefaultKey[];
screen ← NewContext[NIL];
InitContext[screen];
SetLineWidth[screen,1];
windows[0] ← [screen,,command,@commandProcs,800,740];
DefaultWindow[1,740,0];
SetFont[screen,MakeFont["TimesRoman"],12];
manager ←SystemDefs.AllocateSegment[SIZE[Image]*mMax];
commandProcs.display ← DisplayCommandWindow;
FrameDefs.MakeCodeResident[FrameDefs.GlobalFrame[Music]];
Score.InitializeSynthesizer[];
END;

ReadCommandLine:PROCEDURE =
BEGIN
OPEN StreamDefs,StringDefs;
ENABLE StreamDefs.FileNameError => BEGIN
DisplayMessage["no such file"];
CONTINUE; END;
char:CHARACTER;
CR:CHARACTER=015C;
filename:STRING ← [40];
ending:STRING ← [12];
test,error:BOOLEAN ← FALSE;
hardcopy:BOOLEAN ← FALSE;
command,second,period:BOOLEAN ← FALSE;
inputStream:DiskHandle;
inputStream←NewByteStream["com.cm",Read];
inputStream.reset[inputStream];
DO IF inputStream.endof[inputStream] THEN EXIT;
char ← inputStream.get[inputStream];
-- command processing
IF command THEN SELECT char FROM
’/ => EXIT;
’n => NULL;
’t => test←TRUE;
’h => hardcopy←TRUE;
’ => command←FALSE;
ENDCASE => NULL;
IF char=’/ THEN command←TRUE;
IF command THEN LOOP;
-- filename processing
SELECT TRUE FROM
char=CR OR char=’ => NULL;
inputStream.endof[inputStream] => NULL;
char=’. => IF period THEN EXIT ELSE {period←TRUE; LOOP};
period => {AppendChar[ending,char]; LOOP};
ENDCASE => {AppendChar[filename,char]; LOOP};
-- we have a complete filename
IF filename.length#0
AND ~EqualString[ending,"image"]
AND ~EqualString[ending,"bcd"] THEN {
AppendString[filename,".music"];
windows[1].file.length ← 0;
AppendString[windows[1].file,filename];
DisplayHeader[black,1];
DisplayMessage["Please wait-- retrieving file"];
IODefs.WriteLine[filename];
windows[1].command.fileIn[filename,second];
EraseMessage[];
windows[1].command.display[TRUE];
-- command line switches (per file)
IF test AND Score.Test[] THEN {error←TRUE; EXIT};
IF hardcopy THEN Hardcopy[[[215,747],[249,761]]];
second ← TRUE};
filename.length ← 0;
ending.length ← 0;
period ← FALSE;
ENDLOOP;
inputStream.destroy[inputStream];
-- command line switches (at completion)
IF test AND ~error THEN Quit[[[0,0],[0,0]]];
IF hardcopy THEN Quit[[[0,0],[0,0]]];
END;

MousePoint:PROCEDURE=
BEGIN
x,y:REAL ← 1;
x ← x*MouseX↑;
y ← y*MouseY↑;
MouseP ← [x,y];
END;

MouseP,ScreenP:Vec;


--******************************************************************
--Window & Subwindow procedures
--******************************************************************

DrawScreen:PROCEDURE =
BEGIN
i:CARDINAL;
FOR i IN [0..noWindows) DO windows[i].command.display[TRUE] ENDLOOP;
END;

MoveWindow:PROCEDURE[y:REAL] =
BEGIN
i,a,w:CARDINAL←0;
dist,last:REAL ← 1000;
cursor↑ ← moveWindowCursor;
FOR i IN [1..noWindows) DO
IF (windows[i].top-y<dist OR y-windows[i].top<dist)
AND FindWindow[windows[i].top +1]#0
THEN BEGIN dist ← ABS[windows[i].top-y]; w ← i; END;
ENDLOOP;
IF w = 0 THEN RETURN;
a ← FindWindow[windows[w].top +1];
DisplayHeader[white,w];
WHILE RedBug[] DO NULL; ENDLOOP;
MousePoint[];
ScreenP ← ScreenToUser[screen,MouseP];
y ← ScreenP.y;
last ← windows[w].top;
IF y > windows[a].top - 16 THEN y ← windows[a].top - 16;
IF y < windows[w].bottom + 16 THEN y ← windows[w].bottom + 16;
MoveContext[w,y];
windows[a].bottom ← y;
windows[w].top ← y;
SetClipper[a];
SetClipper[w];
DisplayHeader[black,w];
IF last > windows[w].top THEN windows[a].command.display[TRUE];
windows[w].command.display[TRUE];
Select[w];
END;

NewWindow:PROCEDURE[w:CARDINAL,y:REAL] =
BEGIN --
called by bluebug in the righthand margin
cursor↑ ← newWindowCursor;
WHILE BlueBug[] AND NOT YellowBug[] DO NULL; ENDLOOP;
IF YellowBug[] THEN BEGIN DeleteWindow[w]; RETURN; END;
DefaultWindow[noWindows,y,windows[w].bottom];
windows[w].bottom ← y;
Select[noWindows];
DisplayHeader[black,noWindows];
SetClipper[w];
SetClipper[noWindows];
noWindows ← noWindows + 1;
END;

SubWindow:PROCEDURE[w:CARDINAL,y:REAL] =
BEGIN --yellowbug in the righthand margin
cursor↑ ← subWindowCursor;
WHILE NOT BlueBug[] AND YellowBug[] DO NULL; ENDLOOP;
IF BlueBug[] THEN BEGIN DeleteWindow[w]; RETURN; END;
Flash[];
END;

FindWindow:PROCEDURE[y:REAL] RETURNS[CARDINAL] =
BEGIN
i:CARDINAL;
FOR i IN [0..noWindows) DO
IF y < windows[i].top AND y > windows[i].bottom THEN RETURN[i];
ENDLOOP;
RETURN[0];
END;

DeleteWindow:PROCEDURE[w:CARDINAL] =
BEGIN
neighbor:CARDINAL;
cursor↑ ← windowCursor;
Invert[windows[w].context];
WHILE BlueBug[] AND YellowBug[] DO NULL; ENDLOOP;
Wait[1];
Invert[windows[w].context];
IF BlueBug[] OR YellowBug[] THEN RETURN;
neighbor ← FindWindow[windows[w].top + 1];
IF neighbor # 0
THEN BEGIN
windows[neighbor].bottom ← windows[w].bottom;
DisplayHeader[white,w];
END
ELSE BEGIN
neighbor ← FindWindow[windows[w].bottom - 1];
IF neighbor # 0 THEN BEGIN
MoveContext[neighbor,windows[w].top];
DisplayHeader[white,neighbor];
windows[neighbor].top ← windows[w].top;
DisplayHeader[black,neighbor];
END;
END;
IF neighbor = 0
THEN BEGIN
DisplayMessage["cannot delete that window"];
RETURN;
END;
noWindows ← noWindows - 1;
SystemDefs.FreeHeapString[windows[w].file];
windows[w] ← windows[noWindows];
SetClipper[neighbor];
Select[neighbor];
windows[neighbor].command.display[TRUE];
END;

Invert:PROCEDURE[dc:DisplayContext] =
BEGIN
SetTexture[dc,black];
SetPaint[dc,invert];
DrawScreenArea[dc];
SetPaint[dc,replace];
END;

MoveContext:PROCEDURE[w:CARDINAL,y:REAL] =
BEGIN
sip,sop,tip,top:Vec;
sip ← [0,y];
tip ← [0,windows[w].top];
sop ← Map[screen,windows[w].context,sip];
top ← Map[screen,windows[w].context,tip];
Translate[windows[w].context,[0,sop.y- top.y]];
END;

Select:PROCEDURE[w:CARDINAL] =
BEGIN
SetBrush[black,erase];
DrawRectangle[screen,[20,windows[selectedWindow].top- header- 10],
[28,windows[selectedWindow].top- header]];
SetBrush[grey,paint];
DrawRectangle[screen,[20,windows[w].top- header- 10],[28,windows[w].top- header]];
selectedWindow ← w;
END;

selectedWindow:CARDINAL ← 1;
listenWindow:CARDINAL←1;

DefaultWindow:PROCEDURE[w:CARDINAL,top,bottom:REAL] =
BEGIN
Logical:POINTER TO FRAME[InterfaceImplA];
windows[w] ← [,,unknown,@unknownProcs,top,bottom];
windows[w].context ← CopyContext[screen];
windows[w].file ← SystemDefs.AllocateHeapString[40];
Translate[windows[w].context,[30,top - header - 40]]; --
30 regular, 45 for videotaping
SetClipper[w];
SetTexture[windows[w].context,black];
SetPaint[windows[w].context,erase];
DrawScreenArea[windows[w].context];
windows[w].type ← logical;
Logical ← NEW Interface;
windows[w].command ← START Logical;
windows[w].command.initialize[windows[w].context];
END;


--******************************************************************
--Command Window procedures
--******************************************************************

DisplayCommandWindow:PROCEDURE[redraw:BOOLEAN] =
BEGIN
i:CARDINAL;
Erase[TRUE];
SetBrush[black,replace];
ClearManager[];
DisplayCommand["FileIn",30,750,FileIn];
DisplayCommand["PlayBack",110,750,Play];
DisplayCommand["Print",215,750,Hardcopy];
DisplayCommand["Quit",300,750,Quit];
DisplayCommand["Record",385,750,Listen];
DisplayCommand["FileOut",480,750,FileOut];
FOR i IN [1..noWindows) DO DisplayHeader[black,i] ENDLOOP;
Select[1];
END;

DisplayCommand:PROCEDURE[command:STRING,x,y:REAL,proc:PROCEDURE[r:Rect]] =
BEGIN
ll,ur:Vec;
data:CharData;
MoveTo[screen,[x,y]];
SetBrush[black,invert];
DisplayString[screen,command];
GetStringBox[screen,command,@data];
ll.x← x + data.origin.x;
ll.y← y + data.origin.y;
ur.x← ll.x + data.size.x;
ur.y← ll.y + data.size.y;
IF ur.x - ll.x < 30 THEN ur.x ← ll.x + 30;
AddCommand[[ll,ur],proc];
END;

DisplayHeader:PROCEDURE[color:Texture,w:CARDINAL] =
BEGIN
top:REAL ← windows[w].top;
p:Vec ← [30,windows[w].top - 16];
SetBrush[color,replace];
DrawRectangle[screen,[0,top-header],[620,top]];
DeleteCommand[p];
IF color = grey THEN SetBrush[grey,invert];
IF color = black THEN SetBrush[black,invert ];
IF color = white THEN RETURN;
DisplayCommand[windows[w].file,30,top-12,EditFileName];
END;

EraseMessage:PROCEDURE =
BEGIN
SetBrush[white,replace];
DrawRectangle[screen,[0,763],[620,780]];
SetBrush[black,invert];
END;

DisplayMessage:PUBLIC PROCEDURE[s:STRING] =
BEGIN
data:CharData;
IF s=NIL THEN {EraseMessage[]; RETURN};
SetBrush[black,replace];
DrawRectangle[screen,[0,765],[620,780]];
SetBrush[black,invert];
GetStringBox[screen,s,@data];
MoveTo[screen,[300 - data.width.x/2, 767]];
DisplayString[screen,s];
END;

AddCommand:PROCEDURE[r:Rect,proc:PROCEDURE[r:Rect]] =
BEGIN
FOR i:CARDINAL IN [0..mIndex) DO
IF manager[i].command=proc THEN RETURN;
ENDLOOP;
manager[mIndex] ← [r,proc];
IF proc=Play THEN playIndex ← mIndex;
IF proc=Listen THEN listenIndex ← mIndex;
mIndex ← mIndex + 1;
END;

DeleteCommand:PROCEDURE[p:Vec] =
BEGIN
i:CARDINAL;
FOR i IN [0..mIndex) DO
IF manager[i].rect.ll = p THEN
BEGIN
mIndex ← mIndex - 1;
manager[i] ← manager[mIndex];
END; ENDLOOP;
END;

FindCommand:PROCEDURE[p:Vec] RETURNS[m:Image]=
BEGIN
i:CARDINAL;
FOR i IN [0..mIndex) DO
BEGIN
m ← manager[i];
IF p.x > m.rect.ll.x AND p.x < m.rect.ur.x AND
p.y > m.rect.ll.y AND p.y < m.rect.ur.y
THEN RETURN[m];
END; ENDLOOP;
m.command ← NullProc;
RETURN[m];
END;

Flash:PROCEDURE =
BEGIN
Invert[windows[selectedWindow].context];
Invert[windows[selectedWindow].context];
END;

Erase:PROCEDURE[redraw:BOOLEAN] =
BEGIN
SetTexture[screen,white];
SetPaint[screen,replace];
DrawScreenArea[screen];
SetTexture[screen,black];
SetPaint[screen,paint];
END;

--******************************************************************
--Commands
--******************************************************************

Confirm:PROCEDURE[w:CARDINAL,s:STRING] RETURNS[BOOLEAN] =
BEGIN
confirm:BOOLEAN;
IF windows[w].command.count=0 THEN RETURN[TRUE];
DisplayMessage[s];
confirm← IODefs.ReadChar[]=CR;
EraseMessage[];
RETURN[confirm];
END;

FileIn:PROCEDURE[r:Rect] =
BEGIN
ENABLE StreamDefs.FileNameError => BEGIN
DisplayMessage["no such file"];
DrawRectangle[screen,r.ll,r.ur];
CONTINUE; END;
s:CARDINAL ← selectedWindow;
SetBrush[black,invert];
DrawRectangle[screen,r.ll,r.ur];
IF Confirm[s,"the current file has not been filed out-- type CR to confirm"] THEN
BEGIN
windows[s].command.fileIn[windows[s].file,FALSE];
EraseMessage[];
windows[s].command.display[TRUE];
windows[s].command.count ← 0;
END;
DrawRectangle[screen,r.ll,r.ur];
END;

FileOut:PROCEDURE[r:Rect] =
BEGIN
ENABLE StreamDefs.FileNameError => BEGIN
DisplayMessage["illegal file name"];
DrawRectangle[screen,r.ll,r.ur];
CONTINUE; END;
s:CARDINAL ← selectedWindow;
SetBrush[black,invert];
DrawRectangle[screen,r.ll,r.ur];
windows[s].command.fileOut[windows[s].file];
windows[s].command.count ← 0;
DrawRectangle[screen,r.ll,r.ur];
END;

BackUp:PROCEDURE[w:CARDINAL] =
BEGIN
DisplayMessage["Please wait... storing file on backup.music"];
windows[w].command.fileOut["backup.music"];
windows[w].command.count ← 1; -- so it will look dirty
EraseMessage[];
END;

Play:PROCEDURE[r:Rect] =
BEGIN
windows[selectedWindow].command.play[];
END;

InvertPlay:PUBLIC PROCEDURE =
BEGIN
r:Rect = manager[playIndex].rect;
SetBrush[black,invert];
DrawRectangle[screen,r.ll,r.ur];
END;

Listen:PROCEDURE[r:Rect] =
BEGIN
windows[listenWindow].command.listen[];
listenWindow ← selectedWindow;
END;

InvertListen:PUBLIC PROCEDURE =
BEGIN
r:Rect = manager[listenIndex].rect;
SetBrush[black,invert];
DrawRectangle[screen,r.ll,r.ur];
END;

playIndex:CARDINAL←0;
listenIndex:CARDINAL←0;

Hardcopy:PROCEDURE[r:Rect] =
BEGIN
SetBrush[black,invert];
DrawRectangle[screen,r.ll,r.ur];
DisplayMessage["Please wait... printing screen on music.press"];
windows[selectedWindow].command.hardcopy["music.press"];
DrawRectangle[screen,r.ll,r.ur];
EraseMessage[];
END;

EditFileName:PROCEDURE[r:Rect] =
BEGIN
w:CARDINAL ← FindWindow[ScreenP.y];
c,last:CHARACTER ← 040C;
CR:CHARACTER ← 015C;
IF w = 0 THEN RETURN;
windows[w].file.length ← 0;
DisplayMessage["Please enter filename."];
DisplayHeader[white,w];
DisplayHeader[black,w];
DO BEGIN
c←IODefs.ReadChar[];
IF c=177C OR c=010C THEN
BEGIN
IF windows[w].file.length = 0 THEN LOOP;
windows[w].file.length← windows[w].file.length- 1;
DisplayHeader[black,w];
LOOP; END;
IF c=’ ̆THEN BEGIN
windows[w].file.length← 0;
DisplayHeader[black,w];
LOOP; END;
IF c#CR AND c#’. AND c#033C THEN BEGIN
StringDefs.AppendChar[windows[w].file,c];
DisplayChar[screen,c]; LOOP;
END;
StringDefs.AppendString[windows[w].file,".music"];
windows[w].type ← logical;
EXIT; END;
ENDLOOP;
DisplayHeader[black,w];
EraseMessage[];
Select[w];
END;

Quit:PROCEDURE[r:Rect] =
BEGIN
IF NOT Confirm[1,"type CR to confirm Quit"] THEN RETURN;
Score.StopPlaying[];
Score.StopListening[];
ImageDefs.StopMesa;
END;

ClearManager:PROCEDURE =
BEGIN
mIndex ← 0;
END;

manager: POINTER TO ARRAY [0..20) OF Image;
Image: TYPE = RECORD[rect:Rect,command:PROCEDURE[r:Rect]];
mIndex: CARDINAL←0;
mMax: CARDINAL = 20;

--******************************************************************
--Assorted
--******************************************************************

SetBrush:PROCEDURE[tex:Texture,pnt:PaintingFunction] =
INLINE BEGIN
SetTexture[screen,tex];
SetPaint[screen,pnt];
END;

SetClipper:PROCEDURE[w:CARDINAL] =
BEGIN
ll,ur:Vec;
ll ← Map[screen,windows[w].context,[0,windows[w].bottom]];
ur ← Map[screen,windows[w].context,[650,windows[w].top - header]];
StartAreaPath[windows[w].context];
EnterPoint[windows[w].context,[ll.x,ll.y]];
EnterPoint[windows[w].context,[ll.x,ur.y]];
EnterPoint[windows[w].context,[ur.x,ur.y]];
EnterPoint[windows[w].context,[ur.x,ll.y]];
EnterPoint[windows[w].context,[ll.x,ll.y]];
SetClipArea[windows[w].context];
DestroyPath[windows[w].context];
END;

Wait:PROCEDURE[t:CARDINAL] =
BEGIN
now:LONG CARDINAL ← MiscDefs.CurrentTime[];
WHILE MiscDefs.CurrentTime[] < now + t DO NULL; ENDLOOP;
END;

screen:PUBLIC DisplayContext;
leftMargin:CARDINAL = 5;
rightMargin:CARDINAL = 585;
header:CARDINAL = 15;
CR:CHARACTER = 015C;

windows:ARRAY [0..10) OF Window;
noWindows:CARDINAL ← 2;
Window:TYPE = RECORD[context:DisplayContext,file:STRING,
type:WType, command:POINTER TO CommandProcs,
top,bottom:REAL];
WType:TYPE = {physical,graphical,logical,command,unknown};
commandProcs:CommandProcs;
unknownProcs:CommandProcs ← [Flash, Flash, Flash, Flash, Flash, Flash,, Flash, Erase,,,,,0];
Rect:TYPE = RECORD[ll,ur:Vec];
NullProc:PROCEDURE[r:Rect] = BEGIN END;
Main[];
END.