<> <> <> <> DIRECTORY CD USING [Rect, Design], CDBasics, CDDrawQueue; CDDrawQueueImpl: CEDAR MONITOR IMPORTS CDBasics EXPORTS CDDrawQueue SHARES CD = BEGIN DrawQueue: TYPE = REF DrawQueueRep; Request: TYPE = CDDrawQueue.Request; maxC: CARDINAL = 50; DrawQueueRep: PUBLIC TYPE = RECORD [ cnt, in, out: CARDINAL, commandAvail: CONDITION, desgn: CD.Design, deviceClip: CD.Rect, comm: ARRAY [0..maxC) OF Request, stopFlag: REF BOOL, -- must be monitored <<-- request to stop the execution of (only!) the current command>> inlist: BOOL _ FALSE, deleted: BOOL _ FALSE, alldoneToBeReported: BOOL _ FALSE, inProgress: Request _ [NIL, CDBasics.universe] ]; TableList: TYPE = LIST OF DrawQueue; queueEmpty: PUBLIC REF _ NEW[INT]; finishedForEver: PUBLIC REF _ NEW[INT]; InsertList: INTERNAL PROC [dq: DrawQueue] = { IF NOT dq.inlist THEN { list: TableList _ NARROW[dq.desgn.cdDrawQueuePriv]; list _ CONS[dq, list]; dq.desgn.cdDrawQueuePriv _ list; dq.inlist _ TRUE; }; }; RemoveList: INTERNAL PROC [dq: DrawQueue] = { IF dq.inlist THEN { list: TableList _ NARROW[dq.desgn.cdDrawQueuePriv]; IF list=NIL THEN RETURN; -- list#NIL; otherwise nothing too delete! not dq.inlist; but don't be picky IF list.first=dq THEN dq.desgn.cdDrawQueuePriv _ list.rest ELSE FOR l: TableList _ list, l.rest WHILE l.rest#NIL DO IF l.rest.first=dq THEN {l.rest _ l.rest.rest; EXIT} ENDLOOP; dq.inlist _ FALSE; }; }; Create: PUBLIC ENTRY PROC [design: CD.Design, stopFlag: REF BOOL, clip: CD.Rect_[1,1,0,0]] RETURNS [DrawQueue] = { <<--stopFlag^ tells any drawing process to stop volunterly and to fetch the next>> <<--command. >> <<--stopFlag^ must not be modified any more outside this monitor!!>> <<--until DeleteCommandTable is called>> <<--Users may request a stop by calling Flush.>> ENABLE UNWIND => NULL; --catches eg on stopFlag=NIL dq: DrawQueue = NEW[DrawQueueRep]; IF stopFlag=NIL THEN stopFlag _ NEW[BOOL_FALSE]; dq.cnt _ dq.in _ dq.out _ 0; dq.deviceClip _ CDBasics.empty; dq.desgn _ design; dq.stopFlag _ stopFlag; -- assigns addresses dq.deleted _ dq.inlist _ dq.alldoneToBeReported _ dq.stopFlag^ _ FALSE; InternalChangeClipArea[dq, clip]; RETURN [dq] }; ChangeClipArea: PUBLIC ENTRY PROC [dq: DrawQueue, clip: CD.Rect] = { <<--changes the clipping aera>> ENABLE UNWIND => NULL; InternalChangeClipArea[dq, clip]; }; InternalChangeClipArea: INTERNAL PROC [dq: DrawQueue, clip: CD.Rect] = { <<--changes the clipping aera>> InternalFlush[dq]; dq.deviceClip _ clip; IF CDBasics.NonEmpty[clip] THEN InsertList[dq] ELSE RemoveList[dq] }; Destroy: PUBLIC ENTRY PROC [dq: DrawQueue] = { <<--no more insertion calls on dq allowed!!>> <<--redraw process must disapear from itself,>> <<--when redraw process dispears and contains nomore>> <<--reference of dq, it is subject for garbage collection >> <<--The stopFlag is immediately set to TRUE and later>> <<--reset to FALSE when the redraw >> <<--process stopped the current command, if it then fetches another >> <<--command it gets the disapearforever command.>> ENABLE UNWIND => NULL; IF dq#NIL THEN { dq.deleted _ TRUE; InternalFlush[dq]; RemoveList[dq]; dq.desgn _ NIL; } }; QueueInsertDrawCommand: PUBLIC ENTRY PROC [dq: DrawQueue, req: Request] = { ENABLE UNWIND => NULL; IF dq.inlist THEN InternalInsertCommand[dq, req] }; InternalInsertCommand: INTERNAL PROC [dq: DrawQueue, req: Request] = { <<--this procedure introduces redraw all if table is full>> <<--and in removes certain rectangles contained by others>> <<--this implies that the module knows some commands>> IF dq.cnt>=maxC THEN { InternalFlush[dq]; InternalInsertCommand[dq, Request[$flushed, dq.deviceClip]]; InternalInsertCommand[dq, Request[$redraw, dq.deviceClip]]; RETURN; }; dq.alldoneToBeReported _ TRUE; IF req.key=$redraw THEN {--erase and draw IF CDBasics.Intersect[req.rect, dq.deviceClip] THEN { j: CARDINAL _ dq.out; req.rect _ CDBasics.Intersection[req.rect, dq.deviceClip]; <<--this search for similar commands could be made much better>> THROUGH [0..dq.cnt) DO IF dq.comm[j].key=$redraw OR dq.comm[j].key=$draw THEN { IF CDBasics.Inside[req.rect, dq.comm[j].rect] THEN { dq.comm[j].key _ $redraw; RETURN }; IF CDBasics.Inside[dq.comm[j].rect, req.rect] THEN { dq.comm[j].key _ $redraw; dq.comm[j].rect _ req.rect; RETURN }; }; j _ (j+1) MOD maxC; ENDLOOP; IF CDBasics.Inside[dq.inProgress.rect, req.rect] AND (dq.inProgress.key=$redraw OR dq.inProgress.key=$draw) THEN dq.stopFlag^ _ TRUE; } ELSE RETURN; } ELSE IF req.key=$draw THEN {--draw on top of whats there IF CDBasics.Intersect[req.rect, dq.deviceClip] THEN { j: CARDINAL _ dq.out; req.rect _ CDBasics.Intersection[req.rect, dq.deviceClip]; <<--this search for similar commands could be made much better>> THROUGH [0..dq.cnt) DO IF dq.comm[j].key=$redraw OR dq.comm[j].key=$draw THEN { IF CDBasics.Inside[req.rect, dq.comm[j].rect] THEN RETURN; IF CDBasics.Inside[dq.comm[j].rect, req.rect] THEN { dq.comm[j].rect _ req.rect; RETURN }; }; j _ (j+1) MOD maxC; ENDLOOP; IF dq.inProgress.key=$draw AND CDBasics.Inside[dq.inProgress.rect, req.rect] THEN dq.stopFlag^ _ TRUE; } ELSE RETURN; }; dq.cnt _ dq.cnt+1; dq.comm[dq.in] _ req; dq.in _ (dq.in+1) MOD maxC; NOTIFY dq.commandAvail; }; InsertDrawCommand: PUBLIC ENTRY PROC [design: CD.Design, req: Request] = { ENABLE UNWIND => NULL; FOR lst: TableList _ NARROW[design.cdDrawQueuePriv], lst.rest WHILE lst#NIL DO InternalInsertCommand[lst.first, req]; ENDLOOP }; FetchCommand: PUBLIC ENTRY PROC [dq: DrawQueue] RETURNS [req: Request] = { <<--waits till command is available or DrawQueue deleted>> ENABLE UNWIND => NULL; dq.stopFlag^ _ FALSE; WHILE dq.cnt<=0 DO IF dq.deleted THEN InsertDisappear[dq] ELSE IF dq.alldoneToBeReported THEN InsertAlldone[dq] ELSE WAIT dq.commandAvail; dq.stopFlag^ _ FALSE; ENDLOOP; dq.cnt _ dq.cnt-1; dq.inProgress _ req _ dq.comm[dq.out]; dq.out _ (dq.out+1) MOD maxC; }; Flush: PUBLIC ENTRY PROC [dq: DrawQueue] = { <<-- the drawing may stop after some time only,>> <<-- and NOT immediately with procedure return>> ENABLE UNWIND => NULL; IF dq#NIL THEN InternalFlush[dq] }; InternalFlush: INTERNAL PROC [dq: DrawQueue] = { dq.cnt _ dq.in _ dq.out _ 0; dq.stopFlag^ _ TRUE; dq.alldoneToBeReported _ FALSE; NOTIFY dq.commandAvail; dq.inProgress _ [NIL, CDBasics.universe]; }; InsertDisappear: INTERNAL PROC [dq: DrawQueue] = { dq.out _ 0; dq.cnt _ 1; dq.comm[0] _ Request[finishedForEver, [1,1,0,0]]; dq.in _ (1 MOD maxC); }; InsertAlldone: INTERNAL PROC [dq: DrawQueue] = { dq.alldoneToBeReported _ FALSE; dq.out _ 0; dq.cnt _ 1; dq.comm[0] _ Request[queueEmpty, [1,1,0,0]]; dq.in _ (1 MOD maxC); }; END.