RemoteViewersTerminalByX11.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer on September 3, 1992 1:36 pm PDT
Cedardummy, June 17, 1988 10:24:12 am PDT
Willie-s, April 22, 1992 12:52 pm PDT
Christian Jacobi, August 16, 1993 12:45 pm PDT
DIRECTORY BasicTime, CedarProcess, CodeControl, Commander, CommanderOps, Convert, Cursors, EchoStream, FS, Histograms, HistogramsViewing, HostAndTerminalOps, Imager, ImagerReceiver, IO, IOClasses, Labels, NetAddressing, NetworkStream, PFS, Process, RefTab, RefText, RelativeTimes, RemoteEventTime, RemoteImagerDataTypes, RemoteViewersTerminalsKernel, Rope, SF, SimpleFeedback, TerminalMultiServing, TerminalSender, ThisMachine, UserInput, UserInputGetActions, UserInputInsertActions, UserInputLookahead, UserInputOps, UserInputTypes, UserProfile, ViewerContexts, Xl, XlBitmap, XlColorAccess, XlCutBuffers, XTk, XTkBitmapScroller, XTkShellWidgets, XTkTIPSource, XTkWidgets;
RemoteViewersTerminalByX11:
CEDAR
MONITOR
IMPORTS BasicTime, CodeControl, Commander, CommanderOps, Convert, EchoStream, FS, Histograms, ImagerReceiver, IO, IOClasses, NetworkStream, PFS, Process, RefTab, RefText, RemoteEventTime, RemoteViewersTerminalsKernel, Rope, SF, SimpleFeedback, TerminalMultiServing, TerminalSender, ThisMachine, UserInputGetActions, UserInputInsertActions, UserInputLookahead, UserInputOps, UserProfile, Xl, XlBitmap, XlColorAccess, XlCutBuffers, XTk, XTkBitmapScroller, XTkShellWidgets, XTkTIPSource, XTkWidgets
=
BEGIN OPEN HAT:HostAndTerminalOps, RIDT:RemoteImagerDataTypes, NA:NetAddressing, Nws:NetworkStream, RET:RemoteEventTime, RVTK:RemoteViewersTerminalsKernel, TMS:TerminalMultiServing;
CursorHandle: TYPE ~ REF CursorRec; -- used to be in Cursors
CursorRec:
TYPE ~
RECORD [
info: Cursors.CursorInfo,
bits: Cursors.CursorArray
];
ROPE: TYPE ~ Rope.ROPE;
LORA: TYPE = LIST OF REF ANY;
LOR: TYPE ~ LIST OF ROPE;
LON: TYPE ~ LIST OF NAT;
EventTime: TYPE ~ RET.EventTime;
VEC: TYPE ~ Imager.VEC;
KeyName: TYPE ~ [0..127];
KeyBits: TYPE ~ PACKED ARRAY KeyName OF DownUp;
DownUp:
TYPE ~
MACHINE
DEPENDENT {down(0), up(1)} ¬ up;
Keys and mouse buttons are normally up.
KeyState:
TYPE ~
RECORD[
SELECT
OVERLAID *
FROM
bits => [bits: KeyBits],
words => [words: ARRAY [0..WORDS[KeyBits]) OF WORD],
ENDCASE
];
TimingHistory: TYPE ~ REF TimingHistoryPrivate;
TimingHistoryPrivate:
TYPE ~
RECORD [
i, shownI: HistoryIndex ¬ 0,
j, shownJ: INT ¬ 0,
hist: ARRAY HistoryIndex OF Hist ¬ ALL[[]]];
NHistoryIndices: CARDINAL ~ 256;
HistoryIndex: TYPE ~ CARDINAL[0 .. NHistoryIndices);
Hist:
TYPE ~
RECORD [
org, mid, fin: EventTime ¬ [0, 0],
desc: RIDT.TimeEventDesc ¬ [mousePosition: [0, FALSE, 0], action: [contents: timedOut[]] ]];
BoxI: TYPE ~ RECORD [xmin, ymin, xmax, ymax: INTEGER];
LBRT: TYPE ~ RECORD [left, bottom, right, top: NAT];
PktStats: TYPE ~ RECORD [total, bad: CARD ¬ 0];
RtStats:
TYPE ~
RECORD [
min: REAL ¬ 99.99,
max: REAL ¬ -50.0,
sum: REAL ¬ 0.0,
count: NAT ¬ 0];
Session: TYPE ~ REF SessionPrivate;
SessionPrivate: TYPE ~ RECORD [sst: ServerState, myIncreek: UserInput.Handle ¬ NIL];
minPassTime: UserInputGetActions.DeltaTime ¬ 50;
maxPassTime: UserInputGetActions.DeltaTime ¬ 30D3;
ServerState: TYPE ~ REF ServerStateRecord;
ServerStateRecord:
TYPE ~
RECORD [
upTime: Histograms.Histogram ¬ NIL,
downTime: Histograms.Histogram ¬ NIL,
roundTime: Histograms.Histogram ¬ NIL,
th: TimingHistory ¬ NIL,
nextCursorPattern: Cursors.CursorArray ¬ ALL[5A5AH],
scrollPos: Xl.Point ¬ [0, 0],
scrollWaiting: BOOL ¬ FALSE,
tho: IO.STREAM ¬ NIL,
setSize: BOOL ¬ TRUE,
theSize: ViewerContexts.VECI ¬ [300, 300],
widthGrain: NAT ¬ 1,
shell: XTk.Widget ¬ NIL,
xConn: Xl.Connection ¬ NIL,
colorData: XlColorAccess.ColorData ¬ NIL,
xThread: Xl.TQ ¬ NIL,
tipSourceHandle: XTkTIPSource.TipSourceHandle ¬ NIL,
ruler1: XTk.Widget ¬ NIL,
bitmapScroller: XTk.Widget ¬ NIL,
realBitmap: XTk.Widget ¬ NIL,
bitmap: XlBitmap.Bitmap ¬ NIL,
bmHeight: INTEGER ¬ 0,
cursorPixmap: Xl.Pixmap ¬ Xl.nullPixmap,
cursorGC: Xl.GContext ¬ NIL,
cursTab: RefTab.Ref--CursorHandle -> CachedCursor-- ¬ NIL,
curVer: BYTE ¬ 0,
client: NA.Address ¬ NA.nullAddress,
topName: ROPE ¬ ">unconnected<",
sender: TerminalSender.Sender ¬ NIL,
connLabel: XTk.Widget ¬ NIL,
statLabel: XTk.Widget ¬ NIL,
outStream, inStream: IO.STREAM ¬ NIL,
rtStats: RtStats ¬ [],
increek: UserInput.Handle ¬ NIL,
openCreeks, sendingCreeks: RefTab.Ref,
xlateKC: TerminalSender.KeyCodeTranslation ¬ NIL,
inside: BOOL ¬ TRUE, --cursor is inside the terminal
passTime: UserInputGetActions.DeltaTime ¬ minPassTime, --don't let this much time pass without reporting it
curTimeStamp: UserInputGetActions.TimeStamp ¬ [0], --time at head of increek
The state last reported through FilterGetAction :
lastTimeStamp: UserInputGetActions.TimeStamp ¬ [0],
needRepaint: BOOL ¬ FALSE
];
CachedCursor: TYPE ~ REF CachedCursorRep;
CachedCursorRep: TYPE ~ RECORD [ch: CursorHandle, xcursor: Xl.Cursor];
bot: EventTime ~ [0, 0];
installDir: PFS.PATH ~ PFS.GetWDir[];
codingMethod: ROPE ¬ "B4KS";
reject: BOOL ¬ FALSE;
willingToCode: BOOL ¬ TRUE;
logInput, logPlainOutput, logCodedOutput, viewInput, viewDetails: BOOL ¬ FALSE;
debugMonitor: BOOL ¬ TRUE;
headTime: NAT ¬ 15;
decayRate: REAL ¬ 0.95;
theServer: RVTK.ViewersServer ¬ NIL;
theSST: ServerState ¬ NIL;
hbo: LBRT ~ [0, 1, 0, 0];
hbi: LBRT ~ [1, 2, 1, 1];
hboHeight: NAT ~ hbo.bottom + hbo.top;
hboWidth: NAT ~ hbo.left + hbo.right;
hbiHeight: NAT ~ hbi.bottom + hbi.top;
hbiWidth: NAT ~ hbi.left + hbi.right;
inputLogName: ROPE ~ "-vux:/tmp/RemoteInput.log";
plainOutputLogName: ROPE ~ "-vux:/tmp/RemoteOutput-plain.log";
codedOutputLogName: ROPE ~ "-vux:/tmp/RemoteOutput-coded.log";
fileTapLogName: ROPE ~ "-vux:/tmp/FileTap.log";
minTime: REAL ~ -4.0;
maxTime: REAL ~ 30.0;
dTime: REAL ~ 0.01;
lossyBitmap: BOOL ¬ FALSE;
<<showClass: PUB.Class ~ PUB.MakeClass[[
proc: MyPop,
choices: LIST[[$ShowTimes, "Show round trip time histograms"]]
]];
MyPop:
PROC [view, instanceData, classData, key:
REF
ANY]
--PUB.PopUpButtonProc-- ~ {
sst: ServerState ~ NARROW[instanceData];
SELECT key
FROM
$ShowTimes => ShowTimes[sst];
ENDCASE => SimpleFeedback.Append[$RemoteViewersTerminalByX11, oneLiner, $Error, "Can't happen!"];
RETURN};>>
GetCreek:
PROC [from: UserInput.Handle]
RETURNS [UserInput.Handle] ~ {
uih: UserInput.Handle ~ UserInputOps.Create[];
UserInputLookahead.SaveState[uih, from];
RETURN [uih]};
StopServing:
ENTRY
PROC [server:
RVTK.ViewersServer] ~ {
ENABLE UNWIND => NULL;
sst: ServerState ~ NARROW[server.data];
IF sst.client = NA.nullAddress THEN RETURN;
InnerStop[sst];
IF sst.xConn#NIL AND sst.xConn.Alive[] THEN sst.xConn.CloseConnection[];
TRUSTED {sst.client ¬ NA.nullAddress};
RETURN};
StartServing:
ENTRY
PROC [server:
RVTK.ViewersServer, host:
TMS.Host] ~ {
ENABLE UNWIND => NULL;
sst: ServerState ~ NARROW[server.data];
IF TMS.EqualHosts[host, sst.client] THEN RETURN;
TRUSTED {sst.client ¬ host};
RETURN};
Work:
PROC [server:
RVTK.ViewersServer, in, out:
IO.
STREAM, host:
TMS.Host, sessionDescr:
ROPE, version:
HAT.ProtocolVersion,
Push:
RVTK.PushProc] = {
sst: ServerState ~ NARROW[server.data];
et1: EventTime;
myOutStream: IO.STREAM ¬ NIL;
mySender: TerminalSender.Sender ¬ NIL;
decodingStream: IO.STREAM ¬ in;
inputLog, plainOutputLog, codedOutputLog, push, pull, myRcvStream: IO.STREAM ¬ NIL;
myXConn: Xl.Connection ¬ NIL;
initialState: TerminalSender.ActionBodyList ¬ NIL;
sess: Session ¬ NIL;
Messup:
ENTRY
PROC ~ {
ENABLE UNWIND => IF sess#NIL AND sess.myIncreek#NIL THEN InnerCloseCreek[sst, sess.myIncreek];
IF sst.sender#NIL THEN TRUSTED {Process.Detach[FORK TerminalSender.Close[sst.sender]]; sst.sender ¬ NIL; sst.outStream ¬ NIL};
IF sst.inStream#
NIL
THEN {
it: IO.STREAM ~ sst.inStream;
sst.inStream ¬ NIL;
IO.Close[it, TRUE !IO.Error => CONTINUE]};
IF logInput THEN inputLog ¬ FS.StreamOpen[fileName: inputLogName, accessOptions: create, keep: 10 !FS.Error => CONTINUE];
IF logPlainOutput THEN plainOutputLog ¬ FS.StreamOpen[fileName: plainOutputLogName, accessOptions: create, keep: 10 !FS.Error => CONTINUE];
<<IF viewInput THEN {
[push, pull] ¬ IOClasses.CreatePipe[5000];
spyer ¬ CedarProcess.Fork[Spy, pull]};>>
sst.curVer ¬ version;
myOutStream ¬ out;
sst.inStream ¬ in;
myXConn ¬ InnerEnsureViewer[sst, sessionDescr];
sst.needRepaint ¬ FALSE;
IF inputLog#NIL THEN myOutStream ¬ IOClasses.CreateDribbleOutputStream[myOutStream, inputLog];
IF push#NIL THEN myOutStream ¬ IOClasses.CreateDribbleOutputStream[myOutStream, push];
sess ¬ NEW [SessionPrivate ¬ [sst]];
IF sst.increek=
NIL
THEN {
sess.myIncreek ¬ sst.increek ¬ UserInputOps.Create[];
IF NOT sst.openCreeks.Insert[sess.myIncreek, $T] THEN ERROR;
XTkTIPSource.ReplaceUIOHandle[handle: sst.tipSourceHandle, uioHandle: sess.myIncreek, setAbsoluteTime: setAbsoluteTime];
}
ELSE sess.myIncreek ¬ GetCreek[sst.increek];
IF sst.scrollWaiting
THEN {
XTkTIPSource.ChangeScrollPos[sst.tipSourceHandle, sst.scrollPos];
sst.scrollWaiting ¬ FALSE};
UserInputOps.SetAtLatest[sess.myIncreek];
[initialState, sst.lastTimeStamp] ¬ TerminalSender.StateOfIncreek[sess.myIncreek];
et1 ¬ TimeStampToEventTime[sess.myIncreek, sst.lastTimeStamp]
.Sub[RET.SmallConsCC[0, sst.lastTimeStamp]]
.Add[RET.SmallConsCC[0, 1]];
sst.curTimeStamp ¬ sst.lastTimeStamp;
sst.xlateKC ¬ TerminalSender.FromUihToDcedar[sess.myIncreek];
mySender ¬ sst.sender ¬ TerminalSender.StartSending[et1, sst.xlateKC, sessionDescr, sess, FilterGetAction, initialState, myOutStream, FinishSending, Push, out, SendOther];
IF NOT sst.sendingCreeks.Insert[sess.myIncreek, $T] THEN ERROR;
sst.outStream ¬ myOutStream;
RETURN};
Cleanup:
PROC = {
IF debugMonitor THEN SimpleFeedback.PutFL[$RemoteViewersTerminalByX11, oneLiner, $Debug, "%g Trying to enter monitor to report end of Viewers service for %g", LIST[[rope[Now[]]], [rope[sessionDescr]]] ];
EnterAndCleanup[];
RETURN};
EnterAndCleanup:
ENTRY
PROC = {
ENABLE UNWIND => NULL;
Process.SetPriority[Process.priorityForeground];
IF myXConn.Alive[] THEN myXConn.CloseConnection[];
TerminalSender.StopSending[mySender];
IF plainOutputLog#NIL THEN plainOutputLog.Close[!IO.Error => CONTINUE];
IF codedOutputLog#NIL THEN codedOutputLog.Close[!IO.Error => CONTINUE];
IF (sst.sender=mySender) # (sst.outStream=myOutStream) THEN ERROR--something's screwed up: sender and outStream are supposed to change together--;
IF sst.sender=mySender
THEN {
sst.sender ¬ NIL; sst.outStream ¬ NIL;
};
IF sst.inStream#
NIL
THEN {
it: IO.STREAM ~ sst.inStream;
sst.inStream ¬ NIL;
IO.Close[it, TRUE !IO.Error => CONTINUE]};
SimpleFeedback.PutFL[$RemoteViewersTerminalByX11, oneLiner, $Event, "%g Done serving as Viewers terminal for %g", LIST[[rope[Now[]]], [rope[sessionDescr]]] ];
RETURN};
rejection: ROPE;
expand, rcvTiming: BOOL;
[rejection, expand, rcvTiming] ¬ Good[in, out, version];
SimpleFeedback.PutFL[$RemoteViewersTerminalByX11, oneLiner, $Event,
IF rejection=
NIL
THEN
IF expand
THEN "%g Serving as coding Viewers terminal for %g.%g"
ELSE "%g Serving as plaintext Viewers terminal for %g.%g"
ELSE "%g Not serving %g 'cause %g.",
LIST[[rope[Now[]]],
[rope[sessionDescr]],
[rope[rejection]]] ];
IF rejection=
NIL
THEN {
screenSettingses: RIDT.ScreenSettingses ~ NEW [RIDT.ScreenSettingsesPrivate[1]];
screenSettingses[0] ¬ [LIST["BlackAndWhite"], LIST[[sst.theSize.x, sst.theSize.y]]];
IF logCodedOutput THEN codedOutputLog ¬ FS.StreamOpen[fileName: codedOutputLogName, accessOptions: create, keep: 10 !FS.Error => CONTINUE];
IF codedOutputLog#NIL THEN in ¬ EchoStream.CreateEchoStream[in: in, out: codedOutputLog];
IF expand
THEN decodingStream ¬ CodeControl.CreateDecodingStream[in, codingMethod !CodeControl.BadCodingMethod => {
SimpleFeedback.PutFL[$RemoteViewersTerminalByX11, oneLiner, $Error, "%g Abandoning %g because of error %g creating decoding stream, method %g", LIST[[rope[Now[]]], [rope[sessionDescr]], [rope[errorMsg]], [rope[codingMethod]]] ];
TMS.DontServeHost[host];
GOTO Abandoned}]
ELSE decodingStream ¬ in;
ImagerReceiver.SendSettingses[out, screenSettingses];
out.Flush[];
Messup[];
{ENABLE UNWIND => Cleanup[];
IF plainOutputLog#NIL THEN myRcvStream ¬ EchoStream.CreateEchoStream[in: decodingStream, out: plainOutputLog] ELSE myRcvStream ¬ decodingStream;
TRUSTED {Process.Detach[FORK Head[sst, myOutStream, ThisMachine.Name[]]]};
Process.SetPriority[Process.priorityNormal];
ImagerReceiver.DoSession[sst, myRcvStream, IF plainOutputLog#NIL THEN plainOutputLog ELSE in--decoding streams can't GetIndex (on October 29, 1990)--, version, rcvTiming, CreateContext, SetInterminalVariable, SetScreen, ImagerReceiver.NullBlink, ImagerReceiver.NullBeep, ImagerReceiver.IgnoreTime, PerTimeProbe, PerTimeReply, ImagerReceiver.IgnoreAbsoluteTime, SetCutBuffer, GetCutBuffer];
};
Cleanup[];
EXITS Abandoned => rejection ¬ rejection};
RETURN};
inputSender, spyer: CedarProcess.Process ¬ NIL;
InnerCloseCreek:
INTERNAL
PROC [sst: ServerState, increek: UserInput.Handle] ~ {
IF NOT sst.openCreeks.Fetch[increek].found THEN RETURN;
IF sst.sendingCreeks.Fetch[increek].found
THEN UserInputInsertActions.InsertExit[increek, 0] -- ???
ELSE {
UserInputOps.Close[increek];
IF NOT sst.openCreeks.Delete[increek] THEN ERROR};
RETURN};
FinishSending:
PROC [sender: TerminalSender.Sender, sourceData:
REF
ANY, consumer, pushStream:
IO.
STREAM] ~ {
sess: Session ~ NARROW[sourceData];
FinishCreek[sess.sst, sess.myIncreek];
RETURN};
FinishCreek:
ENTRY
PROC [sst: ServerState, increek: UserInput.Handle] ~ {
ENABLE UNWIND => NULL;
IF NOT sst.sendingCreeks.Delete[increek] THEN ERROR;
InnerCloseCreek[sst, increek];
RETURN};
Good:
PROC [in, out:
IO.
STREAM, version:
BYTE]
RETURNS [rejection:
ROPE, expand, rcvTiming:
BOOL ¬
TRUE] ~ {
ENABLE IO.Error => {rejection ¬ "IO.Error or stream close during initial negotiation"; CONTINUE};
codeChar: CHAR;
out.PutChar[IF willingToCode THEN 'C ELSE 'P];
out.PutChar[IF reject THEN 'R ELSE 'A];
out.Flush[];
codeChar ¬ in.GetChar[];
SELECT codeChar
FROM
'C => expand ¬ willingToCode;
'P => expand ¬ FALSE;
ENDCASE => RETURN ["host didn't properly conduct initial negotiations"];
codeChar ¬ in.GetChar[];
IF codeChar='R OR reject THEN RETURN ["terminal or host is rejecting"];
IF codeChar#'A THEN RETURN ["host didn't properly conduct initial negotiations"];
IF version >= 12
THEN {
codeChar ¬ in.GetChar[];
SELECT codeChar
FROM
'T => rcvTiming ¬ TRUE;
'N => rcvTiming ¬ FALSE;
ENDCASE => RETURN ["host didn't properly conduct initial negotiations"]}
ELSE rcvTiming ¬ FALSE;
rejection ¬ NIL;
RETURN};
InnerEnsureViewer:
INTERNAL
PROC [sst: ServerState, topName:
ROPE]
RETURNS [myXConn: Xl.Connection] ~ {
sst.topName ¬ topName;
IF sst.xConn=
NIL
OR
NOT Xl.Alive[sst.xConn]
THEN {
shell: XTk.Widget ¬ sst.shell ¬ XTkWidgets.CreateShell[
className: $X11RemoteCedarTerminal,
windowHeader: "A Cedar Remote Terminal",
standardMigration: TRUE];
XTk.RegisterNotifier[shell, XTk.bindScreenKey, BindScreenNotify, sst];
XTkWidgets.BindScreenShell[shell: shell, connection: NIL]; -- ???
{
thread: Xl.TQ ¬ sst.xThread ¬ shell.rootTQ;
xConn: Xl.Connection ¬ myXConn ¬ sst.xConn ¬ shell.connection;
screen: Xl.Screen ¬ shell.screenDepth.screen;
screenSize: Xl.Size ¬ screen.sizeInPixels;
bmSize: Xl.Size;
XTk.SetWidgetFlag[shell, XTk.preferredSizeFromDB, sst.setSize];
sst.colorData ¬ XlColorAccess.Access[screen, 8, Xl.VisualClass.pseudoColor];
sst.widthGrain ¬ IF sst.colorData.hasColors THEN 8 ELSE 64;
IF sst.increek#NIL THEN InnerCloseCreek[sst, sst.increek];
IF sst.setSize
THEN bmSize ¬ [
width:
((UserProfile.Number["RemoteTerminal.DefaultWidth",
MAX[screenSize.width-100, 300]])
/ sst.widthGrain) * sst.widthGrain, --XlBitmap.Create rounds bits/line up to multiple of 64--
height: UserProfile.Number["RemoteTerminal.DefaultHeight",
MAX[screenSize.height-100, 300]]]
ELSE bmSize ¬ [width: sst.theSize.x, height: sst.theSize.y];
{
sizeButton: XTk.Widget ¬ XTkWidgets.CreateButton[widgetSpec: [], text: "SetSize", hitProc: SizeButt, registerData: sst, tq: thread];
getSizeButton: XTk.Widget ¬ XTkWidgets.CreateButton[widgetSpec: [], text: "GetSize", hitProc: GetSizeButt, registerData: sst, tq: thread];
connLabel: XTk.Widget ¬ sst.connLabel ¬ XTkWidgets.CreateLabel[
text: topName];
firstRow: XTk.Widget ¬ XTkWidgets.CreateXStack[[], LIST[sizeButton, getSizeButton, connLabel]];
ruler0: XTk.Widget ¬ XTkWidgets.CreateRuler[widgetSpec: [geometry: [size: [1, 1]]]];
statLabel: XTk.Widget ¬ sst.statLabel ¬ XTkWidgets.CreateLabel[
text: "Round trip stats go here"];
ruler1: XTk.Widget ¬ sst.ruler1 ¬ XTkWidgets.CreateRuler[widgetSpec: [geometry: [size: [1, 2]]]];
bitmapScroller: XTk.Widget ¬ sst.bitmapScroller ¬ XTkBitmapScroller.CreateBitmapScroller[insideSize: [width: bmSize.width, height: bmSize.height], scrollTQ: thread, scrollData: sst, scrolledCallBack: ScrolledCallBack];
contents: XTk.Widget ¬ XTkWidgets.CreateYStack[[], LIST[firstRow, ruler0, statLabel, ruler1, bitmapScroller]];
XTkWidgets.SetShellChild[shell, contents];
sst.realBitmap ¬ XTkBitmapScroller.GetImplWidget[bitmapScroller];
sst.increek ¬ NIL;
sst.tipSourceHandle ¬ XTkTIPSource.BindTipSource[widget: sst.realBitmap, uioHandle: sst.increek, inputTQ: sst.xThread, yup: TRUE, setAbsoluteTime: setAbsoluteTime];
XTkTIPSource.AdditionalKeySource[sst.tipSourceHandle, shell];
XTkWidgets.RegisterCallWMDeleteWindow[shell, DestroyConnection, sst];
XTkWidgets.SetFocusMethod[shell: shell, focusProtocol: false, inputHint: true]; --Passive
XTkWidgets.RealizeShell[shell];
IF sst.setSize
THEN {
vb: SF.Box ~ XTkBitmapScroller.GetVisibleBox[sst.bitmapScroller];
is: SF.Vec ~ vb.Size[];
sst.theSize ¬ [x: ((MAX[is.f, 104])/sst.widthGrain)*sst.widthGrain, y: MAX[is.s, 104] ];
sst.setSize ¬ FALSE};
SetBitmap[sst];
}}}
ELSE {
xConn: Xl.Connection ¬ sst.xConn;
Xl.IncRefCount[xConn, $RemoteViewersTerminalByX11];
XTkWidgets.ForgetScreenShell[sst.shell];
XTkWidgets.BindScreenShell[shell: sst.shell, connection: xConn];
XTkWidgets.RealizeShell[sst.shell];
myXConn ¬ xConn;
XTkWidgets.SetText[sst.connLabel, topName];
Xl.DecRefCount[xConn, $RemoteViewersTerminalByX11];
};
RETURN};
setAbsoluteTime: BOOL ¬ TRUE;
BindScreenNotify:
PROC [widget: XTk.Widget, registerData, callData:
REF ¬
NIL, event: XTk.Event ¬
NIL]
--XTk.WidgetNotifyProc-- ~ {
sst: ServerState ¬ NARROW[registerData];
rulerPixel: Xl.Pixel ¬ Xl.illegalPixel;
xConn: Xl.Connection ¬ sst.shell.connection;
screen: Xl.Screen ¬ sst.shell.screenDepth.screen;
cm: Xl.ColorMap ¬ screen.defaultColorMap;
XTkShellWidgets.SetHeader[sst.shell, sst.topName];
sst.colorData ¬ XlColorAccess.Access[screen, 8, Xl.VisualClass.pseudoColor];
IF sst.ruler1#
NIL
THEN {
rulerPixel ¬ Xl.AllocNamedColor[xConn, cm, "blue"
! Xl.XError => {colorFails ¬ colorFails.SUCC; CONTINUE}
].pixel;
sst.ruler1.attributes.backgroundPixel ¬ rulerPixel;
};
sst.cursorPixmap ¬ Xl.CreatePixmap[c: xConn, drawable: screen.root.drawable, size: [16, 16], depth: 1];
sst.cursorGC ¬ Xl.MakeGContext[xConn, screen.root.drawable];
Xl.SetGCGraphicsExposures[sst.cursorGC, FALSE];
Xl.SetGCGrounds[gc: sst.cursorGC, foreground: screen.blackPixel, background: screen.whitePixel];
sst.cursTab.Erase[];
};
colorFails: INT ¬ 0;
SetBitmap:
INTERNAL
PROC [sst: ServerState] ~ {
sst.bitmap ¬ XlBitmap.Create[size: [s: sst.theSize.y, f: sst.theSize.x], bpp: IF sst.colorData.hasColors THEN 8 ELSE 1];
sst.bmHeight ¬ sst.theSize.y;
XTkTIPSource.ChangePseudoHeight[sst.tipSourceHandle, sst.theSize.y];
XlBitmap.SetColormap[sst.bitmap, sst.colorData.entries]; --comment in XlColorAccess sez to do this
XTkBitmapScroller.SetBitmap[sst.bitmapScroller, sst.bitmap];
};
ScrolledCallBack:
PROC [scroller: XTk.Widget, pos: Xl.Point, data:
REF]
--XTkBitmapScroller.ScrolledCallBackProc-- = {
sst: ServerState ¬ NARROW[data];
tsh: XTkTIPSource.TipSourceHandle ¬ sst.tipSourceHandle;
sst.scrollPos ¬ pos;
IF tsh#
NIL
THEN XTkTIPSource.ChangeScrollPos[tsh, pos]
ELSE sst.scrollWaiting ¬ TRUE;
};
DestroyConnection:
PROC [widget: XTk.Widget, registerData, callData:
REF ¬
NIL, event: XTk.Event ¬
NIL]
--XTk.WidgetNotifyProc-- = {
sst: ServerState ¬ NARROW[registerData];
c: Xl.Connection ~ sst.xConn;
IF Xl.Alive[c] THEN Xl.CloseConnection[c];
RETURN};
FilterGetAction:
PROC [sourceData:
REF
ANY, waitMode: UserInputTypes.WaitMode, waitInterval:
INT, acceptance: UserInputTypes.Acceptance]
RETURNS [UserInputGetActions.InputActionBody]
--TerminalSender.GetActionProc-- ~
TRUSTED {
sess: Session ~ NARROW[sourceData];
RETURN [UserInputGetActions.GetInputActionBody[sess.myIncreek, waitMode, waitInterval, acceptance]]};
Head:
ENTRY
PROC [sst: ServerState, stream:
IO.
STREAM, termAddr:
ROPE] ~ {
ENABLE UNWIND => NULL;
timer: CONDITION;
b: REAL ~ decayRate;
a: REAL ~ 1.0 - b;
rtShow: RtStats ¬ sst.rtStats ¬ [];
rtWt, badWt: REAL ¬ 1.0E-20;
rtRunAvg, runBad: REAL ¬ 0.0;
rtAvg, badPct: REAL ¬ 0.0;
Process.SetPriority[Process.priorityForeground];
TRUSTED {Process.InitializeCondition[@timer, Process.SecondsToTicks[headTime]]};
WHILE stream=sst.outStream
DO
WAIT timer;
IF sst.rtStats.count>0
THEN {
rtShow ¬ sst.rtStats;
sst.rtStats ¬ [];
rtAvg ¬ rtShow.sum/rtShow.count;
rtRunAvg ¬ b*rtRunAvg + a*rtAvg; rtWt ¬ b*rtWt + a};
{msg: ROPE ~ IO.PutFLR[" %g Term=%g; rt min=%4.2fs max=%05.2fs avg=%4.2fs run avg=%4.2fs", LIST[ [rope[Convert.RopeFromTime[from: BasicTime.Now[], start: hours, end: seconds, useAMPM: FALSE, includeZone: FALSE]]], [rope[termAddr]], [real[rtShow.min]], [real[rtShow.max]], [real[rtAvg]], [real[rtRunAvg/rtWt]] ]];
XTkWidgets.SetText[sst.statLabel, msg ! Xl.XError => CONTINUE];
}ENDLOOP;
stream ¬ stream;
RETURN};
SendOther:
PROC [sender: TerminalSender.Sender] ~ {
sst: ServerState ~ theSST;
IF sst.needRepaint
THEN {
PutRepaint: PROC [out: IO.STREAM] ~ {out.PutChar[VAL[16]]};
sst.needRepaint ¬ FALSE;
sender.WithSender[PutRepaint];
};
RETURN};
PerTimeProbe:
PROC [org: EventTime, desc:
RIDT.TimeEventDesc] ~ {
sst: ServerState ~ theSST;
s: TerminalSender.Sender ~ sst.sender;
ver: BYTE ~ sst.curVer;
mid: EventTime ~ RET.ReadEventTime[];
Reply:
PROC [out:
IO.
STREAM] ~ {
ENABLE {
IO.Error => IF (ec=StreamClosed OR ec=Failure) AND stream = out THEN CONTINUE;
Nws.Timeout => RESUME;
};
out.PutChar[VAL[17]];
TerminalSender.SendTimeReply[s, org, mid, ver>=13, [desc.mousePosition, desc.action]];
RETURN};
IF s#NIL THEN s.WithSender[Reply];
RETURN};
PerTimeReply:
PROC [org, mid: EventTime, desc:
RIDT.TimeEventDesc] ~ {
fin: EventTime ~ RET.ReadEventTime[];
ut: REAL ~ RET.Sub[mid, org].ToSmall[]/1000.0;
dt: REAL ~ RET.Sub[fin, mid].ToSmall[]/1000.0;
rt: REAL ~ RET.Sub[fin, org].ToSmall[]/1000.0;
sst: ServerState ~ theSST;
sst.upTime.IncrementTransformed[minTime, maxTime, ut];
sst.downTime.IncrementTransformed[minTime, maxTime, dt];
sst.roundTime.IncrementTransformed[-1.0, maxTime, rt];
IF rt < 0 THEN lastBogon ¬ [org, mid, fin];
IF rt >= maxTime THEN lastBigon ¬ [org, mid, fin];
AddHist[sst, [org, mid, fin, desc]];
sst.rtStats.min ¬ MIN[sst.rtStats.min, rt];
sst.rtStats.max ¬ MAX[sst.rtStats.max, rt];
sst.rtStats.sum ¬ sst.rtStats.sum + rt;
sst.rtStats.count ¬ sst.rtStats.count + 1;
RETURN};
Bogon: TYPE ~ RECORD [org, mid, fin: EventTime ¬ bot];
lastBogon, lastBigon: Bogon ¬ [];
AddHist:
ENTRY
PROC [sst: ServerState, h: Hist] ~ {
ENABLE UNWIND => NULL;
sst.th.hist[sst.th.i] ¬ h;
IF sst.th.i = HistoryIndex.LAST THEN {sst.th.i ¬ 0; sst.th.j ¬ sst.th.j+1} ELSE sst.th.i ¬ sst.th.i + 1;
RETURN};
SetInterminalVariable:
PROC [clientData:
REF
ANY, setting:
RIDT.InterminalSetting] ~ {
sst: ServerState ~ NARROW[clientData];
WITH setting
SELECT
FROM
CursorPattern => sst.nextCursorPattern ¬ pattern;
CursorOffset => {
ch: CursorHandle ~
NEW [CursorRec ¬ [
info: [type: last, hotX: hotX, hotY: hotY, inverted: FALSE],
bits: sst.nextCursorPattern]];
cc: CachedCursor ¬ NARROW[sst.cursTab.Fetch[ch].val];
IF cc=
NIL
THEN {
space: PACKED ARRAY [0..16) OF CARD16;
FOR i: INTEGER IN [0..16) DO space[i] ¬ sst.nextCursorPattern[i] ENDLOOP;
TRUSTED {Xl.PutImage[c: sst.xConn, drawable: sst.cursorPixmap.drawable, gc: sst.cursorGC, size: [16, 16], dest: [0, 0], base: @space, offx: 0, offy: 0, scanLineBytes: 2]};
{hot: Xl.Point ¬ [MAX[MIN[-hotX, 16], 0], MAX[MIN[-hotY, 16], 0] ];
cc ¬ NEW [CachedCursorRep ¬ [ch, Xl.CreateCursor[c: sst.xConn, source: sst.cursorPixmap, mask: sst.cursorPixmap, hotP: hot] ]];
[] ¬ sst.cursTab.Store[ch, cc];
cc ¬ cc}};
Xl.ChangeWindowAttributes[sst.xConn, sst.realBitmap.window, [cursor: cc.xcursor]];
Xl.Flush[sst.xConn];
RETURN};
MousePosition => {
--given positive (0 at bottom) Y coords
Xl.WarpPointer[c: sst.xConn, dstWindow: sst.realBitmap.window, dstPos: [x: pos.mouseX + sst.scrollPos.x, y: sst.bmHeight.PRED - pos.mouseY + sst.scrollPos.y ], srcWindow: sst.realBitmap.window];
Xl.Flush[sst.xConn];
};
<<MouseGrain => NULL;
FastMouseParms => NULL;
Escapes => NULL;>>
ENDCASE => NULL;
RETURN};
SetScreen:
PROC [clientData:
REF
ANY, screen:
NATURAL, setting:
RIDT.ScreenSetting] ~ {
RETURN};
SetCutBuffer:
PROC [clientData:
REF
ANY, buffer:
ATOM, data:
ROPE] ~ {
ENABLE UNWIND => NULL;
sst: ServerState ~ NARROW[clientData];
XlCutBuffers.Put[sst.xConn, data];
RETURN};
GetCutBuffer:
PROC [clientData:
REF
ANY, buffer:
ATOM, key:
CARD] ~ {
ENABLE UNWIND => NULL;
sst: ServerState ~ NARROW[clientData];
data: ROPE ~ XlCutBuffers.Get[sst.xConn];
s: TerminalSender.Sender ~ sst.sender;
Reply:
PROC [out:
IO.
STREAM] ~ {
ENABLE {
IO.Error => IF (ec=StreamClosed OR ec=Failure) AND stream = out THEN CONTINUE;
Nws.Timeout => RESUME;
};
out.PutChar[VAL[18]];
TerminalSender.SendCutBuffer[s, buffer, key, data];
RETURN};
IF s#NIL THEN s.WithSender[Reply];
RETURN};
CreateContext:
PROC [clientData:
REF
ANY, screen:
NATURAL]
RETURNS [c: Imager.Context]
--ImagerReceiver.ContextCreator-- = {
sst: ServerState ~ NARROW[clientData];
c ¬ XlBitmap.CreateContext[sst.bitmap];
RETURN};
TapFile:
PROC [fileName:
ROPE, codingMethod:
ROPE ¬
NIL, version:
BYTE ¬ 0, timing:
BOOL ¬
TRUE] = {
fileStream: IO.STREAM ~ FS.StreamOpen[fileName];
log: IO.STREAM ~ FS.StreamOpen[fileTapLogName, create];
plainStream: IO.STREAM ¬ fileStream;
inStream: IO.STREAM;
Closem:
PROC ~ {
log.Close[!IO.Error => CONTINUE];
fileStream.Close[!IO.Error => CONTINUE];
RETURN};
IF codingMethod=
NIL
THEN {IF version=0 THEN version ¬ fileStream.GetChar[].ORD}
ELSE plainStream ¬ CodeControl.CreateDecodingStream[fileStream, codingMethod];
inStream ¬ EchoStream.CreateEchoStream[in: plainStream, out: log];
ImagerReceiver.DoSession[theSST, inStream, log, version, timing, CreateContext, SetInterminalVariable, SetScreen, ImagerReceiver.NullBlink, ImagerReceiver.NullBeep, ImagerReceiver.IgnoreTime, ImagerReceiver.IgnoreTimeProbe, ImagerReceiver.IgnoreTimeReply, ImagerReceiver.IgnoreAbsoluteTime, ImagerReceiver.IgnoreCutBuffer, ImagerReceiver.DontGetCutBuffer ! UNWIND => Closem[] ];
Closem[];
RETURN};
EnterAndStop:
ENTRY
PROC [sst: ServerState, outStream:
IO.
STREAM] ~ {
ENABLE UNWIND => NULL;
IF sst.outStream = outStream THEN InnerStop[sst];
RETURN};
InnerStop:
INTERNAL
PROC [sst: ServerState] ~ {
IF sst.sender#
NIL
THEN
TRUSTED {
Process.Detach[FORK TerminalSender.Close[sst.sender]];
sst.sender ¬ NIL; sst.outStream ¬ NIL};
IF sst.inStream#
NIL
THEN {
it: IO.STREAM ~ sst.inStream;
sst.inStream ¬ NIL;
IO.Close[it, TRUE !IO.Error => CONTINUE]};
RETURN};
Wake:
ENTRY
PROC [server:
RVTK.ViewersServer]
RETURNS [wasAwake:
BOOL] ~ {
ENABLE UNWIND => NULL;
sst: ServerState ~ NARROW[server.data];
IF sst.xConn=
NIL
OR
NOT sst.xConn.Alive[]
THEN {
[] ¬ InnerEnsureViewer[sst, ">unconnected<"];
RETURN [FALSE]}
ELSE RETURN [TRUE]};
InnerRestore:
INTERNAL
PROC [sst: ServerState] ~ {
wait: CONDITION;
TRUSTED {Process.InitializeCondition[@wait, Process.MsecToTicks[500]]};
UNTIL sst.xConn=NIL OR NOT sst.xConn.Alive[] DO WAIT wait ENDLOOP;
[] ¬ InnerEnsureViewer[sst, ">unconnected<"];
RETURN};
GetSizeButt:
PROC [widget: XTk.Widget, registerData, callData:
REF
ANY, event: Xl.Event] ~ {
sst: ServerState ~ NARROW[registerData];
vb: SF.Box ~ XTkBitmapScroller.GetVisibleBox[sst.bitmapScroller];
is: SF.Vec ~ vb.Size[];
msg: ROPE ~ IO.PutFLR["Ideal size is <w:%g, h:%g>, actual is <%g, %g>.", LIST[[integer[sst.theSize.x]], [integer[sst.theSize.y]], [integer[is.f]], [integer[is.s]]] ];
IF sst.xConn#NIL AND sst.xConn.Alive[] THEN XTkWidgets.SetText[sst.statLabel, msg];
RETURN};
SizeButt:
PROC [widget: XTk.Widget, registerData, callData:
REF
ANY, event: Xl.Event] ~ {
sst: ServerState ~ NARROW[registerData];
vb: SF.Box ~ XTkBitmapScroller.GetVisibleBox[sst.bitmapScroller];
is: SF.Vec ~ vb.Size[];
SetSize[sst, [width: (is.f/sst.widthGrain)*sst.widthGrain, height: is.s]];
RETURN};
SetSize:
ENTRY
PROC [sst: ServerState, size: Xl.Size] ~ {
ENABLE UNWIND => NULL;
sst.theSize ¬ [x: size.width, y: size.height];
IF sst.xConn=NIL OR NOT sst.xConn.Alive[] THEN RETURN;
IF sst.outStream=
NIL
THEN {
SetBitmap[sst];
XTkWidgets.SetText[sst.statLabel, IO.PutFR["Size set to <w:%g, h:%g>.", [integer[sst.theSize.x]], [integer[sst.theSize.y]] ]];
}
ELSE {
XTkWidgets.SetText[sst.statLabel, IO.PutFR["Host won't notice new size(w:%g, h:%g) 'till new session.", [integer[sst.theSize.x]], [integer[sst.theSize.y]] ]];
};
RETURN};
Now:
PROC
RETURNS [
ROPE] ~ {
up: BasicTime.Unpacked ~ BasicTime.Unpack[BasicTime.Now[]];
RETURN [
IO.PutFLR["%g/%g %g:%02g:%02g",
LIST[[cardinal[up.month.ORD+1]],
[cardinal[up.day]],
[cardinal[up.hour]],
[cardinal[up.minute]],
[cardinal[up.second]]
]] ]};
<<Spy: PROC [data: REF ANY] RETURNS [results: REF ¬ NIL] = {
vStream: IO.STREAM = ViewerIO.CreateViewerStreams["Send Script"].out;
pull: IO.STREAM = NARROW[data];
TerminalSpy.SpyOnStream[vStream, pull, viewDetails, viewDetails];
RETURN};
ViewHistory:
ENTRY
PROC [sst: ServerState] ~ {
ENABLE UNWIND => NULL;
IF sst.thv=NIL OR sst.thv.destroyed THEN Menus.AppendMenuEntry[(sst.thv ¬ ViewerIO.GetViewerFromStream[sst.tho ¬ ViewerIO.CreateViewerStreams["Detailed timing history"].out]).menu, Menus.CreateEntry["History", HistoryClick, sst]];
RETURN};
HistoryClick:
ENTRY
PROC [parent: Viewer, clientData:
REF
ANY ¬
NIL, mouseButton: ViewerClasses.MouseButton ¬ red, shift, control:
BOOL ¬
FALSE] ~ {
ENABLE UNWIND => NULL;
sst: ServerState ~ NARROW[clientData];
th: TimingHistory ~ sst.th;
i1: HistoryIndex ¬ th.i;
j1: INT ¬ 0;
intro: ROPE ¬ "";
IF th.j=0 THEN i1 ¬ 0 ELSE j1 ¬ th.j-1;
IF mouseButton=blue THEN intro ¬ "\n\n"
ELSE IF j1>th.shownJ OR j1=th.shownJ AND i1>th.shownI THEN intro ¬ "...\n"
ELSE {j1 ¬ th.shownJ; i1 ¬ th.shownI};
sst.tho.PutRope[intro];
WHILE j1 < th.j
OR i1 < th.i
DO
ij: INT ~ j1*NHistoryIndices + i1;
sst.tho.PutF["%04g: ", [integer[ij]]];
TerminalSpy.PrintEventTime[sst.tho, th.hist[i1].org, FALSE];
sst.tho.PutChar[' ];
TerminalSpy.PrintEventTime[sst.tho, th.hist[i1].mid, FALSE];
sst.tho.PutChar[' ];
TerminalSpy.PrintEventTime[sst.tho, th.hist[i1].fin, FALSE];
sst.tho.PutF[" %g[%g, %g] ", [rope[IF th.hist[i1].desc.mousePosition.color THEN "color" ELSE "bw"]], [integer[th.hist[i1].desc.mousePosition.mouseX]], [integer[th.hist[i1].desc.mousePosition.mouseY]]];
<<TerminalSpy.PrintAction[sst.tho, th.hist[i1].desc.action];>>
sst.tho.PutRope["\n"];
IF i1=HistoryIndex.LAST THEN {j1 ¬ j1+1; i1 ¬ 0} ELSE i1 ¬ i1+1;
ENDLOOP;
th.shownI ¬ th.i;
th.shownJ ¬ th.j;
RETURN};
ShowTimes:
PROC [sst: ServerState] ~ {
sst.roundTimeView ¬ HistogramsViewing.Show[h: sst.roundTime, format: "%5.2f", width: 5, viewerInit: [name: "Term -> host -> Term seconds"], base: 2.0, updatePeriod: 60];
sst.downTimeView ¬ HistogramsViewing.Show[h: sst.downTime, format: "%5.2f", width: 5, viewerInit: [name: "host -> Term seconds"], base: 2.0, updatePeriod: 60];
sst.upTimeView ¬ HistogramsViewing.Show[h: sst.upTime, format: "%5.2f", width: 5, viewerInit: [name: "Term -> host seconds"], base: 2.0, updatePeriod: 60];
RETURN};>>
TimeStampToEventTime:
PROC [uih: UserInput.Handle, ts: RelativeTimes.TimeStamp]
RETURNS [et: EventTime] ~ {
gmt: BasicTime.GMT;
s, ms: INT;
[gmt, ms] ¬ UserInputOps.GetAbsoluteTime[uih, ts];
IF debugTime THEN SimpleFeedback.PutFL[$RemoteViewersTerminalByX11, oneLiner, $Debug, "TimeStamp %g = %g + %gms.", LIST[[cardinal[ts]], [rope[FmtTime[gmt]]], [integer[ms]]] ];
s ¬ ms/1000;
ms ¬ ms - s*1000;
IF ms<0 THEN {s ¬ s - 1; ms ¬ ms + 1000};
et ¬ RET.FromEGMT[[gmt.Update[s], ms*1000]];
IF debugTime THEN SimpleFeedback.PutFL[$RemoteViewersTerminalByX11, oneLiner, $Debug, "... which = %g + %gs + %gms = %g*10000H+%g ms.", LIST[[rope[FmtTime[gmt]]], [integer[s]], [integer[ms]], [integer[et.hi]], [cardinal[et.lo]]] ];
RETURN [et]};
debugTime: BOOL ¬ TRUE;
FmtTime:
PROC [t: BasicTime.
GMT]
RETURNS [
ROPE] ~ {
IF t=BasicTime.nullGMT THEN RETURN ["nullGMT"];
IF BasicTime.Period[from: BasicTime.earliestGMT, to: t]<0
OR BasicTime.Period[from: BasicTime.latestGMT, to: t]>0
THEN RETURN Rope.Cat["<broken time: ", Convert.RopeFromCard[LOOPHOLE[t, CARD32], 16], ">"];
RETURN [Convert.RopeFromTime[t, years, seconds, FALSE, FALSE]]};
DisplayToColor:
PROC [ra:
REF
ANY]
RETURNS [
BOOL] ~ {
SELECT ra
FROM
$Main, $Display0, NIL => RETURN [FALSE];
ENDCASE => RETURN [TRUE]};
EqualCursorHandle:
PROC [key1, key2:
REF
ANY]
RETURNS [
BOOL]
--RefTab.EqualProc-- ~ {
ch1: CursorHandle ~ NARROW[key1];
ch2: CursorHandle ~ NARROW[key2];
RETURN [ch1.bits=ch2.bits AND ch1.info.hotX=ch2.info.hotX AND ch1.info.hotY=ch2.info.hotY]};
HashCursorHandle:
PROC [key:
REF
ANY]
RETURNS [
CARDINAL]
--RefTab.HashProc-- ~ {
ch: CursorHandle ~ NARROW[key];
hotHash: INT ~ INT[ch.info.hotX + 5] * ch.info.hotY;
ans: CARDINAL ¬ (hotHash MOD 37) + 37;
FOR i: NAT IN [0..16) DO ans ¬ ans*9 + ch.bits[i] ENDLOOP;
RETURN [ans]};
getDecodingCharUsage: ROPE ¬ "GetDecodingChar <fileName> <methodName> <blockBytes> <singleBytes>";
GetDecodingCharCmd:
PROC [cmd: Commander.Handle]
RETURNS [result:
REF
ANY ¬
NIL, msg:
ROPE ¬
NIL] ~ {
argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
buffer: REF TEXT ~ RefText.New[256];
plainName: ROPE;
otherBytes, singleBytes: INT;
codedIn, plainIn, plainOut: IO.STREAM ¬ NIL;
testChar: CHAR;
IF argv.argc#5 THEN RETURN [$Failure, getDecodingCharUsage];
otherBytes ¬ Convert.IntFromRope[argv[3]];
singleBytes ¬ Convert.IntFromRope[argv[4]];
plainName ¬ argv[1].Concat[".plain"];
codedIn ¬ FS.StreamOpen[argv[1]];
plainIn ¬ CodeControl.CreateDecodingStream[codedIn, argv[2]];
plainOut ¬ FS.StreamOpen[plainName, create];
cmd.out.PutF1["Skipping first %g bytes.\n", [integer[otherBytes]] ];
WHILE otherBytes>0
DO
ask: INT ¬ MIN[otherBytes, 256];
got: INT ¬ plainIn.GetBlock[block: buffer, count: ask];
IF got#ask THEN RETURN [$Failure, IO.PutFR["Got %g instead of %g at %g", [integer[got]], [integer[ask]], [integer[otherBytes]] ]];
plainOut.PutBlock[block: buffer, count: got];
otherBytes ¬ otherBytes - got;
ENDLOOP;
cmd.out.PutF1["Next %g bytes are:", [integer[singleBytes]] ];
FOR i:
INT
IN [0..singleBytes)
DO
testChar ¬ plainIn.GetChar[ !IO.EndOfStream => {cmd.out.PutRope[" --EndOfStream"]; EXIT}];
IF (i MOD 4) = 0 THEN cmd.out.PutChar[' ];
cmd.out.PutF1["%02x", [integer[testChar.ORD]]];
plainOut.PutChar[testChar];
ENDLOOP;
cmd.out.PutChar['\n];
plainIn.Close[];
plainOut.Close[];
RETURN};
tapFileUsage: ROPE ¬ "TapFile ( ((+|-)timing) | -codeMethod <methodName> | -version <int> )* <fileName>";
TapFileCmd:
PROC [cmd: Commander.Handle]
RETURNS [result:
REF
ANY ¬
NIL, msg:
ROPE ¬
NIL] ~ {
argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
fileName: ROPE ¬ NIL;
codingMethod: ROPE ¬ NIL;
version: BYTE ¬ 16;
timing: BOOL ¬ TRUE;
i: NAT ¬ 1;
IF argv.argc<2 THEN RETURN [$Null, tapFileUsage];
WHILE i < argv.argc
DO
SELECT
TRUE
FROM
argv[i].Equal["-codeMethod",
FALSE] =>
IF (i ¬ i.
SUCC) < argv.argc
THEN codingMethod ¬ argv[i]
ELSE RETURN [$Failure, tapFileUsage];
argv[i].Equal["-version",
FALSE] =>
IF (i ¬ i.
SUCC) < argv.argc
THEN version ¬ Convert.IntFromRope[argv[i]]
ELSE RETURN [$Failure, tapFileUsage];
argv[i].Equal["+timing"] => timing ¬ TRUE;
argv[i].Equal["-timing"] => timing ¬ FALSE;
ENDCASE => {
IF i.SUCC#argv.argc THEN RETURN [$Failure, tapFileUsage];
fileName ¬ argv[i];
EXIT};
i ¬ i.SUCC;
ENDLOOP;
cmd.out.PutFL["Tapping %g (codeMethod=\"%q\", version=%g, timing=%g).\n", LIST[[rope[fileName]], [rope[codingMethod]], [integer[version]], [boolean[timing]]] ];
TapFile[fileName, codingMethod, version, timing];
cmd.out.PutF1["Done tapping %g.\n", [rope[fileName]] ];
RETURN};
optionDesc: ROPE ¬ "((+|-)(logInput|logPlainOutput|logCodedOutput|viewInput|viewDetails|code) | -codeMethod <methodName>)* --- set flg(s)";
optionUsage: ROPE ¬ Rope.Concat["RemoteViewersTerminalByX11 ", optionDesc];
OptionCmd:
PROC [cmd: Commander.Handle]
RETURNS [result:
REF
ANY ¬
NIL, msg:
ROPE ¬
NIL] ~ {
argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
Set:
PROC [name:
ROPE, sense:
BOOL]
RETURNS [
BOOL] ~ {
SELECT
TRUE
FROM
name.Equal["logInput", FALSE] => logInput ¬ sense;
name.Equal["logPlainOutput", FALSE] => logPlainOutput ¬ sense;
name.Equal["logCodedOutput", FALSE] => logCodedOutput ¬ sense;
name.Equal["viewInput", FALSE] => viewInput ¬ sense;
name.Equal["viewDetails", FALSE] => viewDetails ¬ sense;
name.Equal["code", FALSE] => willingToCode ¬ sense;
ENDCASE => RETURN [TRUE];
RETURN [FALSE]};
i: NAT ¬ 1;
IF argv.argc<1 THEN RETURN [$Null, optionUsage];
WHILE i < argv.argc
DO
SELECT
TRUE
FROM
argv[i].Length = 0 => RETURN [$Failure, optionUsage];
argv[i].Equal["-codeMethod",
FALSE] =>
IF (i ¬ i.
SUCC) < argv.argc
THEN codingMethod ¬ argv[i]
ELSE RETURN [$Failure, optionUsage];
argv[i].Fetch[0] = '+ => IF Set[argv[i].Substr[1], TRUE] THEN RETURN [$Failure, optionUsage];
argv[i].Fetch[0] = '- => IF Set[argv[i].Substr[1], FALSE] THEN RETURN [$Failure, optionUsage];
ENDCASE => RETURN [$Failure, optionUsage];
i ¬ i.SUCC;
ENDLOOP;
cmd.out.PutFL["RemoteViewersTerminalByX11 options are: logInput=%g, logPlainOutput=%g, logCodedOutput=%g, viewInput=%g, viewDetails=%g, code=%g, codeMethod=%g.\n", LIST[ [boolean[logInput]], [boolean[logPlainOutput]], [boolean[logCodedOutput]], [boolean[viewInput]], [boolean[viewDetails]], [boolean[willingToCode]], [rope[codingMethod]] ]];
RETURN};
MakeAnother:
PROC ~ {
PFS.DoInWDir[installDir, TryForAnother];
RETURN};
TryForAnother:
PROC ~ {
[] ¬ CommanderOps.DoCommand["Run -a RemoteViewersTerminalByX11", NIL];
RETURN};
Start:
PROC ~ {
style: ROPE ~ "X";
sst: ServerState ~
NEW [ServerStateRecord ¬ [
openCreeks: RefTab.Create[],
sendingCreeks: RefTab.Create[],
cursTab: RefTab.Create[hash: HashCursorHandle, equal: EqualCursorHandle]
]];
server: RVTK.ViewersServer ~ NEW [RVTK.ViewersServerPrivate ¬ [style, StartServing, Work, StopServing, Wake, sst]];
Commander.Register["RemoteViewersTerminalByX11", OptionCmd, optionDesc];
Commander.Register["RVTXB.TapFile", TapFileCmd, tapFileUsage];
Commander.Register["GetDecodingChar", GetDecodingCharCmd, getDecodingCharUsage];
sst.upTime ¬ Histograms.Create1D[factor: dTime, offset: minTime];
sst.downTime ¬ Histograms.Create1D[factor: dTime, offset: minTime];
sst.roundTime ¬ Histograms.Create1D[factor: dTime, offset: -1.0];
sst.th ¬ NEW [TimingHistoryPrivate ¬ []];
theServer ¬ server;
theSST ¬ sst;
server.AddViewersServer[];
RVTK.SetViewersImpl[[min: 10, max: 17], style, MakeAnother];
RETURN};
Start[];
END.