public void Setup(TelnetSingletonServer tServer) { telnetServer = tServer; lastMenuQueryTime = DateTime.MinValue; // Force a stale timestamp the first time. telnetServer.Write( (char)UnicodeCommand.TITLEBEGIN + "kOS Terminal Server Welcome Menu" + (char)UnicodeCommand.TITLEEND ); forceMenuReprint = true; // force it to print the menu once the first time regardless of the state of the CPU list. }
/// <summary> /// Tell the telnet session to resize itself /// </summary> /// <param name="telnet">which telnet session to send to</param> /// <param name="width">new width</param> /// <param name="height">new height</param> /// <param name="unconditional">if true, then send the resize message no matter what. If false, /// then only send it if we calculate that the size changed.</param> private void ResizeAndRepaintTelnet(TelnetSingletonServer telnet, int width, int height, bool unconditional) { // Don't bother telling it to resize if its already the same size - this should stop resize spew looping: if (unconditional || telnet.ClientWidth != width || telnet.ClientHeight != height) { string resizeCmd = new string( new [] {(char)UnicodeCommand.RESIZESCREEN, (char)width, (char)height} ); telnet.Write(resizeCmd); RepaintTelnet(telnet, true); } }
/// <summary> /// Cover the case where the whole screen needs to be repainted from scratch. /// </summary> /// <param name="telnet">which telnet to paint to.</param> private void RepaintTelnetFull(TelnetSingletonServer telnet) { List<IScreenBufferLine> buffer = mostRecentScreen.Buffer; // just to keep the name shorter below: // Sometimes the buffer is shorter than the terminal height if the resize JUST happened in the last Update(): int rowsToPaint = Math.Min(shared.Screen.RowCount, buffer.Count); telnet.Write((char)UnicodeCommand.CLEARSCREEN); for (int row = 0 ; row < rowsToPaint ; ++row) { IScreenBufferLine lineBuffer = buffer[row]; int columnOfLastContent = -1; for (int col = 0 ; col < lineBuffer.Length ; ++col) { char ch = lineBuffer[col]; switch (ch) { case (char)0x0000: // The buffer pads null chars into the 'dead' space of the screen past the last printed char. break; case (char)0x0009: // tab chars - really shouldn't be in the buffer. break; default: columnOfLastContent = col; break; } } if (columnOfLastContent >= 0) // skip for empty lines { string line = lineBuffer.ToString().Substring(0, columnOfLastContent+1); telnet.Write(line); } if (row < rowsToPaint-1) //don't write the eoln for the lastmost line. telnet.Write((char)UnicodeCommand.STARTNEXTLINE); } // ensure cursor locatiom (in case it's not at the bottom): telnet.Write(String.Format("{0}{1}{2}", (char)UnicodeCommand.TELEPORTCURSOR, // The next two are cast to char because, for example, a value of 76 should be // encoded as (char)76 (which is 'L'), rather than as '7' followed by '6': (char)mostRecentScreen.CursorColumn, (char)mostRecentScreen.CursorRow)); prevTelnetScreens[telnet] = mostRecentScreen.DeepCopy(); }
internal void SendTitleToTelnet(TelnetSingletonServer telnet) { // Make the telnet client learn about the new title: string changeTitleCmd = String.Format("{0}{1}{2}", (char)UnicodeCommand.TITLEBEGIN, TitleText, (char)UnicodeCommand.TITLEEND); telnet.Write(changeTitleCmd); }
/// <summary> /// Do the repaint of the telnet session. /// </summary> /// <param name="telnet">which telnet session to repaint</param> /// <param name="fullSync">if true, then ignore the diffing algorithm and just redraw everything.</param> internal void RepaintTelnet(TelnetSingletonServer telnet, bool fullSync) { if (fullSync || prevTelnetScreens[telnet] == null) { RepaintTelnetFull(telnet); return; } // If the state of the screen reverse, or the visual bell flags, has changed since last time, // spit out the characters to change the state: if (telnet.ReverseScreen != shared.Screen.ReverseScreen) { telnet.Write(shared.Screen.ReverseScreen ? ((char)UnicodeCommand.REVERSESCREENMODE) : ((char)UnicodeCommand.NORMALSCREENMODE)); telnet.ReverseScreen = shared.Screen.ReverseScreen; } if (telnet.VisualBeep != shared.Screen.VisualBeep) { telnet.Write(shared.Screen.VisualBeep ? ((char)UnicodeCommand.VISUALBEEPMODE) : ((char)UnicodeCommand.AUDIOBEEPMODE)); telnet.VisualBeep = shared.Screen.VisualBeep; } string updateText = mostRecentScreen.DiffFrom(prevTelnetScreens[telnet]); telnet.Write(updateText); prevTelnetScreens[telnet] = mostRecentScreen.DeepCopy(); for (int i = 0 ; i < shared.Screen.BeepsPending ; ++i) telnet.Write((char)UnicodeCommand.BEEP); // The terminal's UnicodeMapper will convert this to ascii 0x07 if the right terminal type. }
internal void DetachTelnet(TelnetSingletonServer server) { server.DisconnectFromProcessor(); telnets.Remove(server); }
internal void AttachTelnet(TelnetSingletonServer server) { telnets.AddUnique(server); prevTelnetScreens[server] = null; }
/// <summary> /// Respond to one single input character in Unicode, using the pretend /// virtual Unicode terminal keycodes described in the UnicodeCommand enum. /// To keep things simple, all key input is coerced into single Unicode chars /// even if the actual keypress takes multiple characters to express in its /// native form (i.e. ESC [ A means left-arrow on a VT100 terminal. If /// a telnet is talking via VT100 codes, that ESC [ A will get converted into /// a single UnicdeCommand.LEFTCURSORONE character before being sent here.) /// <br/> /// This method is public because it is also how other mods should send input /// to the terminal if they want some other source to send simulated keystrokes. /// </summary> /// <param name="ch">The character, which might be a UnicodeCommand char</param> /// <param name="whichTelnet">If this came from a telnet session, which one did it come from? /// Set to null in order to say it wasn't from a telnet but was from the interactive GUI</param> /// <param name="doQueuing">true if the keypress should get queued if we're not ready for it /// right now. If false, then the keypress will be ignored if we're not ready for it.</param> public void ProcessOneInputChar(char ch, TelnetSingletonServer whichTelnet, bool doQueuing = true) { // Weird exceptions for multi-char data combos that would have been begun on previous calls to this method: switch (inputExpected) { case ExpectNextChar.RESIZEWIDTH: pendingWidth = ch; inputExpected = ExpectNextChar.RESIZEHEIGHT; return; case ExpectNextChar.RESIZEHEIGHT: int height = ch; shared.Screen.SetSize(height, pendingWidth); inputExpected = ExpectNextChar.NORMAL; return; default: break; } // Printable ASCII section of Unicode - the common vanilla situation // (Idea: Since this is all Unicode anyway, should we allow a wider range to // include multi-language accent characters and so on? Answer: to do so we'd // first need to expand the font pictures in the font image file, so it's a // bigger task than it may first seem.) if (0x0020 <= ch && ch <= 0x007f) { Type(ch, doQueuing); } else { switch(ch) { // A few conversions from UnicodeCommand into those parts of ASCII that it // maps directly into nicely, otherwise just pass it through to SpecialKey(): case (char)UnicodeCommand.DELETELEFT: case (char)8: Type((char)8, doQueuing); break; case (char)UnicodeCommand.STARTNEXTLINE: case '\r': Type('\r', doQueuing); break; case '\t': Type('\t', doQueuing); break; case (char)UnicodeCommand.RESIZESCREEN: inputExpected = ExpectNextChar.RESIZEWIDTH; break; // next expected char is the width. // Finish session: If GUI, then close window. If Telnet, then detatch from the session: case (char)0x0004/*control-D*/: // How users of unix shells are used to doing this. case (char)0x0018/*control-X*/: // How kOS did it in the past in the GUI window. if (shared.Interpreter.IsAtStartOfCommand()) { if (whichTelnet == null) Close(); else whichTelnet.DisconnectFromProcessor(); } break; // User asking for redraw (Unity already requires that we continually redraw the GUI Terminal, so this is only meaningful for telnet): case (char)UnicodeCommand.REQUESTREPAINT: if (whichTelnet != null) { ResizeAndRepaintTelnet(whichTelnet, shared.Screen.ColumnCount, shared.Screen.RowCount, true); } break; // Typical case is to just let SpecialKey do the work: default: SpecialKey(ch, doQueuing); break; } } // else ignore it - unimplemented char. }
internal void SingletonStopped(TelnetSingletonServer telnet) { telnets.Remove(telnet); }
public void Update() { SetConfigEnable(SafeHouse.Config.EnableTelnet); int howManySpawned = 0; if (!isListening) { // Doing the command: // SET CONFIG:TELNET TO FALSE. // should kill any singleton telnet servers that may have been started in the past // when it was true. This does that: foreach (TelnetSingletonServer singleServer in telnets) { singleServer.StopListening(); telnets.Remove(singleServer); } // disabled, so don't listen for connections. return; } // .NET's TCP handler only gives you blocking socket accepting, but for the Unity // Update(), we need to always finish quickly and never block, so check if anything // is pending first to simulate the effect of a nonblocking socket accept check: if (!server.Pending()) return; TcpClient incomingClient = server.AcceptTcpClient(); string remoteIdent = ((IPEndPoint)(incomingClient.Client.RemoteEndPoint)).Address.ToString(); SafeHouse.Logger.Log(string.Format("{0} telnet server got an incoming connection from {1}", KSPLogger.LOGGER_PREFIX, remoteIdent)); TelnetSingletonServer newServer = new TelnetSingletonServer(this, incomingClient, ++howManySpawned); telnets.Add(newServer); newServer.StartListening(); }
public void Detach() { enabled = false; telnetServer = null; }