ScreenBreakImpl:
CEDAR
MONITOR
IMPORTS Rope, AMModel, AMModelLocation, FastBreak, WorldVM, ImagerPixelMap, Terminal, Commander, LFBoundingBox, InterminalBackdoor
Break: TYPE = REF BreakRec;
BreakRec:
TYPE =
RECORD [
exit: BOOLEAN,
index: FastBreak.FastBreakId,
condProc: FastBreak.FastBreakProc,
data: REF,
pc: PrincOps.BytePC,
section: AMModel.Section
];
SectionFromName:
PROC [name:
ROPE, sourceIndex:
INT ← 0]
RETURNS [section: AMModel.Section] =
TRUSTED {
dotIndex: INT;
module: AMModel.Section;
moduleContext: AMModel.Context;
moduleName, procName: ROPE;
FindProcedure:
UNSAFE
PROC [proc: AMModel.Section]
RETURNS [stop:
BOOL] = {
name: Rope.ROPE ← AMModel.SectionName[proc];
RETURN[Rope.Equal[name, procName]]};
IF sourceIndex # 0
THEN {
source: AMModel.Source ← NEW[AMModel.SourceObj ← [name, statement, BcdDefs.NullVersion, field[sourceIndex, sourceIndex]]];
section ← AMModel.SourceSection[source, AMModel.RootContext[WorldVM.LocalWorld[]]].section;
RETURN;
};
dotIndex ← Rope.Find[name, "."];
IF dotIndex < 0 THEN RETURN[NIL];
moduleName ← name.Substr[0, dotIndex];
procName ← name.Substr[dotIndex+1, name.Length[]-dotIndex-1];
moduleContext ← AMModel.MostRecentNamedContext[moduleName, AMModel.RootContext[WorldVM.LocalWorld[]]];
IF moduleContext = NIL THEN RETURN[NIL];
module ← AMModel.ContextSection[moduleContext];
IF module = NIL THEN RETURN[NIL];
section ← AMModel.SectionChildren[module, FindProcedure];
};
SetBreak:
ENTRY
PROC [procName:
ROPE, condProc: FastBreak.FastBreakProc, data:
REF ←
NIL, onExit:
BOOL ←
TRUE]
RETURNS [newBreaks:
LIST
OF Break] =
TRUSTED {
ENABLE UNWIND => NULL;
exit: BOOLEAN ← FALSE;
locList: LIST OF AMModelLocation.CodeLocation;
section: AMModel.Section ← SectionFromName[procName];
index: FastBreak.FastBreakId ← NIL;
SELECT AMModel.SectionClass[section]
FROM
proc =>
IF onExit
THEN {locList ← AMModelLocation.ExitLocations[section].list; exit ← TRUE}
ELSE locList ← AMModelLocation.EntryLocations[section].list;
ENDCASE => RETURN[NIL];
UNTIL locList =
NIL
DO
index: FastBreak.FastBreakId ~ FastBreak.SetFastBreak[LOOPHOLE[locList.first.codeBase], locList.first.pc, condProc, LOOPHOLE[data]];
IF index #
NIL
THEN {
break: Break ~
NEW[BreakRec ← [
exit: exit,
index: index,
condProc: condProc,
data: data,
pc: locList.first.pc,
section: section
]];
breaks ← CONS[break, breaks];
newBreaks ← CONS[break, newBreaks];
};
locList ← locList.rest;
ENDLOOP;
};
GetDataRef:
ENTRY
PROC [dataPointer:
LONG
POINTER]
RETURNS [
REF] =
TRUSTED {
ENABLE UNWIND => NULL;
FOR b:
LIST
OF Break ← breaks, b.rest
UNTIL b =
NIL
DO
data: REF ~ b.first.data;
IF dataPointer = LOOPHOLE[data, LONG POINTER] THEN RETURN [data];
ENDLOOP;
RETURN [NIL];
};
ClearBreaks:
ENTRY
PROC
RETURNS [v:
RECORD[numberCleared:
INT ← 0]] =
TRUSTED {
ENABLE UNWIND => NULL;
WHILE breaks #
NIL
DO
break: Break ~ breaks.first;
IF FastBreak.ClearFastBreak[break.index, break.condProc, LOOPHOLE[break.data]] THEN v.numberCleared ← v.numberCleared+1;
breaks ← breaks.rest;
ENDLOOP;
};
PaintTrap: TYPE ~ REF PaintTrapRep;
PaintTrapRep:
TYPE ~
RECORD [
watchForChange: BOOL,
matchedBefore: BOOL,
hit: BOOL,
screen: ImagerPixelMap.PixelMap,
target: ImagerPixelMap.PixelMap
];
PrePaintBreak: FastBreak.FastBreakProc = {
dataRef: REF ~ GetDataRef[data];
WITH dataRef
SELECT
FROM
p: PaintTrap =>
IF
NOT p.hit
THEN {
IF p.watchForChange THEN p.target.Transfer[p.screen]
ELSE p.matchedBefore ← p.target.Equal[p.screen.Clip[p.target.Window]];
};
ENDCASE => NULL;
};
PostPaintBreak: FastBreak.FastBreakProc = {
dataRef: REF ~ GetDataRef[data];
breakHere: BOOL ← FALSE;
WITH dataRef
SELECT
FROM
p: PaintTrap => {
IF p.hit THEN breakHere ← FALSE
ELSE {
match: BOOL ~ p.target.Equal[p.screen.Clip[p.target.Window]];
IF p.watchForChange
THEN {
breakHere ← NOT match;
}
ELSE {
breakHere ← match AND NOT p.matchedBefore;
};
p.hit ← breakHere;
};
};
ENDCASE => NULL;
IF breakHere THEN useOldBreak ← TRUE;
};
PixelMapFromTerminal:
PUBLIC
PROC [vt: Terminal.Virtual]
RETURNS [ImagerPixelMap.PixelMap] ~ {
frameBuffer: Terminal.FrameBuffer ~ Terminal.GetBWFrameBuffer[vt];
refRep:
REF ImagerPixelMap.PixelMapRep ~
NEW[ImagerPixelMap.PixelMapRep ← [
ref: frameBuffer,
pointer: frameBuffer.base, words: LONG[frameBuffer.wordsPerLine]*frameBuffer.height,
lgBitsPerPixel: 0, rast: frameBuffer.wordsPerLine, lines: frameBuffer.height
]];
frame: ImagerPixelMap.PixelMap ~ [
sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0,
sSize: frameBuffer.height, fSize: frameBuffer.width, refRep: refRep
];
RETURN [frame]
};
SetPaintTrap:
PROC [vt: Terminal.Virtual, area: ImagerPixelMap.DeviceRectangle, watchForChange:
BOOL]
RETURNS [list:
LIST
OF Break] = {
Record:
PROC [b:
LIST
OF Break] ~ {
WHILE b#
NIL
DO
list ← CONS[b.first, list];
b ← b.rest;
ENDLOOP;
};
screen: ImagerPixelMap.PixelMap ~ PixelMapFromTerminal[vt];
target: ImagerPixelMap.PixelMap ~ ImagerPixelMap.Copy[screen.Clip[area]];
paintTrap: PaintTrap ←
NEW[PaintTrapRep ← [
watchForChange: watchForChange,
matchedBefore: FALSE,
hit: FALSE,
screen: screen,
target: target
]];
Do:
PROC [procName:
ROPE] ~ {
Record[SetBreak[procName, PrePaintBreak, paintTrap, FALSE]];
Record[SetBreak[procName, PostPaintBreak, paintTrap, TRUE]];
};
Do["ImagerBitmapDeviceImpl.BitmapMaskRuns"];
Do["ImagerBitmapDeviceImpl.BitmapMaskBoxes"];
Do["ImagerBitmapDeviceImpl.BitmapMaskBits"];
Do["ImagerBitmapDeviceImpl.BitmapMoveBoxes"];
};
Count: FastBreak.FastBreakProc = {
dataRef: REF ~ GetDataRef[data];
WITH dataRef
SELECT
FROM
refInt: REF INT => refInt^ ← refInt^ + 1;
ENDCASE => NULL;
};
CountDown: FastBreak.FastBreakProc = {
dataRef: REF ~ GetDataRef[data];
WITH dataRef
SELECT
FROM
refInt:
REF
INT => {
refInt^ ← refInt^ + 1;
IF refInt^ = 0 THEN useOldBreak ← TRUE;
};
ENDCASE => NULL;
};
Try:
PROC
RETURNS [
LIST
OF Break] = {
RETURN [SetBreak["ScreenBreakImpl.Mumble", Count, NEW[INT𡤀], TRUE]]
};
iWasHere: INT ← 0;
Mumble:
PROC = {
iWasHere ← iWasHere + 1;
};
ScreenChangeCommand: Commander.CommandProc = {
vt: Terminal.Virtual ~ InterminalBackdoor.terminal;
x, y, w, h: NAT;
[] ← ClearBreaks[];
[x, y, w, h] ← LFBoundingBox.GetArea[! LFBoundingBox.AbortAdjust => ERROR ABORTED];
[] ← SetPaintTrap[vt, [vt.bwHeight-(y+h), x, h, w], TRUE];
};
ScreenMatchCommand: Commander.CommandProc = {
vt: Terminal.Virtual ~ InterminalBackdoor.terminal;
x, y, w, h: NAT;
[] ← ClearBreaks[];
[x, y, w, h] ← LFBoundingBox.GetArea[! LFBoundingBox.AbortAdjust => ERROR ABORTED];
[] ← SetPaintTrap[vt, [vt.bwHeight-(y+h), x, h, w], FALSE];
};
ClearCommand: Commander.CommandProc = {
[] ← ClearBreaks[];
};
Commander.Register["BreakOnScreenChange", ScreenChangeCommand];
Commander.Register["BreakOnScreenMatch", ScreenMatchCommand];
Commander.Register["ClearScreenBreaks", ClearCommand];
END.