(* Copyright (C) 1992, Digital Equipment Corporation                         *)
(* All rights reserved.                                                      *)
(* See the file COPYRIGHT for a full description.                            *)
(*                                                                           *)
(* VBTRep.def, code Sun Aug 11 12:18:43 1985 by Greg Nelson *)
(* Last modified on Mon Dec 21 17:55:03 PST 1992 by msm     *)
(*      modified on Mon Feb 24 13:59:00 PST 1992 by muller  *)
(*      modified on Thu Jan 23 10:02:46 PST 1992 by gnelson *)
(*      modified on Tue Jul 24 13:15:10 PDT 1990 by steveg *)

<*PRAGMA LL*>

(* The "VBTRep" interface defines the representation of "VBTs", and provides
   operations that are useful for implementing low-level or esoteric
   split classes. *)

INTERFACE VBTRep;

IMPORT Batch, Cursor, PropertyV, Rect, Region, 
  ScreenType, ScrnPaintOp, ScrnCursor, ScrnPixmap, 
  ScrnFont, VBT, VBTClass, Word, PaintPrivate, Axis, Palette,
  PaintOp, Font, Pixmap;

CONST EmptyCage = VBT.EmptyCage;

TYPE
  MiscRef = REF MiscRec;
  MiscRec = RECORD
    cage := EmptyCage;
    badRgn := Region.Empty;
    rpseqno: Word.T;
    oldDomain := Rect.Empty;
    link: MiscRef := NIL
  END;

(* To save space in a "VBT", the "cage", "badRgn", "rpseqno", and 
   "oldDomain" fields are only stored if at least one of them has an 
   ``unusual'' value.  This is achieved by including a "MiscRef" in the 
   "VBT" object.  If the "MiscRef" is "NIL", then the "badRgn" and "oldDomain"
   are empty, the "rpseqno" is irrelevant, and the cage is determined
   from the "cagetype" field as follows: if the "cagetype" is
   "VBT.CageType.Rectangle", then the cage rectangle is assumed to be
   "Rect.Empty"; otherwise the rectangle is irrelevant.
   
   The "rpseqno" field is the <I>repainting sequence number</I>.  It is
   incremented whenever the "badRgn" is expanded and recorded before
   activating a "repaint" method.  Thus when the repaint method returns,
   the current value can be compared with the recorded value to
   determine whether the current "badRgn" is the one that the "repaint"
   method responded to, or whether a new bad region arrived while the
   client was responding to the old one.  *)

TYPE
  Prop =
    {EscapePending, Reshaping, RepaintPending, OnQ, 
     Covered, Combiner, ShortCircuit, CageCovered, 
     Marked, ExcessBegins, HasNewShape, BlockNewShape,
     EscapeCovered};
  Props = SET OF Prop;

CONST
  AllProps = Props{FIRST(Prop)..LAST(Prop)};
  NoProps = Props{};

(* Here is the meaning of the properties:

   "EscapePending": Set when a thread of control is forked to deliver a
   cage escape to gone. Set to "FALSE" when any position is delivered.

   "Reshaping": set when the "VBT" has a non-empty old domain.

   "RepaintPending": Set when a thread of control exists that will deliver
   the bad region.

   "OnQ": Set when the "VBT" is on the "Metermaid"'s to-be-serviced queue.

   "Covered":  Ordinarily painting into an empty batch will put the
   "VBT" on the "Metermaid"'s queue and set "onQ".  If the "covered"
   bit is set, this will not happen.  For example, "PutPosition" sets
   "covered" before passing the position to child, and clears it and
   forces the batch before it returns.  Only set during event delivery,
   so any action that could be deferred until after event delivery can
   check "covered" and clean up on method exit.

   "Combiner": Set to indicate that this "VBT" is a good place to pile up
   small paint batches, even if it is not covered. Trestle sets this bit
   in one "VBT" near the root of a client address space to avoid shipping
   many small batches across "RPC".

   "ShortCircuit":  Set on a "VBT" when painting on the VBT can be
   implemented by clipping to the "VBT"'s domain and painting on its 
   parent.

   "Marked": set by "VBT.Mark".

   "CageCovered": "VBTClass.PutPosition" sets this bit on a "VBT" before 
   calling its position method; after calling the position method, 
   the procedure relays the child's cage to the parent and clears 
   the bit.  "VBTClass.SetCage" notices the bit and omits relaying the 
   cage to the parent.  

   "ExcessBegins": Set when "excessBegins > 0".

   "HasNewShape" is set when "VBT.NewShape" is called, and cleared by
   a call to "VBTClass.HasNewShape" or "VBTClass.GetShape". 

   If "BlockNewShape" is set, "VBT.NewShape" calls will not be relayed
   to the parent of the "VBT".
 *)

