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; 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 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; < 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]; <> 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]; }; < 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 , 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 .", [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]] ]] ]}; < 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]]]; <> 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[""]; 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 "; 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 | -version )* "; 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 )* --- 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. „ 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 Keys and mouse buttons are normally up. The state last reported through FilterGetAction : ส&ฎ–(cedarcode) style•NewlineDelimiter ™code™Kšœ ฯeœ7™BK™?K™)K™&K™.—K˜Kš ฯk œ^žœMžœ3žœwžœน˜ K˜šฯnœžœž˜)Kš žœGžœžœžœQžœ˜๏K˜—K˜KšžœžœžœžœžœŸœžœžœžœ˜ตK˜Kšœžœžœ ฯc˜Kšœžœ&˜>Kšœžœ˜/Kšœ žœ˜Kšœ žœ˜Kšœžœ˜K˜Kšœ žœžœ˜K˜šœ(˜(K˜ Kšœ žœ1˜>K˜—K˜š Ÿœžœ&žœžœ œ˜UKšœžœ˜(šžœž˜Kšœ˜KšžœZ˜a—Kšžœ˜ —K˜šŸœžœžœ˜FK˜.K˜(Kšžœ˜—K˜šŸ œžœžœ žœ˜8Kšžœžœžœ˜Kšœžœ˜'Kšžœžœ žœžœ˜+Kšœ˜Kšžœ žœžœžœ˜HKšžœžœ˜&Kšžœ˜—K˜š Ÿ œžœžœ žœžœ ˜IKšžœžœžœ˜Kšœžœ˜'Kšžœžœžœžœ˜0Kšžœ˜Kšžœ˜—K˜šŸœžœ žœžœžœžœžœ žœŸœžœ˜–Kšœžœ˜'K˜Kšœ žœžœžœ˜Kšœ"žœ˜&Kšœžœžœ˜KšœCžœžœžœ˜SKšœžœ˜Kšœ.žœ˜2Kšœžœ˜šŸœžœžœ˜Kšžœžœžœžœžœžœžœ&˜^Kšžœ žœžœžœžœ1žœžœ˜~šžœžœžœ˜Kšœžœžœ˜Kšœžœ˜Kšžœ žœžœ žœ˜*—Kš žœ žœ žœEžœ žœ˜yKš žœžœžœKžœ žœ˜‹šœ˜K˜*K˜(—K˜K˜K˜K˜/Kšœžœ˜Kšžœ žœžœJ˜^KšžœžœžœF˜VKšœžœ˜$šžœ žœžœ˜K˜5Kšžœžœ+žœžœ˜K˜-K˜*Kšœ˜K˜?K˜LKšœžœžœžœ˜;Kšžœ žœžœ#˜:šžœ ˜šžœ ˜šœ˜šœ3˜3Kšžœ˜ —Kšœ$ 9˜]—šœ:˜:Kšžœ˜!——Kšžœ8˜<—Kšœ˜K˜„K˜Š˜?Kšœ˜—Kšœ3žœ(˜_K˜T˜?Kšœ"˜"—K˜aK˜ฺKšœ3žœ7˜nKšœ*˜*K˜AKšœžœ˜Kšœ|žœ$˜คKšœ=˜=KšœE˜EKšœP  ˜YKšœ˜šžœ žœ˜Kšœžœ;˜AKšœžœ˜Kšœžœ0žœ˜XKšœžœ˜—Kšœ˜K˜—šžœ˜Kšœ!˜!K˜3Kšœ(˜(Kšœ@˜@Kšœ#˜#Kšœ˜Kšœ+˜+K˜3Kšœ˜—Kšžœ˜Kšœžœžœ˜—K˜š Ÿœžœ.žœžœžœ œ˜ƒKšœžœ˜(K˜'K˜,K˜1K˜)Kšœ2˜2K˜Lšžœ žœžœ˜˜1Kšœ(žœžœ˜8Kšœ˜—K˜3K˜—K˜gK˜˜>Kšœ˜Kšžœžœžœžœ ˜@Kšžœ˜—K˜K˜Kšžœ˜—K˜šŸ œžœ˜&K˜ฉK˜ŸK˜›Kšžœ˜ —K˜šŸœžœ6žœ˜kKšœžœ˜Kšœžœ˜ K˜2Kšžœ žœbžœ8˜ฏK˜ K˜Kšžœžœ˜)Kšœžœ$˜,Kšžœ žœwžœ[˜็Kšžœ˜ Kšœ žœžœ˜—K˜š Ÿœžœžœžœžœ˜3Kšžœžœžœ ˜/šžœ7˜9Kšžœ5˜7Kšžœžœ1žœžœ ˜[—Kšžœ*žœžœ˜@—K˜š Ÿœžœžœžœžœžœ˜5šžœž˜Kšœžœžœžœ˜(Kšžœžœžœ˜——K˜šŸœžœžœžœžœžœ œ˜UKšœžœ˜!Kšœžœ˜!Kšžœžœžœ˜\—K˜šŸœžœžœžœžœžœ œ˜PKšœžœ˜Kšœ žœžœ"˜4Kšœžœ žœ ˜&Kš žœžœžœ žœžœ˜:Kšžœ˜—K˜KšœžœH˜bK˜šŸœžœžœ žœžœžœžœžœ˜eK˜Kšœžœ˜>Kšœžœ˜4Kšœžœ˜8Kšœžœ˜3Kšžœžœžœ˜—Kšžœžœ˜—Kšœžœ˜ Kšžœ žœžœ˜0šžœž˜šžœžœž˜Kšœžœ˜5šœžœžœžœ ˜BKšžœ˜Kšžœžœ˜$—Kš œžœžœžœžœ˜]Kš œžœžœžœžœ˜^Kšžœžœ˜*—Kšœžœ˜ Kšžœ˜—Kšœคžœญ˜ีKšžœ˜—K˜šŸ œžœ˜Kšžœ%˜(Kšžœ˜—K˜šŸ œžœ˜KšœAžœ˜FKšžœ˜—K˜šŸœžœ˜Kšœžœ˜šœžœ˜-K˜K˜KšœH˜HK˜—KšœžœžœžœM˜sK˜HKšœ>˜>KšœP˜PK˜AK˜CK˜AKšœ žœ˜)K˜K˜ Kšœ˜Kšžœ8˜