-- Pupwatch: non-scrolling display

-- [Indigo]<Grapevine>PupWatch>FunnyDisplay.mesa

-- Andrew Birrell  20-Nov-81 15:30:15

DIRECTORY
AltoDisplay,
Ascii		USING[ CR, SP ],
BitBltDefs,
FrameDefs	USING[ GlobalFrame, IsBound, SwapInCode ],
ImageDefs	USING[ AddCleanupProcedure, CleanupItem, CleanupMask,
		       CleanupProcedure ],
LookerDefs	USING[ PupwatchFont ],
MiscDefs	USING[ DestroyFakeModule ],
SegmentDefs	USING[ FileSegmentHandle, SegmentAddress, SwapIn ],
SystemDefs;

FunnyDisplay: MONITOR
   IMPORTS BitBltDefs, FrameDefs, ImageDefs, LookerDefs, MiscDefs,
           SegmentDefs, SystemDefs
   EXPORTS LookerDefs =

BEGIN

-- Layout of an "AL" font --

Font: TYPE = MACHINE DEPENDENT RECORD [
    maxHeight: CARDINAL,    -- height of tallest char in font (scan lines)
    variableWidth: BOOLEAN, -- IF TRUE, proportionally spaced font
    blank: [0..177B],       -- not used
    maxWidth: [0..377B],    -- width of widest char in font (raster units).
    FCDptrs: FontArray,     -- array of self-relative pointers to
                            -- FCD's. Indexed by char value.
    extFCDptrs: FontArray   -- array of self-relative pointers to
                            -- FCD's for extentions. As large an
                            -- array as needed.
    ];

FontArray: TYPE = ARRAY CHARACTER OF CARDINAL;

FCDptr: TYPE = POINTER TO FCD;

FCD: TYPE = MACHINE DEPENDENT RECORD [
    widthORext: [0..77777B], -- width or extention index
    hasNoExtension: BOOLEAN, -- TRUE=> no ext.;prevfield=width
    height: [0..377B],       -- # scan lines to skip for char
    displacement: [0..377B]  -- displacement back to char bitmap
    ];

GetFont: PROC RETURNS[POINTER TO Font] =
   BEGIN
   IF FrameDefs.IsBound[MiscDefs.DestroyFakeModule]
   THEN BEGIN
        seg: SegmentDefs.FileSegmentHandle;
        offset: CARDINAL;
        [seg,offset] ← 
           MiscDefs.DestroyFakeModule[LOOPHOLE[LookerDefs.PupwatchFont]];
        SegmentDefs.SwapIn[seg];
        RETURN[SegmentDefs.SegmentAddress[seg]+offset]
        END
   ELSE BEGIN -- running in boot file? --
        FrameDefs.SwapInCode[LOOPHOLE[LookerDefs.PupwatchFont]];
        RETURN[ -- assume it came into the MDS! --
          FrameDefs.GlobalFrame[LookerDefs.PupwatchFont].code.shortbase]
        END
   END;

myFont: POINTER TO Font = GetFont[];

fontdesc: POINTER TO FontArray = @myFont.FCDptrs;


-- Cursor and mouse --

CursorData: TYPE = ARRAY [0..16) OF WORD;
cursor:  POINTER TO CursorData = LOOPHOLE[431B];
mouseX:  POINTER TO CARDINAL = LOOPHOLE[424B];
mouseY:  POINTER TO CARDINAL = LOOPHOLE[425B];
cursorX: POINTER TO CARDINAL = LOOPHOLE[426B];
cursorY: POINTER TO CARDINAL = LOOPHOLE[427B];



-- Screen layout --

top:    CARDINAL = 32; --unused screen at top--
bottom: CARDINAL = 32; --unused screen at bottom--
margin: CARDINAL = 4; --bitmap-relative margin, sides and bottom--

wPerLine:   CARDINAL = 2*( 11 );
bPerLine:   CARDINAL = wPerLine * 16;
scanLines:  CARDINAL = (AltoDisplay.MaxScanLines-top-bottom)/2*2;
lineHeight: CARDINAL = myFont.maxHeight;
nLines:     CARDINAL = (scanLines-2*margin)/lineHeight;
topY:       CARDINAL = 1+lineHeight+margin; -- top of typescript --

bitmapWords: CARDINAL = wPerLine * scanLines;
myBitmap:    POINTER = EvenAlloc[bitmapWords];

myDCB: AltoDisplay.DCBHandle =  EvenAlloc[SIZE[AltoDisplay.DCB]];
padDCB: AltoDisplay.DCBHandle = EvenAlloc[SIZE[AltoDisplay.DCB]];

