ViewerBLTImpl.mesa;
Last Edited by McGregor, May 31, 1983 11:18 am
Last Edited by: Maxwell, February 10, 1984 10:35:04 am PST
Last Edited by: Paul Rovner, June 15, 1983 5:38 pm
Last Edited by: Doug Wyatt, December 13, 1983 2:35 pm
DIRECTORY
Carets USING [pCaretViewer, ResumeCarets, sCaretViewer, StartCaret, SuspendCarets],
Graphics USING [black, ClipBox, Context, DrawBox, SetColor, SetStipple, white],
GraphicsColor USING [IntensityToColor],
GraphicsOps USING [MoveDeviceRectangle],
Interminal USING [terminal],
Menus,
Process USING [Detach],
ViewerBLT,
ViewerClasses,
ViewerLocks USING [CallUnderWriteLock],
ViewerOps,
ViewerSpecs,
WindowManagerPrivate USING [rootViewerTree];
ViewerBLTImpl:
CEDAR PROGRAM
IMPORTS Carets, Graphics, GraphicsColor, GraphicsOps, Interminal, Menus, Process, ViewerLocks, ViewerOps, ViewerSpecs, WindowManagerPrivate
EXPORTS ViewerBLT, ViewerOps
SHARES Menus, ViewerOps, ViewerClasses =
BEGIN OPEN ViewerClasses, ViewerOps, ViewerSpecs, WindowManagerPrivate;
ChangeNumberOfLines:
PUBLIC
PROC[viewer: Viewer, newLines: [0..5)] =
BEGIN
LockedChangeLines:
PROC =
BEGIN
oldBox, newBox: Box;
IF viewer.menu = NIL THEN RETURN;
oldBox ← [viewer.cx, viewer.cy, viewer.cw, viewer.ch];
Menus.ChangeNumberOfLines[viewer.menu, newLines];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
newBox ← [viewer.cx, viewer.cy, viewer.cw, viewer.ch];
BLTViewer[viewer, oldBox, newBox];
END;
ViewerLocks.CallUnderWriteLock[LockedChangeLines, viewer];
END;
InternalPaintColumn:
PUBLIC
PROC [column: Column, paintIcons:
BOOL ←
TRUE] =
BEGIN
oldIndex: CARDINAL;
oldSnapshot: ColumnSnapshot;
newSnapshot: ColumnSnapshot;
invalidTop, invalidBottom: INTEGER;
IF column = static
THEN {
FOR v: Viewer ← rootViewerTree[column], v.sibling
UNTIL v=
NIL
DO
IF ~v.offDeskTop THEN TRUSTED {Process.Detach[FORK PaintViewer[v, all]]};
ENDLOOP;
RETURN};
IF rootViewerTree[column] =
NIL
THEN {
height, leftX, width, bottomY: INTEGER;
[height, leftX, width, bottomY, ] ← ColumnInfo[column];
GreyScreen[leftX, bottomY, width, height, column=color, FALSE];
IF paintIcons
AND column # color
THEN
-- paint newly visible icons
FOR v: Viewer ← rootViewerTree[static], v.sibling
UNTIL v=
NIL
DO
IF ~v.iconic OR v.offDeskTop THEN LOOP;
IF v.wy + v.wh < bottomY THEN LOOP;
IF v.wx + v.ww < leftX THEN LOOP;
IF v.wx > leftX + width THEN LOOP;
TRUSTED {Process.Detach[FORK PaintViewer[v, all]]};
ENDLOOP;
RETURN};
oldSnapshot ← snapshots[column].before;
newSnapshot ← SnapshotColumn[column];
snapshots[column].before ← newSnapshot;
snapshots[column].after ← oldSnapshot;
IF oldSnapshot =
NIL
THEN {
Carets.SuspendCarets[];
FOR v: Viewer ← rootViewerTree[column], v.sibling
UNTIL v=
NIL
DO
TRUSTED {Process.Detach[FORK PaintViewer[v, all]]};
ENDLOOP;
WaitForPaintingToFinish[];
Carets.ResumeCarets[];
RETURN};
Carets.SuspendCarets[];
-- try to save cycles by BLTing bit maps around.
-- this algorithm assumes that the column is almost in the same order as it was before,
-- except for creating, deleting, or moving at most one viewer.
-- the algorithm should never blt an invalid bitmap.
invalidTop ← -1; -- top of section that has been blitted over
invalidBottom ← 1000; -- bottom of section that has been blitted over
-- iterate from bottom to top of column, BLTing bit maps if the space is free
FOR i:
CARDINAL
IN [0..newSnapshot.size)
DO
IF newSnapshot[i].viewer = NIL THEN EXIT;
oldIndex ← GetIndex[oldSnapshot, newSnapshot[i].viewer];
IF oldIndex = oldSnapshot.size THEN LOOP; -- a new viewer
-- check to see if the bitmap is invalid
IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom
AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP;
-- check to see if BLTing would wipe out a bitmap above
IF newSnapshot[i].box.y + newSnapshot[i].box.h >
oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h THEN LOOP;
IF newSnapshot[i].box # oldSnapshot[oldIndex].box
THEN {
BLTViewer[newSnapshot[i].viewer, oldSnapshot[oldIndex].box, newSnapshot[i].box];
invalidTop ← MAX[invalidTop, newSnapshot[i].box.y + newSnapshot[i].box.h];
invalidBottom ← MIN[invalidBottom, newSnapshot[i].box.y]};
newSnapshot[i].painted ← TRUE;
ENDLOOP;
-- now do it from top to bottom, catching the ones we couldn't do before
FOR i:
CARDINAL
DECREASING
IN [0..newSnapshot.size)
DO
IF newSnapshot[i].viewer = NIL THEN LOOP;
IF newSnapshot[i].painted THEN LOOP;
oldIndex ← GetIndex[oldSnapshot, newSnapshot[i].viewer];
IF oldIndex = oldSnapshot.size THEN LOOP; -- a new viewer
-- check to see if the bitmap is invalid
IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom
AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP;
-- check to see if BLTing would wipe out a bitmap below
IF newSnapshot[1].viewer #
NIL
AND
-- more than one viewer in column
newSnapshot[i].box.y < oldSnapshot[oldIndex].box.y THEN LOOP;
IF newSnapshot[i].box # oldSnapshot[oldIndex].box
THEN {
BLTViewer[newSnapshot[i].viewer, oldSnapshot[oldIndex].box, newSnapshot[i].box];
invalidTop ← MAX[invalidTop, newSnapshot[i].box.y + newSnapshot[i].box.h];
invalidBottom ← MIN[invalidBottom, newSnapshot[i].box.y]};
newSnapshot[i].painted ← TRUE;
ENDLOOP;
FOR i:
CARDINAL
IN [0..newSnapshot.size)
DO
IF newSnapshot[i].viewer = NIL THEN EXIT;
IF ~newSnapshot[i].painted
THEN
TRUSTED {
Process.Detach[FORK PaintViewer[newSnapshot[i].viewer, all]]};
ENDLOOP;
reset caret below relies on special deal with CaretsImpl
Carets.StartCaret[Carets.pCaretViewer, -10000, -10000, primary]; -- reset the primary caret
Carets.StartCaret[Carets.sCaretViewer, -10000, -10000, secondary]; -- reset the secondary caret
WaitForPaintingToFinish[];
Carets.ResumeCarets[];
END;
BLTViewer:
PROC[viewer: Viewer, oldBox, newBox: Box] =
BEGIN
rect: PaintRectangle;
hBLT, vBLT: BltRule;
w, h, header: INTEGER;
context: Graphics.Context;
fromX, fromY, toX, toY: INTEGER;
wbs: INTEGER ← IF viewer.border THEN windowBorderSize ELSE 0;
header ← (viewer.wy + viewer.wh) - (viewer.cy + viewer.ch);
-- assume that the menu is the same for before and after
w ← MIN[oldBox.w, newBox.w];
h ← MIN[oldBox.h, newBox.h];
IF (viewer.class.flavor = $Text
OR viewer.class.flavor = $Typescript)
THEN vBLT ← none
ELSE vBLT ← viewer.class.bltContents;
IF oldBox.w = newBox.w THEN vBLT ← top;
SELECT vBLT
FROM
top, bottom, center => {
-- later we will distinguish between left, right and center
IF oldBox.w # newBox.w THEN w ← w - wbs; -- don't include right border
toX ← newBox.x;
fromX ← oldBox.x};
ENDCASE => TRUSTED {Process.Detach[FORK PaintViewer[viewer, all]]; RETURN};
hBLT ← IF oldBox.h = newBox.h THEN top ELSE viewer.class.bltContents;
SELECT hBLT
FROM
top => {
IF oldBox.h # newBox.h THEN h ← h - wbs; -- don't include bottom border
fromY ← oldBox.y + oldBox.h - h;
toY ← newBox.y + newBox.h - h};
bottom => {
h ← h - header; -- don't include top header
fromY ← oldBox.y;
toY ← newBox.y};
center => {
fromY ← oldBox.y + wbs + oldBox.h/2 - h/2;
toY ← newBox.y + wbs + newBox.h/2 - h/2;
h ← h - header - wbs}; -- don't include top or bottom
ENDCASE => TRUSTED {Process.Detach[FORK PaintViewer[viewer, all]]; RETURN};
BEGIN OPEN viewer;
ENABLE UNWIND => IF context#NIL THEN ReleaseContext[context];
screenW: INTEGER ← ViewerSpecs.screenW;
screenH: INTEGER ← ViewerSpecs.screenH;
IF viewer.column=color
THEN {
screenW ← Interminal.terminal.colorWidth;
screenH ← Interminal.terminal.colorHeight;
};
context ← AcquireContext[NIL, viewer.column=color];
rect ← NEW[PaintRectangleRec ← [blt, toX, toY, w, h]];
IF fromX # toX OR fromY # toY AND w >=0 AND h >= 0
THEN GraphicsOps.MoveDeviceRectangle[
self: context,
width: w, height: h,
fromX: fromX, fromY: screenH - (fromY + h),
toX: toX, toY: screenH - (toY + h)];
-- erase remainder
Graphics.SetColor[context, Graphics.white];
IF wy < rect.y
THEN
-- some on the bottom
Graphics.DrawBox[context, XYWH2Box[wx, wy, ww, rect.y - wy]];
IF rect.h >=0
AND wy+wh > rect.y+rect.h
THEN
-- some on the top
Graphics.DrawBox[context, XYWH2Box[wx, rect.y+rect.h, ww, (wy+wh) - (rect.y+rect.h)]];
IF wx < rect.x
THEN
-- some on the left
Graphics.DrawBox[context, XYWH2Box[wx, wy, rect.x - wx, wh]];
IF rect.w >=0
AND wx+ww > rect.x+rect.w
THEN
-- some on the right
Graphics.DrawBox[context, XYWH2Box[rect.x+rect.w, wy, (wx+ww) - (rect.x+rect.w), wh]];
Graphics.SetColor[context, Graphics.black];
IF viewer.border
AND (oldBox.w # newBox.w
OR oldBox.h # newBox.h)
THEN {
Graphics.DrawBox[context, XYWH2Box[wx, wy, ww, wbs]];
Graphics.DrawBox[context, XYWH2Box[wx, wy, wbs, wh]];
Graphics.DrawBox[context, XYWH2Box[wx+ww-wbs, wy, wbs, wh]];
Graphics.DrawBox[context, XYWH2Box[wx, wy+wh-wbs, ww, wbs]]};
ReleaseContext[context];
END;
IF viewer.wx < rect.x
OR (viewer.wx + viewer.ww > rect.x + rect.w)
OR
(viewer.wy + viewer.wh - header) < rect.y OR
(viewer.wy + viewer.wh > rect.y + rect.h) THEN { -- repaint header
PaintViewer[viewer, caption, FALSE]; PaintViewer[viewer, menu, FALSE]};
-- call RepaintViewer even if the shape did't change
TRUSTED {Process.Detach[
-- if you don't fork, you will get deadlocks!
FORK RepaintViewer[viewer, oldBox.w # newBox.w, oldBox.h # newBox.h, rect]]};
IF viewer.menu #
NIL
THEN {
-- reset cached position
viewer.menu.x ← viewer.wx + wbs;
viewer.menu.y ← viewer.wy + viewer.wh - menuHeight - captionHeight};
END;
RepaintViewer:
PROC[viewer: Viewer, xChanged, yChanged:
BOOL, rect: PaintRectangle] = {
PaintViewer[viewer, client, FALSE, rect];
if this is a container, repaint the sub-viewers that were x and y bound.
we cannot use the class name because the viewer may be a subclass of $Container.
IF ContainerPaint = NIL THEN ContainerPaint ← ViewerOps.FetchViewerClass[$Container].paint;
IF viewer.class.paint = ContainerPaint
THEN RepaintBoundViewers[viewer, rect, xChanged, yChanged];
};
RepaintBoundViewers:
PROC[viewer: Viewer, rect: PaintRectangle, xChanged, yChanged:
BOOLEAN] =
BEGIN
xBound: LIST OF Viewer ← NARROW[viewer.class.get[viewer, $XBound]];
yBound: LIST OF Viewer ← NARROW[viewer.class.get[viewer, $YBound]];
IF xChanged
THEN {
-- repaint x bound
FOR list:
LIST
OF Viewer ← xBound, list.rest
WHILE list #
NIL
DO
v: Viewer ← list.first;
IF v.class.flavor = $Text OR v.class.flavor = $Typescript THEN {PaintViewer[v, all]; LOOP};
IF WithinRect[v, rect] THEN {
PaintViewer[v, all, FALSE, rect];
IF v.class.paint = ContainerPaint
THEN {
doubleBounded: BOOLEAN ← FALSE;
IF yChanged
THEN
FOR l:
LIST
OF Viewer ← yBound, l.rest
WHILE l #
NIL
DO
IF l.first = v THEN {doubleBounded ← TRUE; EXIT};
ENDLOOP;
RepaintBoundViewers[v, rect, TRUE, doubleBounded]};
ENDLOOP};
IF yChanged
THEN {
-- repaint y bound
FOR list:
LIST
OF Viewer ← yBound, list.rest
WHILE list #
NIL
DO
v: Viewer ← list.first;
painted: BOOLEAN ← FALSE;
FOR list:
LIST
OF Viewer ← xBound, list.rest
WHILE list #
NIL
DO
IF list.first = v THEN {painted ← TRUE; EXIT};
ENDLOOP;
IF painted THEN LOOP;
IF WithinRect[v, rect] THEN {
PaintViewer[v, all, FALSE, rect];
IF v.class.paint = ContainerPaint THEN RepaintBoundViewers[v, rect, FALSE, TRUE];
ENDLOOP};
END;
WithinRect:
PROCEDURE[v: Viewer, rect: PaintRectangle]
RETURNS[
BOOLEAN] =
BEGIN
vx, vy: INTEGER;
IF v.parent.class.coordSys = top
THEN [vx, vy] ← ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy+v.wh]
ELSE [vx, vy] ← ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy];
RETURN[vx >= rect.x
AND (vx + v.ww <= rect.x + rect.w)
AND
vy >= rect.y AND (vy + v.wh <= rect.y + rect.h)];
END;
ContainerPaint: ViewerClasses.PaintProc ← NIL;
ColumnInfo:
PROC[column: Column]
RETURNS[totalSpace, leftX, width, bottomY, nextY:
INTEGER] =
INLINE BEGIN
SELECT column
FROM
left => {
totalSpace ← openLeftTopY-openBottomY;
bottomY ← nextY ← openBottomY;
leftX ← openLeftLeftX;
width ← openLeftWidth};
right => {
totalSpace ← openRightTopY-openBottomY;
bottomY ← nextY ← openBottomY;
leftX ← openRightLeftX;
width ← openRightWidth};
color => {
totalSpace ← Interminal.terminal.colorHeight;
bottomY ← nextY ← 0;
leftX ← 0;
width ← Interminal.terminal.colorWidth};
ENDCASE => ERROR;
END;
ColumnSnapshot: TYPE = REF ColumnSequence;
ColumnSequence:
TYPE =
RECORD[
SEQUENCE size:
NAT
OF
RECORD[
viewer: Viewer, box: Box, painted: BOOL ← FALSE]];
Box: TYPE = RECORD[x, y, w, h: INTEGER];
snapshots:
ARRAY Column
OF
RECORD[before, after: ColumnSnapshot];
SnapshotColumn:
PROC[column: Column]
RETURNS[snapshot: ColumnSnapshot] = BEGIN
length: CARDINAL ← 0;
snapshot ← snapshots[column].after;
IF snapshot = NIL THEN snapshot ← NEW[ColumnSequence[10]];
ClearSnapshot[snapshot];
FOR v: Viewer ← rootViewerTree[column], v.sibling
UNTIL v =
NIL
DO
IF length = snapshot.size
THEN {
-- make a bigger snapshot
temp: ColumnSnapshot ← NEW[ColumnSequence[snapshot.size + 10]];
FOR i:
CARDINAL
IN [0..snapshot.size)
DO
temp[i] ← snapshot[i];
ENDLOOP;
snapshot ← temp};
snapshot[length] ← [v, [v.wx, v.wy, v.ww, v.wh]];
length ← length + 1;
ENDLOOP;
END;
ClearSnapshot:
PROC[snapshot: ColumnSnapshot] =
INLINE
BEGIN
IF snapshot = NIL THEN RETURN;
FOR i: CARDINAL IN [0..snapshot.size) DO snapshot[i].viewer ← NIL; ENDLOOP;
END;
GetIndex:
PROC[snapshot: ColumnSnapshot, viewer: Viewer]
RETURNS[
CARDINAL] =
BEGIN
FOR i:
CARDINAL
IN [0..snapshot.size)
DO
IF snapshot[i].viewer = NIL THEN EXIT;
IF snapshot[i].viewer = viewer THEN RETURN[i];
ENDLOOP;
RETURN[snapshot.size];
END;
GreyScreen:
PUBLIC
PROC [x,y,w,h:
INTEGER, color, icon:
BOOL,
stipple: CARDINAL ← 104042B] = BEGIN
context: Graphics.Context ← AcquireContext[NIL, color];
IF icon AND y+h > openBottomY THEN ClipIcon[context];
IF color
THEN Graphics.SetColor[context, GraphicsColor.IntensityToColor[.5]]
ELSE Graphics.SetStipple[context, stipple];
Graphics.DrawBox[context, ViewerOps.XYWH2Box[x, y, w, h]];
ReleaseContext[context];
IF icon THEN RETURN;
IF color
THEN ClearSnapshot[snapshots[color].before]
ELSE {
IF x + w > openRightLeftX THEN ClearSnapshot[snapshots[right].before];
IF x < openLeftLeftX + openLeftWidth THEN ClearSnapshot[snapshots[left].before]};
END;
ClipIcon:
PROC [context: Graphics.Context] =
BEGIN
clips for overlapping top level windows given valid Context and Viewer
ClipOpenViewer: ViewerOps.EnumProc =
BEGIN
IF ~v.iconic
AND (v.column=right
OR v.column=left)
THEN
Graphics.ClipBox[context, [v.wx, v.wy, v.wx+v.ww, v.wy+v.wh], TRUE];
END;
ViewerOps.EnumerateViewers[ClipOpenViewer];
END;
END . . .
Glitch:
PUBLIC
PROC [viewer: Viewer, nLines:
INTEGER] =
BEGIN
dY, sY, vY, vX: INTEGER;
w: INTEGER ← viewer.cw;
h: INTEGER ← viewer.ch-ABS[nLines];
dc: Graphics.Context ← ViewerOps.AcquireContext[NIL];
[vX, vY] ← ViewerOps.UserToScreenCoords[viewer, 0,
IF viewer.class.coordSys=top THEN 0 ELSE viewer.ch];
dY ← IF nLines>0 THEN vY ELSE vY-nLines;
sY ← IF nLines>0 THEN vY-nLines ELSE vY;
Graphics.SetCP[dc, vX, dY];
GraphicsOps.DrawBitmap[
self: dc,
bitmap: GraphicsOps.ScreenBitmap[],
w: w,
h: h,
x: vX,
y: ViewerSpecs.screenH-sY,
xorigin: vX,
yorigin: ViewerSpecs.screenH-sY
];
ViewerOps.ReleaseContext[dc];
END;
VBLT:
PUBLIC
PROC [src: VPlace, dest: VPlace, srcw, srch:
INTEGER] =
BEGIN
ERROR; -- not yet implemented
END;
END.