<> <> <> <> DIRECTORY CD USING [DesignRect, Rect, Design], CDBasics, CDDraw; CDDrawImpl: CEDAR MONITOR IMPORTS CDBasics EXPORTS CDDraw = BEGIN CommandTable: TYPE = REF CTableRep; CommType: TYPE = CDDraw.CommType; Comm: TYPE = CDDraw.Comm; maxC: CARDINAL = 50; CTableRep: PUBLIC TYPE = RECORD [ cnt, in, out: CARDINAL, commandAvail: CONDITION, desgn: CD.Design, deviceClip: CD.DesignRect, comm: ARRAY [0..maxC) OF Comm, stopFlag: REF BOOLEAN, -- must be monitored <<-- request to stop the execution of (only!) the current command>> inlist: BOOLEAN _ FALSE, deleted: BOOLEAN _ FALSE, alldoneToBeReported: BOOLEAN _ FALSE ]; TableList: TYPE = LIST OF CommandTable; tableList: TableList_NIL; InsertList: INTERNAL PROC [ct: CommandTable] = BEGIN IF NOT ct.inlist THEN { tableList _ CONS[ct, tableList]; ct.inlist _ TRUE; }; END; RemoveList: INTERNAL PROC [ct: CommandTable] = BEGIN IF ct.inlist THEN { ct.inlist _ FALSE; <<-- tableList#NIL; otherwise nothing too delete! not ct.inlist>> IF tableList.first=ct THEN tableList _ tableList.rest ELSE FOR l: TableList _ tableList, l.rest WHILE l.rest#NIL DO IF l.rest.first=ct THEN {l.rest _ l.rest.rest; EXIT} ENDLOOP }; END; CreateCommandTable: PUBLIC ENTRY PROC [design: CD.Design, clip: CD.DesignRect, cleanStopFlag: REF BOOLEAN] RETURNS [CommandTable] = <<--cleanStopFlag^ tells any drawing process to stop volunterly and to fetch the next>> <<--command. >> <<--cleanStopFlag^ must not be modified any more outside this monitor!!>> <<--until DeleteCommandTable is called>> <<--Users may request a stop by calling FlushCommands.>> BEGIN ENABLE UNWIND => NULL; --catches eg on cleanStopFlag=NIL ct: CommandTable ~ NEW[CTableRep]; ct.cnt _ ct.in _ ct.out _ 0; ct.deviceClip _ clip; ct.desgn _ design; ct.stopFlag _ cleanStopFlag; -- assigns addresses ct.deleted _ ct.inlist _ ct.alldoneToBeReported _ ct.stopFlag^ _ FALSE; InsertList[ct]; RETURN [ct] END; ModifyCommandTable: PUBLIC ENTRY PROC [design: CD.Design, ct: CommandTable, clip: CD.DesignRect] = <<--changes the clipping aera>> BEGIN ENABLE UNWIND => NULL; InternalFlush[ct]; ct.deviceClip _ clip; ct.desgn _ design; IF CDBasics.NonEmpty[clip] THEN InsertList[ct] ELSE RemoveList[ct] END; DestroyCommandTable: PUBLIC ENTRY PROC [ct: CommandTable] = <<--no more calls of InsertCommand on ct allowed!!>> <<--draw process must disapear from itself,>> <<--when draw process dispears and contains nomore>> <<--reference of ct, it is subject for garbage collection >> <<--The cleanStopFlag is immediately set to TRUE and later>> <<--reset to FALSE when the draw >> <<--process stopped the current command, if it then fetches another >> <<--command it gets the disapearforever command.>> BEGIN ENABLE UNWIND => NULL; IF ct#NIL THEN { ct.deleted _ TRUE; InternalFlush[ct]; RemoveList[ct]; ct.desgn _ NIL; } END; InsertCommand: PUBLIC ENTRY PROC [ct: CommandTable, c: Comm] = BEGIN ENABLE UNWIND => NULL; IF ct.inlist THEN InternalInsertCommand[ct, c] END; InternalInsertCommand: INTERNAL PROC [ct: CommandTable, c: Comm] = <<--this procedure introduces redraw all if table is full>> <<--and in removes certain rectangles contained by others>> <<--this implies that the module knows the commands>> BEGIN IF (ct.cnt>=maxC) OR (c.cmd=all) THEN {c _ Comm[cmd: all, erase: TRUE, rect: ct.deviceClip]; InternalFlush[ct]}; IF ct.deleted THEN ERROR; -- at least until it is known IF c.cmd=disapearforever THEN ERROR; -- at least until it is known IF c.cmd=alldone THEN ERROR; -- at least until it is known ct.alldoneToBeReported _ TRUE; IF (ct.cnt=1) AND (ct.comm[ct.out].cmd=all) THEN RETURN; IF c.cmd=rect THEN { IF CDBasics.Intersect[c.rect, ct.deviceClip] THEN { j: CARDINAL _ ct.out; c.rect _ CDBasics.Intersection[c.rect, ct.deviceClip]; <<--this search for similar commands could be made much better>> THROUGH [0..ct.cnt) DO IF ct.comm[j].cmd=rect THEN { IF CDBasics.Inside[c.rect, ct.comm[j].rect] THEN {ct.comm[j].erase _ ct.comm[j].erase OR c.erase; RETURN}; IF CDBasics.Inside[ct.comm[j].rect, c.rect] THEN {ct.comm[j].erase _ ct.comm[j].erase OR c.erase; ct.comm[j].rect _ c.rect; RETURN}; }; j _ (j+1) MOD maxC; ENDLOOP } ELSE RETURN; }; ct.cnt _ ct.cnt+1; ct.comm[ct.in] _ c; ct.in _ (ct.in+1) MOD maxC; NOTIFY ct.commandAvail; END; InsertCommandAll: PUBLIC ENTRY PROC [design: CD.Design, c: Comm] = BEGIN ENABLE UNWIND => NULL; FOR lst: TableList _ tableList, lst.rest WHILE lst#NIL DO IF lst.first.desgn=design THEN InternalInsertCommand[lst.first, c]; ENDLOOP END; FetchCommand: PUBLIC ENTRY PROC [ct: CommandTable] RETURNS [c: Comm] = <<--waits till command is available or CommandTable deleted>> BEGIN ENABLE UNWIND => NULL; ct.stopFlag^ _ FALSE; WHILE ct.cnt<=0 DO IF ct.deleted THEN InsertDisappear[ct] ELSE IF ct.alldoneToBeReported THEN InsertAlldone[ct] ELSE WAIT ct.commandAvail; ct.stopFlag^ _ FALSE; ENDLOOP; ct.cnt _ ct.cnt-1; c _ ct.comm[ct.out]; ct.out _ (ct.out+1) MOD maxC; END; FlushCommands: PUBLIC ENTRY PROC [ct: CommandTable]= <<-- the drawing may stop after some time only,>> <<-- and NOT immediately with procedure return>> BEGIN ENABLE UNWIND => NULL; IF ct#NIL THEN InternalFlush[ct] END; InternalFlush: INTERNAL PROC [ct: CommandTable] = BEGIN ct.cnt _ ct.in _ ct.out _ 0; ct.stopFlag^ _ TRUE; ct.alldoneToBeReported _ FALSE; NOTIFY ct.commandAvail; END; InsertDisappear: INTERNAL PROC [ct: CommandTable] = BEGIN ct.out _ 0; ct.cnt _ 1; ct.comm[0] _ Comm[cmd: disapearforever, erase: FALSE, rect: [1,1,0,0]]; ct.in _ (1 MOD maxC); END; InsertAlldone: INTERNAL PROC [ct: CommandTable] = BEGIN ct.alldoneToBeReported _ FALSE; ct.out _ 0; ct.cnt _ 1; ct.comm[0] _ Comm[cmd: alldone, erase: FALSE, rect: [1,1,0,0]]; ct.in _ (1 MOD maxC); END; END.