whiteBBT: BitBltDefs.BBptr = EvenAlloc[SIZE[BitBltDefs.BBTable]];
blackBBT: BitBltDefs.BBptr = EvenAlloc[SIZE[BitBltDefs.BBTable]];

PaintRectangle: PROC[x,y: CARDINAL, w,h: CARDINAL,
                     color: {white, black, invert}] =
   BEGIN
   bbt: POINTER TO BitBltDefs.BBTable =
                              IF color = white THEN whiteBBT ELSE blackBBT;
   bbt.dlx ← x; bbt.dty ← y; bbt.dw ← w; bbt.dh ← h;
   bbt.function ← IF color = invert THEN invert ELSE replace;
   BitBltDefs.BITBLT[ bbt ];
   END;



-- Access to Font: character painting and width --

bbtSpace: BitBltDefs.BBTableSpace;

InitBBTSpace: PROC RETURNS[bbt:BitBltDefs.BBptr] =
   BEGIN
   bbt ← BitBltDefs.AlignedBBTable[@bbtSpace];
   bbt↑ ← [sourcetype: block, function: paint, dbca: myBitmap,
           dbmr: wPerLine,
	   sbmr: 1, slx: 0, sty: 0];
   END;

bbt: BitBltDefs.BBptr = InitBBTSpace[]; -- BBT for character painting --

bmState: RECORD[x, y: CARDINAL] ← [x: 0, y: 0 ]; -- pos for next char --

PaintChar: PROCEDURE[char: CHARACTER] = INLINE
   BEGIN
   -- extensions are not supported!
   cw: FCDptr = LOOPHOLE[@fontdesc[char]+fontdesc[char]];
   bbt.sbca ← cw - (bbt.dh ← cw.displacement);
   bbt.dty ← bmState.y + cw.height;
   bmState.x ← (bbt.dlx ← bmState.x) + (bbt.dw ← cw.widthORext);
   BitBltDefs.BITBLT[bbt];
   END;

CharWidth: PROCEDURE[char: CHARACTER] RETURNS[CARDINAL] =
   BEGIN
   -- extensions are not supported!
   cw: FCDptr = LOOPHOLE[@fontdesc[char]+fontdesc[char]];
   RETURN[cw.widthORext]
   END;

EvenAlloc: PROC[words: CARDINAL] RETURNS[res: POINTER] =
   BEGIN
   mem: POINTER = SystemDefs.AllocateHeapNode[words+1];
   res ← LOOPHOLE[ ((LOOPHOLE[mem,CARDINAL]+1)/2)*2, POINTER ];
   END;

charWidth: ARRAY CHARACTER OF CARDINAL;

GetLength: PUBLIC PROC[s: STRING] RETURNS[ length: CARDINAL ] =
   BEGIN
   length ← 0;
   FOR index: CARDINAL IN [0..s.length)
   DO length ← length + charWidth[s[index]] ENDLOOP;
   END;

SetPos: PUBLIC ENTRY PROC[pos: CARDINAL] =
   -- set to raster position relative to start of current line --
   { bmState.x ← margin + pos };

Clear: PUBLIC ENTRY PROC =
   -- Whiten text area of display, and reset next character position --
   BEGIN
   bmState.x ← margin; bmState.y ← topY;
   PaintRectangle[x: 1, y: lineHeight+2, w: bPerLine-2,
                  h: scanLines - (lineHeight+2) - 1, color: white];
   END;



-- Publictext displaying procedures --

WriteChar: PUBLIC PROC[c: CHARACTER] =
   BEGIN
   IF c < Ascii.SP
   THEN BEGIN
        IF c = Ascii.CR
        THEN BEGIN
             bmState.x ← margin; bmState.y ← bmState.y + lineHeight;
             IF bmState.y >= nLines * lineHeight THEN bmState.y ← topY;
             BEGIN
                nextY: CARDINAL ← bmState.y + lineHeight;
                IF nextY >= nLines * lineHeight THEN nextY ← topY;
                PaintRectangle[x: margin, y: nextY,
                               w: bPerLine-2*margin, h: lineHeight,
                               color: white];
             END;
             RETURN
             END
        ELSE c ← '?;
        END;
   IF bmState.x + charWidth[c] <= bPerLine-margin
   THEN PaintChar[c]
   ELSE bmState.x ← bPerLine-margin;
   END;

WriteMultiple: PUBLIC PROC[desc: DESCRIPTOR FOR PACKED ARRAY OF CHARACTER]=
   BEGIN
   FOR i: CARDINAL IN [0..LENGTH[desc])
   DO c: CHARACTER ← desc[i];
      IF c < Ascii.SP THEN c ← '?; -- including CR --
      IF bmState.x + charWidth[c] <= bPerLine-margin
      THEN PaintChar[c]
      ELSE { bmState.x ← bPerLine-margin; EXIT };
   ENDLOOP;
   END;

