-- 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.