ScreenImpl.mesa
Copyright (C) 1981, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: January 16, 1982 12: 21 PM
Edited by Doug Wyatt, June 14, 1984 1:09:00 pm PDT
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: CEDAR 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: PROC = {
Initialize[];
DrawScreen[];
ReadCommandLine[];
DisplayMessage["Mockingbird of January 16, 1982"];
DO IF badCommand THEN {
WHILE IODefs.ReadChar[]#177C DO NULL; ENDLOOP;
EraseMessage[]; badCommand ← FALSE; };
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;
};
badCommand: BOOLFALSE;
Command: PROC RETURNS[BOOL] = {
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];
};
HandleRed: PROC = {
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[];
};
HandleBlue: PROC = {
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[];
};
HandleYellow: PROC = {
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[];
};
ReadKeyboard: PROC = {
ENABLE {
StringDefs.InvalidNumber => {
DisplayMessage["illegal number- type DEL to continue"];
badCommand ← TRUE;
CONTINUE; };
IODefs.Rubout => {
DisplayMessage["command terminated"];
CONTINUE; };
};
EraseMessage[];
windows[selectedWindow].command.keyboard;
};
ChangeCursor: PROC = {
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;
};
oldCursor: Cursor ← textCursor;
CursorState: TYPE = {window, scroll, command, screen, commandwindow};
currentState: CursorState ← screen;
Scroll: PROC[w: CARDINAL, y: INTEGER] = {
IF y > 0 THEN cursor^ ← scrollUpCursor;
IF y < 0 THEN cursor^ ← scrollDownCursor;
windows[w].command.scroll[y];
cursor^ ← scrollUpDownCursor;
};
Initialize: PROC = {
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[];
};
ReadCommandLine: PROC = {
OPEN StreamDefs, StringDefs;
ENABLE StreamDefs.FileNameError => {
DisplayMessage["no such file"];
CONTINUE; };
char: CHARACTER;
CR: CHARACTER=015C;
filename: STRING ← [40];
ending: STRING ← [12];
test, error: BOOLFALSE;
hardcopy: BOOLFALSE;
command, second, period: BOOLFALSE;
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]]];
};
MousePoint: PROC= {
x, y: REAL ← 1;
x ← x*MouseX^;
y ← y*MouseY^;
MouseP ← [x, y];
};
MouseP, ScreenP: Vec;
******************************************************************
Window & Subwindow procedures
******************************************************************
DrawScreen: PROC = {
i: CARDINAL;
FOR i IN [0..noWindows) DO windows[i].command.display[TRUE] ENDLOOP;
};
MoveWindow: PROC[y: REAL] = {
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 { dist ← ABS[windows[i].top-y]; w ← i; };
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];
};
NewWindow: PROC[w: CARDINAL, y: REAL] = {
called by bluebug in the righthand margin
cursor^ ← newWindowCursor;
WHILE BlueBug[] AND NOT YellowBug[] DO NULL; ENDLOOP;
IF YellowBug[] THEN { DeleteWindow[w]; RETURN; };
DefaultWindow[noWindows, y, windows[w].bottom];
windows[w].bottom ← y;
Select[noWindows];
DisplayHeader[black, noWindows];
SetClipper[w];
SetClipper[noWindows];
noWindows ← noWindows + 1;
};
SubWindow: PROC[w: CARDINAL, y: REAL] = {
yellowbug in the righthand margin
cursor^ ← subWindowCursor;
WHILE NOT BlueBug[] AND YellowBug[] DO NULL; ENDLOOP;
IF BlueBug[] THEN { DeleteWindow[w]; RETURN; };
Flash[];
};
FindWindow: PROC[y: REAL] RETURNS[CARDINAL] = {
i: CARDINAL;
FOR i IN [0..noWindows) DO
IF y < windows[i].top AND y > windows[i].bottom THEN RETURN[i];
ENDLOOP;
RETURN[0];
};
DeleteWindow: PROC[w: CARDINAL] = {
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 {
windows[neighbor].bottom ← windows[w].bottom;
DisplayHeader[white, w];
}
ELSE {
neighbor ← FindWindow[windows[w].bottom - 1];
IF neighbor # 0 THEN {
MoveContext[neighbor, windows[w].top];
DisplayHeader[white, neighbor];
windows[neighbor].top ← windows[w].top;
DisplayHeader[black, neighbor];
};
};
IF neighbor = 0
THEN {
DisplayMessage["cannot delete that window"];
RETURN;
};
noWindows ← noWindows - 1;
SystemDefs.FreeHeapString[windows[w].file];
windows[w] ← windows[noWindows];
SetClipper[neighbor];
Select[neighbor];
windows[neighbor].command.display[TRUE];
};
Invert: PROC[dc: DisplayContext] = {
SetTexture[dc, black];
SetPaint[dc, invert];
DrawScreenArea[dc];
SetPaint[dc, replace];
};
MoveContext: PROC[w: CARDINAL, y: REAL] = {
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]];
};
Select: PROC[w: CARDINAL] = {
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;
};
selectedWindow: CARDINAL ← 1;
listenWindow: CARDINAL ← 1;
DefaultWindow: PROC[w: CARDINAL, top, bottom: REAL] = {
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];
};
******************************************************************
Command Window procedures
******************************************************************
DisplayCommandWindow: PROC[redraw: BOOL] = {
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];
};
DisplayCommand: PROC[command: STRING, x, y: REAL, proc: PROC[r: Rect]] = {
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];
};
DisplayHeader: PROC[color: Texture, w: CARDINAL] = {
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];
};
EraseMessage: PROC = {
SetBrush[white, replace];
DrawRectangle[screen,[0, 763],[620, 780]];
SetBrush[black, invert];
};
DisplayMessage: PUBLIC PROC[s: STRING] = {
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];
};
AddCommand: PROC[r: Rect, proc: PROC[r: Rect]] = {
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;
};
DeleteCommand: PROC[p: Vec] = {
i: CARDINAL;
FOR i IN [0..mIndex) DO
IF manager[i].rect.ll = p THEN
{
mIndex ← mIndex - 1;
manager[i] ← manager[mIndex];
}; ENDLOOP;
};
FindCommand: PROC[p: Vec] RETURNS[m: Image]= {
i: CARDINAL;
FOR i IN [0..mIndex) DO
{
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];
}; ENDLOOP;
m.command ← NullProc;
RETURN[m];
};
Flash: PROC = {
Invert[windows[selectedWindow].context];
Invert[windows[selectedWindow].context];
};
Erase: PROC[redraw: BOOL] = {
SetTexture[screen, white];
SetPaint[screen, replace];
DrawScreenArea[screen];
SetTexture[screen, black];
SetPaint[screen, paint];
};
******************************************************************
Commands
******************************************************************
Confirm: PROC[w: CARDINAL, s: STRING] RETURNS[BOOL] = {
confirm: BOOL;
IF windows[w].command.count=0 THEN RETURN[TRUE];
DisplayMessage[s];
confirm← IODefs.ReadChar[]=CR;
EraseMessage[];
RETURN[confirm];
};
FileIn: PROC[r: Rect] = {
ENABLE StreamDefs.FileNameError => {
DisplayMessage["no such file"];
DrawRectangle[screen, r.ll, r.ur];
CONTINUE; };
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
{
windows[s].command.fileIn[windows[s].file, FALSE];
EraseMessage[];
windows[s].command.display[TRUE];
windows[s].command.count ← 0;
};
DrawRectangle[screen, r.ll, r.ur];
};
FileOut: PROC[r: Rect] = {
ENABLE StreamDefs.FileNameError => {
DisplayMessage["illegal file name"];
DrawRectangle[screen, r.ll, r.ur];
CONTINUE; };
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];
};
BackUp: PROC[w: CARDINAL] = {
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[];
};
Play: PROC[r: Rect] = {
windows[selectedWindow].command.play[];
};
InvertPlay: PUBLIC PROC = {
r: Rect = manager[playIndex].rect;
SetBrush[black, invert];
DrawRectangle[screen, r.ll, r.ur];
};
Listen: PROC[r: Rect] = {
windows[listenWindow].command.listen[];
listenWindow ← selectedWindow;
};
InvertListen: PUBLIC PROC = {
r: Rect = manager[listenIndex].rect;
SetBrush[black, invert];
DrawRectangle[screen, r.ll, r.ur];
};
playIndex: CARDINAL ← 0;
listenIndex: CARDINAL ← 0;
Hardcopy: PROC[r: Rect] = {
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[];
};
EditFileName: PROC[r: Rect] = {
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 {
c ← IODefs.ReadChar[];
IF c=177C OR c=010C THEN
{
IF windows[w].file.length = 0 THEN LOOP;
windows[w].file.length← windows[w].file.length- 1;
DisplayHeader[black, w];
LOOP; };
IF c=' THEN {
windows[w].file.length← 0;
DisplayHeader[black, w];
LOOP; };
IF c#CR AND c#'. AND c#033C THEN {
StringDefs.AppendChar[windows[w].file, c];
DisplayChar[screen, c]; LOOP;
};
StringDefs.AppendString[windows[w].file,".music"];
windows[w].type ← logical;
EXIT; };
ENDLOOP;
DisplayHeader[black, w];
EraseMessage[];
Select[w];
};
Quit: PROC[r: Rect] = {
IF NOT Confirm[1,"type CR to confirm Quit"] THEN RETURN;
Score.StopPlaying[];
Score.StopListening[];
ImageDefs.StopMesa;
};
ClearManager: PROC = {
mIndex ← 0;
};
manager: POINTER TO ARRAY [0..20) OF Image;
Image: TYPE = RECORD[rect: Rect, command: PROC[r: Rect]];
mIndex: CARDINAL ← 0;
mMax: CARDINAL = 20;
******************************************************************
Assorted
******************************************************************
SetBrush: PROC[tex: Texture, pnt: PaintingFunction] = INLINE {
SetTexture[screen, tex];
SetPaint[screen, pnt];
};
SetClipper: PROC[w: CARDINAL] = {
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];
};
Wait: PROC[t: CARDINAL] = {
now: LONG CARDINAL ← MiscDefs.CurrentTime[];
WHILE MiscDefs.CurrentTime[] < now + t DO NULL; ENDLOOP;
};
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: PROC[r: Rect] = { };
Main[];
END.