/// /// Copyright (c) 2017 Sensus Metering Systems /// using System; using System.Diagnostics; namespace TBF.BenchControl.Network.Telnet { /// /// Virtual terminal (an array of characters). /// This class is thread safe under these circumstances: /// - ClearScreen() and WriteVt() methods called from one thread only /// - properties Text, etc. can be safely read from an arbitrary thread /// public class VirtualTerminal : IVirtualTerminal { const uint DefaultVtRowsCount = 80; /// Default VT height (number of rows) const uint DefaultVtColumnsCount = 132; /// Default VT width (number of columns) /// /// Virtual terminal special characters. /// /// enum SpecialChar { BS = 8, /// 8 = Backspace (move one position to the left) HT, /// 9 = Horizontal tab LF, /// 10 = Line feed VT, /// 11 = Vertical tab FF, /// 12 = Form feed (on visual displays most commonly clears the screen) CR /// 13 = Carriage return } /// /// Public properties /// public uint Rows { get { return vtRows; } } public uint Columns { get { return vtColumns; } } public uint CurRow { get { return curRow; } } public uint CurColumn { get { return curColumn; } } /// /// Virtual terminal text buffer as a single string. To be printed out using monospaced font. /// public string Text { get { string result = ""; char[] oneRow = new char[vtColumns]; for (int row = 0; row < vtRows; row++) { for (int column = 0; column < vtColumns; column++) { oneRow[column] = vt[row, column]; } result += new String(oneRow); /// New line at the end of each line except of the last one if (row < vtRows - 1) result += System.Environment.NewLine; } return result; } } /// /// Private fields /// char[,] vt; /// Virtual terminal text buffer uint vtRows = 0; /// VT height (number of rows) uint vtColumns = 0; /// VT width (number of columns) uint curRow = 0; /// Current row (cursor position) uint curColumn = 0; /// Current column (cursor position) /// /// Default constructor. /// public VirtualTerminal() { vt = new char[DefaultVtRowsCount, DefaultVtColumnsCount]; if (vt != null) { vtRows = DefaultVtRowsCount; vtColumns = DefaultVtColumnsCount; ClearScreen(); } } /// /// Arbitrary VT size constructor. /// public VirtualTerminal(uint nrRows, uint nrColumns) { vt = new char[nrRows, nrColumns]; if (vt != null) { vtRows = nrRows; vtColumns = nrColumns; ClearScreen(); } } /// /// Clear VT. /// public void ClearScreen() { for (uint row = 0; row < vtRows; row++) { for (uint column = 0; column < vtColumns; column++) { vt[row, column] = ' '; } } curRow = 0; curColumn = 0; } /// /// Scroll VT one line up and clear the bottom line. /// void ScrollUp() { for (uint row = 0; row < vtRows - 1; row++) { for (uint column = 0; column < vtColumns; column++) { vt[row, column] = vt[row + 1, column]; } } for (uint column = 0; column < vtColumns; column++) { vt[vtRows - 1, column] = ' '; } } /// /// Write one regular printable character to VC. /// void WritePrintableChar(char c) { vt[curRow, curColumn] = c; if (curColumn < vtColumns - 1) { curColumn++; } else if (curRow < vtRows - 1) { curRow++; curColumn = 0; } else { ScrollUp(); curRow = vtRows - 1; curColumn = 0; } } /// /// Write one character to VT (regular or special). /// public void WriteVt(char c) { if (c >= 32 && c <= 127) { WritePrintableChar(c); } else { switch ((SpecialChar)c) { /// Carriage return case SpecialChar.CR: curColumn = 0; break; /// Line feed case SpecialChar.LF: if (curRow < vtRows - 1) { curRow++; } else { ScrollUp(); curRow = vtRows - 1; } break; /// Form feed (= clear screen) case SpecialChar.FF: ClearScreen(); break; /// Backspace case SpecialChar.BS: if (curColumn > 0) { vt[curRow, --curColumn] = ' '; } else if (curRow > 0) { curColumn = vtColumns - 1; vt[--curRow, curColumn] = ' '; } break; default: break; } } } /// /// Write a string to VT. /// public void WriteVt(string txt) { if (txt != null) { for (int i = 0; i < txt.Length; i++) { WriteVt(txt[i]); } } } } }