--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 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 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 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. (0,3810)\6455i18I205i19I246i27I2123i41I2741i30I1799b14B