CDDrawQueueImpl.mesa (part of ChipNDale)
Copyright © 1983, 1986, 1987 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi July 6, 1983 5:09 pm
Last edited by: Christian April 17, 1987 3:44:03 pm PDT
DIRECTORY
CD USING [Rect, Design],
CDBasics,
CDDrawQueue;
CDDrawQueueImpl:
CEDAR
MONITOR
IMPORTS CDBasics
EXPORTS CDDrawQueue
SHARES CD =
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.