<> <> <> <> 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; QueueList: TYPE = LIST OF DrawQueue; maxC: CARD16 = 50; DrawQueueRep: PUBLIC TYPE = RECORD [ fancyList: LIST OF Request _ NIL, fancyLast: LIST OF Request _ NIL, cnt, in, out: CARD16, 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>> active: BOOL _ FALSE, deleted: BOOL _ FALSE, alldoneToBeReported: BOOL _ FALSE, inProgress: Request _ [NIL, CDBasics.universe] ]; queueEmpty: PUBLIC REF _ NEW[INT]; finishedForEver: PUBLIC REF _ NEW[INT]; Activate: INTERNAL PROC [dq: DrawQueue] = { IF NOT dq.active THEN { list: QueueList _ NARROW[dq.desgn.cdDrawQueuePriv]; list _ CONS[dq, list]; dq.desgn.cdDrawQueuePriv _ list; dq.active _ TRUE; }; }; DeActivate: INTERNAL PROC [dq: DrawQueue] = { InternalFlushAll[dq]; IF dq.active THEN { list: QueueList _ NARROW[dq.desgn.cdDrawQueuePriv]; IF list=NIL THEN RETURN; -- list#NIL; otherwise nothing too delete! not dq.active; but don't be picky IF list.first=dq THEN dq.desgn.cdDrawQueuePriv _ list.rest ELSE FOR l: QueueList _ list, l.rest WHILE l.rest#NIL DO IF l.rest.first=dq THEN {l.rest _ l.rest.rest; EXIT} ENDLOOP; dq.active _ 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.active _ 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>> IF dq.deviceClip#clip THEN { InternalFlushAll[dq]; dq.deviceClip _ clip; }; IF CDBasics.NonEmpty[clip] THEN Activate[dq] ELSE DeActivate[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; InternalFlushAll[dq]; DeActivate[dq]; dq.desgn _ NIL; } }; QueueInsertDrawCommand: PUBLIC ENTRY PROC [dq: DrawQueue, req: Request] = { ENABLE UNWIND => NULL; IF dq#NIL AND dq.active THEN InternalInsertCommand[dq, req] }; InsertFancy: INTERNAL PROC [dq: DrawQueue, req: Request] = INLINE { IF dq.fancyList=NIL THEN dq.fancyList _ dq.fancyLast _ LIST[req] ELSE { dq.fancyLast.rest _ LIST[req]; dq.fancyLast _ dq.fancyLast.rest; }; NOTIFY dq.commandAvail; }; Stronger: PROC [a, b: REF] RETURNS [ATOM] = INLINE { RETURN [IF a=$redraw OR b=$redraw THEN $redraw ELSE $draw]; }; InternalInsertCommand: INTERNAL PROC [dq: DrawQueue, req: Request] = { dq.alldoneToBeReported _ TRUE; IF req.key#$redraw AND req.key#$draw THEN InsertFancy[dq, req] ELSE { <<--regular draw requests>> j: CARD16; IF ~CDBasics.Intersect[req.rect, dq.deviceClip] THEN RETURN; req.rect _ CDBasics.Intersection[req.rect, dq.deviceClip]; <<--table overflow check>> IF dq.cnt>=maxC THEN { InternalFlushDraw[dq]; InsertFancy[dq, Request[$flushed, dq.deviceClip]]; req _ Request[$redraw, dq.deviceClip]; }; <<--should painting stop?>> IF CDBasics.Inside[dq.inProgress.rect, req.rect] THEN { IF dq.inProgress.key=$redraw OR dq.inProgress.key=$draw THEN { dq.stopFlag^ _ TRUE; req.key _ Stronger[req.key, dq.inProgress.key]; }; }; <<--try matching>> j _ dq.out; THROUGH [0..dq.cnt) DO IF CDBasics.Intersect[req.rect, dq.comm[j].rect] THEN { IF CDBasics.Inside[req.rect, dq.comm[j].rect] THEN { dq.comm[j].key _ Stronger[dq.comm[j].key, req.key]; RETURN }; IF CDBasics.Inside[dq.comm[j].rect, req.rect] THEN { dq.comm[j].key _ Stronger[dq.comm[j].key, req.key]; dq.comm[j].rect _ req.rect; RETURN }; }; j _ (j+1) MOD maxC; ENDLOOP; <<--enqueue>> 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: QueueList _ NARROW[design.cdDrawQueuePriv], lst.rest WHILE lst#NIL DO InternalInsertCommand[lst.first, req]; ENDLOOP }; lastFirst: BOOL _ TRUE; 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 AND dq.fancyList=NIL DO IF dq.deleted THEN InsertDisappear[dq] ELSE IF dq.alldoneToBeReported THEN { InsertFancy[dq, Request[queueEmpty, [1,1,0,0]]]; dq.alldoneToBeReported _ FALSE; } ELSE WAIT dq.commandAvail; dq.stopFlag^ _ FALSE; ENDLOOP; IF dq.fancyList#NIL THEN { dq.inProgress _ req _ dq.fancyList.first; dq.fancyList _ dq.fancyList.rest; RETURN }; dq.cnt _ dq.cnt-1; <<--silly heuristic; >> <<--if 2 elements it looks more like movement to clear the old area first>> <<--but if there is a backlog we want to draw the newest things first>> IF dq.cnt>2 THEN { dq.in _ (dq.in+maxC-1) MOD maxC; dq.inProgress _ req _ dq.comm[dq.in]; } ELSE { 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 InternalFlushAll[dq] }; InternalFlushAll: INTERNAL PROC [dq: DrawQueue] = { dq.fancyList _ dq.fancyLast _ NIL; InternalFlushDraw[dq]; }; InternalFlushDraw: INTERNAL PROC [dq: DrawQueue] = { dq.cnt _ dq.in _ dq.out _ 0; dq.stopFlag^ _ TRUE; dq.alldoneToBeReported _ FALSE; dq.inProgress _ [NIL, CDBasics.universe]; NOTIFY dq.commandAvail; }; InsertDisappear: INTERNAL PROC [dq: DrawQueue] = { dq.fancyList _ dq.fancyLast _ LIST[Request[finishedForEver, [1,1,0,0]]]; }; END.