ViewerBLTImpl.mesa; written by S. McGregor
Last Edited by McGregor, August 4, 1983 11:23 am
Last Edited by: Maxwell, May 24, 1983 11:53 am
Last Edited by: Pausch, August 18, 1983 11:51 am
Last Edited by: Wyatt, October 26, 1983 11:40 am
DIRECTORY
Carets USING [ResetCarets, ResumeCarets, SuspendCarets],
Imager USING [black, Color, Context, IntegerExcludeRectangle, IntegerMaskRectangle, MakeStipple, MoveSurfaceRectangle, SetColor, white],
Process USING [Detach],
ViewerBLT USING [],
ViewerClasses USING [BltRule, Column, Viewer, PaintRectangle, PaintRectangleRec, cymax, wxmax, wxmin, wymax, wymin, xmax, xmin, ymax, ymin],
ViewerLocks USING [CallUnderWriteLock],
ViewerOps USING [AcquireContext, ColumnInfo, EnumerateViewers, EnumProc, EstablishViewerPosition, PaintHint, PaintViewer, ReleaseContext, WaitForPaintingToFinish],
ViewerSpecs USING [openBottomY, openLeftLeftX, openLeftWidth, openRightLeftX, windowBorderSize],
WindowManagerPrivate USING [rootViewerTree];
ViewerBLTImpl: CEDAR PROGRAM
IMPORTS Carets, Imager, Process, ViewerClasses, ViewerLocks, ViewerOps, ViewerSpecs, WindowManagerPrivate
EXPORTS ViewerBLT, ViewerOps
SHARES ViewerOps, ViewerClasses
= BEGIN
BltRule: TYPE = ViewerClasses.BltRule;
Column: TYPE = ViewerClasses.Column;
Viewer: TYPE = ViewerClasses.Viewer;
Box: TYPE = RECORD[x, y, w, h: INTEGER];
ColumnSnapshot: TYPE = REF ColumnSequence;
ColumnSequence: TYPE = RECORD[SEQUENCE size: NAT OF RECORD[
viewer: Viewer, box: Box, painted: BOOLFALSE]];
snapshots: ARRAY Column OF RECORD[before, after: ColumnSnapshot];
ChangeMenuHeight: PUBLIC PROC[viewer: Viewer, delta: INTEGER] = {
LockedChangeMenuHeight: PROC = {
oldBox, newBox: Box;
MenusPrivate.Debug[msg: "ChangeMenuHeightCalled", level: 2];
IF viewer.menus = NIL OR delta = 0 THEN RETURN;
oldBox ← [viewer.cx, viewer.cy, viewer.cw, viewer.ch];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
newBox ← [viewer.cx, viewer.cy, viewer.cw, viewer.ch];
BLTViewer[viewer, oldBox, newBox];
};
ViewerLocks.CallUnderWriteLock[LockedChangeMenuHeight, viewer];
};
ForkPaintViewer: PROC[viewer: Viewer,
hint: ViewerOps.PaintHint ← all, clearClient: BOOLTRUE, whatChanged: REF ANYNIL] = {
p: PROCESS = FORK ViewerOps.PaintViewer[viewer, hint, clearClient, whatChanged];
TRUSTED{Process.Detach[p]};
};
ColumnHead: PROC[column: Column] RETURNS[Viewer] = INLINE {
RETURN[WindowManagerPrivate.rootViewerTree[column]] };
InternalPaintColumn: PUBLIC PROC[column: Column, paintIcons: BOOLTRUE] = {
oldIndex: CARDINAL;
oldSnapshot: ColumnSnapshot;
newSnapshot: ColumnSnapshot;
invalidTop, invalidBottom: INTEGER;
IF column = static THEN {
FOR v: Viewer ← ColumnHead[column], v.sibling UNTIL v=NIL DO
IF ~v.offDeskTop THEN ForkPaintViewer[v];
ENDLOOP;
RETURN};
IF ColumnHead[column] = NIL THEN {
height, leftX, width, bottomY: INTEGER;
[totalSpace: height, leftX: leftX, width: width, bottomY: bottomY]
← ViewerOps.ColumnInfo[column];
GreyScreen[leftX, bottomY, width, height, column=color, FALSE];
IF paintIcons AND column # color THEN -- paint newly visible icons
FOR v: Viewer ← ColumnHead[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;
ForkPaintViewer[v];
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 ← ColumnHead[column], v.sibling UNTIL v=NIL DO
ForkPaintViewer[v];
ENDLOOP;
ViewerOps.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 ForkPaintViewer[newSnapshot[i].viewer];
ENDLOOP;
Carets.ResetCarets[];
ViewerOps.WaitForPaintingToFinish[];
Carets.ResumeCarets[];
};
MaskBox: PROC[context: Imager.Context, xmin, ymin, xmax, ymax: INTEGER] = INLINE {
Imager.IntegerMaskRectangle[context: context, x: xmin, y: ymin, w: xmax-xmin, h: ymax-ymin];
};
BLTViewer: PROC[viewer: Viewer, oldBox, newBox: Box] = {
rect: ViewerClasses.PaintRectangle ← NIL;
hBLT, vBLT: BltRule;
w, h: INTEGER;
context: Imager.Context ← NIL;
fromX, fromY, toX, toY: INTEGER;
wbs: INTEGERIF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0;
header: INTEGER = viewer.wymax - viewer.cymax;
-- assume that the menu is the same for before and after
w ← MIN[oldBox.w, newBox.w];
h ← MIN[oldBox.h, newBox.h];
MenusPrivate.Debug[msg: "BLTViewer called", level: 2];
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 => {ForkPaintViewer[viewer]; 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 => {ForkPaintViewer[viewer]; RETURN};
{
ENABLE UNWIND => IF context#NIL THEN ViewerOps.ReleaseContext[context];
context ← ViewerOps.AcquireContext[NIL, viewer.column=color];
rect ← NEW[ViewerClasses.PaintRectangleRec ← [flavor: blt, x: toX, y: toY, w: w, h: h]];
IF fromX # toX OR fromY # toY THEN
[] ← Imager.MoveSurfaceRectangle[context, [fromX, fromY, w, h], [toX, toY]];
-- erase remainder
Imager.SetColor[context, Imager.white];
IF viewer.wymin < rect.ymin THEN -- some on the bottom
MaskBox[context, viewer.wxmin, viewer.wymin, viewer.wxmax, rect.ymin];
IF viewer.wymax > rect.ymax THEN -- some on the top
MaskBox[context, viewer.wxmin, rect.ymax, viewer.wxmax, viewer.wymax];
IF viewer.wxmin < rect.xmin THEN -- some on the left
MaskBox[context, viewer.wxmin, viewer.wymin, rect.xmin, viewer.wymax];
IF viewer.wxmax > rect.xmax THEN -- some on the right
MaskBox[context, rect.xmax, viewer.wymin, viewer.wxmax, viewer.wymax];
Imager.SetColor[context, Imager.black];
IF viewer.border AND (oldBox.w#newBox.w OR oldBox.h#newBox.h) THEN { -- draw border
MaskBox[context, viewer.wxmin, viewer.wymin, viewer.wxmax, viewer.wymin+wbs];
MaskBox[context, viewer.wxmin, viewer.wymin, viewer.wxmin+wbs, viewer.wymax];
MaskBox[context, viewer.wxmax-wbs, viewer.wymin, viewer.wxmax, viewer.wymax];
MaskBox[context, viewer.wxmin, viewer.wymax-wbs, viewer.wxmax, viewer.wymax];
};
ViewerOps.ReleaseContext[context];
};
IF viewer.wxmin < rect.xmin OR viewer.wxmax > rect.xmax
OR (viewer.wymax - header) < rect.y OR (viewer.wymax > rect.ymax) THEN { -- repaint header
ViewerOps.PaintViewer[viewer, caption, FALSE];
ViewerOps.PaintViewer[viewer, menu, FALSE];
};
IF oldBox.w # newBox.w OR oldBox.h # newBox.h THEN -- shape has changed
ForkPaintViewer[viewer, client, FALSE, rect];
IF viewer.menus # NIL THEN { -- reset cached position
NARROW[viewer.menus, MenusPrivate.ViewerMenus].x ← viewer.wx + wbs;
NARROW[viewer.menus, MenusPrivate.ViewerMenus].y ← viewer.wy + viewer.wh - menuHeight - captionHeight};
};
SnapshotColumn: PROC[column: Column]
RETURNS[snapshot: ColumnSnapshot] = {
length: CARDINAL ← 0;
snapshot ← snapshots[column].after;
IF snapshot = NIL THEN snapshot ← NEW[ColumnSequence[10]];
ClearSnapshot[snapshot];
FOR v: Viewer ← ColumnHead[column], v.sibling UNTIL v = NIL DO
index: CARDINAL = length;
length ← length+1;
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[index] ← [v, [v.wx, v.wy, v.ww, v.wh]];
ENDLOOP;
};
ClearSnapshot: PROC[snapshot: ColumnSnapshot] = INLINE {
IF snapshot = NIL THEN RETURN;
FOR i: CARDINAL IN [0..snapshot.size) DO snapshot[i].viewer ← NIL; ENDLOOP;
};
GetIndex: PROC[snapshot: ColumnSnapshot, viewer: Viewer] RETURNS[CARDINAL] = {
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];
};
screenGrey: Imager.Color = Imager.MakeStipple[104042B];
GreyScreen: PUBLIC PROC [x,y,w,h: INTEGER, color, icon: BOOL,
stipple: Imager.Color ← NIL] = {
context: Imager.Context ← ViewerOps.AcquireContext[NIL, color];
IF icon AND y+h > ViewerSpecs.openBottomY THEN ClipIcon[context];
Imager.SetColor[context, IF stipple=NIL THEN screenGrey ELSE stipple];
Imager.IntegerMaskRectangle[context, x, y, w, h];
ViewerOps.ReleaseContext[context];
IF icon THEN RETURN;
IF color THEN ClearSnapshot[snapshots[color].before]
ELSE { OPEN ViewerSpecs;
IF x + w > openRightLeftX THEN ClearSnapshot[snapshots[right].before];
IF x < openLeftLeftX + openLeftWidth THEN ClearSnapshot[snapshots[left].before]
};
};
ClipIcon: PROC [context: Imager.Context] = {
clips for overlapping top level windows given valid Context and Viewer
ClipOpenViewer: ViewerOps.EnumProc = {
IF ~v.iconic AND (v.column=right OR v.column=left) THEN
Imager.IntegerExcludeRectangle[context, v.wx, v.wy, v.ww, v.wh];
};
ViewerOps.EnumerateViewers[ClipOpenViewer];
};
END.
ChangeNumberOfLines: PUBLIC PROC[viewer: Viewer, newLines: [0..5)] = {
LockedChangeLines: PROC = {
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];
};
ViewerLocks.CallUnderWriteLock[LockedChangeLines, viewer];
};
Glitch: PUBLIC PROC [viewer: Viewer, nLines: INTEGER] = {
dY, sY, vY, vX: INTEGER;
w: INTEGER ← viewer.cw;
h: INTEGER ← viewer.ch-ABS[nLines];
dc: Imager.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;
Imager.SetIntCP[dc, vX, dY];
Imager.DrawBitmap[
self: dc,
bitmap: Imager.ScreenBitmap[],
w: w,
h: h,
x: vX,
y: ViewerSpecs.screenH-sY,
xorigin: vX,
yorigin: ViewerSpecs.screenH-sY
];
ViewerOps.ReleaseContext[dc];
};
VBLT: PUBLIC PROC [src: VPlace, dest: VPlace, srcw, srch: INTEGER] = {
ERROR; -- not yet implemented
};