File: VoiceStreamMonitor.mesa
This file contains part of the implementation of voicestreams. The
routines here provide a simple monitoring facility, displaying the
state of existing streams in viewers. The monitor is a process that
runs in background and periodically updates information on the
display.
Last Edited by: Ousterhout, March 8, 1983 11:39 amcompile
Last Edited by: L. Stewart, December 27, 1983 1:50 pm
DIRECTORY
BluejayTool,
Containers USING [Container],
Graphics USING [black, Color, white],
IO USING [int, PutFR],
Jukebox USING [bytesPerChirp],
Process USING [Detach, MsecToTicks, Pause],
PupDefs USING [PupSocket],
Rules USING [Create, Set],
VFonts USING [FontHeight, StringWidth],
ViewerTools USING [MakeNewTextViewer, SetContents],
VoiceStream;
VoiceStreamMonitor: MONITOR LOCKS Lock
IMPORTS IO, Process, Rules, VFonts, ViewerTools, VoiceStream
EXPORTS BluejayTool, VoiceStream
SHARES VoiceStream =
BEGIN OPEN VoiceStream;
masterSwitch: BOOLFALSE;
EnableViewerUpdates: PUBLIC PROC [enable: BOOLTRUE] = {
IF masterSwitch = enable THEN RETURN;
masterSwitch ← enable;
IF masterSwitch THEN Process.Detach[FORK monitor[]];
};
StartMonitor: PUBLIC PROCEDURE[container: Containers.Container,
y: INT] RETURNS [newY: INT] =
Set up the monitor structure, then invoke the background
monitor process.
BEGIN
lineHeight: INT ← VFonts.FontHeight[] + 3;
playbackWidth: INT ← VFonts.StringWidth["Recording:"] + 20;
pieceWidth: INT ← VFonts.StringWidth["XXXX[00000000:00000000]"];
[] ← Rules.Create[info: [parent: container, wx: 0, wy: y, ww: 1024,
wh: 1, border: FALSE]];
y ← y+2;
FOR i:INTEGER IN [0..nMonEntries) DO
MonEntries[i].handle ← NIL;
MonEntries[i].new ← TRUE;
MonEntries[i].playbackViewer ← ViewerTools.MakeNewTextViewer[
info: [parent: container, wx: 0, wy: y, wh: lineHeight,
ww: playbackWidth, data: " ", border: FALSE, scrollable: FALSE]];
MonEntries[i].pieceViewer ← ViewerTools.MakeNewTextViewer[
info: [parent: container, wx: playbackWidth+10,
wy: y, wh: lineHeight, ww: pieceWidth, data: " ",
border: FALSE, scrollable: FALSE]];
MonEntries[i].actionRule ← Rules.Create[
info: [parent: container, wx: playbackWidth+ pieceWidth+15,
wy: y, wh: lineHeight, ww: lineHeight,
border: FALSE], color: Graphics.white];
[] ← Rules.Create[info: [parent: container, wx: 0, wy: y+lineHeight+1,
ww: 1024, wh: 1, border: FALSE]];
y ← y + lineHeight + 3;
ENDLOOP;
EnableViewerUpdates[TRUE];
RETURN [y];
END;
monGet: ENTRY PROCEDURE[i: INTEGER]
RETURNS[handle: Handle, socket: PupDefs.PupSocket, tuneId: INT,
firstByte: INT, nBytes: INT, playback: BOOLEAN, action: BOOLEAN] =
This is just a utility procedure to read things out of a stream
record safely for the monitor procedure.
BEGIN
piece: REF Piece;
buffer: REF Buffer;
adjust: INT;
handle ← MonEntries[i].handle;
IF handle = NIL THEN RETURN;
IF handle.connection # NIL THEN socket ← handle.connection.socket;
action ← handle.action;
handle.action ← FALSE;
piece ← handle.piece;
IF piece = NIL THEN
BEGIN
tuneId ← -1;
firstByte ← -1;
nBytes ← -1;
playback ← FALSE;
END
ELSE
BEGIN
tuneId ← piece.tuneId;
To compute the actual number of bytes left in the piece,
don't include the number of bytes waiting for the client.
Ditto for the first byte.
firstByte ← piece.firstByte;
nBytes ← piece.nBytes;
buffer ← handle.firstClientBuffer;
adjust ← 0;
WHILE buffer # NIL DO
adjust ← adjust + buffer.block.stopIndexPlusOne
- buffer.block.startIndex;
buffer ← buffer.next;
ENDLOOP;
firstByte ← firstByte - adjust;
nBytes ← nBytes + adjust;
playback ← piece.playback;
END;
RETURN;
END;
monitor: PROCEDURE =
This procedure wakes up four times a second to update things in
the display.
BEGIN
handle: Handle;
socket: PupDefs.PupSocket;
tuneId, firstByte, nBytes: INT;
playback, action, new: BOOLEAN;
white: REF Graphics.Color ← NEW[Graphics.Color ← Graphics.white];
black: REF Graphics.Color ← NEW[Graphics.Color ← Graphics.black];
WHILE TRUE DO
Process.Pause[Process.MsecToTicks[250]];
IF NOT masterSwitch THEN EXIT;
FOR i:INTEGER IN [0..nMonEntries) DO
Read out information from the handle. This also synchronizes
with the other processes touching the handle.
[handle, socket, tuneId, firstByte, nBytes, playback, action]
← monGet[i];
new ← MonEntries[i].new;
IF (handle = NIL) THEN
BEGIN
If this entry is idle, do nothing.
IF new THEN LOOP
ELSE
If the entry has just become idle, then blank it out.
BEGIN
MonEntries[i].new ← TRUE;
ViewerTools.SetContents[MonEntries[i].playbackViewer, " "];
ViewerTools.SetContents[MonEntries[i].pieceViewer, " "];
Rules.Set[MonEntries[i].actionRule, white];
LOOP;
END;
END;
Update entries that have changed. Update everything if the
entry is a new one.
IF (tuneId # MonEntries[i].tuneId)
OR (firstByte # MonEntries[i].firstByte)
OR (nBytes # MonEntries[i].nBytes)
OR new THEN
BEGIN
IF tuneId >= 0 THEN
BEGIN
ViewerTools.SetContents[MonEntries[i].pieceViewer,
IO.PutFR["%d[%d:%d]", IO.int[tuneId],
IO.int[firstByte/Jukebox.bytesPerChirp],
IO.int[(firstByte+nBytes-1)/Jukebox.bytesPerChirp]]];
END
ELSE
BEGIN
ViewerTools.SetContents[MonEntries[i].pieceViewer, " "];
ViewerTools.SetContents[MonEntries[i].playbackViewer, " "];
END;
MonEntries[i].tuneId ← tuneId;
MonEntries[i].firstByte ← firstByte;
MonEntries[i].nBytes ← nBytes;
END;
IF (playback # MonEntries[i].playback) OR new THEN
BEGIN
IF tuneId >= 0 THEN
BEGIN
IF playback THEN
ViewerTools.SetContents[MonEntries[i].playbackViewer,
"Playback:"]
ELSE ViewerTools.SetContents[MonEntries[i].playbackViewer,
"Recording:"];
END;
MonEntries[i].playback ← playback;
END;
IF MonEntries[i].black THEN
BEGIN
Rules.Set[MonEntries[i].actionRule, white];
MonEntries[i].black ← FALSE;
END
ELSE IF action THEN
BEGIN
Rules.Set[MonEntries[i].actionRule, black];
MonEntries[i].black ← TRUE;
END;
MonEntries[i].new ← FALSE;
ENDLOOP;
ENDLOOP;
END;
END.
Last Edited by: L. Stewart, March 25, 1983 3:55 pm, VoiceStream changes
Last Edited by: L. Stewart, December 27, 1983 1:50 pm, Cedar 5