DruidToolImpl.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Peter B. Kessler, August 24, 1990 11:27 am PDT
Spreitze, October 3, 1990 10:28 am PDT
Willie-s, May 22, 1992 1:37 pm PDT
Christian Jacobi, June 19, 1992 2:00 pm PDT
DIRECTORY
--BackStop,-- BasicTime, Breakpoint, BreakWorldArchitecture, Commander, Convert, Druid, DruidTool, Feedback, FeedbackClasses, IO, Menus, NumberLabels, PFS, Process, Rope, SameBreakWorld, SourceFileOps, ViewerClasses, ViewerOps, ViewerTools, VTables;
DruidToolImpl:
CEDAR
MONITOR
LOCKS displayHandle USING displayHandle: DruidTool.Handle
IMPORTS
--BackStop,-- BasicTime, Breakpoint, BreakWorldArchitecture, Commander, Druid, Feedback, FeedbackClasses, IO, Menus, NumberLabels, PFS, Process, Rope, SameBreakWorld, SourceFileOps, ViewerOps, ViewerTools, VTables
~ {
Columns:
TYPE ~
MACHINE
DEPENDENT { clear (0), zero, count, location, text, max };
clearColumn: NAT ~ ORD[Columns.clear];
zeroColumn: NAT ~ ORD[Columns.zero];
countColumn: NAT ~ ORD[Columns.count];
locationColumn: NAT ~ ORD[Columns.location];
textColumn: NAT ~ ORD[Columns.text];
maxColumn: NAT ~ ORD[Columns.max];
sampleRate: Process.Milliseconds ¬ 1000;
UpdateDisplayProcess:
PROC ~ {
displayHandle: DruidTool.Handle ¬ NEW[DruidTool.HandleRep];
Inner:
PROC [displayHandle: DruidTool.Handle] ~ {
newPulses: BasicTime.Pulses ¬ BasicTime.GetClockPulses[];
inverseSecs: REAL;
deltaMicros:
CARD ¬
BasicTime.PulsesToMicroseconds[newPulses]
- BasicTime.PulsesToMicroseconds[displayHandle.samplePulses];
rowData: DruidTool.RowData ¬ DruidTool.nullRowData;
index: NAT ¬ 1;
changed: BOOL ¬ FALSE;
newRows: NAT ¬ displayHandle.rows;
subTotal: CARD ¬ 0;
grandTotal: CARD ¬ 0;
IF deltaMicros < 100 THEN RETURN;
inverseSecs ¬ 1E6 / deltaMicros;
displayHandle.samplePulses ¬ newPulses;
FOR rowData ¬ displayHandle.rowData, rowData.next
WHILE rowData #
NIL
DO {
RemoveRow:
PROCEDURE [displayHandle: DruidTool.Handle, row: DruidTool.RowData] ~ {
rowData: DruidTool.RowData ¬ DruidTool.nullRowData;
lag: DruidTool.RowData ¬ DruidTool.nullRowData;
index: NAT ¬ 1;
FOR rowData ¬ displayHandle.rowData, rowData.next
WHILE rowData # DruidTool.nullRowData
DO {
IF rowData = row
THEN {
FOR i:
NAT
IN [index+1..displayHandle.rows)
DO {
VTables.ExchangeRows[displayHandle.vTable, i, i-1];
} ENDLOOP;
IF lag = DruidTool.nullRowData
THEN {
displayHandle.rowData ¬ rowData.next;
}
ELSE {
lag.next ¬ rowData.next;
};
displayHandle.changed ¬ TRUE;
EXIT;
}
ELSE {
lag ¬ rowData;
index ¬ index + 1;
};
} ENDLOOP;
RETURN;
};
IF displayHandle.vTable.destroyed THEN RETURN;
WITH rowData
SELECT
FROM
subtotalRow: DruidTool.SubtotalRow => {
IF subtotalRow.active
THEN {
update the count
nl: NumberLabels.NumberLabel ¬ VTables.GetTableEntry[
table: displayHandle.vTable, row: index, column: countColumn];
IF nl #
NIL
AND subTotal # subtotalRow.count
THEN {
Update event count
subtotalRow.count ¬ subTotal;
NumberLabels.NumberLabelUpdate[nl, subTotal];
};
subTotal ¬ 0;
index ¬ index + 1;
}
ELSE {
remove the row from the table
RemoveRow[displayHandle: displayHandle, row: subtotalRow];
};
};
counterRow: DruidTool.CounterRow => {
IF Druid.IsActive[megalith: counterRow.megalith]
THEN {
update the count
nl: NumberLabels.NumberLabel ¬ VTables.GetTableEntry[
table: displayHandle.vTable, row: index, column: countColumn];
current: CARD ¬ Druid.Read[megalith: counterRow.megalith];
IF nl #
NIL
AND current # counterRow.count
THEN {
Update event count
counterRow.count ¬ current;
NumberLabels.NumberLabelUpdate[nl, current];
};
subTotal ¬ subTotal + current;
grandTotal ¬ grandTotal + current;
index ¬ index + 1;
}
ELSE {
remove the row from the table
RemoveRow[displayHandle: displayHandle, row: counterRow];
};
};
ENDCASE => ERROR;
} ENDLOOP;
IF
NOT displayHandle.vTable.parent.iconic
THEN {
NumberLabels.NumberLabelUpdate[
nl: VTables.GetTableEntry[displayHandle.vTable, 0, countColumn], new: grandTotal];
};
IF displayHandle.changed
THEN {
InstallNewRows[displayHandle, index];
};
};
displayHandle.vTable ¬ VTables.Create[
rows: 1, columns: maxColumn, name: "Druid", x: 0, y: 2];
displayHandle.sameBreakWorld ¬ SameBreakWorld.Create[];
displayHandle.feedbackStream ¬ FeedbackClasses.CreateStreamOnRouter[
msgRouter: Feedback.CreateRouter[], msgClass: $Default];
displayHandle.rows ¬ 0;
displayHandle.rowData ¬ DruidTool.nullRowData;
SetTimeout[displayHandle: displayHandle];
displayHandle.samplePulses ¬ BasicTime.GetClockPulses[];
{
menu: ViewerClasses.Menu ¬ Menus.CreateMenu[];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "ClearAll", proc: ClearAll, clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "ZeroAll", proc: ZeroAll, clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Set", proc: SetMonitoredCountingBreak,
clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "UnmonitoredSet", proc: SetUnmonitoredCountingBreak,
clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "AddSubTotal", proc: AddSubTotal, clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Freeze", proc: Freeze, clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Sample", proc: Sample, clientData: displayHandle]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Thaw", proc: Thaw, clientData: displayHandle]];
ViewerOps.SetMenu[displayHandle.vTable.parent, menu, FALSE];
};
VTables.SetTableEntry[
table: displayHandle.vTable,
row: 0,
column: countColumn,
flavor: $Viewer,
clientData: NumberLabels.CreateNumber[
info: [parent: displayHandle.vTable], chars: 9, paint: FALSE],
useMaxSize: TRUE];
ViewerOps.ChangeColumn[displayHandle.vTable.parent, left];
InstallNewRows[displayHandle, 1];
UNTIL WaitForChange[displayHandle]
DO
ENABLE ABORTED => EXIT;
DoUnderLock[displayHandle, Inner];
ENDLOOP;
DoUnderLock[displayHandle, InnerClearAll];
DoUnderLock[displayHandle, InnerFlushCache];
InstallNewRows[displayHandle, 0];
};
SetUnmonitoredCountingBreak: Menus.MenuProc ~ {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
UnmonitoredInner:
PROCEDURE[displayHandle: DruidTool.Handle] ~ {
InnerSetCountingBreak[displayHandle: displayHandle, monitored: FALSE];
RETURN;
};
DoUnderLock[clientData, UnmonitoredInner];
RETURN;
};
SetMonitoredCountingBreak: Menus.MenuProc ~ {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
MonitoredInner:
PROCEDURE[displayHandle: DruidTool.Handle] ~ {
InnerSetCountingBreak[displayHandle: displayHandle, monitored: TRUE];
RETURN;
};
DoUnderLock[clientData, MonitoredInner];
RETURN;
};
InnerSetCountingBreak:
PROC [displayHandle: DruidTool.Handle, monitored:
BOOLEAN] ~ {
Inner:
PROC = {
vtab: VTables.VTable = displayHandle.vTable;
counterRow: DruidTool.CounterRow ¬
NEW[DruidTool.CounterRowRep ¬ [
feedbackStream: displayHandle.feedbackStream, details: counter[]]];
row: NAT ¬ displayHandle.rows;
errorMessage: Rope.ROPE ¬ NIL;
{
-- extra block to contain EXITS.
{
GetSelection:
PROCEDURE []
RETURNS [
name: Rope.
ROPE ¬
NIL,
label: Rope.
ROPE ¬
NIL,
position:
INT ¬ 0,
text: Rope.
ROPE ¬
NIL] ~ {
This isn't as atomic as I'd like, but the TiogaOps interface looks too complicated.
viewer: ViewerClasses.Viewer ~ ViewerTools.GetSelectedViewer[];
IF viewer#
NIL
THEN {
selection: ViewerTools.SelPos ~ ViewerTools.GetSelection[viewer];
text ¬ ViewerTools.GetSelectionContents[];
RETURN [
name: viewer.name,
label:
IF Rope.IsEmpty[viewer.label]
THEN viewer.name
ELSE viewer.label,
--what I'd like here is an interface to IconHacks.
position:
IF selection.caretPos = before
THEN selection.start
ELSE selection.start + selection.length,
text: text];
};
};
[ name: counterRow.file,
label: counterRow.label,
position: counterRow.position,
text: counterRow.text] ¬ GetSelection[];
};
{
pc: CARD ¬ 0;
address: BreakWorldArchitecture.Address ¬ BreakWorldArchitecture.nullAddress;
[address: pc, correspondingPosition: counterRow.correspondingPosition] ¬
displayHandle.sameBreakWorld.PCFromFileAndPosition[
mesaFilename: counterRow.file,
position: counterRow.position,
report: displayHandle.feedbackStream
! SameBreakWorld.NotFound => {
errorMessage ¬ "Can't find pc for selection.";
GO TO Cant;
};
];
address ¬ BreakWorldArchitecture.NewAddress[
breakWorld: displayHandle.sameBreakWorld.BreakWorld[],
address: LOOPHOLE[pc]];
counterRow.megalith ¬ Druid.Set[address: address, monitored: monitored
! Breakpoint.CantSet => {
errorMessage ¬ message;
GO TO Cant;
};
];
displayHandle.feedbackStream.PutF["Counting break set at %g|%g.\n",
[rope[counterRow.label]], [integer[counterRow.correspondingPosition]]];
SourceFileOps.OpenSource[
desc: "Counting break loc",
pos: [fileName:
PFS.PathFromRope[rope: counterRow.file],
index: [char: [counterRow.correspondingPosition] ]],
feedBack: displayHandle.feedbackStream];
};
AppendRow[displayHandle: displayHandle, row: counterRow];
InstallNewRows[displayHandle, row + 1];
VTables.SetTableEntry[
table: vtab, row: row, column: clearColumn,
name: "clear", proc: ClearBreak, clientData: counterRow,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: zeroColumn,
name: "zero", proc: ZeroCount, clientData: counterRow,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: countColumn, flavor: $Viewer,
clientData: NumberLabels.CreateNumber[info: [parent: vtab], chars: 9, paint: FALSE],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: locationColumn,
name: IO.PutFR["%g|%g",
[rope[counterRow.label]], [integer[counterRow.correspondingPosition]]],
proc: ShowPosition, clientData: counterRow,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: textColumn, flavor: $Text,
name: counterRow.text,
useMaxSize: TRUE];
VTables.Install[vtab];
EXITS
Cant => {
displayHandle.feedbackStream.PutRope["Can't set counting break: "];
displayHandle.feedbackStream.PutRope[errorMessage];
displayHandle.feedbackStream.PutRope[".\n"];
};
};
RETURN;
};
backStopMessage: Rope.ROPE ¬ NIL--BackStop.Call[Inner]--; Inner[];
IF backStopMessage #
NIL
THEN {
displayHandle.feedbackStream.PutF1["Couldn't set counting break: %g.\n",
[rope[backStopMessage]]];
};
RETURN;
};
ClearAll: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
DoUnderLock[clientData, InnerClearAll];
RETURN;
};
InnerClearAll:
PROCEDURE [displayHandle: DruidTool.Handle] ~ {
vtab: VTables.VTable = displayHandle.vTable;
FOR each: DruidTool.RowData ¬ displayHandle.rowData, each.next
WHILE each #
NIL
DO {
ClearBreak[parent: NIL, clientData: each];
} ENDLOOP;
RETURN;
};
ClearBreak: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
errorMessage: Rope.ROPE ¬ NIL;
WITH clientData
SELECT
FROM
counterRow: DruidTool.CounterRow => {
megalith: Druid.Megalith ~ counterRow.megalith;
Druid.Clear[megalith: counterRow.megalith
! Breakpoint.CantClear => {
errorMessage ¬ message;
GO TO Cant;
};
];
counterRow.feedbackStream.PutF["Counting break cleared from %g|%g.\n",
[rope[counterRow.label]], [integer[counterRow.position]]];
EXITS
Cant => {
counterRow.feedbackStream.PutRope["Can't clear counting break: "];
counterRow.feedbackStream.PutRope[errorMessage];
counterRow.feedbackStream.PutRope[".\n"];
};
};
subtotalRow: DruidTool.SubtotalRow => {
subtotalRow.active ¬ FALSE;
};
ENDCASE => NULL;
RETURN;
};
ZeroAll: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
DoUnderLock[clientData, InnerZeroAllCounts];
RETURN;
};
InnerZeroAllCounts:
PROC [displayHandle: DruidTool.Handle] = {
vtab: VTables.VTable = displayHandle.vTable;
FOR each: DruidTool.RowData ¬ displayHandle.rowData, each.next
WHILE each #
NIL
DO {
ZeroCount[parent: NIL, clientData: each];
} ENDLOOP;
RETURN;
};
ZeroCount: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
WITH clientData
SELECT
FROM
counterRow: DruidTool.CounterRow => {
megalith: Druid.Megalith ~ counterRow.megalith;
[] ¬ Druid.Zero[megalith: counterRow.megalith];
};
ENDCASE => NULL;
RETURN;
};
ShowPosition: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
WITH clientData
SELECT
FROM
counterRow: DruidTool.CounterRow => {
SourceFileOps.OpenSource[
desc: "Source",
pos: [fileName:
PFS.PathFromRope[rope: counterRow.file],
index: [char: [counterRow.correspondingPosition] ]],
feedBack: counterRow.feedbackStream];
};
ENDCASE => NULL;
RETURN;
};
fudge: NAT ¬ 20;
InstallNewRows:
PROC [displayHandle: DruidTool.Handle, rows:
NAT] = {
IF displayHandle # DruidTool.nullHandle
THEN {
vtab: VTables.VTable ¬ displayHandle.vTable;
IF vtab #
NIL
AND
NOT vtab.destroyed
AND rows # displayHandle.rows
THEN {
parent: VTables.VTable ¬ vtab.parent;
smaller: BOOL ¬ rows < displayHandle.rows;
displayHandle.rows ¬ rows;
VTables.SetRowsAndColumns[vtab, rows, maxColumn];
SELECT rows
FROM
0 => {};
1 => {
VTables.SetTableEntry[
table: vtab, column: locationColumn, name: NIL, border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: textColumn, name: NIL, border: VTables.NullBorder];
};
2 => {
VTables.SetTableEntry[
table: vtab, column: locationColumn, name: "location",
border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: textColumn, name: "selection",
border: VTables.NullBorder];
};
ENDCASE;
VTables.Install[vtab, FALSE];
ViewerOps.SetOpenHeight[parent, vtab.wh + fudge];
IF
NOT parent.iconic
THEN {
ViewerOps.ComputeColumn[parent.column];
IF smaller THEN ViewerOps.PaintViewer[parent, all, TRUE, NIL];
};
};
};
};
AddSubTotal: Menus.MenuProc ~ {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
DoUnderLock[clientData, InnerAddSubTotal];
};
InnerAddSubTotal:
PROC [displayHandle: DruidTool.Handle] ~ {
vtab: VTables.VTable ~ displayHandle.vTable;
rows: NAT ~ displayHandle.rows;
subtotalRow: DruidTool.RowData ¬
NEW[DruidTool.RowDataRep ¬ [
count: 0, details: subtotal[active: TRUE]]];
AppendRow[displayHandle: displayHandle, row: subtotalRow];
InstallNewRows[displayHandle, rows + 1];
VTables.SetTableEntry[
table: vtab, row: rows, column: clearColumn,
name: "clear", proc: ClearBreak, clientData: subtotalRow,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: rows, column: countColumn, flavor: $Viewer,
clientData: NumberLabels.CreateNumber[info: [parent: vtab], chars: 9, paint: FALSE],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: rows, column: locationColumn, name: "subTotal"];
VTables.Install[vtab];
};
Freeze: Menus.MenuProc ~ {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
WITH clientData
SELECT
FROM
displayHandle: DruidTool.Handle => {
DisableTimeout[displayHandle: displayHandle];
};
ENDCASE;
};
Sample: Menus.MenuProc ~ {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
WITH clientData
SELECT
FROM
displayHandle: DruidTool.Handle => {
CauseTimeout[displayHandle: displayHandle];
};
ENDCASE;
};
Thaw: Menus.MenuProc ~ {
PROC [parent: REF ANY, clientData: REF ANY ← NIL,
mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE]
WITH clientData
SELECT
FROM
displayHandle: DruidTool.Handle => {
SetTimeout[displayHandle: displayHandle, pause: sampleRate];
CauseTimeout[displayHandle: displayHandle];
};
ENDCASE;
};
InnerFlushCache:
PROCEDURE [displayHandle: DruidTool.Handle] ~ {
displayHandle.sameBreakWorld.Destroy[];
displayHandle.sameBreakWorld ¬ SameBreakWorld.nullHandle;
};
RopeFromEntry:
PROC [vtab: VTables.VTable, row, col:
NAT]
RETURNS [Rope.
ROPE] = {
v: ViewerClasses.Viewer ¬ VTables.GetTableEntry[vtab, row, col];
IF v #
NIL
THEN
WITH ViewerOps.GetViewer[v]
SELECT
FROM
rope: Rope.ROPE => RETURN [rope];
ENDCASE;
RETURN [NIL];
};
WaitForChange:
ENTRY
PROCEDURE [displayHandle: DruidTool.Handle]
RETURNS [quit:
BOOLEAN] ~ {
WaitForChange waits on the timeCondition.
ENABLE UNWIND => NULL;
WAIT displayHandle.timeCondition;
RETURN [quit: displayHandle.vTable.destroyed];
};
SetTimeout:
ENTRY
PROCEDURE [displayHandle: DruidTool.Handle, pause: Process.Milliseconds ¬ 1000] ~
TRUSTED {
Process.SetTimeout[@displayHandle.timeCondition, Process.MsecToTicks[pause]];
};
DisableTimeout:
ENTRY
PROCEDURE [displayHandle: DruidTool.Handle] ~
TRUSTED {
Process.DisableTimeout[@displayHandle.timeCondition];
};
CauseTimeout:
ENTRY
PROCEDURE [displayHandle: DruidTool.Handle] ~
TRUSTED {
BROADCAST displayHandle.timeCondition;
};
DoUnderLock:
PROCEDURE [
ref:
REF
ANY,
inner:
PROCEDURE [displayHandle: DruidTool.Handle]] ~ {
WITH ref
SELECT
FROM
displayHandle: DruidTool.Handle => {
Locked:
ENTRY
PROCEDURE [displayHandle: DruidTool.Handle] ~ {
ENABLE UNWIND => NULL;
inner[displayHandle];
};
Locked[displayHandle: displayHandle];
};
ENDCASE;
};
AppendRow:
PROCEDURE [
displayHandle: DruidTool.Handle,
row: DruidTool.RowData] ~ {
IF displayHandle.rowData =
NIL
THEN {
displayHandle.rowData ¬ row;
}
ELSE {
FOR each: DruidTool.RowData ¬ displayHandle.rowData, each.next
DO {
IF each.next =
NIL
THEN {
each.next ¬ row;
EXIT;
};
} ENDLOOP;
};
RETURN;
};
StartDruidTool: Commander.CommandProc ~ {
Process.Detach[FORK UpdateDisplayProcess[]];
};
Commander.Register[
key: "Druid",
proc: StartDruidTool,
doc: "Druid is a tool for watching counting breaks."];
}.