REVEAL VBT.Prefix = 
  VBTClass.Prefix BRANDED OBJECT
    <* LL >= {SELF} *>
    cursor := Cursor.DontCare;
    cageType: (*BITS 16 FOR*) VBTClass.VBTCageType
      := VBTClass.VBTCageType.Gone;
    props: (*BITS 16 FOR*) Props := NoProps;
    batch: Batch.T := NIL;
    remaining: INTEGER := 0; 
    propset: PropertyV.Set := NIL;
    miscRef: MiscRef := NIL;
  OVERRIDES
    getcursor := GetcursorDefault;
    axisOrder := AxisOrderDefault;
  END;

(* The "batch" field contains the batch of uncompleted painting commands
   for the "VBT", and "remaining" contains the number of free addressable
   units remaining in the "batch".  In particular, if "remaining # 0", the
   "batch" field is not "NIL".  The "miscRef" field is always "NIL"
   if the parent is "NIL".  *)

REVEAL VBT.ScreenType <: STPub;

TYPE STPub = 
  ScreenType.Public OBJECT
    ops: REF ARRAY OF ScrnPaintOp.T;
    cursors: REF ARRAY OF ScrnCursor.T;
    pixmaps: REF ARRAY OF ScrnPixmap.T;
    fonts: REF ARRAY OF ScrnFont.T
  METHODS
    opApply(cl: Palette.OpClosure; op: PaintOp.T): ScrnPaintOp.T;
    cursorApply(cl: Palette.CursorClosure; cs: Cursor.T): ScrnCursor.T;
    pixmapApply(cl: Palette.PixmapClosure; pm: Pixmap.T): ScrnPixmap.T;
    fontApply(cl: Palette.FontClosure; ft: Font.T): ScrnFont.T
  END;

(* The tables "st.ops", "st.fonts", "st.cursors", and "st.pixmaps" are
   collectively called the screentype's <I>palette</I>.  They are used
   to translate between screen-independent resources and
   screen-dependent resources.  For example, recall that "Pixmap.Gray"
   is a record containing the integer field "Pixmap.Gray.pm".  The
   screen-dependent equivalent of "Pixmap.Gray" on the screentype "st"
   is simply "st.pixmaps[Pixmap.Gray.pm]".  When creating the palette,
   the above apply methods are called for all resources; if cl is NIL, the
   resource is built-in.  The default values for these return the result of
   invoking the closure or the built-in method; your procedure must not
   return NIL when invoked on a built-in. *)
   
TYPE OffscreenType = VBT.ScreenType OBJECT st: VBT.ScreenType END;

(* An "OffscreenType" "s", is a screen type that is derived from the screen
   type "s.st".  An OffscreenType will be replaced by its associated
   screentype in calls to "Trestle.InstallOffscreen".  The "st" field
   is read-only after creation. *)

PROCEDURE CheckMisc(v: VBT.T); <* LL >= {v} *>
(* Set "v.misc := NIL" if "v"'s "badRgn" and "oldDomain" are empty and its
   cage type is not "Rect". *)

PROCEDURE CreateMisc(v: VBT.T); <* LL >= {v} *>
(* If "v.misc = NIL", then create a "misc" for "v" with empty "badRgn" and
   "oldDomain", and with appropriate cage. Otherwise, do nothing. *)

