-- Copyright (C) 1982, 1984 by Xerox Corporation. All rights reserved. -- RectangleImpl.mesa - last edited by -- Bruce 20-Dec-82 17:37:23 -- Daniels 6-Jun-84 14:20:18 -- Karlton 30-Dec-82 14:50:19 DIRECTORY BitBlt: TYPE USING [BITBLT, BitBltFlags], Environment: TYPE USING [wordsPerPage], Inline: TYPE USING [LongMult], RecOps: TYPE USING [What], SpecialDisplay: TYPE USING [defaultContext, SpecialContext], SpecialWindow: TYPE USING [GetPages], Window: TYPE USING [Box, EnumerateTree, GetBitmapUnder, Place], WindowOps: TYPE USING [ bbPtr, DIVMOD16, Object, RecList, Rectangle, rootWindow, ScreenBox, SpecialTimesWpl]; RectangleImpl: MONITOR IMPORTS BitBlt, Inline, SpecialDisplay, SpecialWindow, Window, WindowOps EXPORTS RecOps, Window = BEGIN Handle: TYPE = LONG POINTER TO Object; Object: PUBLIC TYPE = WindowOps.Object; RecList: TYPE = WindowOps.RecList; ErrorCode: TYPE = {ambiguous, nilWindow, notInTree}; ClientBug: SIGNAL [code: ErrorCode] = CODE; -- conversion WindowToScreenBox: PUBLIC PROC [wb: Window.Box] RETURNS [b: WindowOps.ScreenBox] = { b.left ← wb.place.x; b.right ← b.left + wb.dims.w; b.top ← wb.place.y; b.bottom ← b.top + wb.dims.h}; ScreenToWindowBox: PUBLIC PROC [b: WindowOps.ScreenBox] RETURNS [wb: Window.Box] = { wb.place.x ← b.left; wb.place.y ← b.top; wb.dims.w ← b.right - b.left; wb.dims.h ← b.bottom - b.top}; Convert: PUBLIC PROC [w: Handle] RETURNS [r: RecList] = { r ← Alloc[]; r↑ ← [link: NIL, box: WindowToScreenBox[[w.place, w.box.dims]]]}; ConvertBox: PUBLIC PROC [w: Handle, box: Window.Box] RETURNS [r: RecList] = { box.place.x ← w.place.x + box.place.x; box.place.y ← w.place.y + box.place.y; r ← Alloc[]; r↑ ← [link: NIL, box: WindowToScreenBox[box]]}; -- operations Append: PUBLIC PROC [list1, list2: RecList] RETURNS [list3: RecList] = { next: RecList; IF list1 = NIL THEN RETURN[list2]; IF list2 = NIL THEN RETURN[list1]; list1 ← Bite[list: list1, biter: Copy[list2]]; IF list1 = NIL THEN RETURN[list2]; FOR r: RecList ← list1, next DO next ← r.link; IF next = NIL THEN {r.link ← list2; RETURN[list1]}; ENDLOOP}; Copy: PUBLIC PROC [old: RecList] RETURNS [new: RecList ← NIL] = { FOR r: RecList ← old, r.link UNTIL r = NIL DO copy: RecList = Alloc[]; copy↑ ← r↑; copy.link ← new; new ← copy; ENDLOOP}; CleanInvalid: PUBLIC PROC [window: Handle] RETURNS [invalid: RecList ← NIL] = { -- This is where boxesCount logic can be hidden... this: RecList; FOR this ← window.invalid, window.invalid UNTIL this = NIL DO window.invalid ← this.link; this.link ← NIL; this.src ← this.dst ← NIL; invalid ← Append[invalid, this]; ENDLOOP; RETURN[window.invalid ← invalid]}; ClipBox: PUBLIC PROC [window: Handle, box: RecList] RETURNS [clippedBox: RecList, lastParent: Handle] = { next: Handle; IF window = NIL THEN {SIGNAL ClientBug[nilWindow]; RETURN[box, NIL]}; IF ~window.inTree THEN {SIGNAL ClientBug[notInTree]; RETURN[box, NIL]}; IF window = WindowOps.rootWindow THEN RETURN[box, NIL]; IF (next ← window.parent) = WindowOps.rootWindow THEN RETURN[box, window]; clippedBox ← box; FOR lastParent ← next, next UNTIL lastParent = NIL DO box: RecList = Convert[lastParent]; temp: RecList = Intersect[box, clippedBox]; FreeRecList[box]; FreeRecList[clippedBox]; clippedBox ← temp; IF (next ← lastParent.parent) = WindowOps.rootWindow THEN RETURN; ENDLOOP; SIGNAL ClientBug[notInTree]}; Disjoint: PUBLIC PROC [list1, list2: RecList] RETURNS [BOOLEAN] = { IF list1 = NIL OR list2 = NIL THEN RETURN[TRUE]; FOR r1: RecList ← list1, r1.link UNTIL r1 = NIL DO FOR r2: RecList ← list2, r2.link UNTIL r2 = NIL DO box: WindowOps.ScreenBox; box.left ← MAX[r1.box.left, r2.box.left]; box.top ← MAX[r1.box.top, r2.box.top]; box.right ← MIN[r1.box.right, r2.box.right]; box.bottom ← MIN[r1.box.bottom, r2.box.bottom]; IF box.left < box.right AND box.top < box.bottom THEN RETURN[FALSE]; ENDLOOP; REPEAT FINISHED => RETURN[TRUE] ENDLOOP}; Intersect: PUBLIC PROC [list1, list2: RecList] RETURNS [list3: RecList ← NIL] = { FOR r1: RecList ← list1, r1.link UNTIL r1 = NIL DO FOR r2: RecList ← list2, r2.link UNTIL r2 = NIL DO box: WindowOps.ScreenBox; box.left ← MAX[r1.box.left, r2.box.left]; box.top ← MAX[r1.box.top, r2.box.top]; box.right ← MIN[r1.box.right, r2.box.right]; box.bottom ← MIN[r1.box.bottom, r2.box.bottom]; IF box.left < box.right AND box.top < box.bottom THEN { new: RecList = Alloc[]; new↑ ← [ box: box, link: list3, src: SetUnder[r1.src, r2.src], dst: SetUnder[r1.dst, r2.dst]]; list3 ← new}; ENDLOOP; ENDLOOP}; SetUnder: PROC [w1, w2: Handle] RETURNS [Handle] = INLINE { IF w1 = NIL THEN RETURN[w2]; IF w2 # NIL AND w1 # w2 THEN SIGNAL ClientBug[ambiguous]; RETURN[w1]}; Shift: PUBLIC PROC [list: RecList, dx, dy: INTEGER] RETURNS [r: RecList] = { -- This used to clip to bitmap bounds. Should it? FOR r: RecList ← list, r.link UNTIL r = NIL DO r.box.left ← r.box.left + dx; r.box.top ← r.box.top + dy; r.box.right ← r.box.right + dx; r.box.bottom ← r.box.bottom + dy; ENDLOOP; RETURN[list]}; Blt: PUBLIC PROC [list: RecList, dx, dy: INTEGER] = { disjointItems: BOOLEAN = dy # 0; south: BOOLEAN = dy > 0; east: BOOLEAN = dx > 0; dir: {nw, ne, se, sw} = IF east THEN IF south THEN se ELSE ne ELSE IF south THEN sw ELSE nw; flags: BitBlt.BitBltFlags ← [ direction: IF south OR (~disjointItems AND east) THEN backward ELSE forward, disjoint: FALSE, disjointItems: disjointItems, gray: FALSE, srcFunc: null, dstFunc: null, reserved: 0]; WHILE list # NIL DO prev: RecList ← NIL; didSomething: BOOLEAN ← FALSE; next: RecList; FOR r: RecList ← list, next UNTIL r = NIL DO left: INTEGER = r.box.left; top: INTEGER = r.box.top; bottom: INTEGER = r.box.bottom; right: INTEGER = r.box.right; next ← r.link; FOR spoiler: RecList ← list, spoiler.link UNTIL spoiler = NIL DO IF spoiler = r THEN LOOP; SELECT dir FROM nw => IF spoiler.box.left < right AND spoiler.box.top < bottom THEN { prev ← r; EXIT}; ne => IF spoiler.box.right > left AND spoiler.box.top < bottom THEN { prev ← r; EXIT}; sw => IF spoiler.box.left < right AND spoiler.box.bottom > top THEN { prev ← r; EXIT}; se => IF spoiler.box.right > left AND spoiler.box.bottom > top THEN { prev ← r; EXIT}; ENDCASE; REPEAT FINISHED => { width: INTEGER = right - left; height: INTEGER = bottom - top; src, dst: LONG POINTER; srcOffset, srcBit, dstOffset, dstBit, srcBpl, dstBpl: INTEGER; Calc: PROC [w: Handle, offset, dy: INTEGER] RETURNS [LONG POINTER, INTEGER] = INLINE { ctx: SpecialDisplay.SpecialContext; IF w = NIL THEN ctx ← SpecialDisplay.defaultContext↑ ELSE { ctx.wpl ← (CARDINAL[w.box.dims.w] + 31)/16; ctx.bpl ← ctx.wpl*16; ctx.bmAddress ← LOOPHOLE[ Window.GetBitmapUnder[w] - w.place.x/16 - Inline.LongMult[ctx.wpl, w.place.y]]; ctx.alloc ← NIL; ctx.free ← NIL}; IF flags.disjoint OR flags.direction = forward THEN RETURN[ ctx.bmAddress + offset + WindowOps.SpecialTimesWpl[top+dy, @ctx], ctx.bpl]; RETURN[ ctx.bmAddress + offset + WindowOps.SpecialTimesWpl[bottom+dy-1, @ctx], -ctx.bpl]}; [srcOffset, srcBit] ← WindowOps.DIVMOD16[CARDINAL[left]]; [dstOffset, dstBit] ← WindowOps.DIVMOD16[CARDINAL[left + dx]]; flags.disjoint ← ABS[dy] > height OR ABS[dx] > width OR ((r.dst # NIL OR r.src # NIL) AND r.dst # r.src); [src, srcBpl] ← Calc[r.src, srcOffset, 0]; [dst, dstBpl] ← Calc[r.dst, dstOffset, dy]; WindowOps.bbPtr↑ ← [ dst: [word: dst, bit: dstBit], dstBpl: dstBpl, src: [word: src, bit: srcBit], srcDesc: [srcBpl[srcBpl]], flags: flags, height: height, width: width]; BitBlt.BITBLT[WindowOps.bbPtr]; didSomething ← TRUE; IF prev = NIL THEN list ← next ELSE prev.link ← next; Free[r]}; ENDLOOP; REPEAT FINISHED => { CouldntBlt: SIGNAL = CODE; IF ~didSomething THEN SIGNAL CouldntBlt}; ENDLOOP; ENDLOOP}; Coalesce: PUBLIC PROC [r: RecList] RETURNS [new: RecList] = { changesMade: BOOLEAN ← TRUE; dummy: RecList; -- sort IF r = NIL OR r.link = NIL THEN RETURN[r]; dummy ← Alloc[]; dummy↑ ← [box: [0, 0, 0, 0], link: NIL]; new ← dummy; -- first, sort by upper left corner UNTIL r = NIL DO cur: RecList = r; candidate: RecList ← new; r ← cur.link; FOR this: RecList ← new, this.link UNTIL this = NIL DO IF cur.box.top >= this.box.top AND cur.box.left >= this.box.left THEN candidate ← this; ENDLOOP; cur.link ← candidate.link; candidate.link ← cur; ENDLOOP; IF new = dummy THEN {new ← dummy.link; Free[dummy]} ELSE {BadSort: ERROR = CODE; ERROR BadSort}; -- ASSERT[rectangles below and to the right of r come after it in the list] << A heuristic for merging rectangles: try to merge down first, since the biting algorithms produce more breakage in the y-axis. >> WHILE changesMade DO that, pred: RecList; changesMade ← FALSE; FOR this: RecList ← new, this.link UNTIL this = NIL DO { -- try to merge down FOR pred ← this, pred.link UNTIL pred = NIL DO IF (that ← pred.link) = NIL THEN EXIT; IF this.box.bottom = that.box.top AND this.box.left = that.box.left AND this.box.right = that.box.right AND this.src = that.src AND this.dst = that.dst THEN { this.box.bottom ← that.box.bottom; GOTO FreeThat}; ENDLOOP; -- try to merge right FOR pred ← this, pred.link UNTIL pred = NIL DO IF (that ← pred.link) = NIL THEN EXIT; IF this.box.right = that.box.left AND this.box.top = that.box.top AND this.box.bottom = that.box.bottom AND this.src = that.src AND this.dst = that.dst THEN { this.box.right ← that.box.right; GOTO FreeThat}; ENDLOOP; EXITS FreeThat => { changesMade ← TRUE; pred.link ← that.link; Free[that]}}; ENDLOOP; ENDLOOP}; -- things that bite out rectangles Bite: PUBLIC PROC [list, biter: RecList] RETURNS [newList: RecList ← NIL] = { prev: RecList ← NIL; next: RecList; FOR l: RecList ← list, next UNTIL l = NIL DO lRemaining: BOOLEAN ← TRUE; boxes: RecList ← NIL; next ← l.link; FOR b: RecList ← biter, b.link UNTIL b = NIL DO thisBox: WindowOps.Rectangle ← b↑; thisBox.link ← NIL; CheckOldBites[@boxes, @thisBox]; IF lRemaining AND BiteOut[l, @thisBox, @boxes] THEN { IF l = list THEN list ← next ELSE prev.link ← next; Free[l]; lRemaining ← FALSE}; REPEAT FINISHED => IF lRemaining THEN prev ← l; ENDLOOP; newList ← SimpleAppend[newList, boxes]; ENDLOOP; FreeRecList[biter]; RETURN[SimpleAppend[newList, list]]}; BiteOutSiblings: PROC [ sib, me: Handle, list, under: RecList, what: RecOps.What] RETURNS [newList, newUnder: RecList ← NIL] = { << Constructs a RecList that consists of the sibling boxes, then calls Bite to do the work. If one of the siblings has a BMU, escape to the hard case. Easy case doesn't touch under. >> sibList: RecList ← NIL; IF list = NIL THEN RETURN[list, under]; FOR w: Handle ← sib, w.sibling UNTIL w = me DO thisSib: RecList; IF w.underNow AND what # null THEN { FreeRecList[sibList]; RETURN BiteOutSiblingsTheHardWay[sib, me, list, under, what]}; -- sibList ← SimpleAppend[Convert[w], sibList]... thisSib ← Convert[w]; thisSib.link ← sibList; sibList ← thisSib; ENDLOOP; RETURN[Bite[list, sibList], under]}; BiteOutSiblingsTheHardWay: PROC [ sib, me: Handle, list, under: RecList, what: RecOps.What] RETURNS [newList, newUnder: RecList ← NIL] = { << Recursively calls itself until it reaches me, then unwinds the call stack. The effect of this is that the sibling leading to me is processed in reverse order. The bite-out code is a clone of BiteOut that also saves the intersection in newUnder if sib is a BMU. Consumes list and under >> saveUnder: BOOLEAN; biter: WindowOps.ScreenBox; AddBox: PROC [left, top, right, bottom: INTEGER, from: RecList] = { b: RecList = Alloc[]; b↑ ← [ link: newList, box: [left, top, right, bottom], src: from.src, dst: from.dst]; newList ← b}; AddUnderBox: PROC [left, top, right, bottom: INTEGER, from: RecList] = { b: RecList = Alloc[]; b↑ ← [ link: newUnder, box: [left, top, right, bottom], src: IF what = src THEN sib ELSE from.src, dst: IF what = dst THEN sib ELSE from.dst]; newUnder ← b}; SELECT TRUE FROM sib = me => RETURN[list, under]; sib = NIL => { WrongSiblings: SIGNAL = CODE; SIGNAL WrongSiblings; RETURN[list, under]}; list = NIL => RETURN[list, under]; -- optimization ENDCASE; -- call self recursively for list traversal... [list, newUnder] ← BiteOutSiblingsTheHardWay[ sib: sib.sibling, me: me, list: list, under: under, what: what]; -- bite out sib from list, saving bitten pieces on newUnder if saveUnder saveUnder ← sib.underNow AND what # null; biter ← WindowToScreenBox[[sib.place, sib.box.dims]]; FOR r: RecList ← list, r.link UNTIL r = NIL DO lIn: CARDINAL = SELECT TRUE FROM biter.left >= r.box.right => DisjointBoxes, biter.right <= r.box.left => DisjointBoxes, biter.left <= r.box.left => 0, ENDCASE => LeftIn; rIn: CARDINAL = IF biter.right < r.box.right THEN RightIn ELSE 0; tIn: CARDINAL = SELECT TRUE FROM biter.top >= r.box.bottom => DisjointBoxes, biter.bottom <= r.box.top => DisjointBoxes, biter.top <= r.box.top => 0, ENDCASE => TopIn; bIn: CARDINAL = IF biter.bottom < r.box.bottom THEN BottomIn ELSE 0; SELECT lIn + rIn + tIn + bIn FROM -- what's obscured? 0 => -- everything IF saveUnder THEN AddUnderBox[ r.box.left, r.box.top, r.box.right, r.box.bottom, r]; LeftIn => { -- right AddBox[r.box.left, r.box.top, biter.left, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, r.box.top, r.box.right, r.box.bottom, r]}; BottomIn => { -- top AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ r.box.left, r.box.top, r.box.right, biter.bottom, r]}; TopIn => { -- bottom AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; IF saveUnder THEN AddUnderBox[ r.box.left, biter.top, r.box.right, r.box.bottom, r]}; RightIn => { -- left AddBox[biter.right, r.box.top, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ r.box.left, r.box.top, biter.right, r.box.bottom, r]}; LeftIn + RightIn => { -- vertical strip AddBox[r.box.left, r.box.top, biter.left, r.box.bottom, r]; AddBox[biter.right, r.box.top, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, r.box.top, biter.right, r.box.bottom, r]}; TopIn + BottomIn => { -- horizontal strip AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ r.box.left, biter.top, r.box.right, biter.bottom, r]}; RightIn + BottomIn => { -- top left AddBox[biter.right, r.box.top, r.box.right, biter.bottom, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ r.box.left, r.box.top, biter.right, biter.bottom, r]}; LeftIn + BottomIn => { -- top right AddBox[r.box.left, r.box.top, biter.left, biter.bottom, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, r.box.top, r.box.right, biter.bottom, r]}; RightIn + TopIn => { -- bottom left AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[biter.right, biter.top, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ r.box.left, biter.top, biter.right, r.box.bottom, r]}; LeftIn + TopIn => { -- bottom right AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[r.box.left, biter.top, biter.left, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, biter.top, r.box.right, r.box.bottom, r]}; RightIn + BottomIn + TopIn => { -- left edge AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[biter.right, biter.top, r.box.right, biter.bottom, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ r.box.left, biter.top, biter.right, biter.bottom, r]}; LeftIn + BottomIn + TopIn => { -- right edge AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[r.box.left, biter.top, biter.left, biter.bottom, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, biter.top, r.box.right, biter.bottom, r]}; RightIn + LeftIn + TopIn => { -- bottom edge AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[r.box.left, biter.top, biter.left, r.box.bottom, r]; AddBox[biter.right, biter.top, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, biter.top, biter.right, r.box.bottom, r]}; RightIn + LeftIn + BottomIn => { -- top edge AddBox[r.box.left, r.box.top, biter.left, biter.bottom, r]; AddBox[biter.right, r.box.top, r.box.right, biter.bottom, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, r.box.top, biter.right, biter.bottom, r]}; RightIn + TopIn + BottomIn + LeftIn => { -- center AddBox[r.box.left, r.box.top, r.box.right, biter.top, r]; AddBox[r.box.left, biter.top, biter.left, biter.bottom, r]; AddBox[biter.right, biter.top, r.box.right, biter.bottom, r]; AddBox[r.box.left, biter.bottom, r.box.right, r.box.bottom, r]; IF saveUnder THEN AddUnderBox[ biter.left, biter.top, biter.right, biter.bottom, r]}; ENDCASE => -- disjoint AddBox[r.box.left, r.box.top, r.box.right, r.box.bottom, r]; ENDLOOP; FreeRecList[list]}; Visible: PUBLIC PROC [ w: Handle, list: RecList, what: RecOps.What, biteChildren: BOOLEAN ← FALSE] RETURNS [newList: RecList] = { abs: RecList ← Convert[w]; underList: RecList ← NIL; newList ← Intersect[list, abs]; FreeRecList[list]; IF biteChildren AND w.child # NIL THEN [newList, underList] ← BiteOutSiblings[ sib: w.child, me: NIL, list: newList, under: underList, what: what]; IF w.parent # NIL THEN { [newList, underList] ← BiteOutSiblings[ sib: w.parent.child, me: w, list: newList, under: underList, what: what]; FOR p: Handle ← w.parent, p.parent DO temp: RecList; -- Trim box to parent Free[abs]; abs ← Convert[p]; temp ← Intersect[abs, newList]; FreeRecList[newList]; newList ← temp; temp ← Intersect[abs, underList]; FreeRecList[underList]; underList ← temp; -- Bite out uncles IF p.parent = NIL THEN EXIT; [newList, underList] ← BiteOutSiblings[ sib: p.parent.child, me: p, list: newList, under: underList, what: what]; ENDLOOP}; Free[abs]; RETURN[SimpleAppend[underList, newList]]}; CheckOldBites: PROC [boxes: POINTER TO RecList, biter: RecList] = { << we have to check the biter against all the rectangles created by the previous bites. They are on the boxes list. >> prev: RecList ← NIL; next: RecList; FOR r: RecList ← boxes↑, next UNTIL r = NIL DO next ← r.link; IF BiteOut[r, biter, boxes] THEN { IF prev = NIL THEN boxes↑ ← next ELSE prev.link ← next; Free[r]} ELSE prev ← r; ENDLOOP}; SimpleAppend: PUBLIC PROC [list1, list2: RecList] RETURNS [list3: RecList] = { next: RecList; IF list1 = NIL THEN RETURN[list2]; IF list2 = NIL THEN RETURN[list1]; FOR r: RecList ← list1, next DO next ← r.link; IF next = NIL THEN {r.link ← list2; RETURN[list1]}; ENDLOOP}; LeftIn: CARDINAL = 1; RightIn: CARDINAL = 2; TopIn: CARDINAL = 4; BottomIn: CARDINAL = 8; DisjointBoxes: CARDINAL = 100B; BiteOut: PROC [list: RecList, clip: RecList, boxes: POINTER TO RecList] RETURNS [delete: BOOLEAN] = { << Bites out the clip box from a single rectangle (list). Any new boxes are prepeneded to boxes↑. If delete is true, the rectangle passed in has been completely clipped. BiteOut changes list.box but does not touch the link. >> NewRect: PROC RETURNS [r: RecList] = { r ← Alloc[]; r↑ ← list↑; r.link ← boxes↑; boxes↑ ← r}; lIn: CARDINAL = SELECT TRUE FROM clip.box.left >= list.box.right => DisjointBoxes, clip.box.right <= list.box.left => DisjointBoxes, clip.box.left <= list.box.left => 0, ENDCASE => LeftIn; rIn: CARDINAL = IF clip.box.right < list.box.right THEN RightIn ELSE 0; tIn: CARDINAL = SELECT TRUE FROM clip.box.top >= list.box.bottom => DisjointBoxes, clip.box.bottom <= list.box.top => DisjointBoxes, clip.box.top <= list.box.top => 0, ENDCASE => TopIn; bIn: CARDINAL = IF clip.box.bottom < list.box.bottom THEN BottomIn ELSE 0; SELECT lIn + rIn + tIn + bIn FROM 0 => RETURN[TRUE]; -- all of w was in clip LeftIn => list.box.right ← clip.box.left; -- clipped on right BottomIn => list.box.top ← clip.box.bottom; -- clipped on top TopIn => list.box.bottom ← clip.box.top; -- clipped on bottom RightIn => list.box.left ← clip.box.right; -- clipped on left LeftIn + RightIn => { -- vertical strip from middle n: RecList = NewRect[]; list.box.right ← clip.box.left; n.box.left ← clip.box.right}; TopIn + BottomIn => { -- horizontal strip from middle n: RecList = NewRect[]; list.box.bottom ← clip.box.top; n.box.top ← clip.box.bottom}; RightIn + BottomIn => { -- upper left-hand corner n: RecList = NewRect[]; list.box.top ← clip.box.bottom; n.box.bottom ← clip.box.bottom; n.box.left ← clip.box.right}; LeftIn + BottomIn => { -- upper right-hand corner n: RecList = NewRect[]; list.box.top ← clip.box.bottom; n.box.bottom ← clip.box.bottom; n.box.right ← clip.box.left}; RightIn + TopIn => { -- lower left-hand corner n: RecList = NewRect[]; list.box.bottom ← clip.box.top; n.box.top ← clip.box.top; n.box.left ← clip.box.right}; LeftIn + TopIn => { -- lower right-hand corner n: RecList = NewRect[]; list.box.bottom ← clip.box.top; n.box.top ← clip.box.top; n.box.right ← clip.box.left}; RightIn + BottomIn + TopIn => { -- left edge n: RecList = NewRect[]; m: RecList = NewRect[]; list.box.left ← clip.box.right; n.box.bottom ← clip.box.top; n.box.right ← clip.box.right; m.box.top ← clip.box.bottom; m.box.right ← clip.box.right}; LeftIn + BottomIn + TopIn => { -- right edge n: RecList = NewRect[]; m: RecList = NewRect[]; list.box.right ← clip.box.left; n.box.bottom ← clip.box.top; n.box.left ← clip.box.left; m.box.top ← clip.box.bottom; m.box.left ← clip.box.left}; RightIn + LeftIn + TopIn => { -- bottom edge n: RecList = NewRect[]; m: RecList = NewRect[]; list.box.bottom ← clip.box.top; n.box.right ← clip.box.left; n.box.top ← clip.box.top; m.box.left ← clip.box.right; m.box.top ← clip.box.top}; RightIn + LeftIn + BottomIn => { -- top edge n: RecList = NewRect[]; m: RecList = NewRect[]; list.box.top ← clip.box.bottom; n.box.right ← clip.box.left; n.box.bottom ← clip.box.bottom; m.box.left ← clip.box.right; m.box.bottom ← clip.box.bottom}; RightIn + TopIn + BottomIn + LeftIn => { -- center n: RecList = NewRect[]; m: RecList = NewRect[]; l: RecList = NewRect[]; list.box.bottom ← clip.box.top; n.box.top ← clip.box.bottom; m.box.top ← clip.box.top; m.box.bottom ← clip.box.bottom; m.box.right ← clip.box.left; l.box.top ← clip.box.top; l.box.bottom ← clip.box.bottom; l.box.left ← clip.box.right}; ENDCASE; -- left w alone RETURN[FALSE]}; -- allocator FreeHandle: TYPE = LONG POINTER TO FreeObject; FreeObject: TYPE = RECORD [link: FreeHandle]; Header: TYPE = LONG POINTER TO HeaderObject; HeaderObject: TYPE = RECORD [ link: Header, cnt: CARDINAL, list: ARRAY [0..0) OF WindowOps.Rectangle ← NULL]; Max: CARDINAL = WordsAvailable/WindowOps.Rectangle.SIZE; TileSize: CARDINAL = 4; WordsAvailable: CARDINAL = Environment.wordsPerPage*TileSize - HeaderObject.SIZE; allocHead: Header ← SpecialWindow.GetPages[TileSize]; freeList: FreeHandle ← NIL; Alloc: PUBLIC ENTRY PROC RETURNS [lp: RecList] = { ENABLE UNWIND => NULL; IF freeList # NIL THEN { lp ← LOOPHOLE[freeList]; freeList ← freeList.link; RETURN}; IF allocHead.cnt = Max THEN { new: Header ← SpecialWindow.GetPages[TileSize]; new↑ ← [link: allocHead, cnt: 0]; allocHead ← new}; lp ← @allocHead.list[allocHead.cnt]; allocHead.cnt ← allocHead.cnt + 1; RETURN[lp]}; Free: PUBLIC ENTRY PROC [lp: RecList] = { ENABLE UNWIND => NULL; LOOPHOLE[lp, FreeHandle].link ← freeList; freeList ← LOOPHOLE[lp]}; FreeRecList: PUBLIC ENTRY PROC [list: RecList] = { ENABLE UNWIND => NULL; next: RecList; FOR nil: RecList ← list, next UNTIL nil = NIL DO next ← nil.link; LOOPHOLE[nil, FreeHandle].link ← freeList; freeList ← LOOPHOLE[nil]; ENDLOOP}; debug: BOOLEAN = FALSE; CheckForLeaks: PUBLIC ENTRY PROC [useTree: BOOLEAN, cache: RecList] = { ENABLE UNWIND => NULL; Leak: SIGNAL = CODE; IF debug THEN { allocated, returned, busy, cached: CARDINAL ← 0; FOR h: Header ← allocHead, h.link UNTIL h = NIL DO allocated ← allocated + h.cnt; ENDLOOP; FOR f: FreeHandle ← freeList, f.link UNTIL f = NIL DO returned ← returned + 1; ENDLOOP; FOR r: RecList ← cache, r.link UNTIL r = NIL DO cached ← cached + 1; ENDLOOP; IF useTree THEN { CountBusy: PROC [w: Handle] = { FOR r: RecList ← w.invalid, r.link UNTIL r = NIL DO busy ← busy + 1; ENDLOOP; FOR r: RecList ← w.badPhosphor, r.link UNTIL r = NIL DO busy ← busy + 1; ENDLOOP}; Window.EnumerateTree[WindowOps.rootWindow, CountBusy]}; IF allocated # busy + returned + cached THEN SIGNAL Leak; useTree ← FALSE -- for Ed -- }}; allocHead↑ ← [link: NIL, cnt: 0]; END.