EraseChar: PUBLIC ENTRY PROC[c: CHARACTER] =
   BEGIN
   IF c < Ascii.SP
   THEN BEGIN
        IF c = Ascii.CR THEN RETURN ELSE c ← '?;
        END;
   bmState.x ← bmState.x - charWidth[c];
   PaintRectangle[x: bmState.x, y: bmState.y,
                  w: charWidth[c], h: lineHeight,
                  color: white];
   END;

WriteTitle: PUBLIC PROC[s: STRING] =
   BEGIN
   oldY: CARDINAL = bmState.y;
   oldX: CARDINAL = bmState.x;
   bmState.y ← 1;
   bmState.x ← margin;
   PaintRectangle[x:margin, y: 1,
                  w: bPerLine-2*margin, h: lineHeight,
                  color: white];
   FOR index: CARDINAL IN [0..s.length) DO WriteChar[s[index]]; ENDLOOP;
   PaintRectangle[x:margin, y: 1,
                  w: bPerLine-2*margin, h: lineHeight,
                  color: invert];
   bmState.y ← oldY; bmState.x ← oldX;
   END;



-- Cleanup mechanism --

cleanupDisplay: ImageDefs.CleanupItem ← [link:, proc: Cleanup,
   mask: ImageDefs.CleanupMask[Finish] +
         ImageDefs.CleanupMask[Abort] +
         ImageDefs.CleanupMask[Save] +
         ImageDefs.CleanupMask[Restore] +
         ImageDefs.CleanupMask[Checkpoint] +
         ImageDefs.CleanupMask[Restart] +
         ImageDefs.CleanupMask[Continue] +
         ImageDefs.CleanupMask[InLd] +
         ImageDefs.CleanupMask[OutLd] ];

Cleanup: ImageDefs.CleanupProcedure =
   BEGIN
   SELECT why FROM
     Finish, Abort, Save, Checkpoint, OutLd =>
       BEGIN
       realTime: POINTER TO CARDINAL = LOOPHOLE[430B];
       delayStart: CARDINAL = realTime↑;
       AltoDisplay.DCBchainHead↑ ← NIL;
       UNTIL realTime↑ - delayStart > 2 DO NULL ENDLOOP;
       END;
     Restore, Restart, Continue, InLd =>
       AltoDisplay.DCBchainHead↑ ← padDCB;
   ENDCASE;
   END;



-- Initialisation --

cursor↑ ← [      0,    400B,   4440B,  44444B,
            23710B,  17361B, 130032B,  61614B,
           143706B, 143706B,  61614B,  30030B,
            17360B,   3700B,       0,       0 ];

mouseX↑ ← cursorX↑ ← 16--indent-- + bPerLine/2 - 8;
mouseY↑ ← cursorY↑ ← top - 16;

padDCB↑ ← [next: myDCB,
         resolution: high,
         background: white,
         indenting:  0,
         width:      0,
         bitmap:     NIL,
         height:     top/2];

myDCB↑ ← [next: NIL,
         resolution: high,
         background: white,
         indenting:  1,
         width:      wPerLine,
         bitmap:     myBitmap,
         height:     scanLines/2];

whiteBBT↑ ← [
      pad: 0,
      sourcealt: FALSE,
      destalt: FALSE,
      sourcetype: gray,
      function: ,
      unused: 0,
      dbca: myBitmap,
      dbmr: wPerLine,
      dlx:, dty:, dw:, dh:,
      sbca:, sbmr:, slx:, sty:,
      gray0: 0,
      gray1: 0,
      gray2: 0,
      gray3: 0 ];

blackBBT↑ ← [
      pad: 0,
      sourcealt: FALSE,
      destalt: FALSE,
      sourcetype: gray,
      function: ,
      unused: 0,
      dbca: myBitmap,
      dbmr: wPerLine,
      dlx:, dty:, dw:, dh:,
      sbca:, sbmr:, slx:, sty:,
      gray0: 177777B,
      gray1: 177777B,
      gray2: 177777B,
      gray3: 177777B ];

-- Paint black border --
PaintRectangle[x: 0, y: 0, w: bPerLine, h: scanLines, color: black];

-- Initialise text area --
Clear[];

FOR c: CHARACTER IN CHARACTER
DO charWidth[c] ← CharWidth[c]; ENDLOOP;

-- Turn on screen --
IF FrameDefs.IsBound[ImageDefs.AddCleanupProcedure]
THEN ImageDefs.AddCleanupProcedure[@cleanupDisplay];
AltoDisplay.DCBchainHead↑ ← padDCB;

END.