PROCEDURE DestroyMisc(v: VBT.T);
<* LL >= {v, v.parent} *>
(* Set "v"'s misc to "NIL", clearing "Reshaping" from "v.props". *)

PROCEDURE NewBatch(v: VBT.T; len: INTEGER := -1); 
<* LL.sup = v *>
(* Force "v"'s batch if it is non-nil and allocate a new batch for it
   of size at least "len", or of size "VBTTuning.BatchSize" if "len=-1".  *)

PROCEDURE ForceBatch(v: VBT.T); <* LL.sup = v *>
(* Force "v"'s batch if it is non-nil, and leave it nil. *)

PROCEDURE CancelBatch(v: VBT.T); <* LL.sup = v *>
(* Free "v"'s batch and set it to "NIL". *)

PROCEDURE Enqueue(v: VBT.T); <* LL.sup = v *>
(* Place "v" on the list of "VBTs" scheduled to be serviced by the
   MeterMaid. *)

PROCEDURE GetcursorDefault(v: VBT.Prefix): ScrnCursor.T;
(* Return the result of resolving "cursor(v)" using "v"'s screentype. *)

PROCEDURE AxisOrderDefault(v: VBT.Prefix): Axis.T;
(* Return "Axis.T.Hor". *)

PROCEDURE ExpandBadRect(w: VBT.T; 
  READONLY clp: Rect.T; ba: Batch.T);
<* LL.sup = w *>
(* Expand "w"'s bad region for "ba". *)

(* In "ExpandBadRect", the rectangle "clp" is the original clipping
   rectangle for "ba", before intersection with "w.domain".  The
   expansion is caused by (a) using out-of-domain bits as source (b)
   painting into the old domain (c) scrolling an existing bad rectangle.
   *)
     
PROCEDURE ExtendBatch(v: VBT.T; VAR ba: Batch.T);
(* Extend "v"'s batch to include the painting operations in "ba", and free
   "ba". It is assumed that "v" has a non-empty batch which has room for
   the extension. *)

PROCEDURE MaxRepeat(v: VBT.T): CARDINAL;
<* LL.sup = v *>
(* Return the number of RepeatRec's that can fit in "v"'s current batch.
   *)

PROCEDURE PaintRepeat(v: VBT.T; 
  READONLY clip: ARRAY OF Rect.T);
<* LL.sup = v *>
(* Add a "RepeatRec" to "v"'s batch for each rectangle in "clip". *)

(* "PaintRepeat" is a checked run-time error if there isn't enough space
   in "v"'s batch.  Calling "PaintRepeat" does not call
   "Enqueue(v)". *)

PROCEDURE PaintSingle(v: VBT.T; READONLY clip: Rect.T; 
  com: PaintPrivate.CommandPtr); <* LL.sup = v *>
(* Add the paint operation referenced by "com" to "v"'s batch, but
   use the clipping rectangle "clip" instead of the one in "com". *)

(* "PaintSingle" forces "v"'s batch if necessary and allocates a new
   one.  It does not call "Enqueue(v)".  The command "com" must not be a
   scroll command.  *)

PROCEDURE Scroll(v: VBT.T; READONLY clip: Rect.T; 
  com: PaintPrivate.ScrollPtr); <* LL.sup = v *>
(* Like "PaintSingle", but "com" must be a scroll command. *)

PROCEDURE Mark(v: VBT.T); <* LL >= {v} *>
(* Identical to "VBT.Mark" except for the locking level. *)

PROCEDURE Redisplay(); <* LL.sup = VBT.mu *>
(* Redisplay and unmark all marked windows whose screentype is non-"NIL".  *)

(* That is, "Redisplay" is equivalent to this loop:

| LOOP
|   WITH m = `an array containing all marked windows` DO
|     IF NUMBER(m) = 0 THEN EXIT END;
|     `Sort "m" in order of non-decreasing depth`;
|     FOR i := 0 TO LAST(m) DO 
|       IF IsMarked(m[i]) AND m[i].st # NIL THEN
|          Unmark(m[i]);
|          m[i].redisplay() 
|       END
|     END
|   END
| END

    The depth of a window is the number of parent pointers that must
    be followed to reach "NIL".  Sorting by depth guarantees that
    ancestors will be redisplayed before their descendants.  The reason
    is that redisplaying an ancestor window often reshapes its
    descendants, and if a descendant is going to be reshaped it would
    be wasteful to redisplay it in its old position.
    
   Ordinarily when a window is marked, a thread is forked that will
   call "Redisplay".  This is wasteful if "Redisplay" will be called
   soon anyway.  Therefore, if you know that "Redisplay" will be called
   soon, you can call "CoverRedisplay", which increments a ``coverage
   counter''.  If the coverage counter is non-zero, marking a "VBT"
   does not fork a thread.  Of course by calling "CoverRedisplay" you
   acquire the obligation to ensure that "Redisplay" will be called
   soon.  Calling "UncoverRedisplay" decrements the counter and calls
   "Redisplay" if the result is zero.  *)
    
PROCEDURE CoverRedisplay(); <* LL.sup = VBT.mu *>
(* Increment the redisplay coverage counter. *)

PROCEDURE UncoverRedisplay(); <* LL.sup = VBT.mu *>
(* Decrement the redisplay coverage counter and call "Redisplay" if
   the result is zero.  *)

END VBTRep.
