ViewerPaintImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, November 21, 1985 11:48:50 am PST
Doug Wyatt, May 21, 1985 9:32:28 am PDT
Russ Atkinson (RRA) July 1, 1985 6:49:06 pm PDT
DIRECTORY
Carets USING [ResumeCarets, SuspendCarets],
CaretsExtras USING [DoWithoutCarets],
CedarProcess USING [SetPriority],
Icons USING [DrawIcon],
Imager USING [black, ClipRectangleI, Color, Context, DoSaveAll, MakeGray, MaskRectangleI, SetColor, SetFont, SetXYI, ShowText, Trans, white],
ImagerBackdoor USING [invert, MakeStipple, ViewClipRectangleI, ViewReset, ViewTranslateI],
ImagerTerminal USING [BWContext, ColorContext, SetStandardColorMap],
InterminalBackdoor USING [terminal],
Menus USING [],
Process USING [Detach, MsecToTicks, Pause, SecondsToTicks, Ticks],
RefText USING [AppendRope, ObtainScratch, ReleaseScratch, TrustTextAsRope],
Rope USING [Length, ROPE, Run],
Terminal,
VFonts USING [defaultFont, Font, StringWidth],
ViewerClasses USING [PaintRectangle, Viewer],
ViewerLocks USING [CallUnderWriteLock, Wedged],
ViewerGroupLocks USING [CallRootUnderWriteLock],
ViewerOps USING [ChangeColumn, FetchProp, PaintHint, SaveAllEdits, UserToScreenCoords],
ViewerPrivate,
ViewerSpecs,
ViewerTools USING [GetSelectedViewer];
ViewerPaintImpl:
CEDAR
MONITOR
IMPORTS Carets, CaretsExtras, CedarProcess, Icons, Imager, ImagerBackdoor, ImagerTerminal, InterminalBackdoor, Process, RefText, Rope, Terminal, VFonts, ViewerGroupLocks, ViewerLocks, ViewerOps, ViewerPrivate, ViewerSpecs, ViewerTools
EXPORTS ViewerOps, ViewerPrivate
SHARES Menus, ViewerClasses, ViewerLocks
~ BEGIN OPEN ViewerClasses, ViewerOps, ViewerSpecs;
ContextCreatorProc: TYPE ~ ViewerPrivate.ContextCreatorProc;
ROPE: TYPE ~ Rope.ROPE;
DoWithScratchText:
PROC[len:
NAT, action:
PROC[
REF
TEXT]] ~ {
scratch: REF TEXT ~ RefText.ObtainScratch[len];
action[scratch ! UNWIND => RefText.ReleaseScratch[scratch]];
RefText.ReleaseScratch[scratch];
};
captionAscent: NAT ← 9;
PaintCaption:
PROC [viewer: Viewer, context: Imager.Context] ~ {
wbs: INTEGER ~ IF viewer.border THEN windowBorderSize ELSE 0;
IF viewer.class.caption#
NIL
THEN {
client-painted caption
x: INTEGER ~ wbs;
y: INTEGER ~ viewer.wh-captionHeight;
w: INTEGER ~ viewer.ww-wbs*2;
h: INTEGER ~ captionHeight-wbs;
action:
PROC ~ {
Imager.SetXYI[context, x, y];
Imager.Trans[context];
Imager.ClipRectangleI[context, 0, 0, w, h];
viewer.class.caption[viewer, context];
};
Imager.DoSaveAll[context, action];
}
ELSE {
name: ROPE ~ viewer.name;
nameLen: INT ~ Rope.Length[name];
file: ROPE ~ viewer.file;
fileLen: INT ~ Rope.Length[file];
action:
PROC[header:
REF
TEXT] ~ {
font: VFonts.Font ~ VFonts.defaultFont;
headerW: INTEGER ← 0;
header ← RefText.AppendRope[to: header, from: name];
IF fileLen>nameLen
AND Rope.Run[s1: name, s2: file, case:
FALSE]=nameLen
THEN {
The intent of this crock is to show the file version for $Text viewers: if name is a prefix of file, show the remainder of file (presumably "!n") in parentheses
header ← RefText.AppendRope[to: header, from: " ("];
header ← RefText.AppendRope[to: header, from: file, start: nameLen];
header ← RefText.AppendRope[to: header, from: ")"];
};
SELECT
TRUE
FROM
viewer.saveInProgress =>
header ← RefText.AppendRope[to: header, from: " [Saving...]"];
viewer.newFile =>
header ← RefText.AppendRope[to: header, from: " [New File]"];
viewer.newVersion =>
header ← RefText.AppendRope[to: header, from: " [Edited]"];
ENDCASE;
IF viewer.link#
NIL
THEN
header ← RefText.AppendRope[to: header, from: " [Split]"];
headerW ← VFonts.StringWidth[RefText.TrustTextAsRope[header], font];
headerW ← MIN[headerW, viewer.ww-wbs*2];
Imager.SetColor[context, Imager.black];
Imager.MaskRectangleI[context, 0, viewer.wh, viewer.ww, -captionHeight];
Imager.SetColor[context, Imager.white];
Imager.SetXYI[context, (viewer.ww-headerW)/2, viewer.wh-captionAscent];
Imager.SetFont[context, font];
Imager.ShowText[context, header];
};
DoWithScratchText[100, action];
};
};
PaintDocumentHeader:
PROC [viewer: Viewer, context: Imager.Context,
clear:
BOOL, hint: PaintHint, whatChanged:
REF
ANY] = {
IF hint#menu THEN PaintCaption[viewer, context];
IF hint#caption
AND viewer.menu#
NIL
THEN {
wbs: INTEGER ~ IF viewer.border THEN windowBorderSize ELSE 0;
x: INTEGER ~ wbs;
w: INTEGER ~ viewer.ww-wbs*2;
h: INTEGER ~ viewer.menu.linesUsed*menuHeight;
y: INTEGER ~ viewer.wh-captionHeight-h;
IF whatChanged=
NIL
THEN {
IF ~clear
THEN {
Imager.SetColor[context, Imager.white];
Imager.MaskRectangleI[context, x, y, w, h];
};
Imager.SetColor[context, Imager.black];
Imager.MaskRectangleI[context, x, y, w, -menuBarHeight];
};
ViewerPrivate.DrawMenu[viewer.menu, context, x, y+h, whatChanged];
};
};
PaintIcon:
PROC [viewer: Viewer, hint: PaintHint, whatChanged:
REF
ANY ←
NIL] = {
iconAction:
PROC [context: Imager.Context] ~ {
SELECT
TRUE
FROM
viewer.icon=private =>
[] ← viewer.class.paint[viewer, context, whatChanged, hint=all];
hint=all, hint=caption => {
dirty: BOOL ~ viewer.newVersion OR viewer.newFile;
label: ROPE ~ GetIconLabel[viewer];
drawIcon: PROC ~ { Icons.DrawIcon[flavor: viewer.icon, context: context, label: label] };
SELECT viewer.icon
FROM
document => IF dirty THEN viewer.icon ← dirtyDocument;
dirtyDocument => IF NOT dirty THEN viewer.icon ← document;
ENDCASE;
Imager.DoSaveAll[context, drawIcon];
IF ViewerPrivate.selectedIcon=viewer
THEN {
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.MaskRectangleI[context, 0, 0, iconWidth, iconHeight];
};
};
ENDCASE;
};
IF viewer.parent=NIL THEN PaintWindow[viewer, iconAction];
};
GetIconLabel:
PROC [viewer: Viewer]
RETURNS [
ROPE] = {
IF viewer.label#NIL THEN RETURN [viewer.label];
WITH ViewerOps.FetchProp[viewer, $IconLabel]
SELECT
FROM
label: ROPE => RETURN [label];
ENDCASE => RETURN [viewer.name];
};
InvisiblePaint: ERROR ~ CODE;
RecursivelyPaintViewers:
PROC [viewer: Viewer, hint: PaintHint,
clearClient:
BOOL, clear:
BOOL, rect: PaintRectangle, whatChanged:
REF] = {
quit: BOOL ← FALSE;
vx, vy: INTEGER;
bitmapY: INTEGER ← 0;
FOR v: Viewer ← viewer, v.parent
UNTIL v=
NIL
DO
-- visibility test
IF v.parent#
NIL
THEN {
IF (v.wy+v.wh < 0) OR (v.wy > v.parent.ch) THEN RETURN;
IF (v.ww+v.ww < 0) OR (v.wx > v.parent.cw) THEN RETURN;
}
ENDLOOP;
IF hint#client
THEN {
windowAction:
PROC [context: Imager.Context] ~ {
w: INTEGER ~ viewer.ww;
h: INTEGER ~ viewer.wh;
IF ~viewer.visible OR viewer.destroyed THEN ERROR InvisiblePaint;
IF clearClient
AND ~clear
AND hint=all
THEN {
Imager.SetColor[context, Imager.white];
Imager.MaskRectangleI[context, 0, 0, w, h];
clear ← TRUE;
};
IF viewer.border
AND hint=all
THEN {
wbs: INTEGER ~ windowBorderSize;
Imager.SetColor[context, Imager.black];
Imager.MaskRectangleI[context, 0, 0, windowBorderSize, h];
Imager.MaskRectangleI[context, w, 0, -windowBorderSize, h];
Imager.MaskRectangleI[context, 0, 0, w, windowBorderSize];
Imager.MaskRectangleI[context, 0, h, w, -windowBorderSize];
};
IF viewer.parent=
NIL
AND viewer.column#static
THEN
PaintDocumentHeader[viewer, context, clear, hint, whatChanged];
};
PaintWindow[viewer, windowAction];
};
IF hint=all
OR hint=client
THEN {
clientAction:
PROC [context: Imager.Context] ~ {
IF ~viewer.visible OR viewer.destroyed THEN ERROR InvisiblePaint;
IF rect#
NIL
AND viewer.class.bltH=none
AND viewer.class.bltV=none
THEN
clearClient ← TRUE; -- if class doesn't care about blt, don't pass it a rectangle
IF clearClient
AND ~clear
THEN {
Imager.SetColor[context, Imager.white];
Imager.MaskRectangleI[context, 0, 0, viewer.cw, viewer.ch];
Imager.SetColor[context, Imager.black];
IF rect#NIL THEN whatChanged ← NIL;
rect ← NIL;
clear ← TRUE;
};
IF viewer.class.paint#
NIL
THEN {
quit ← viewer.class.paint[viewer, context, whatChanged, clear];
};
};
PaintClient[viewer, clientAction];
};
do we need to paint any of the children?
IF quit THEN RETURN;
IF rect#
NIL
AND viewer.parent=
NIL
THEN {
IF viewer.cx>=rect.x AND (viewer.cx+viewer.cw <= rect.x+rect.w)
AND viewer.cy>=rect.y AND (viewer.cy+viewer.ch <= rect.y+rect.h) THEN RETURN;
};
recursively paint children
IF hint=all
OR hint=client
OR clear
THEN
these guys should be painted in reverse order for correct overlaps
FOR v: Viewer ← viewer.child, v.sibling
UNTIL v=
NIL
DO
IF rect #
NIL
THEN {
don't paint if it is completely within the blt
map to the coodinate system that rect uses
IF viewer.class.topDownCoordSys
THEN
[vx, vy] ← ViewerOps.UserToScreenCoords[viewer, v.wx, viewer.ch-(v.wy+v.wh)]
ELSE [vx, vy] ← ViewerOps.UserToScreenCoords[viewer, v.wx, v.wy];
IF vx >= rect.x
AND (vx + v.ww <= rect.x + rect.w)
AND
vy >= rect.y AND (vy + v.wh <= rect.y + rect.h) THEN LOOP};
RecursivelyPaintViewers[v, all, FALSE, clear, rect, whatChanged];
ENDLOOP;
};
PaintViewer:
PUBLIC
PROC [viewer: Viewer,
hint: PaintHint ← client, clearClient:
BOOL ←
TRUE, whatChanged:
REF
ANY ←
NIL] ~ {
LocalPaintViewer:
PROC ~ {
rect: PaintRectangle ← NIL;
IF (NOT viewer.visible) OR viewer.destroyed OR viewer.paintingWedged THEN RETURN;
IF viewer.iconic THEN {PaintIcon[viewer, hint, whatChanged ! ABORTED => CONTINUE]; RETURN};
WITH whatChanged
SELECT
FROM
pr: PaintRectangle => rect ← pr;
ENDCASE;
RecursivelyPaintViewers[viewer: viewer, hint: hint,
clearClient: clearClient
AND ~(hint=menu
OR hint=caption), clear:
FALSE,
rect: rect, whatChanged: whatChanged !
ABORTED => CONTINUE;
InvisiblePaint => CONTINUE
];
};
IF viewer =
NIL
OR viewer.destroyed
OR viewer.paintingWedged
THEN
RETURN;
Protective stuff that is not required to be locked
ViewerGroupLocks.CallRootUnderWriteLock[LocalPaintViewer, viewer
! ViewerLocks.Wedged => {viewer.paintingWedged ← TRUE; CONTINUE}];
};
terminal: Terminal.Virtual ← InterminalBackdoor.terminal;
Screen: TYPE ~ ViewerPrivate.Screen;
CreateContext:
PUBLIC
PROC [screen: Screen]
RETURNS [context: Imager.Context] ~ {
RETURN [ContextCreator[screen]];
};
ContextCreator:
PROC [screen: Screen]
RETURNS [context: Imager.Context] ← DefaultCreateContext;
DefaultCreateContext:
PROC [screen: Screen]
RETURNS [context: Imager.Context] ~ {
SELECT screen
FROM
bw => context ← ImagerTerminal.BWContext[vt: terminal, pixelUnits: TRUE];
color => context ← ImagerTerminal.ColorContext[vt: terminal, pixelUnits: TRUE];
ENDCASE => ERROR;
};
ContextList: TYPE ~ LIST OF Imager.Context;
contextPool: ARRAY Screen OF ContextList ← ALL[NIL];
contextPoolOverflows: ARRAY Screen OF INT ← ALL[0];
Init:
ENTRY
PROC ~ {
[] ← Terminal.SetBWBitmapState[terminal, allocated];
ViewerPrivate.SetBWScreenSize[w: terminal.bwWidth, h: terminal.bwHeight];
THROUGH [0..8)
DO
context: Imager.Context ~ CreateContext[bw];
contextPool[bw] ← CONS[context, contextPool[bw]];
ENDLOOP;
};
AllocBWContexts:
INTERNAL
PROC ~ {
contextPool[bw] ← NIL;
THROUGH [0..8)
DO
context: Imager.Context ~ CreateContext[bw];
contextPool[bw] ← CONS[context, contextPool[bw]];
ENDLOOP;
};
AllocColorContexts:
INTERNAL
PROC ~ {
contextPool[color] ← NIL;
THROUGH [0..4)
DO
context: Imager.Context ~ CreateContext[color];
contextPool[color] ← CONS[context, contextPool[color]];
ENDLOOP;
};
SetCreator:
PUBLIC
PROC [creator: ContextCreatorProc]
RETURNS [old: ContextCreatorProc] ~ {
inner:
ENTRY
PROC ~ {
old ← ContextCreator;
ContextCreator ← IF creator=CreateContext OR creator=NIL THEN DefaultCreateContext ELSE creator;
AllocBWContexts[];
IF colorEnabled THEN AllocColorContexts[];
};
DisablePainting[wait: TRUE];
inner[! UNWIND => EnablePainting[]];
EnablePainting[];
};
colorEnabled: BOOL ← FALSE;
ColorModeFromBitsPerPixel:
PROC [bitsPerPixel:
NAT]
RETURNS [Terminal.ColorMode] ~ {
SELECT bitsPerPixel
FROM
IN[0..8] => RETURN[[bitsPerPixelChannelA: bitsPerPixel]];
24 => RETURN[[full: TRUE]];
ENDCASE => RETURN[[FALSE, 0, 0]];
};
EnableColor:
PUBLIC
PROC [bitsPerPixel:
NAT]
RETURNS [ok:
BOOL ←
FALSE] ~ {
inner:
ENTRY
PROC ~ {
mode: Terminal.ColorMode ~ ColorModeFromBitsPerPixel[bitsPerPixel];
IF colorEnabled THEN RETURN;
IF NOT Terminal.LegalColorMode[terminal, mode] THEN RETURN;
[] ← Terminal.SetColorBitmapState[vt: terminal,
newState: allocated, newMode: mode, newVisibility: all];
ViewerPrivate.SetColorScreenSize[w: terminal.colorWidth, h: terminal.colorHeight];
ImagerTerminal.SetStandardColorMap[terminal];
THROUGH [0..4)
DO
context: Imager.Context ~ CreateContext[color];
contextPool[color] ← CONS[context, contextPool[color]];
ENDLOOP;
ok ← colorEnabled ← TRUE;
};
DisablePainting[wait: TRUE];
inner[! UNWIND => EnablePainting[]];
EnablePainting[];
IF ok
THEN {
GreyScreen[color, 0, 0, 9999, 9999];
Terminal.TurnOnColorDisplay[terminal];
};
};
DisableColor:
PUBLIC
PROC ~ {
inner:
ENTRY
PROC ~ {
IF NOT colorEnabled THEN RETURN;
[] ← Terminal.SetColorBitmapState[vt: terminal,
newState: none, newMode: [FALSE, 0, 0], newVisibility: all];
THROUGH [0..3)
DO
contextPool[color] ← NIL;
ENDLOOP;
colorEnabled ← FALSE;
};
DisablePainting[wait: TRUE];
inner[! UNWIND => EnablePainting[]];
EnablePainting[];
};
paintingCount: CARDINAL ← 0; -- number of paints in progress
FinishedPainting: CONDITION; -- notified when a process finishes painting
paintingLock: CARDINAL ← 0; -- incremented to disable painting
PaintingEnabled: CONDITION; -- wait here for painting to be enabled
DisablePainting:
PUBLIC
PROC [wait:
BOOL ←
TRUE] = {
locked:
ENTRY
PROC ~ {
If we did this with carets running, we would run the risk of deadlock, since we need to AcquireContext to paint the caret, and paint procs might insist on touching the caret before they finish. Of course, we can still get in trouble if a paint proc calls PaintViewer . . .
paintingLock ← paintingLock+1;
IF wait THEN WHILE paintingCount>0 DO WAIT FinishedPainting ENDLOOP;
};
Carets.SuspendCarets[];
locked[];
Carets.ResumeCarets[];
};
EnablePainting:
PUBLIC
ENTRY
PROC ~ {
IF paintingLock>0 THEN paintingLock ← paintingLock-1;
IF paintingLock=0 THEN BROADCAST PaintingEnabled;
};
WaitForPaintingToFinish:
PUBLIC
ENTRY
PROC ~ {
WHILE paintingCount>0 DO WAIT FinishedPainting ENDLOOP;
};
AcquireContext:
ENTRY
PROC [screen: Screen]
RETURNS [Imager.Context] ~ {
WHILE paintingLock>0 DO WAIT PaintingEnabled ENDLOOP;
paintingCount ← paintingCount+1;
FOR list: ContextList ← contextPool[screen], list.rest
UNTIL list=
NIL
DO
context: Imager.Context ~ list.first;
IF context#NIL THEN { list.first ← NIL; RETURN[context] };
ENDLOOP;
contextPoolOverflows[screen] ← contextPoolOverflows[screen]+1;
RETURN[NIL];
};
ReleaseContext:
ENTRY
PROC [screen: Screen, context: Imager.Context] ~ {
IF context#
NIL
THEN
FOR list: ContextList ← contextPool[screen], list.rest
UNTIL list=
NIL
DO
IF list.first=NIL THEN { list.first ← context; EXIT };
ENDLOOP;
paintingCount ← paintingCount-1;
NOTIFY FinishedPainting;
};
PaintScreen:
PUBLIC
PROC [screen: Screen, action:
PROC [context: Imager.Context],
suspendCarets:
BOOL ←
TRUE] ~ {
acquired: Imager.Context ~ AcquireContext[screen];
context: Imager.Context ← acquired;
saveAction:
PROC ~ {
ImagerBackdoor.ViewReset[context];
IF suspendCarets THEN Carets.SuspendCarets[];
action[context ! UNWIND => IF suspendCarets THEN Carets.ResumeCarets[]];
IF suspendCarets THEN Carets.ResumeCarets[];
};
IF context=NIL THEN context ← CreateContext[screen];
Imager.DoSaveAll[context, saveAction ! UNWIND => ReleaseContext[screen, acquired]];
ReleaseContext[screen, acquired];
};
DoWithoutCaretsVisible:
PROC [viewer: Viewer, action:
PROC [context: Imager.Context], context: Imager.Context] ~ {
proc: PROC ~ {action[context]};
v: Viewer ← viewer;
UNTIL v.parent = NIL DO v ← v.parent ENDLOOP;
CaretsExtras.DoWithoutCarets[v.wx, v.wy, v.ww, v.wh, ViewerPrivate.ViewerScreen[viewer], proc];
};
PaintWindow:
PUBLIC
PROC [viewer: Viewer, action:
PROC [context: Imager.Context]] ~ {
windowAction:
PROC [context: Imager.Context] ~ {
IF viewer.iconic AND (viewer.wy+viewer.wh)>openBottomY THEN ClipIcon[context];
SetView[context: context, viewer: viewer, client: FALSE];
DoWithoutCaretsVisible[viewer, action, context];
};
PaintScreen[ViewerPrivate.ViewerScreen[viewer], windowAction, FALSE];
};
PaintClient:
PUBLIC
PROC [viewer: Viewer, action:
PROC [context: Imager.Context]] ~ {
Caution: we assume that we have the viewer locked, yet PaintClient is exported to ViewerPrivate. The locking is now the caller's responsibility.
clientAction:
PROC [context: Imager.Context] ~ {
SetView[context: context, viewer: viewer, client: TRUE];
DoWithoutCaretsVisible[viewer, action, context];
};
IF NOT viewer.iconic THEN PaintScreen[ViewerPrivate.ViewerScreen[viewer], clientAction, FALSE];
};
SetView:
PROC [context: Imager.Context, viewer: Viewer, client:
BOOL] ~ {
wx, wy: INTEGER;
[wx, wy] ← ClipView[context, viewer, client];
ImagerBackdoor.ViewTranslateI[context, wx, wy];
};
ClipView:
PROC [context: Imager.Context, viewer: Viewer, client:
BOOL]
RETURNS [wx, wy:
INTEGER] ~ {
parent: Viewer ~ viewer.parent;
topDown: BOOL ~ (parent#NIL AND parent.class.topDownCoordSys);
ww: INTEGER ← viewer.ww;
wh: INTEGER ← viewer.wh;
wx ← viewer.wx;
wy ← IF topDown THEN parent.ch-(viewer.wy+wh) ELSE viewer.wy;
IF parent # NIL THEN SetView[context: context, viewer: parent, client: TRUE];
IF client
THEN {
wx ← wx + viewer.cx;
wy ← wy + viewer.cy;
ww ← viewer.cw;
wh ← viewer.ch;
};
ImagerBackdoor.ViewClipRectangleI[context, wx, wy, ww, wh];
};
ClipIcon:
PROC [context: Imager.Context] ~ {
y: NAT ~ ViewerSpecs.openBottomY;
h: NAT ~ ViewerSpecs.openTopY-y;
Exclude:
PROC [x, w:
NAT] ~ {
ImagerBackdoor.ViewClipRectangleI[context: context, x: x, y: y, w: w, h: h, exclude: TRUE];
};
IF ViewerPrivate.rootViewerTree[left]#
NIL
THEN
Exclude[ViewerSpecs.openLeftLeftX, ViewerSpecs.openLeftWidth];
IF ViewerPrivate.rootViewerTree[right]#
NIL
THEN
Exclude[ViewerSpecs.openRightLeftX, ViewerSpecs.openRightWidth];
};
desktopGrey: Imager.Color ← ImagerBackdoor.MakeStipple[104042B];
uniformGrey: Imager.Color ← Imager.MakeGray[0.4];
GreyScreen:
PUBLIC
PROC [screen: Screen, x, y, w, h:
INTEGER] ~ {
greyAction:
PROC [context: Imager.Context] ~ {
Imager.SetColor[context, IF screen=bw THEN desktopGrey ELSE uniformGrey];
IF screen=bw
THEN Imager.SetColor[context, desktopGrey]
ELSE Imager.SetColor[context, uniformGrey];
Imager.MaskRectangleI[context, x, y, w, h];
};
PaintScreen[screen, greyAction];
};
GreyWindow:
PUBLIC
PROC [viewer: Viewer] ~ {
screen: Screen ~ ViewerPrivate.ViewerScreen[viewer];
greyAction:
PROC [context: Imager.Context] ~ {
wx, wy: INTEGER;
IF viewer.iconic AND (viewer.wy+viewer.wh)>openBottomY THEN ClipIcon[context];
[wx, wy] ← ClipView[context: context, viewer: viewer, client: FALSE];
IF screen=bw THEN Imager.SetColor[context, desktopGrey]
ELSE Imager.SetColor[context, uniformGrey];
Imager.MaskRectangleI[context, wx, wy, viewer.ww, viewer.wh];
};
PaintScreen[screen, greyAction];
};
InvertForMenus:
PUBLIC
PROC [viewer: Viewer, x, y, w, h:
INTEGER] = {
invertAction:
PROC [context: Imager.Context] ~ {
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.MaskRectangleI[context, x, y, w, h];
};
PaintWindow[viewer, invertAction];
};
BlinkOnce:
PROC [viewer: Viewer, duration: Process.Ticks] ~ {
LockedBlinkOnce:
PROC ~ {
blinkAction:
PROC [context: Imager.Context] ~ {
Imager.SetColor[context, ImagerBackdoor.invert];
Imager.MaskRectangleI[context, 0, 0, viewer.ww, viewer.wh];
Process.Pause[duration];
Imager.MaskRectangleI[context, 0, 0, viewer.ww, viewer.wh];
};
IF NOT viewer.visible OR viewer.destroyed THEN RETURN;
PaintWindow[viewer, blinkAction];
};
ViewerLocks.CallUnderWriteLock[LockedBlinkOnce, viewer
! ViewerLocks.Wedged => CONTINUE];
};
BlinkViewer:
PUBLIC
PROC [viewer: Viewer, milliseconds:
NAT ← 400] ~ {
BlinkOnce[viewer, Process.MsecToTicks[milliseconds]];
};
BlinkIcon:
PUBLIC
PROC [viewer: Viewer, count:
INT ← 1,
secondsBetweenBlinks:
NAT ← 2, millisecondsPerBlink:
NAT ← 400] ~ {
IF viewer.offDeskTop THEN ViewerOps.ChangeColumn[viewer, left];
TRUSTED {
Process.Detach[
FORK BlinkProcess[viewer, count, secondsBetweenBlinks, millisecondsPerBlink]];
};
};
BlinkProcess:
PROC [viewer: Viewer, count:
INT,
secondsBetweenBlinks, millisecondsPerBlink:
NAT] ~ {
ticksPerHalfSecond: Process.Ticks ~ Process.MsecToTicks[500];
ticksPerBlink: Process.Ticks ~ Process.MsecToTicks[millisecondsPerBlink];
DO
BlinkOnce[viewer, ticksPerBlink];
IF count = 1 THEN RETURN;
IF count > 0 THEN count ← count - 1;
FOR i:
NAT
IN [0..2*secondsBetweenBlinks)
DO
-- check every half second.
Process.Pause[ticksPerHalfSecond];
IF viewer.destroyed
OR
NOT viewer.iconic
OR viewer=ViewerTools.GetSelectedViewer[] THEN RETURN;
ENDLOOP;
ENDLOOP;
};
BlinkDisplay:
PUBLIC
PROC ~ {
Terminal.BlinkBWDisplay[terminal];
};
EmergencySaveAllEdits:
PROC ~ {
CedarProcess.SetPriority[foreground];
DO
-- forever
keys: Terminal.KeyBits ~ terminal.GetKeys[];
IF keys[LeftShift]=down
AND keys[RightShift]=down
AND keys[Spare3]=down
THEN
ViewerOps.SaveAllEdits[];
Process.Pause[Process.SecondsToTicks[1]];
ENDLOOP;
};
Init[];
GreyScreen[bw, 0, 0, 9999, 9999];
[] ← Terminal.SetBWBitmapState[terminal, displayed];
Terminal.Select[terminal];
TRUSTED {Process.Detach[FORK EmergencySaveAllEdits[]]};
END.