From 2c3c3963d0cb4bfe9ae818ea8cc326745e82c0e3 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 1 Jan 2023 10:48:53 -0700 Subject: [PATCH 01/36] Changed ansi esc sequence used on exit. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 +++--- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 37b43244b3..069ffe3e92 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1306,8 +1306,8 @@ public override void End () void Clear () { if (Rows > 0) { - Console.Clear (); - Console.Out.Write ("\x1b[3J"); + //Console.Clear (); + Console.Out.Write ("\x1b[0J"); //Console.Out.Write ("\x1b[?25l"); } } @@ -1392,7 +1392,7 @@ public override void ResizeScreen () } } Clip = new Rect (0, 0, Cols, Rows); - Console.Out.Write ("\x1b[3J"); + Console.Out.Write ("\x1b[0J"); Console.Out.Flush (); } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 66eeed296c..0d6684a72c 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1478,7 +1478,12 @@ public override void ResizeScreen () Right = (short)Cols }; WinConsole.ForceRefreshCursorVisibility (); - Console.Out.Write ("\x1b[3J"); + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + Console.Out.Write ("\x1b[0J"); Console.Out.Flush (); } From 7dec9f7ef1652b226ff72189ff3f46d0ee89f6e5 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 1 Jan 2023 10:49:25 -0700 Subject: [PATCH 02/36] Changed ansi esc sequence used on exit. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 069ffe3e92..6616e496f2 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1306,7 +1306,7 @@ public override void End () void Clear () { if (Rows > 0) { - //Console.Clear (); + Console.Clear (); Console.Out.Write ("\x1b[0J"); //Console.Out.Write ("\x1b[?25l"); } @@ -1392,7 +1392,7 @@ public override void ResizeScreen () } } Clip = new Rect (0, 0, Cols, Rows); - Console.Out.Write ("\x1b[0J"); + Console.Out.Write ("\x1b[3J"); Console.Out.Flush (); } From c5ebfcff974b7d0ab70fa0439fdc89868e6aa884 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 14 Jan 2023 00:06:32 +0000 Subject: [PATCH 03/36] Improves HeightAsBuffer although currently only works on Windows. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 294 ++++++++++--------- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 13 +- Terminal.Gui/Core/Application.cs | 13 + Terminal.Gui/Core/ConsoleDriver.cs | 13 +- 4 files changed, 180 insertions(+), 153 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 6616e496f2..88d63b4439 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -185,24 +185,11 @@ void WaitWinChange () return; } } else { - //largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - largestWindowHeight = Console.BufferHeight; - if (Console.BufferWidth != consoleDriver.Cols || largestWindowHeight != consoleDriver.Rows - || Console.WindowHeight != lastWindowHeight) { - lastWindowHeight = Console.WindowHeight; - GetWindowSizeEvent (new Size (Console.BufferWidth, lastWindowHeight)); - return; - } - if (Console.WindowTop != consoleDriver.Top) { - // Top only working on Windows. - var winPositionEv = new WindowPositionEvent () { - Top = Console.WindowTop, - Left = Console.WindowLeft - }; - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.WindowPosition, - WindowPositionEvent = winPositionEv - }); + largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); + if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows + || Console.WindowHeight != lastWindowHeight) { + lastWindowHeight = Math.Max (Console.WindowHeight, 0); + GetWindowSizeEvent (new Size (Console.BufferWidth, largestWindowHeight)); return; } #if PROCESS_REQUEST @@ -240,26 +227,26 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) case 0: if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); } break; case uint n when (n >= '\u0001' && n <= '\u001a'): if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { key = ConsoleKey.Enter; newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); } else if (consoleKeyInfo.Key == 0) { key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - true); + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + true); } break; case 27: @@ -283,9 +270,9 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) return; case 127: newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); break; default: newConsoleKeyInfo = consoleKeyInfo; @@ -379,10 +366,10 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, - key, - false, - true, - true); + key, + false, + true, + true); } else { if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; @@ -390,10 +377,10 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf key = (ConsoleKey)cki [1].KeyChar; } newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - (ConsoleKey)Math.Min ((uint)key, 255), - false, - true, - false); + (ConsoleKey)Math.Min ((uint)key, 255), + false, + true, + false); } break; case 3: @@ -401,34 +388,34 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); } newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); break; case 4: if (cki [1].KeyChar == '[' && cki [3].KeyChar == 126) { key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); } break; case 5: if (cki [1].KeyChar == '[' && (cki [2].KeyChar == 49 || cki [2].KeyChar == 50) - && cki [4].KeyChar == 126) { + && cki [4].KeyChar == 126) { key = GetConsoleKey (cki [3].KeyChar, ref mod, cki.Length); } else if (cki [1].KeyChar == 49 && cki [2].KeyChar == ';') { // For WSL mod |= GetConsoleModifiers (cki [3].KeyChar); key = ConsoleKey.End; } newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); break; case 6: if (cki [1].KeyChar == '[' && cki [2].KeyChar == 49 && cki [3].KeyChar == ';') { @@ -439,10 +426,10 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); } newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); break; case 7: GetRequestEvent (GetKeyCharArray (cki)); @@ -574,7 +561,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 48: case 56: buttonState = c == 'M' ? MouseButtonState.Button1Pressed - : MouseButtonState.Button1Released; + : MouseButtonState.Button1Released; break; case 1: case 9: @@ -589,7 +576,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 57: case 61: buttonState = c == 'M' ? MouseButtonState.Button2Pressed - : MouseButtonState.Button2Released; + : MouseButtonState.Button2Released; break; case 2: case 10: @@ -606,7 +593,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 58: case 62: buttonState = c == 'M' ? MouseButtonState.Button3Pressed - : MouseButtonState.Button3Released; + : MouseButtonState.Button3Released; break; case 35: case 39: @@ -723,8 +710,8 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) } if ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0) { + || (buttonState & MouseButtonState.Button2Pressed) != 0 + || (buttonState & MouseButtonState.Button3Pressed) != 0) { if ((buttonState & MouseButtonState.ReportMousePosition) == 0) { buttonPressedCount++; @@ -739,9 +726,9 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) } if (buttonPressedCount == 2 && !isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { + && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed + || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed + || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { isButtonDoubleClicked = true; ProcessButtonDoubleClicked (mouseEvent); @@ -763,9 +750,9 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) //System.Diagnostics.Debug.WriteLine ($"isButtonClicked: {isButtonClicked} isButtonDoubleClicked: {isButtonDoubleClicked} isButtonTripleClicked: {isButtonTripleClicked}"); if ((isButtonClicked || isButtonDoubleClicked || isButtonTripleClicked) - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && ((buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { //isButtonClicked = false; //isButtonDoubleClicked = false; @@ -775,12 +762,12 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) } if (isButtonClicked && !isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && ((buttonState & MouseButtonState.Button1Pressed) != 0 + || (buttonState & MouseButtonState.Button2Pressed) != 0 + || (buttonState & MouseButtonState.Button3Pressed) != 0 + || (buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { isButtonClicked = false; isButtonDoubleClicked = true; @@ -796,12 +783,12 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) return; } if (isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && ((buttonState & MouseButtonState.Button1Pressed) != 0 + || (buttonState & MouseButtonState.Button2Pressed) != 0 + || (buttonState & MouseButtonState.Button3Pressed) != 0 + || (buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { isButtonDoubleClicked = false; isButtonTripleClicked = true; @@ -826,10 +813,10 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) }); if (!isButtonClicked && !lastMouseEvent.ButtonState.HasFlag (MouseButtonState.ReportMousePosition) - && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && lastMouseEvent.Position != default && lastMouseEvent.Position == point + && ((buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { isButtonClicked = true; ProcessButtonClicked (mouseEvent); Application.MainLoop.AddIdle (() => { @@ -1192,7 +1179,7 @@ internal class NetDriver : ConsoleDriver { public override IClipboard Clipboard { get; } public override int [,,] Contents => contents; - int largestWindowHeight; + int largestBufferHeight; public NetDriver () { @@ -1201,8 +1188,6 @@ public NetDriver () IsWinPlatform = true; NetWinConsole = new NetWinVTConsole (); } - //largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - largestWindowHeight = Console.BufferHeight; if (IsWinPlatform) { Clipboard = new WindowsClipboard (); } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) { @@ -1242,12 +1227,12 @@ public override void AddRune (Rune rune) if (validClip) { if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { contents [crow, ccol - 1, 0] = (int)(uint)' '; } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { contents [crow, ccol + 1, 0] = (int)(uint)' '; contents [crow, ccol + 1, 2] = 1; @@ -1297,21 +1282,16 @@ public override void End () StopReportingMouseMoves (); Console.ResetColor (); - Clear (); + + //Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1049l"); + Console.Out.Flush (); + //Set cursor key to cursor. Console.Out.Write ("\x1b[?25h"); Console.Out.Flush (); } - void Clear () - { - if (Rows > 0) { - Console.Clear (); - Console.Out.Write ("\x1b[0J"); - //Console.Out.Write ("\x1b[?25l"); - } - } - public override Attribute MakeColor (Color foreground, Color background) { return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background); @@ -1321,22 +1301,32 @@ static Attribute MakeColor (ConsoleColor f, ConsoleColor b) { // Encode the colors into the int value. return new Attribute ( - value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), - foreground: (Color)f, - background: (Color)b - ); + value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), + foreground: (Color)f, + background: (Color)b + ); } public override void Init (Action terminalResized) { TerminalResized = terminalResized; + //Enable alternative screen buffer. + Console.Out.Write ("\x1b[?1049h"); + Console.Out.Flush (); + //Set cursor key to application. Console.Out.Write ("\x1b[?25l"); Console.Out.Flush (); Console.TreatControlCAsInput = true; + if (HeightAsBuffer) { + largestBufferHeight = Console.BufferHeight; + } else { + largestBufferHeight = Console.WindowHeight; + } + cols = Console.WindowWidth; rows = Console.WindowHeight; @@ -1346,8 +1336,6 @@ public override void Init (Action terminalResized) StartReportingMouseMoves (); CreateColors (); - - Clear (); } public override void ResizeScreen () @@ -1363,17 +1351,20 @@ public override void ResizeScreen () Console.CursorLeft = 0; Console.WindowTop = 0; Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 } else { //Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t"); Console.Out.Write ($"\x1b[0;0" + - $";{Rows};{Cols}w"); + $";{Rows};{Cols}w"); } } catch (System.IO.IOException) { - return; + setClip (); } catch (ArgumentOutOfRangeException) { - return; + setClip (); } } } else { @@ -1381,19 +1372,40 @@ public override void ResizeScreen () // Can raise an exception while is still resizing. try { #pragma warning disable CA1416 - Console.WindowTop = Math.Max (Math.Min (top, Rows - Console.WindowHeight), 0); + Console.CursorTop = 0; + Console.CursorLeft = 0; + //Console.WindowTop = 0; + //Console.WindowLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } + Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 - } catch (Exception) { - return; + } catch (System.IO.IOException) { + setClip (); + } catch (ArgumentOutOfRangeException) { + setClip (); } } else { Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" + - $";{Rows};{Cols}w"); + $";{Rows};{Cols}w"); + } + } + setClip (); + + void setClip () + { + Clip = new Rect (0, 0, Cols, Rows); + if (!HeightAsBuffer) { + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + Console.Out.Write ("\x1b[3J"); + Console.Out.Flush (); } } - Clip = new Rect (0, 0, Cols, Rows); - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); } public override void UpdateOffScreen () @@ -1432,19 +1444,26 @@ public override void Refresh () public override void UpdateScreen () { if (Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3 - || (!HeightAsBuffer && Rows != Console.WindowHeight) - || (HeightAsBuffer && Rows != largestWindowHeight)) { + || (!HeightAsBuffer && Rows != Console.WindowHeight) + || (HeightAsBuffer && Rows != largestBufferHeight)) { return; } - int top = Top; - int left = Left; - int rows = Math.Min (Console.WindowHeight + top, Rows); + int top = 0; + int left = 0; + int rows = Rows; int cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); var lastCol = -1; Console.CursorVisible = false; + if (IsWinPlatform) { + Console.CursorTop = 0; + Console.CursorLeft = 0; + } else { + SetVirtualCursorPosition (0, 0); + } + for (int row = top; row < rows; row++) { if (!dirtyLine [row]) { continue; @@ -1485,7 +1504,7 @@ public override void UpdateScreen () outputWidth++; var rune = contents [row, col, 0]; char [] spair; - if (Rune.DecodeSurrogatePair((uint) rune, out spair)) { + if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) { output.Append (spair); } else { output.Append ((char)rune); @@ -1502,9 +1521,9 @@ public override void UpdateScreen () } } - void SetVirtualCursorPosition (int lastCol, int row) + void SetVirtualCursorPosition (int col, int row) { - Console.Out.Write ($"\x1b[{row + 1};{lastCol + 1}H"); + Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); Console.Out.Flush (); } @@ -1801,36 +1820,25 @@ void ProcessInput (NetEvents.InputResult inputEvent) mouseHandler (ToDriverMouse (inputEvent.MouseEvent)); break; case NetEvents.EventType.WindowSize: - ChangeWin (); - break; - case NetEvents.EventType.WindowPosition: - var newTop = inputEvent.WindowPositionEvent.Top; - var newLeft = inputEvent.WindowPositionEvent.Left; - if (HeightAsBuffer && (top != newTop || left != newLeft)) { - top = newTop; - left = newLeft; - Refresh (); - } + ChangeWin (inputEvent.WindowSizeEvent.Size); break; } } - void ChangeWin () + void ChangeWin (Size size) { - const int Min_WindowWidth = 14; - Size size = new Size (); + Size newSize; if (!HeightAsBuffer) { - size = new Size (Math.Max (Min_WindowWidth, Console.WindowWidth), - Console.WindowHeight); + largestBufferHeight = Math.Max (size.Height, 0); + newSize = new Size (size.Width, largestBufferHeight); top = 0; left = 0; } else { - //largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - largestWindowHeight = Console.BufferHeight; - size = new Size (Console.BufferWidth, largestWindowHeight); + largestBufferHeight = Math.Max (size.Height, largestBufferHeight); + newSize = new Size (Math.Max (size.Width, 0), largestBufferHeight); } - cols = size.Width; - rows = size.Height; + cols = newSize.Width; + rows = newSize.Height; ResizeScreen (); UpdateOffScreen (); TerminalResized?.Invoke (); diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 0d6684a72c..0a7c0cd8a2 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -908,7 +908,11 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) left = pos.X; top = pos.Y; cols = inputEvent.WindowBufferSizeEvent.size.X; - rows = inputEvent.WindowBufferSizeEvent.size.Y; + if (HeightAsBuffer) { + rows = Math.Max (inputEvent.WindowBufferSizeEvent.size.Y, rows); + } else { + rows = inputEvent.WindowBufferSizeEvent.size.Y; + } //System.Diagnostics.Debug.WriteLine ($"{HeightAsBuffer},{cols},{rows}"); ResizeScreen (); UpdateOffScreen (); @@ -1478,13 +1482,6 @@ public override void ResizeScreen () Right = (short)Cols }; WinConsole.ForceRefreshCursorVisibility (); - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - Console.Out.Write ("\x1b[0J"); - Console.Out.Flush (); } public override void UpdateOffScreen () diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index b5f6e5984d..fe0da421bf 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -114,6 +114,19 @@ public static Toplevel MdiTop { /// /// The current used in the terminal. /// + /// + /// + /// If (the default) the height of the Terminal.Gui application () + /// tracks to the height of the visible console view when the console is resized. In this case + /// scrolling in the console will be disabled and all will remain visible. + /// + /// + /// If then height of the Terminal.Gui application does not + /// change when the console is resized on shrinking (it only grows and never decrease). + /// In this case console scrolling is enabled and the contents ( high) will scroll + /// as the console scrolls. + /// + /// public static bool HeightAsBuffer { get { if (Driver == null) { diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 300191b150..2fb0ea74a6 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -663,8 +663,17 @@ public abstract class ConsoleDriver { public abstract IClipboard Clipboard { get; } /// - /// If false height is measured by the window height and thus no scrolling. - /// If true then height is measured by the buffer height, enabling scrolling. + /// + /// If (the default) the height of the Terminal.Gui application () + /// tracks to the height of the visible console view when the console is resized. In this case + /// scrolling in the console will be disabled and all will remain visible. + /// + /// + /// If then height of the Terminal.Gui application does not + /// change when the console is resized on shrinking (it only grows and never decrease). + /// In this case console scrolling is enabled and the contents ( high) will scroll + /// as the console scrolls. + /// /// public abstract bool HeightAsBuffer { get; set; } From 1aab780b0b3828519f402f50cafeaf916ae1b717 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 3 Jan 2023 13:18:09 +0000 Subject: [PATCH 04/36] Fixes #2267. Toplevel.EnsureVisibleBounds throws an exception if border is null. --- Terminal.Gui/Core/Toplevel.cs | 9 +++++---- UnitTests/ToplevelTests.cs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index a170d0c566..fddabb692e 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -613,8 +613,9 @@ internal View EnsureVisibleBounds (Toplevel top, int x, int y, } nx = Math.Max (x, 0); nx = nx + top.Frame.Width > l ? Math.Max (l - top.Frame.Width, 0) : nx; - if (nx + (top.Border != null && top.Border.DrawMarginFrame ? 2 : 1) > top.Frame.X + top.Frame.Width) { - nx = Math.Max (top.Frame.Right - (top.Border.DrawMarginFrame ? 2 : 1), 0); + var mfLength = top.Border?.DrawMarginFrame == true ? 2 : 1; + if (nx + mfLength > top.Frame.X + top.Frame.Width) { + nx = Math.Max (top.Frame.Right - mfLength, 0); } //System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}"); bool m, s; @@ -653,8 +654,8 @@ internal View EnsureVisibleBounds (Toplevel top, int x, int y, } ny = Math.Min (ny, l); ny = ny + top.Frame.Height >= l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny; - if (ny + (top.Border != null && top.Border.DrawMarginFrame ? 2 : 1) > top.Frame.Y + top.Frame.Height) { - ny = Math.Max (top.Frame.Bottom - (top.Border.DrawMarginFrame ? 2 : 1), 0); + if (ny + mfLength > top.Frame.Y + top.Frame.Height) { + ny = Math.Max (top.Frame.Bottom - mfLength, 0); } //System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}"); diff --git a/UnitTests/ToplevelTests.cs b/UnitTests/ToplevelTests.cs index a528a3a617..16a6daf5de 100644 --- a/UnitTests/ToplevelTests.cs +++ b/UnitTests/ToplevelTests.cs @@ -967,5 +967,18 @@ public void Mouse_Drag_On_Top_With_Superview_Not_Null () Application.Run (); } + + [Fact, AutoInitShutdown] + public void EnsureVisibleBounds_With_Border_Null_Not_Throws () + { + var top = new Toplevel (); + Application.Begin (top); + + var exception = Record.Exception (() => ((FakeDriver)Application.Driver).SetBufferSize (0, 10)); + Assert.Null (exception); + + exception = Record.Exception (() => ((FakeDriver)Application.Driver).SetBufferSize (10, 0)); + Assert.Null (exception); + } } } \ No newline at end of file From c23cbd56ef5e5749fbf66c4b89357c63ce98a59d Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 18 Jan 2023 17:09:04 +0000 Subject: [PATCH 05/36] Changing comment as requested. --- Terminal.Gui/Core/Application.cs | 4 ++-- Terminal.Gui/Core/ConsoleDriver.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index fe0da421bf..89341166f6 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -121,8 +121,8 @@ public static Toplevel MdiTop { /// scrolling in the console will be disabled and all will remain visible. /// /// - /// If then height of the Terminal.Gui application does not - /// change when the console is resized on shrinking (it only grows and never decrease). + /// If then height of the Terminal.Gui application only tracks + /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). /// In this case console scrolling is enabled and the contents ( high) will scroll /// as the console scrolls. /// diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 2fb0ea74a6..8899ff7ef3 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -669,8 +669,8 @@ public abstract class ConsoleDriver { /// scrolling in the console will be disabled and all will remain visible. /// /// - /// If then height of the Terminal.Gui application does not - /// change when the console is resized on shrinking (it only grows and never decrease). + /// If then height of the Terminal.Gui application only tracks + /// the height of the visible console view when the console is made larger (the application will only grow in height, never shrink). /// In this case console scrolling is enabled and the contents ( high) will scroll /// as the console scrolls. /// From de45dd9113a33c6f311c127d06473e1b21bb7227 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 18 Jan 2023 17:11:46 +0000 Subject: [PATCH 06/36] Fixes indentation. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 88d63b4439..0e5126cd89 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -227,26 +227,26 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) case 0: if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); } break; case uint n when (n >= '\u0001' && n <= '\u001a'): if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { key = ConsoleKey.Enter; newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); } else if (consoleKeyInfo.Key == 0) { key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - true); + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + true); } break; case 27: From 4832e73f65dd7754492b5571238b4343d6ac23a7 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 18 Jan 2023 17:13:25 +0000 Subject: [PATCH 07/36] Seems not needed for now, maybe some update, comment for now. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 0e5126cd89..18eab5a65e 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1396,15 +1396,15 @@ public override void ResizeScreen () void setClip () { Clip = new Rect (0, 0, Cols, Rows); - if (!HeightAsBuffer) { - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); - } + //if (!HeightAsBuffer) { + // // ANSI ESC "[xJ" Clears part of the screen. + // // If n is 0 (or missing), clear from cursor to end of screen. + // // If n is 1, clear from cursor to beginning of the screen. + // // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // Console.Out.Write ("\x1b[0J"); + // Console.Out.Flush (); + //} } } From 2e5570544df1de5b1a1f04a3fc459c1796339121 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 18 Jan 2023 18:21:34 +0000 Subject: [PATCH 08/36] Renamed HeightAsBuffer to EnableConsoleScrolling and made it obsolete. --- .../CursesDriver/CursesDriver.cs | 7 ++++- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 17 +++++++---- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 21 ++++++++----- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 19 +++++++----- Terminal.Gui/Core/Application.cs | 17 ++++++++--- Terminal.Gui/Core/ConsoleDriver.cs | 6 ++++ UICatalog/UICatalog.cs | 28 ++++++++--------- UnitTests/ApplicationTests.cs | 12 ++++---- UnitTests/ConsoleDriverTests.cs | 30 +++++++++---------- 9 files changed, 96 insertions(+), 61 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index d8d38392f7..4181011849 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -23,7 +23,12 @@ internal class CursesDriver : ConsoleDriver { public override int Rows => Curses.Lines; public override int Left => 0; public override int Top => 0; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } + [Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)] + public override bool HeightAsBuffer { + get => EnableConsoleScrolling; + set => EnableConsoleScrolling = value; + } public override IClipboard Clipboard { get => clipboard; } CursorVisibility? initialCursorVisibility = null; diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 77526629b9..cb1d772a1b 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -33,7 +33,7 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN UseFakeClipboard = useFakeClipboard; FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; - + // double check usage is correct Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false); Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false); @@ -48,7 +48,12 @@ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsN // Only handling left here because not all terminals has a horizontal scroll bar. public override int Left => 0; public override int Top => 0; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } + [Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)] + public override bool HeightAsBuffer { + get => EnableConsoleScrolling; + set => EnableConsoleScrolling = value; + } private IClipboard clipboard = null; public override IClipboard Clipboard => clipboard; @@ -521,7 +526,7 @@ public void SetBufferSize (int width, int height) FakeConsole.SetBufferSize (width, height); cols = width; rows = height; - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { SetWindowSize (width, height); } ProcessResize (); @@ -530,7 +535,7 @@ public void SetBufferSize (int width, int height) public void SetWindowSize (int width, int height) { FakeConsole.SetWindowSize (width, height); - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { if (width != cols || height != rows) { SetBufferSize (width, height); cols = width; @@ -542,7 +547,7 @@ public void SetWindowSize (int width, int height) public void SetWindowPosition (int left, int top) { - if (HeightAsBuffer) { + if (EnableConsoleScrolling) { this.left = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0); this.top = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0); } else if (this.left > 0 || this.top > 0) { @@ -561,7 +566,7 @@ void ProcessResize () public override void ResizeScreen () { - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { if (FakeConsole.WindowHeight > 0) { // Can raise an exception while is still resizing. try { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 18eab5a65e..cb2990dd62 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -177,7 +177,7 @@ void WaitWinChange () while (true) { // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. Thread.Sleep (10); - if (!consoleDriver.HeightAsBuffer) { + if (!consoleDriver.EnableConsoleScrolling) { if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) { var w = Math.Max (Console.WindowWidth, 0); var h = Math.Max (Console.WindowHeight, 0); @@ -1172,7 +1172,12 @@ internal class NetDriver : ConsoleDriver { public override int Rows => rows; public override int Left => left; public override int Top => top; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } + [Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)] + public override bool HeightAsBuffer { + get => EnableConsoleScrolling; + set => EnableConsoleScrolling = value; + } public NetWinVTConsole NetWinConsole { get; } public bool IsWinPlatform { get; } @@ -1321,7 +1326,7 @@ public override void Init (Action terminalResized) Console.TreatControlCAsInput = true; - if (HeightAsBuffer) { + if (EnableConsoleScrolling) { largestBufferHeight = Console.BufferHeight; } else { largestBufferHeight = Console.WindowHeight; @@ -1340,7 +1345,7 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { if (Console.WindowHeight > 0) { // Can raise an exception while is still resizing. try { @@ -1396,7 +1401,7 @@ public override void ResizeScreen () void setClip () { Clip = new Rect (0, 0, Cols, Rows); - //if (!HeightAsBuffer) { + //if (!EnableConsoleScrolling) { // // ANSI ESC "[xJ" Clears part of the screen. // // If n is 0 (or missing), clear from cursor to end of screen. // // If n is 1, clear from cursor to beginning of the screen. @@ -1444,8 +1449,8 @@ public override void Refresh () public override void UpdateScreen () { if (Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3 - || (!HeightAsBuffer && Rows != Console.WindowHeight) - || (HeightAsBuffer && Rows != largestBufferHeight)) { + || (!EnableConsoleScrolling && Rows != Console.WindowHeight) + || (EnableConsoleScrolling && Rows != largestBufferHeight)) { return; } @@ -1828,7 +1833,7 @@ void ProcessInput (NetEvents.InputResult inputEvent) void ChangeWin (Size size) { Size newSize; - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { largestBufferHeight = Math.Max (size.Height, 0); newSize = new Size (size.Width, largestBufferHeight); top = 0; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 0a7c0cd8a2..9ab8cf13b0 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -732,7 +732,12 @@ internal class WindowsDriver : ConsoleDriver { public override int Rows => rows; public override int Left => left; public override int Top => top; - public override bool HeightAsBuffer { get; set; } + public override bool EnableConsoleScrolling { get; set; } + [Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)] + public override bool HeightAsBuffer { + get => EnableConsoleScrolling; + set => EnableConsoleScrolling = value; + } public override IClipboard Clipboard => clipboard; public override int [,,] Contents => contents; @@ -767,7 +772,7 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle private void ChangeWin (Size e) { - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { var w = e.Width; if (w == cols - 3 && e.Height < rows) { w += 3; @@ -908,12 +913,12 @@ void ProcessInput (WindowsConsole.InputRecord inputEvent) left = pos.X; top = pos.Y; cols = inputEvent.WindowBufferSizeEvent.size.X; - if (HeightAsBuffer) { + if (EnableConsoleScrolling) { rows = Math.Max (inputEvent.WindowBufferSizeEvent.size.Y, rows); } else { rows = inputEvent.WindowBufferSizeEvent.size.Y; } - //System.Diagnostics.Debug.WriteLine ($"{HeightAsBuffer},{cols},{rows}"); + //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}"); ResizeScreen (); UpdateOffScreen (); TerminalResized?.Invoke (); @@ -1627,7 +1632,7 @@ public override void UpdateScreen () if (damageRegion.Left == -1) return; - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { var windowSize = WinConsole.GetConsoleBufferWindow (out _); if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) return; @@ -1876,9 +1881,9 @@ void WaitWinChange () { while (true) { Thread.Sleep (100); - if (!consoleDriver.HeightAsBuffer) { + if (!consoleDriver.EnableConsoleScrolling) { windowSize = winConsole.GetConsoleBufferWindow (out _); - //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.HeightAsBuffer},{windowSize.Width},{windowSize.Height}"); + //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}"); if (windowSize != Size.Empty && windowSize.Width != consoleDriver.Cols || windowSize.Height != consoleDriver.Rows) { return; diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 89341166f6..17d39c4f92 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -112,7 +112,7 @@ public static Toplevel MdiTop { public static View WantContinuousButtonPressedView { get; private set; } /// - /// The current used in the terminal. + /// The current used in the terminal. /// /// /// @@ -127,21 +127,30 @@ public static Toplevel MdiTop { /// as the console scrolls. /// /// - public static bool HeightAsBuffer { + public static bool EnableConsoleScrolling { get { if (Driver == null) { throw new ArgumentNullException ("The driver must be initialized first."); } - return Driver.HeightAsBuffer; + return Driver.EnableConsoleScrolling; } set { if (Driver == null) { throw new ArgumentNullException ("The driver must be initialized first."); } - Driver.HeightAsBuffer = value; + Driver.EnableConsoleScrolling = value; } } + /// + /// This API is deprecated; use instead. + /// + [Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)] + public static bool HeightAsBuffer { + get => EnableConsoleScrolling; + set => EnableConsoleScrolling = value; + } + static Key alternateForwardKey = Key.PageDown | Key.CtrlMask; /// diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 8899ff7ef3..82dc7b4f4b 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -675,6 +675,12 @@ public abstract class ConsoleDriver { /// as the console scrolls. /// /// + public abstract bool EnableConsoleScrolling { get; set; } + + /// + /// This API is deprecated; use instead. + /// + [Obsolete ("This API is deprecated; use EnableConsoleScrolling instead.", false)] public abstract bool HeightAsBuffer { get; set; } /// diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 14cd47ecec..e7a60ef50a 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -149,7 +149,7 @@ static Scenario RunUICatalogTopLevel () /// class UICatalogTopLevel : Toplevel { public MenuItem miIsMouseDisabled; - public MenuItem miHeightAsBuffer; + public MenuItem miEnableConsoleScrolling; public FrameView LeftPane; public ListView CategoryListView; @@ -178,7 +178,7 @@ public UICatalogTopLevel () "About UI Catalog", () => MessageBox.Query ("About UI Catalog", _aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A), }), }); - + Capslock = new StatusItem (Key.CharMask, "Caps", null); Numlock = new StatusItem (Key.CharMask, "Num", null); Scrolllock = new StatusItem (Key.CharMask, "Scroll", null); @@ -272,14 +272,14 @@ public UICatalogTopLevel () void LoadedHandler () { - Application.HeightAsBuffer = _heightAsBuffer; + Application.EnableConsoleScrolling = _heightAsBuffer; if (_colorScheme == null) { ColorScheme = _colorScheme = Colors.Base; } miIsMouseDisabled.Checked = Application.IsMouseDisabled; - miHeightAsBuffer.Checked = Application.HeightAsBuffer; + miEnableConsoleScrolling.Checked = Application.EnableConsoleScrolling; DriverName.Title = $"Driver: {Driver.GetType ().Name}"; OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}"; @@ -316,7 +316,7 @@ List CreateDiagnosticMenuItems () List menuItems = new List (); menuItems.Add (CreateDiagnosticFlagsMenuItems ()); menuItems.Add (new MenuItem [] { null }); - menuItems.Add (CreateHeightAsBufferMenuItems ()); + menuItems.Add (CreateEnableConsoleScrollingMenuItems ()); menuItems.Add (CreateDisabledEnabledMouseItems ()); menuItems.Add (CreateKeybindingsMenuItems ()); return menuItems; @@ -354,18 +354,18 @@ MenuItem [] CreateKeybindingsMenuItems () return menuItems.ToArray (); } - MenuItem [] CreateHeightAsBufferMenuItems () + MenuItem [] CreateEnableConsoleScrollingMenuItems () { List menuItems = new List (); - miHeightAsBuffer = new MenuItem (); - miHeightAsBuffer.Title = "_Height As Buffer"; - miHeightAsBuffer.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miHeightAsBuffer.Title.ToString ().Substring (1, 1) [0]; - miHeightAsBuffer.CheckType |= MenuItemCheckStyle.Checked; - miHeightAsBuffer.Action += () => { - miHeightAsBuffer.Checked = !miHeightAsBuffer.Checked; - Application.HeightAsBuffer = miHeightAsBuffer.Checked; + miEnableConsoleScrolling = new MenuItem (); + miEnableConsoleScrolling.Title = "_Enable Console Scrolling"; + miEnableConsoleScrolling.Shortcut = Key.CtrlMask | Key.AltMask | (Key)miEnableConsoleScrolling.Title.ToString ().Substring (1, 1) [0]; + miEnableConsoleScrolling.CheckType |= MenuItemCheckStyle.Checked; + miEnableConsoleScrolling.Action += () => { + miEnableConsoleScrolling.Checked = !miEnableConsoleScrolling.Checked; + Application.EnableConsoleScrolling = miEnableConsoleScrolling.Checked; }; - menuItems.Add (miHeightAsBuffer); + menuItems.Add (miEnableConsoleScrolling); return menuItems.ToArray (); } diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index b9bc4101cf..cf5c07ff6e 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -24,7 +24,7 @@ void Pre_Init_State () Assert.Null (Application.Driver); Assert.Null (Application.Top); Assert.Null (Application.Current); - Assert.Throws (() => Application.HeightAsBuffer == true); + Assert.Throws (() => Application.EnableConsoleScrolling == true); Assert.Null (Application.MainLoop); Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); @@ -36,7 +36,7 @@ void Post_Init_State () Assert.NotNull (Application.Driver); Assert.NotNull (Application.Top); Assert.NotNull (Application.Current); - Assert.False (Application.HeightAsBuffer); + Assert.False (Application.EnableConsoleScrolling); Assert.NotNull (Application.MainLoop); Assert.Null (Application.Iteration); Assert.Null (Application.RootMouseEvent); @@ -313,7 +313,7 @@ public void Run_T_After_InitNullDriver_with_TestTopLevel_Throws () public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws () { Init (); - + Application.Driver = null; Application.Iteration = () => { @@ -333,8 +333,8 @@ public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws () [Fact] public void Run_T_NoInit_DoesNotThrow () { - Application.ForceFakeConsole = true; - + Application.ForceFakeConsole = true; + Application.Iteration = () => { Application.RequestStop (); }; @@ -430,7 +430,7 @@ public void Run_Loaded_Ready_Unlodaded_Events () } // TODO: Add tests for Run that test errorHandler - + #endregion #region ShutdownTests diff --git a/UnitTests/ConsoleDriverTests.cs b/UnitTests/ConsoleDriverTests.cs index db8d167530..22bf49757d 100644 --- a/UnitTests/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDriverTests.cs @@ -304,7 +304,7 @@ public void TerminalResized_Simulation (Type driverType) // MockDriver will still be 120x40 wasTerminalResized = false; - Application.HeightAsBuffer = true; + Application.EnableConsoleScrolling = true; driver.SetWindowSize (40, 20); Assert.Equal (120, Application.Driver.Cols); Assert.Equal (40, Application.Driver.Rows); @@ -319,12 +319,12 @@ public void TerminalResized_Simulation (Type driverType) [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero (Type driverType) + public void EnableConsoleScrolling_Is_False_Left_And_Top_Is_Always_Zero (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Assert.False (Application.HeightAsBuffer); + Assert.False (Application.EnableConsoleScrolling); Assert.Equal (0, Console.WindowLeft); Assert.Equal (0, Console.WindowTop); @@ -337,13 +337,13 @@ public void HeightAsBuffer_Is_False_Left_And_Top_Is_Always_Zero (Type driverType [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType) + public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (81, 25); Assert.Equal (0, Console.WindowLeft); @@ -354,13 +354,13 @@ public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_WindowWidth (Type [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType) + public void EnableConsoleScrolling_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus_WindowWidth (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (81, 25); Assert.Equal (0, Console.WindowLeft); @@ -394,13 +394,13 @@ public void HeightAsBuffer_Is_True_Left_Cannot_Be_Greater_Than_BufferWidth_Minus [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType) + public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (80, 26); Assert.Equal (0, Console.WindowLeft); @@ -411,13 +411,13 @@ public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_WindowHeight (Type [Theory] [InlineData (typeof (FakeDriver))] - public void HeightAsBuffer_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType) + public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_BufferHeight_Minus_WindowHeight (Type driverType) { var driver = (FakeDriver)Activator.CreateInstance (driverType); Application.Init (driver); - Application.HeightAsBuffer = true; - Assert.True (Application.HeightAsBuffer); + Application.EnableConsoleScrolling = true; + Assert.True (Application.EnableConsoleScrolling); driver.SetWindowPosition (80, 26); Assert.Equal (0, Console.WindowLeft); From 7c832e843b43f0b0cfc6098ee5b0e5fddff03ac1 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 18 Jan 2023 18:27:28 +0000 Subject: [PATCH 09/36] Add comment on remarks for EnableConsoleScrolling. --- Terminal.Gui/Core/Application.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 17d39c4f92..cc1a240f7b 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -126,6 +126,7 @@ public static Toplevel MdiTop { /// In this case console scrolling is enabled and the contents ( high) will scroll /// as the console scrolls. /// + /// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear. /// public static bool EnableConsoleScrolling { get { From ad13b85955ecd41520bcb1e1956aeac801999841 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sun, 22 Jan 2023 08:13:27 -0700 Subject: [PATCH 10/36] merged @bdisp's EnableConsoleScrolling PR --- UICatalog/UICatalog.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index e7a60ef50a..062c7e0d63 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -118,6 +118,9 @@ static Scenario RunUICatalogTopLevel () // Run UI Catalog UI. When it exits, if _selectedScenario is != null then // a Scenario was selected. Otherwise, the user wants to exit UI Catalog. Application.Init (); + + Application.EnableConsoleScrolling = true; + Application.Run (); Application.Shutdown (); @@ -139,7 +142,7 @@ static Scenario RunUICatalogTopLevel () static bool _useSystemConsole = false; static ConsoleDriver.DiagnosticFlags _diagnosticFlags; - static bool _heightAsBuffer = false; + static bool _enableConsoleScrolling = true; static bool _isFirstRunning = true; static ColorScheme _colorScheme; @@ -272,7 +275,7 @@ public UICatalogTopLevel () void LoadedHandler () { - Application.EnableConsoleScrolling = _heightAsBuffer; + Application.EnableConsoleScrolling = _enableConsoleScrolling; if (_colorScheme == null) { ColorScheme = _colorScheme = Colors.Base; From acb60601af83d305b411242df7be9f521c7b1689 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 22 Jan 2023 16:33:36 +0000 Subject: [PATCH 11/36] Fixes buffer for Windows Terminal. --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 9ab8cf13b0..0201e856a0 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1461,6 +1461,11 @@ public override void Init (Action terminalResized) TerminalResized = terminalResized; try { + // Needed for Windows Terminal + //Enable alternative screen buffer. + Console.Out.Write ("\x1b[?1049h"); + Console.Out.Flush (); + var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); cols = winSize.Width; rows = winSize.Height; @@ -1487,6 +1492,16 @@ public override void ResizeScreen () Right = (short)Cols }; WinConsole.ForceRefreshCursorVisibility (); + if (!EnableConsoleScrolling) { + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // Needed for Windows Terminal + Console.Out.Write ("\x1b[3J"); + Console.Out.Flush (); + } } public override void UpdateOffScreen () @@ -1679,6 +1694,11 @@ public override void End () { WinConsole.Cleanup (); WinConsole = null; + + // Needed for Windows Terminal + //Disable alternative screen buffer. + Console.Out.Write ("\x1b[?1049l"); + Console.Out.Flush (); } public override Attribute GetAttribute () From d56b4dc011d59203717333f7633015050ce30ece Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 22 Jan 2023 22:10:02 +0000 Subject: [PATCH 12/36] Fixes issue in Windows Terminal on resizing causing some lines not be drawing after exceptions. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index cb2990dd62..e6a3f04856 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1460,25 +1460,27 @@ public override void UpdateScreen () int cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); var lastCol = -1; + var successSetCurPos = true; Console.CursorVisible = false; if (IsWinPlatform) { - Console.CursorTop = 0; - Console.CursorLeft = 0; + successSetCurPos = SetCursorPosition (0, 0); } else { SetVirtualCursorPosition (0, 0); } for (int row = top; row < rows; row++) { + if (Console.WindowHeight < 1) { + return; + } if (!dirtyLine [row]) { continue; } - dirtyLine [row] = false; + if (successSetCurPos) { + dirtyLine [row] = false; + } output.Clear (); for (int col = left; col < cols; col++) { - if (Console.WindowHeight > 0 && !SetCursorPosition (col, row)) { - return; - } lastCol = -1; var outputWidth = 0; for (; col < cols; col++) { @@ -1514,7 +1516,9 @@ public override void UpdateScreen () } else { output.Append ((char)rune); } - contents [row, col, 2] = 0; + if (successSetCurPos) { + contents [row, col, 2] = 0; + } } } if (output.Length > 0) { From 2e8dbf952c1d749803a7c302402e4b019d81def8 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 13:08:50 -0700 Subject: [PATCH 13/36] merge #9 --- UICatalog/UICatalog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 062c7e0d63..e8e4511c21 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -142,7 +142,7 @@ static Scenario RunUICatalogTopLevel () static bool _useSystemConsole = false; static ConsoleDriver.DiagnosticFlags _diagnosticFlags; - static bool _enableConsoleScrolling = true; + static bool _enableConsoleScrolling = false; static bool _isFirstRunning = true; static ColorScheme _colorScheme; From 12e664e28c6eea078ed5b27c849a67c1268b97f2 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 13:37:32 -0700 Subject: [PATCH 14/36] merged #9 --- UICatalog/UICatalog.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index e8e4511c21..80e5d09f54 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -119,7 +119,7 @@ static Scenario RunUICatalogTopLevel () // a Scenario was selected. Otherwise, the user wants to exit UI Catalog. Application.Init (); - Application.EnableConsoleScrolling = true; + Application.EnableConsoleScrolling = _enableConsoleScrolling; Application.Run (); Application.Shutdown (); @@ -275,8 +275,6 @@ public UICatalogTopLevel () void LoadedHandler () { - Application.EnableConsoleScrolling = _enableConsoleScrolling; - if (_colorScheme == null) { ColorScheme = _colorScheme = Colors.Base; } From 912b0e70535aa722f4119ecd422ffe73507cd079 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 14:15:37 -0700 Subject: [PATCH 15/36] use ESC [ ? 1047 --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 0201e856a0..2768a1136e 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1463,8 +1463,8 @@ public override void Init (Action terminalResized) try { // Needed for Windows Terminal //Enable alternative screen buffer. - Console.Out.Write ("\x1b[?1049h"); - Console.Out.Flush (); + Console.Out.Write ("\x1b[?1047h"); + //Console.Out.Flush (); var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); cols = winSize.Width; @@ -1499,8 +1499,8 @@ public override void ResizeScreen () // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer // Needed for Windows Terminal - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); + //Console.Out.Write ("\x1b[3J"); + //Console.Out.Flush (); } } @@ -1697,8 +1697,8 @@ public override void End () // Needed for Windows Terminal //Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1049l"); - Console.Out.Flush (); + Console.Out.Write ("\x1b[?1047l"); + //Console.Out.Flush (); } public override Attribute GetAttribute () From f3d12849b6e26a665005ee33ffd7d8f4c7bf48b9 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 15:33:11 -0700 Subject: [PATCH 16/36] Tweaks with new esc codes --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 33 +++++++++++++++----- Terminal.Gui/Core/ConsoleDriver.cs | 3 ++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 2768a1136e..14542f8b44 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -33,6 +33,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; namespace Terminal.Gui { @@ -1462,9 +1463,18 @@ public override void Init (Action terminalResized) try { // Needed for Windows Terminal - //Enable alternative screen buffer. + // ESC [ ? 1047 h Activate xterm alternative buffer (no backscroll) + // ESC [ ? 1047 l Restore xterm working buffer (with backscroll) + // ESC [ ? 1048 h Save cursor position + // ESC [ ? 1048 l Restore cursor position + // ESC [ ? 1049 h Save cursor position and activate xterm alternative buffer (no backscroll) + // ESC [ ? 1049 l Restore cursor position and restore xterm working buffer (with backscroll) + // Per Issue #2264 using the alterantive screen buffer is required for Windows Terminal to not + // wipe out the backscroll buffer when the application exits. Console.Out.Write ("\x1b[?1047h"); - //Console.Out.Flush (); + + // TODO: Figure out if calling flush is really needed here + Console.Out.Flush (); var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); cols = winSize.Width; @@ -1498,9 +1508,11 @@ public override void ResizeScreen () // If n is 1, clear from cursor to beginning of the screen. // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // Needed for Windows Terminal - //Console.Out.Write ("\x1b[3J"); - //Console.Out.Flush (); + // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer + Console.Out.Write ("\x1b[0J"); + + // TODO: Figure out if calling flush is really needed here + Console.Out.Flush (); } } @@ -1696,9 +1708,16 @@ public override void End () WinConsole = null; // Needed for Windows Terminal - //Disable alternative screen buffer. + // Clear the alternative screen buffer from the cursor to the + // end of the screen. + // Note, [3J causes Windows Terminal to wipe out the entire NON ALTERNATIVE + // backbuffer! So we need to use [0J instead. + Console.Out.Write ("\x1b[0J"); + + // Disable alternative screen buffer. Console.Out.Write ("\x1b[?1047l"); - //Console.Out.Flush (); + + Console.Out.Flush (); } public override Attribute GetAttribute () diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 82dc7b4f4b..4b962d3c43 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -675,6 +675,9 @@ public abstract class ConsoleDriver { /// as the console scrolls. /// /// + /// + /// NOTE: This functionaliy is currently broken on Windows Terminal. + /// public abstract bool EnableConsoleScrolling { get; set; } /// From 697e0683ca1f7284b4c3ca28cda2c215350e6dde Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 23 Jan 2023 16:54:02 -0700 Subject: [PATCH 17/36] Fixed curses driver to not nuke scroll buffer and to resize properly --- .../CursesDriver/CursesDriver.cs | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 4181011849..a5085e9809 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -148,19 +148,6 @@ public override void End () SetCursorVisibility (CursorVisibility.Default); Curses.endwin (); - - // I'm commenting this because was used in a trying to fix the Linux hanging and forgot to exclude it. - // Clear and reset entire screen. - //Console.Out.Write ("\x1b[2J"); - //Console.Out.Flush (); - - // Set top and bottom lines of a window. - //Console.Out.Write ("\x1b[1;25r"); - //Console.Out.Flush (); - - //Set cursor key to cursor. - //Console.Out.Write ("\x1b[?1l"); - //Console.Out.Flush (); } public override void UpdateScreen () => window.redrawwin (); @@ -826,9 +813,6 @@ public override void Init (Action terminalResized) try { //Set cursor key to application. - //Console.Out.Write ("\x1b[?1h"); - //Console.Out.Flush (); - window = Curses.initscr (); Curses.set_escdelay (10); } catch (Exception e) { @@ -939,8 +923,7 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { Clip = new Rect (0, 0, Cols, Rows); - Console.Out.Write ("\x1b[3J"); - Console.Out.Flush (); + Curses.refresh (); } public override void UpdateOffScreen () @@ -1072,13 +1055,11 @@ public override void Suspend () public override void StartReportingMouseMoves () { Console.Out.Write ("\x1b[?1003h"); - Console.Out.Flush (); } public override void StopReportingMouseMoves () { Console.Out.Write ("\x1b[?1003l"); - Console.Out.Flush (); } //int lastMouseInterval; @@ -1126,7 +1107,6 @@ public override bool SetCursorVisibility (CursorVisibility visibility) if (visibility != CursorVisibility.Invisible) { Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); - Console.Out.Flush (); } currentCursorVisibility = visibility; From e85fd20b20112c71568518e463bd45f0c49afd68 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 16:56:11 -0700 Subject: [PATCH 18/36] merge --- .../ConsoleDrivers/CursesDriver/CursesDriver.cs | 17 +++++++++++++++++ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 9 +-------- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 10 ++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index a5085e9809..20b41f5336 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -923,7 +923,12 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { Clip = new Rect (0, 0, Cols, Rows); +<<<<<<< Updated upstream Curses.refresh (); +======= + Console.Out.Write ("\x1b[3J"); + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 +>>>>>>> Stashed changes } public override void UpdateOffScreen () @@ -1055,11 +1060,19 @@ public override void Suspend () public override void StartReportingMouseMoves () { Console.Out.Write ("\x1b[?1003h"); +<<<<<<< Updated upstream +======= + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 +>>>>>>> Stashed changes } public override void StopReportingMouseMoves () { Console.Out.Write ("\x1b[?1003l"); +<<<<<<< Updated upstream +======= + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 +>>>>>>> Stashed changes } //int lastMouseInterval; @@ -1107,6 +1120,10 @@ public override bool SetCursorVisibility (CursorVisibility visibility) if (visibility != CursorVisibility.Invisible) { Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); +<<<<<<< Updated upstream +======= + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 +>>>>>>> Stashed changes } currentCursorVisibility = visibility; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index e6a3f04856..08e9b11357 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1290,11 +1290,10 @@ public override void End () //Disable alternative screen buffer. Console.Out.Write ("\x1b[?1049l"); - Console.Out.Flush (); + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 //Set cursor key to cursor. Console.Out.Write ("\x1b[?25h"); - Console.Out.Flush (); } public override Attribute MakeColor (Color foreground, Color background) @@ -1318,11 +1317,9 @@ public override void Init (Action terminalResized) //Enable alternative screen buffer. Console.Out.Write ("\x1b[?1049h"); - Console.Out.Flush (); //Set cursor key to application. Console.Out.Write ("\x1b[?25l"); - Console.Out.Flush (); Console.TreatControlCAsInput = true; @@ -1408,7 +1405,6 @@ void setClip () // // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). // // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer // Console.Out.Write ("\x1b[0J"); - // Console.Out.Flush (); //} } } @@ -1533,7 +1529,6 @@ public override void UpdateScreen () void SetVirtualCursorPosition (int col, int row) { Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); - Console.Out.Flush (); } System.Text.StringBuilder WriteAttributes (int attr) @@ -1628,13 +1623,11 @@ public override void UpdateCursor () public override void StartReportingMouseMoves () { Console.Out.Write ("\x1b[?1003h\x1b[?1015h\x1b[?1006h"); - Console.Out.Flush (); } public override void StopReportingMouseMoves () { Console.Out.Write ("\x1b[?1003l\x1b[?1015l\x1b[?1006l"); - Console.Out.Flush (); } public override void Suspend () diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 14542f8b44..3d05c8989e 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1473,8 +1473,7 @@ public override void Init (Action terminalResized) // wipe out the backscroll buffer when the application exits. Console.Out.Write ("\x1b[?1047h"); - // TODO: Figure out if calling flush is really needed here - Console.Out.Flush (); + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); cols = winSize.Width; @@ -1510,9 +1509,8 @@ public override void ResizeScreen () // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer Console.Out.Write ("\x1b[0J"); - - // TODO: Figure out if calling flush is really needed here - Console.Out.Flush (); + + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 } } @@ -1717,7 +1715,7 @@ public override void End () // Disable alternative screen buffer. Console.Out.Write ("\x1b[?1047l"); - Console.Out.Flush (); + // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 } public override Attribute GetAttribute () From 202f0f011c14e6cf93a7afaabf6dcb5af014c15a Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Mon, 23 Jan 2023 17:02:21 -0700 Subject: [PATCH 19/36] Cleand up netdriver escape codes --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index e6a3f04856..9a78a0bcfd 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1289,12 +1289,10 @@ public override void End () Console.ResetColor (); //Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1049l"); - Console.Out.Flush (); + Console.Out.Write ("\x1b[?1047l"); //Set cursor key to cursor. Console.Out.Write ("\x1b[?25h"); - Console.Out.Flush (); } public override Attribute MakeColor (Color foreground, Color background) @@ -1317,12 +1315,10 @@ public override void Init (Action terminalResized) TerminalResized = terminalResized; //Enable alternative screen buffer. - Console.Out.Write ("\x1b[?1049h"); - Console.Out.Flush (); + Console.Out.Write ("\x1b[?1047h"); //Set cursor key to application. Console.Out.Write ("\x1b[?25l"); - Console.Out.Flush (); Console.TreatControlCAsInput = true; @@ -1408,7 +1404,6 @@ void setClip () // // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). // // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer // Console.Out.Write ("\x1b[0J"); - // Console.Out.Flush (); //} } } @@ -1533,7 +1528,6 @@ public override void UpdateScreen () void SetVirtualCursorPosition (int col, int row) { Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); - Console.Out.Flush (); } System.Text.StringBuilder WriteAttributes (int attr) @@ -1628,13 +1622,11 @@ public override void UpdateCursor () public override void StartReportingMouseMoves () { Console.Out.Write ("\x1b[?1003h\x1b[?1015h\x1b[?1006h"); - Console.Out.Flush (); } public override void StopReportingMouseMoves () { Console.Out.Write ("\x1b[?1003l\x1b[?1015l\x1b[?1006l"); - Console.Out.Flush (); } public override void Suspend () From c462c3b48b32b7a35b797a5c9d91d066e906cc1e Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:07:17 -0700 Subject: [PATCH 20/36] fixed spaces->tabs --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 152 +++++++++++------------ 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 9a78a0bcfd..894c79f158 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -187,7 +187,7 @@ void WaitWinChange () } else { largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows - || Console.WindowHeight != lastWindowHeight) { + || Console.WindowHeight != lastWindowHeight) { lastWindowHeight = Math.Max (Console.WindowHeight, 0); GetWindowSizeEvent (new Size (Console.BufferWidth, largestWindowHeight)); return; @@ -225,7 +225,7 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) var keyChar = consoleKeyInfo.KeyChar; switch ((uint)keyChar) { case 0: - if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. + if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, @@ -270,9 +270,9 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) return; case 127: newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); break; default: newConsoleKeyInfo = consoleKeyInfo; @@ -366,10 +366,10 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, - key, - false, - true, - true); + key, + false, + true, + true); } else { if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; @@ -377,10 +377,10 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf key = (ConsoleKey)cki [1].KeyChar; } newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - (ConsoleKey)Math.Min ((uint)key, 255), - false, - true, - false); + (ConsoleKey)Math.Min ((uint)key, 255), + false, + true, + false); } break; case 3: @@ -388,34 +388,34 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); } newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); break; case 4: if (cki [1].KeyChar == '[' && cki [3].KeyChar == 126) { key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); } break; case 5: if (cki [1].KeyChar == '[' && (cki [2].KeyChar == 49 || cki [2].KeyChar == 50) - && cki [4].KeyChar == 126) { + && cki [4].KeyChar == 126) { key = GetConsoleKey (cki [3].KeyChar, ref mod, cki.Length); } else if (cki [1].KeyChar == 49 && cki [2].KeyChar == ';') { // For WSL mod |= GetConsoleModifiers (cki [3].KeyChar); key = ConsoleKey.End; } newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); break; case 6: if (cki [1].KeyChar == '[' && cki [2].KeyChar == 49 && cki [3].KeyChar == ';') { @@ -426,10 +426,10 @@ void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInf key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); } newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); break; case 7: GetRequestEvent (GetKeyCharArray (cki)); @@ -561,7 +561,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 48: case 56: buttonState = c == 'M' ? MouseButtonState.Button1Pressed - : MouseButtonState.Button1Released; + : MouseButtonState.Button1Released; break; case 1: case 9: @@ -576,7 +576,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 57: case 61: buttonState = c == 'M' ? MouseButtonState.Button2Pressed - : MouseButtonState.Button2Released; + : MouseButtonState.Button2Released; break; case 2: case 10: @@ -593,7 +593,7 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 58: case 62: buttonState = c == 'M' ? MouseButtonState.Button3Pressed - : MouseButtonState.Button3Released; + : MouseButtonState.Button3Released; break; case 35: case 39: @@ -613,12 +613,12 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 68: case 72: case 80: - buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp + buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp break; case 69: case 73: case 81: - buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown + buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown break; } // Modifiers. @@ -710,8 +710,8 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) } if ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0) { + || (buttonState & MouseButtonState.Button2Pressed) != 0 + || (buttonState & MouseButtonState.Button3Pressed) != 0) { if ((buttonState & MouseButtonState.ReportMousePosition) == 0) { buttonPressedCount++; @@ -726,18 +726,18 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) } if (buttonPressedCount == 2 && !isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { + && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed + || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed + || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { isButtonDoubleClicked = true; ProcessButtonDoubleClicked (mouseEvent); inputReady.Set (); return; } else if (buttonPressedCount == 3 && isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { + && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed + || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed + || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { isButtonDoubleClicked = false; isButtonTripleClicked = true; @@ -750,9 +750,9 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) //System.Diagnostics.Debug.WriteLine ($"isButtonClicked: {isButtonClicked} isButtonDoubleClicked: {isButtonDoubleClicked} isButtonTripleClicked: {isButtonTripleClicked}"); if ((isButtonClicked || isButtonDoubleClicked || isButtonTripleClicked) - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && ((buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { //isButtonClicked = false; //isButtonDoubleClicked = false; @@ -762,12 +762,12 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) } if (isButtonClicked && !isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && ((buttonState & MouseButtonState.Button1Pressed) != 0 + || (buttonState & MouseButtonState.Button2Pressed) != 0 + || (buttonState & MouseButtonState.Button3Pressed) != 0 + || (buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { isButtonClicked = false; isButtonDoubleClicked = true; @@ -783,12 +783,12 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) return; } if (isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && ((buttonState & MouseButtonState.Button1Pressed) != 0 + || (buttonState & MouseButtonState.Button2Pressed) != 0 + || (buttonState & MouseButtonState.Button3Pressed) != 0 + || (buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { isButtonDoubleClicked = false; isButtonTripleClicked = true; @@ -813,10 +813,10 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) }); if (!isButtonClicked && !lastMouseEvent.ButtonState.HasFlag (MouseButtonState.ReportMousePosition) - && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { + && lastMouseEvent.Position != default && lastMouseEvent.Position == point + && ((buttonState & MouseButtonState.Button1Released) != 0 + || (buttonState & MouseButtonState.Button2Released) != 0 + || (buttonState & MouseButtonState.Button3Released) != 0)) { isButtonClicked = true; ProcessButtonClicked (mouseEvent); Application.MainLoop.AddIdle (() => { @@ -1232,12 +1232,12 @@ public override void AddRune (Rune rune) if (validClip) { if (runeWidth < 2 && ccol > 0 - && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { + && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { contents [crow, ccol - 1, 0] = (int)(uint)' '; } else if (runeWidth < 2 && ccol <= Clip.Right - 1 - && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { + && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { contents [crow, ccol + 1, 0] = (int)(uint)' '; contents [crow, ccol + 1, 2] = 1; @@ -1304,10 +1304,10 @@ static Attribute MakeColor (ConsoleColor f, ConsoleColor b) { // Encode the colors into the int value. return new Attribute ( - value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), - foreground: (Color)f, - background: (Color)b - ); + value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff), + foreground: (Color)f, + background: (Color)b + ); } public override void Init (Action terminalResized) @@ -1360,7 +1360,7 @@ public override void ResizeScreen () } else { //Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t"); Console.Out.Write ($"\x1b[0;0" + - $";{Rows};{Cols}w"); + $";{Rows};{Cols}w"); } } catch (System.IO.IOException) { setClip (); @@ -1389,7 +1389,7 @@ public override void ResizeScreen () } } else { Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" + - $";{Rows};{Cols}w"); + $";{Rows};{Cols}w"); } } setClip (); @@ -1444,8 +1444,8 @@ public override void Refresh () public override void UpdateScreen () { if (Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3 - || (!EnableConsoleScrolling && Rows != Console.WindowHeight) - || (EnableConsoleScrolling && Rows != largestBufferHeight)) { + || (!EnableConsoleScrolling && Rows != Console.WindowHeight) + || (EnableConsoleScrolling && Rows != largestBufferHeight)) { return; } @@ -1539,8 +1539,8 @@ System.Text.StringBuilder WriteAttributes (int attr) redrawAttr = attr; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (attr & 0xffff)) { bg = MapColors ((ConsoleColor)(attr & 0xffff), false); } @@ -1994,8 +1994,8 @@ public override bool GetColors (int value, out Color foreground, out Color backg foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (value & 0xffff)) { hasColor = true; background = (Color)(ConsoleColor)(value & 0xffff); From 7298eeee961e468708e0c14401245278a9ff032c Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:07:50 -0700 Subject: [PATCH 21/36] fixed spaces->tabs --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 894c79f158..6e7bebfc22 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -225,7 +225,7 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) var keyChar = consoleKeyInfo.KeyChar; switch ((uint)keyChar) { case 0: - if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. + if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, @@ -613,12 +613,12 @@ void GetMouseEvent (ConsoleKeyInfo [] cki) case 68: case 72: case 80: - buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp + buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp break; case 69: case 73: case 81: - buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown + buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown break; } // Modifiers. From 9576a913a3ce1637507bb0c54e48a53b08eaea75 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:09:04 -0700 Subject: [PATCH 22/36] fixed spaces->tabs --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 31 ++------------------ 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 3d05c8989e..213e4941e9 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1,30 +1,6 @@ // // WindowsDriver.cs: Windows specific driver // -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// Nick Van Dyck (vandyck.nick@outlook.com) -// -// Copyright (c) 2018 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// using NStack; using System; using System.Collections.Generic; @@ -33,7 +9,6 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; namespace Terminal.Gui { @@ -640,7 +615,7 @@ public InputRecord [] ReadConsoleInput () } } -#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. +#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. [DllImport ("kernel32.dll", ExactSpelling = true)] static extern IntPtr GetConsoleWindow (); @@ -1801,8 +1776,8 @@ public override bool GetColors (int value, out Color foreground, out Color backg foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains ((value >> 4) & 0xffff)) { hasColor = true; background = (Color)(ConsoleColor)((value >> 4) & 0xffff); From f90a482ffe3c24f9180fdc56cafeeab161a12b58 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:09:14 -0700 Subject: [PATCH 23/36] fixed spaces->tabs --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 213e4941e9..8a51f5d8b9 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -615,7 +615,7 @@ public InputRecord [] ReadConsoleInput () } } -#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. +#if false // Not needed on the constructor. Perhaps could be used on resizing. To study. [DllImport ("kernel32.dll", ExactSpelling = true)] static extern IntPtr GetConsoleWindow (); From 636904f8558995be61ab4793f86695468aaacc27 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:10:25 -0700 Subject: [PATCH 24/36] fixed merge issue and spaces->tabs --- .../CursesDriver/CursesDriver.cs | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 20b41f5336..1553f53892 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -1,9 +1,6 @@ // // Driver.cs: Curses-based Driver // -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// using System; using System.Collections.Generic; using System.Diagnostics; @@ -342,9 +339,9 @@ MouseEvent ToDriverMouse (Curses.MouseEvent cev) cancelButtonClicked = true; } else if (buttonPressedCount == 3 - && (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed)) { + && (cev.ButtonState == Curses.Event.Button1Pressed + || cev.ButtonState == Curses.Event.Button2Pressed + || cev.ButtonState == Curses.Event.Button3Pressed)) { switch (cev.ButtonState) { case Curses.Event.Button1Pressed: @@ -362,8 +359,8 @@ MouseEvent ToDriverMouse (Curses.MouseEvent cev) buttonPressedCount = 0; } else if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked || - cev.ButtonState == Curses.Event.Button3Clicked) && - lastMouseButtonPressed == null) { + cev.ButtonState == Curses.Event.Button3Clicked) && + lastMouseButtonPressed == null) { isButtonPressed = false; mouseFlag = ProcessButtonClickedEvent (cev); @@ -923,12 +920,7 @@ public override void Init (Action terminalResized) public override void ResizeScreen () { Clip = new Rect (0, 0, Cols, Rows); -<<<<<<< Updated upstream Curses.refresh (); -======= - Console.Out.Write ("\x1b[3J"); - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 ->>>>>>> Stashed changes } public override void UpdateOffScreen () @@ -1060,19 +1052,11 @@ public override void Suspend () public override void StartReportingMouseMoves () { Console.Out.Write ("\x1b[?1003h"); -<<<<<<< Updated upstream -======= - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 ->>>>>>> Stashed changes } public override void StopReportingMouseMoves () { Console.Out.Write ("\x1b[?1003l"); -<<<<<<< Updated upstream -======= - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 ->>>>>>> Stashed changes } //int lastMouseInterval; @@ -1120,10 +1104,6 @@ public override bool SetCursorVisibility (CursorVisibility visibility) if (visibility != CursorVisibility.Invisible) { Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF); -<<<<<<< Updated upstream -======= - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 ->>>>>>> Stashed changes } currentCursorVisibility = visibility; @@ -1188,8 +1168,8 @@ public override bool GetColors (int value, out Color foreground, out Color backg background = default; int back = -1; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains ((value >> 12) & 0xffff)) { hasColor = true; back = (value >> 12) & 0xffff; From 0695904d902b1aa1c3a639ed3ed177f936c97318 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:11:41 -0700 Subject: [PATCH 25/36] fixed spaces->tabs --- .../ConsoleDrivers/FakeDriver/FakeConsole.cs | 1369 ++++++++--------- 1 file changed, 682 insertions(+), 687 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index e78baa96a4..017050ef09 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -1,15 +1,10 @@ // // FakeConsole.cs: A fake .NET Windows Console API implementation for unit tests. // -// Authors: -// Charlie Kindel (github.com/tig) -// using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Terminal.Gui { @@ -22,23 +17,23 @@ public static class FakeConsole { // // Summary: - // Gets or sets the width of the console window. + // Gets or sets the width of the console window. // // Returns: - // The width of the console window measured in columns. + // The width of the console window measured in columns. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight - // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight - // property plus the value of the System.Console.WindowTop property is greater than - // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth - // property or the value of the System.Console.WindowHeight property is greater - // than the largest possible window width or height for the current screen resolution - // and console font. + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. #pragma warning disable RCS1138 // Add summary to documentation comment. /// @@ -57,113 +52,113 @@ public static class FakeConsole { public static int WindowWidth { get; set; } = WIDTH; // // Summary: - // Gets a value that indicates whether output has been redirected from the standard - // output stream. + // Gets a value that indicates whether output has been redirected from the standard + // output stream. // // Returns: - // true if output is redirected; otherwise, false. + // true if output is redirected; otherwise, false. /// /// /// public static bool IsOutputRedirected { get; } // // Summary: - // Gets a value that indicates whether the error output stream has been redirected - // from the standard error stream. + // Gets a value that indicates whether the error output stream has been redirected + // from the standard error stream. // // Returns: - // true if error output is redirected; otherwise, false. + // true if error output is redirected; otherwise, false. /// /// /// public static bool IsErrorRedirected { get; } // // Summary: - // Gets the standard input stream. + // Gets the standard input stream. // // Returns: - // A System.IO.TextReader that represents the standard input stream. + // A System.IO.TextReader that represents the standard input stream. /// /// /// public static TextReader In { get; } // // Summary: - // Gets the standard output stream. + // Gets the standard output stream. // // Returns: - // A System.IO.TextWriter that represents the standard output stream. + // A System.IO.TextWriter that represents the standard output stream. /// /// /// public static TextWriter Out { get; } // // Summary: - // Gets the standard error output stream. + // Gets the standard error output stream. // // Returns: - // A System.IO.TextWriter that represents the standard error output stream. + // A System.IO.TextWriter that represents the standard error output stream. /// /// /// public static TextWriter Error { get; } // // Summary: - // Gets or sets the encoding the console uses to read input. + // Gets or sets the encoding the console uses to read input. // // Returns: - // The encoding used to read console input. + // The encoding used to read console input. // // Exceptions: - // T:System.ArgumentNullException: - // The property value in a set operation is null. + // T:System.ArgumentNullException: + // The property value in a set operation is null. // - // T:System.IO.IOException: - // An error occurred during the execution of this operation. + // T:System.IO.IOException: + // An error occurred during the execution of this operation. // - // T:System.Security.SecurityException: - // Your application does not have permission to perform this operation. + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. /// /// /// public static Encoding InputEncoding { get; set; } // // Summary: - // Gets or sets the encoding the console uses to write output. + // Gets or sets the encoding the console uses to write output. // // Returns: - // The encoding used to write console output. + // The encoding used to write console output. // // Exceptions: - // T:System.ArgumentNullException: - // The property value in a set operation is null. + // T:System.ArgumentNullException: + // The property value in a set operation is null. // - // T:System.IO.IOException: - // An error occurred during the execution of this operation. + // T:System.IO.IOException: + // An error occurred during the execution of this operation. // - // T:System.Security.SecurityException: - // Your application does not have permission to perform this operation. + // T:System.Security.SecurityException: + // Your application does not have permission to perform this operation. /// /// /// public static Encoding OutputEncoding { get; set; } // // Summary: - // Gets or sets the background color of the console. + // Gets or sets the background color of the console. // // Returns: - // A value that specifies the background color of the console; that is, the color - // that appears behind each character. The default is black. + // A value that specifies the background color of the console; that is, the color + // that appears behind each character. The default is black. // // Exceptions: - // T:System.ArgumentException: - // The color specified in a set operation is not a valid member of System.ConsoleColor. + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black; @@ -174,21 +169,21 @@ public static class FakeConsole { // // Summary: - // Gets or sets the foreground color of the console. + // Gets or sets the foreground color of the console. // // Returns: - // A System.ConsoleColor that specifies the foreground color of the console; that - // is, the color of each character that is displayed. The default is gray. + // A System.ConsoleColor that specifies the foreground color of the console; that + // is, the color of each character that is displayed. The default is gray. // // Exceptions: - // T:System.ArgumentException: - // The color specified in a set operation is not a valid member of System.ConsoleColor. + // T:System.ArgumentException: + // The color specified in a set operation is not a valid member of System.ConsoleColor. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray; @@ -198,299 +193,299 @@ public static class FakeConsole { public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor; // // Summary: - // Gets or sets the height of the buffer area. + // Gets or sets the height of the buffer area. // // Returns: - // The current height, in rows, of the buffer area. + // The current height, in rows, of the buffer area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than or equal to zero.-or- The value in - // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value - // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowTop + System.Console.WindowHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int BufferHeight { get; set; } = HEIGHT; // // Summary: - // Gets or sets the width of the buffer area. + // Gets or sets the width of the buffer area. // // Returns: - // The current width, in columns, of the buffer area. + // The current width, in columns, of the buffer area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than or equal to zero.-or- The value in - // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value - // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than or equal to zero.-or- The value in + // a set operation is greater than or equal to System.Int16.MaxValue.-or- The value + // in a set operation is less than System.Console.WindowLeft + System.Console.WindowWidth. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int BufferWidth { get; set; } = WIDTH; // // Summary: - // Gets or sets the height of the console window area. + // Gets or sets the height of the console window area. // // Returns: - // The height of the console window measured in rows. + // The height of the console window measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight - // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight - // property plus the value of the System.Console.WindowTop property is greater than - // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth - // property or the value of the System.Console.WindowHeight property is greater - // than the largest possible window width or height for the current screen resolution - // and console font. + // T:System.ArgumentOutOfRangeException: + // The value of the System.Console.WindowWidth property or the value of the System.Console.WindowHeight + // property is less than or equal to 0.-or-The value of the System.Console.WindowHeight + // property plus the value of the System.Console.WindowTop property is greater than + // or equal to System.Int16.MaxValue.-or-The value of the System.Console.WindowWidth + // property or the value of the System.Console.WindowHeight property is greater + // than the largest possible window width or height for the current screen resolution + // and console font. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. /// /// /// public static int WindowHeight { get; set; } = HEIGHT; // // Summary: - // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control - // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary - // input or as an interruption that is handled by the operating system. + // Gets or sets a value indicating whether the combination of the System.ConsoleModifiers.Control + // modifier key and System.ConsoleKey.C console key (Ctrl+C) is treated as ordinary + // input or as an interruption that is handled by the operating system. // // Returns: - // true if Ctrl+C is treated as ordinary input; otherwise, false. + // true if Ctrl+C is treated as ordinary input; otherwise, false. // // Exceptions: - // T:System.IO.IOException: - // Unable to get or set the input mode of the console input buffer. + // T:System.IO.IOException: + // Unable to get or set the input mode of the console input buffer. /// /// /// public static bool TreatControlCAsInput { get; set; } // // Summary: - // Gets the largest possible number of console window columns, based on the current - // font and screen resolution. + // Gets the largest possible number of console window columns, based on the current + // font and screen resolution. // // Returns: - // The width of the largest possible console window measured in columns. + // The width of the largest possible console window measured in columns. /// /// /// public static int LargestWindowWidth { get; } // // Summary: - // Gets the largest possible number of console window rows, based on the current - // font and screen resolution. + // Gets the largest possible number of console window rows, based on the current + // font and screen resolution. // // Returns: - // The height of the largest possible console window measured in rows. + // The height of the largest possible console window measured in rows. /// /// /// public static int LargestWindowHeight { get; } // // Summary: - // Gets or sets the leftmost position of the console window area relative to the - // screen buffer. + // Gets or sets the leftmost position of the console window area relative to the + // screen buffer. // // Returns: - // The leftmost console window position measured in columns. + // The leftmost console window position measured in columns. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // In a set operation, the value to be assigned is less than zero.-or-As a result - // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth - // would exceed System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowLeft plus System.Console.WindowWidth + // would exceed System.Console.BufferWidth. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. /// /// /// public static int WindowLeft { get; set; } // // Summary: - // Gets or sets the top position of the console window area relative to the screen - // buffer. + // Gets or sets the top position of the console window area relative to the screen + // buffer. // // Returns: - // The uppermost console window position measured in rows. + // The uppermost console window position measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // In a set operation, the value to be assigned is less than zero.-or-As a result - // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight - // would exceed System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // In a set operation, the value to be assigned is less than zero.-or-As a result + // of the assignment, System.Console.WindowTop plus System.Console.WindowHeight + // would exceed System.Console.BufferHeight. // - // T:System.IO.IOException: - // Error reading or writing information. + // T:System.IO.IOException: + // Error reading or writing information. /// /// /// public static int WindowTop { get; set; } // // Summary: - // Gets or sets the column position of the cursor within the buffer area. + // Gets or sets the column position of the cursor within the buffer area. // // Returns: - // The current position, in columns, of the cursor. + // The current position, in columns, of the cursor. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than zero.-or- The value in a set operation - // is greater than or equal to System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferWidth. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int CursorLeft { get; set; } // // Summary: - // Gets or sets the row position of the cursor within the buffer area. + // Gets or sets the row position of the cursor within the buffer area. // // Returns: - // The current position, in rows, of the cursor. + // The current position, in rows, of the cursor. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value in a set operation is less than zero.-or- The value in a set operation - // is greater than or equal to System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // The value in a set operation is less than zero.-or- The value in a set operation + // is greater than or equal to System.Console.BufferHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int CursorTop { get; set; } // // Summary: - // Gets or sets the height of the cursor within a character cell. + // Gets or sets the height of the cursor within a character cell. // // Returns: - // The size of the cursor expressed as a percentage of the height of a character - // cell. The property value ranges from 1 to 100. + // The size of the cursor expressed as a percentage of the height of a character + // cell. The property value ranges from 1 to 100. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // The value specified in a set operation is less than 1 or greater than 100. + // T:System.ArgumentOutOfRangeException: + // The value specified in a set operation is less than 1 or greater than 100. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static int CursorSize { get; set; } // // Summary: - // Gets or sets a value indicating whether the cursor is visible. + // Gets or sets a value indicating whether the cursor is visible. // // Returns: - // true if the cursor is visible; otherwise, false. + // true if the cursor is visible; otherwise, false. // // Exceptions: - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static bool CursorVisible { get; set; } // // Summary: - // Gets or sets the title to display in the console title bar. + // Gets or sets the title to display in the console title bar. // // Returns: - // The string to be displayed in the title bar of the console. The maximum length - // of the title string is 24500 characters. + // The string to be displayed in the title bar of the console. The maximum length + // of the title string is 24500 characters. // // Exceptions: - // T:System.InvalidOperationException: - // In a get operation, the retrieved title is longer than 24500 characters. + // T:System.InvalidOperationException: + // In a get operation, the retrieved title is longer than 24500 characters. // - // T:System.ArgumentOutOfRangeException: - // In a set operation, the specified title is longer than 24500 characters. + // T:System.ArgumentOutOfRangeException: + // In a set operation, the specified title is longer than 24500 characters. // - // T:System.ArgumentNullException: - // In a set operation, the specified title is null. + // T:System.ArgumentNullException: + // In a set operation, the specified title is null. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// public static string Title { get; set; } // // Summary: - // Gets a value indicating whether a key press is available in the input stream. + // Gets a value indicating whether a key press is available in the input stream. // // Returns: - // true if a key press is available; otherwise, false. + // true if a key press is available; otherwise, false. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.InvalidOperationException: - // Standard input is redirected to a file instead of the keyboard. + // T:System.InvalidOperationException: + // Standard input is redirected to a file instead of the keyboard. /// /// /// public static bool KeyAvailable { get; } // // Summary: - // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or - // turned off. + // Gets a value indicating whether the NUM LOCK keyboard toggle is turned on or + // turned off. // // Returns: - // true if NUM LOCK is turned on; false if NUM LOCK is turned off. + // true if NUM LOCK is turned on; false if NUM LOCK is turned off. /// /// /// public static bool NumberLock { get; } // // Summary: - // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or - // turned off. + // Gets a value indicating whether the CAPS LOCK keyboard toggle is turned on or + // turned off. // // Returns: - // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. + // true if CAPS LOCK is turned on; false if CAPS LOCK is turned off. /// /// /// public static bool CapsLock { get; } // // Summary: - // Gets a value that indicates whether input has been redirected from the standard - // input stream. + // Gets a value that indicates whether input has been redirected from the standard + // input stream. // // Returns: - // true if input is redirected; otherwise, false. + // true if input is redirected; otherwise, false. /// /// /// @@ -498,12 +493,12 @@ public static class FakeConsole { // // Summary: - // Plays the sound of a beep through the console speaker. + // Plays the sound of a beep through the console speaker. // // Exceptions: - // T:System.Security.HostProtectionException: - // This method was executed on a server, such as SQL Server, that does not permit - // access to a user interface. + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to a user interface. /// /// /// @@ -513,24 +508,24 @@ public static void Beep () } // // Summary: - // Plays the sound of a beep of a specified frequency and duration through the console - // speaker. + // Plays the sound of a beep of a specified frequency and duration through the console + // speaker. // // Parameters: - // frequency: - // The frequency of the beep, ranging from 37 to 32767 hertz. + // frequency: + // The frequency of the beep, ranging from 37 to 32767 hertz. // - // duration: - // The duration of the beep measured in milliseconds. + // duration: + // The duration of the beep measured in milliseconds. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // frequency is less than 37 or more than 32767 hertz.-or- duration is less than - // or equal to zero. + // T:System.ArgumentOutOfRangeException: + // frequency is less than 37 or more than 32767 hertz.-or- duration is less than + // or equal to zero. // - // T:System.Security.HostProtectionException: - // This method was executed on a server, such as SQL Server, that does not permit - // access to the console. + // T:System.Security.HostProtectionException: + // This method was executed on a server, such as SQL Server, that does not permit + // access to the console. /// /// /// @@ -540,11 +535,11 @@ public static void Beep (int frequency, int duration) } // // Summary: - // Clears the console buffer and corresponding console window of display information. + // Clears the console buffer and corresponding console window of display information. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. static char [,] _buffer = new char [WindowWidth, WindowHeight]; @@ -559,41 +554,41 @@ public static void Clear () // // Summary: - // Copies a specified source area of the screen buffer to a specified destination - // area. + // Copies a specified source area of the screen buffer to a specified destination + // area. // // Parameters: - // sourceLeft: - // The leftmost column of the source area. + // sourceLeft: + // The leftmost column of the source area. // - // sourceTop: - // The topmost row of the source area. + // sourceTop: + // The topmost row of the source area. // - // sourceWidth: - // The number of columns in the source area. + // sourceWidth: + // The number of columns in the source area. // - // sourceHeight: - // The number of rows in the source area. + // sourceHeight: + // The number of rows in the source area. // - // targetLeft: - // The leftmost column of the destination area. + // targetLeft: + // The leftmost column of the destination area. // - // targetTop: - // The topmost row of the destination area. + // targetTop: + // The topmost row of the destination area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft - // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop - // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight - // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth - // is greater than or equal to System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -604,54 +599,54 @@ public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidt // // Summary: - // Copies a specified source area of the screen buffer to a specified destination - // area. + // Copies a specified source area of the screen buffer to a specified destination + // area. // // Parameters: - // sourceLeft: - // The leftmost column of the source area. + // sourceLeft: + // The leftmost column of the source area. // - // sourceTop: - // The topmost row of the source area. + // sourceTop: + // The topmost row of the source area. // - // sourceWidth: - // The number of columns in the source area. + // sourceWidth: + // The number of columns in the source area. // - // sourceHeight: - // The number of rows in the source area. + // sourceHeight: + // The number of rows in the source area. // - // targetLeft: - // The leftmost column of the destination area. + // targetLeft: + // The leftmost column of the destination area. // - // targetTop: - // The topmost row of the destination area. + // targetTop: + // The topmost row of the destination area. // - // sourceChar: - // The character used to fill the source area. + // sourceChar: + // The character used to fill the source area. // - // sourceForeColor: - // The foreground color used to fill the source area. + // sourceForeColor: + // The foreground color used to fill the source area. // - // sourceBackColor: - // The background color used to fill the source area. + // sourceBackColor: + // The background color used to fill the source area. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft - // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop - // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight - // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth - // is greater than or equal to System.Console.BufferWidth. + // T:System.ArgumentOutOfRangeException: + // One or more of the parameters is less than zero.-or- sourceLeft or targetLeft + // is greater than or equal to System.Console.BufferWidth.-or- sourceTop or targetTop + // is greater than or equal to System.Console.BufferHeight.-or- sourceTop + sourceHeight + // is greater than or equal to System.Console.BufferHeight.-or- sourceLeft + sourceWidth + // is greater than or equal to System.Console.BufferWidth. // - // T:System.ArgumentException: - // One or both of the color parameters is not a member of the System.ConsoleColor - // enumeration. + // T:System.ArgumentException: + // One or both of the color parameters is not a member of the System.ConsoleColor + // enumeration. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -663,10 +658,10 @@ public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidt // // Summary: - // Acquires the standard error stream. + // Acquires the standard error stream. // // Returns: - // The standard error stream. + // The standard error stream. /// /// /// @@ -677,18 +672,18 @@ public static Stream OpenStandardError () // // Summary: - // Acquires the standard error stream, which is set to a specified buffer size. + // Acquires the standard error stream, which is set to a specified buffer size. // // Parameters: - // bufferSize: - // The internal stream buffer size. + // bufferSize: + // The internal stream buffer size. // // Returns: - // The standard error stream. + // The standard error stream. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. /// /// /// @@ -699,18 +694,18 @@ public static Stream OpenStandardError (int bufferSize) // // Summary: - // Acquires the standard input stream, which is set to a specified buffer size. + // Acquires the standard input stream, which is set to a specified buffer size. // // Parameters: - // bufferSize: - // The internal stream buffer size. + // bufferSize: + // The internal stream buffer size. // // Returns: - // The standard input stream. + // The standard input stream. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. /// /// /// @@ -721,10 +716,10 @@ public static Stream OpenStandardInput (int bufferSize) // // Summary: - // Acquires the standard input stream. + // Acquires the standard input stream. // // Returns: - // The standard input stream. + // The standard input stream. /// /// /// @@ -735,18 +730,18 @@ public static Stream OpenStandardInput () // // Summary: - // Acquires the standard output stream, which is set to a specified buffer size. + // Acquires the standard output stream, which is set to a specified buffer size. // // Parameters: - // bufferSize: - // The internal stream buffer size. + // bufferSize: + // The internal stream buffer size. // // Returns: - // The standard output stream. + // The standard output stream. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // bufferSize is less than or equal to zero. + // T:System.ArgumentOutOfRangeException: + // bufferSize is less than or equal to zero. /// /// /// @@ -757,10 +752,10 @@ public static Stream OpenStandardOutput (int bufferSize) // // Summary: - // Acquires the standard output stream. + // Acquires the standard output stream. // // Returns: - // The standard output stream. + // The standard output stream. /// /// /// @@ -771,15 +766,15 @@ public static Stream OpenStandardOutput () // // Summary: - // Reads the next character from the standard input stream. + // Reads the next character from the standard input stream. // // Returns: - // The next character from the input stream, or negative one (-1) if there are currently - // no more characters to be read. + // The next character from the input stream, or negative one (-1) if there are currently + // no more characters to be read. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -790,25 +785,25 @@ public static int Read () // // Summary: - // Obtains the next character or function key pressed by the user. The pressed key - // is optionally displayed in the console window. + // Obtains the next character or function key pressed by the user. The pressed key + // is optionally displayed in the console window. // // Parameters: - // intercept: - // Determines whether to display the pressed key in the console window. true to - // not display the pressed key; otherwise, false. + // intercept: + // Determines whether to display the pressed key in the console window. true to + // not display the pressed key; otherwise, false. // // Returns: - // An object that describes the System.ConsoleKey constant and Unicode character, - // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo - // object also describes, in a bitwise combination of System.ConsoleModifiers values, - // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously - // with the console key. + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. // // Exceptions: - // T:System.InvalidOperationException: - // The System.Console.In property is redirected from some stream other than the - // console. + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. //[SecuritySafeCritical] /// /// @@ -829,20 +824,20 @@ public static ConsoleKeyInfo ReadKey (bool intercept) // // Summary: - // Obtains the next character or function key pressed by the user. The pressed key - // is displayed in the console window. + // Obtains the next character or function key pressed by the user. The pressed key + // is displayed in the console window. // // Returns: - // An object that describes the System.ConsoleKey constant and Unicode character, - // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo - // object also describes, in a bitwise combination of System.ConsoleModifiers values, - // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously - // with the console key. + // An object that describes the System.ConsoleKey constant and Unicode character, + // if any, that correspond to the pressed console key. The System.ConsoleKeyInfo + // object also describes, in a bitwise combination of System.ConsoleModifiers values, + // whether one or more Shift, Alt, or Ctrl modifier keys was pressed simultaneously + // with the console key. // // Exceptions: - // T:System.InvalidOperationException: - // The System.Console.In property is redirected from some stream other than the - // console. + // T:System.InvalidOperationException: + // The System.Console.In property is redirected from some stream other than the + // console. /// /// /// @@ -853,21 +848,21 @@ public static ConsoleKeyInfo ReadKey () // // Summary: - // Reads the next line of characters from the standard input stream. + // Reads the next line of characters from the standard input stream. // // Returns: - // The next line of characters from the input stream, or null if no more lines are - // available. + // The next line of characters from the input stream, or null if no more lines are + // available. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.OutOfMemoryException: - // There is insufficient memory to allocate a buffer for the returned string. + // T:System.OutOfMemoryException: + // There is insufficient memory to allocate a buffer for the returned string. // - // T:System.ArgumentOutOfRangeException: - // The number of characters in the next line of characters is greater than System.Int32.MaxValue. + // T:System.ArgumentOutOfRangeException: + // The number of characters in the next line of characters is greater than System.Int32.MaxValue. /// /// /// @@ -878,14 +873,14 @@ public static string ReadLine () // // Summary: - // Sets the foreground and background console colors to their defaults. + // Sets the foreground and background console colors to their defaults. // // Exceptions: - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -898,27 +893,27 @@ public static void ResetColor () // // Summary: - // Sets the height and width of the screen buffer area to the specified values. + // Sets the height and width of the screen buffer area to the specified values. // // Parameters: - // width: - // The width of the buffer area measured in columns. + // width: + // The width of the buffer area measured in columns. // - // height: - // The height of the buffer area measured in rows. + // height: + // The height of the buffer area measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // height or width is less than or equal to zero.-or- height or width is greater - // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft - // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop - // + System.Console.WindowHeight. + // T:System.ArgumentOutOfRangeException: + // height or width is less than or equal to zero.-or- height or width is greater + // than or equal to System.Int16.MaxValue.-or- width is less than System.Console.WindowLeft + // + System.Console.WindowWidth.-or- height is less than System.Console.WindowTop + // + System.Console.WindowHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -932,27 +927,27 @@ public static void SetBufferSize (int width, int height) // // Summary: - // Sets the position of the cursor. + // Sets the position of the cursor. // // Parameters: - // left: - // The column position of the cursor. Columns are numbered from left to right starting - // at 0. + // left: + // The column position of the cursor. Columns are numbered from left to right starting + // at 0. // - // top: - // The row position of the cursor. Rows are numbered from top to bottom starting - // at 0. + // top: + // The row position of the cursor. Rows are numbered from top to bottom starting + // at 0. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- - // top is greater than or equal to System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left is greater than or equal to System.Console.BufferWidth.-or- + // top is greater than or equal to System.Console.BufferHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -967,19 +962,19 @@ public static void SetCursorPosition (int left, int top) // // Summary: - // Sets the System.Console.Error property to the specified System.IO.TextWriter - // object. + // Sets the System.Console.Error property to the specified System.IO.TextWriter + // object. // // Parameters: - // newError: - // A stream that is the new standard error output. + // newError: + // A stream that is the new standard error output. // // Exceptions: - // T:System.ArgumentNullException: - // newError is null. + // T:System.ArgumentNullException: + // newError is null. // - // T:System.Security.SecurityException: - // The caller does not have the required permission. + // T:System.Security.SecurityException: + // The caller does not have the required permission. //[SecuritySafeCritical] /// /// @@ -991,18 +986,18 @@ public static void SetError (TextWriter newError) // // Summary: - // Sets the System.Console.In property to the specified System.IO.TextReader object. + // Sets the System.Console.In property to the specified System.IO.TextReader object. // // Parameters: - // newIn: - // A stream that is the new standard input. + // newIn: + // A stream that is the new standard input. // // Exceptions: - // T:System.ArgumentNullException: - // newIn is null. + // T:System.ArgumentNullException: + // newIn is null. // - // T:System.Security.SecurityException: - // The caller does not have the required permission. + // T:System.Security.SecurityException: + // The caller does not have the required permission. //[SecuritySafeCritical] /// /// @@ -1014,18 +1009,18 @@ public static void SetIn (TextReader newIn) // // Summary: - // Sets the System.Console.Out property to the specified System.IO.TextWriter object. + // Sets the System.Console.Out property to the specified System.IO.TextWriter object. // // Parameters: - // newOut: - // A stream that is the new standard output. + // newOut: + // A stream that is the new standard output. // // Exceptions: - // T:System.ArgumentNullException: - // newOut is null. + // T:System.ArgumentNullException: + // newOut is null. // - // T:System.Security.SecurityException: - // The caller does not have the required permission. + // T:System.Security.SecurityException: + // The caller does not have the required permission. //[SecuritySafeCritical] /// /// @@ -1038,26 +1033,26 @@ public static void SetOut (TextWriter newOut) // // Summary: - // Sets the position of the console window relative to the screen buffer. + // Sets the position of the console window relative to the screen buffer. // // Parameters: - // left: - // The column position of the upper left corner of the console window. + // left: + // The column position of the upper left corner of the console window. // - // top: - // The row position of the upper left corner of the console window. + // top: + // The row position of the upper left corner of the console window. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // left or top is less than zero.-or- left + System.Console.WindowWidth is greater - // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater - // than System.Console.BufferHeight. + // T:System.ArgumentOutOfRangeException: + // left or top is less than zero.-or- left + System.Console.WindowWidth is greater + // than System.Console.BufferWidth.-or- top + System.Console.WindowHeight is greater + // than System.Console.BufferHeight. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -1072,27 +1067,27 @@ public static void SetWindowPosition (int left, int top) // // Summary: - // Sets the height and width of the console window to the specified values. + // Sets the height and width of the console window to the specified values. // // Parameters: - // width: - // The width of the console window measured in columns. + // width: + // The width of the console window measured in columns. // - // height: - // The height of the console window measured in rows. + // height: + // The height of the console window measured in rows. // // Exceptions: - // T:System.ArgumentOutOfRangeException: - // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft - // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. - // -or- width or height is greater than the largest possible window width or height - // for the current screen resolution and console font. + // T:System.ArgumentOutOfRangeException: + // width or height is less than or equal to zero.-or- width plus System.Console.WindowLeft + // or height plus System.Console.WindowTop is greater than or equal to System.Int16.MaxValue. + // -or- width or height is greater than the largest possible window width or height + // for the current screen resolution and console font. // - // T:System.Security.SecurityException: - // The user does not have permission to perform this action. + // T:System.Security.SecurityException: + // The user does not have permission to perform this action. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[SecuritySafeCritical] /// /// @@ -1107,15 +1102,15 @@ public static void SetWindowSize (int width, int height) // // Summary: - // Writes the specified string value to the standard output stream. + // Writes the specified string value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1127,16 +1122,16 @@ public static void Write (string value) // // Summary: - // Writes the text representation of the specified object to the standard output - // stream. + // Writes the text representation of the specified object to the standard output + // stream. // // Parameters: - // value: - // The value to write, or null. + // value: + // The value to write, or null. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1148,16 +1143,16 @@ public static void Write (object value) // // Summary: - // Writes the text representation of the specified 64-bit unsigned integer value - // to the standard output stream. + // Writes the text representation of the specified 64-bit unsigned integer value + // to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1170,16 +1165,16 @@ public static void Write (ulong value) // // Summary: - // Writes the text representation of the specified 64-bit signed integer value to - // the standard output stream. + // Writes the text representation of the specified 64-bit signed integer value to + // the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1191,28 +1186,28 @@ public static void Write (long value) // // Summary: - // Writes the text representation of the specified objects to the standard output - // stream using the specified format information. + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1226,16 +1221,16 @@ public static void Write (string format, object arg0, object arg1) // // Summary: - // Writes the text representation of the specified 32-bit signed integer value to - // the standard output stream. + // Writes the text representation of the specified 32-bit signed integer value to + // the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1247,25 +1242,25 @@ public static void Write (int value) // // Summary: - // Writes the text representation of the specified object to the standard output - // stream using the specified format information. + // Writes the text representation of the specified object to the standard output + // stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // An object to write using format. + // arg0: + // An object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1278,16 +1273,16 @@ public static void Write (string format, object arg0) // // Summary: - // Writes the text representation of the specified 32-bit unsigned integer value - // to the standard output stream. + // Writes the text representation of the specified 32-bit unsigned integer value + // to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1314,25 +1309,25 @@ public static void Write (string format, object arg0, object arg1, object arg2, // // Summary: - // Writes the text representation of the specified array of objects to the standard - // output stream using the specified format information. + // Writes the text representation of the specified array of objects to the standard + // output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg: - // An array of objects to write using format. + // arg: + // An array of objects to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format or arg is null. + // T:System.ArgumentNullException: + // format or arg is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1345,16 +1340,16 @@ public static void Write (string format, params object [] arg) // // Summary: - // Writes the text representation of the specified Boolean value to the standard - // output stream. + // Writes the text representation of the specified Boolean value to the standard + // output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1366,15 +1361,15 @@ public static void Write (bool value) // // Summary: - // Writes the specified Unicode character value to the standard output stream. + // Writes the specified Unicode character value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1386,15 +1381,15 @@ public static void Write (char value) // // Summary: - // Writes the specified array of Unicode characters to the standard output stream. + // Writes the specified array of Unicode characters to the standard output stream. // // Parameters: - // buffer: - // A Unicode character array. + // buffer: + // A Unicode character array. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1409,30 +1404,30 @@ public static void Write (char [] buffer) // // Summary: - // Writes the specified subarray of Unicode characters to the standard output stream. + // Writes the specified subarray of Unicode characters to the standard output stream. // // Parameters: - // buffer: - // An array of Unicode characters. + // buffer: + // An array of Unicode characters. // - // index: - // The starting position in buffer. + // index: + // The starting position in buffer. // - // count: - // The number of characters to write. + // count: + // The number of characters to write. // // Exceptions: - // T:System.ArgumentNullException: - // buffer is null. + // T:System.ArgumentNullException: + // buffer is null. // - // T:System.ArgumentOutOfRangeException: - // index or count is less than zero. + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. // - // T:System.ArgumentException: - // index plus count specify a position that is not within buffer. + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1446,31 +1441,31 @@ public static void Write (char [] buffer, int index, int count) // // Summary: - // Writes the text representation of the specified objects to the standard output - // stream using the specified format information. + // Writes the text representation of the specified objects to the standard output + // stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // - // arg2: - // The third object to write using format. + // arg2: + // The third object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1485,16 +1480,16 @@ public static void Write (string format, object arg0, object arg1, object arg2) // // Summary: - // Writes the text representation of the specified System.Decimal value to the standard - // output stream. + // Writes the text representation of the specified System.Decimal value to the standard + // output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1506,16 +1501,16 @@ public static void Write (decimal value) // // Summary: - // Writes the text representation of the specified single-precision floating-point - // value to the standard output stream. + // Writes the text representation of the specified single-precision floating-point + // value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1527,16 +1522,16 @@ public static void Write (float value) // // Summary: - // Writes the text representation of the specified double-precision floating-point - // value to the standard output stream. + // Writes the text representation of the specified double-precision floating-point + // value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1548,11 +1543,11 @@ public static void Write (double value) // // Summary: - // Writes the current line terminator to the standard output stream. + // Writes the current line terminator to the standard output stream. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1563,16 +1558,16 @@ public static void WriteLine () // // Summary: - // Writes the text representation of the specified single-precision floating-point - // value, followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified single-precision floating-point + // value, followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1584,16 +1579,16 @@ public static void WriteLine (float value) // // Summary: - // Writes the text representation of the specified 32-bit signed integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 32-bit signed integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1605,16 +1600,16 @@ public static void WriteLine (int value) // // Summary: - // Writes the text representation of the specified 32-bit unsigned integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 32-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1627,16 +1622,16 @@ public static void WriteLine (uint value) // // Summary: - // Writes the text representation of the specified 64-bit signed integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 64-bit signed integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1648,16 +1643,16 @@ public static void WriteLine (long value) // // Summary: - // Writes the text representation of the specified 64-bit unsigned integer value, - // followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified 64-bit unsigned integer value, + // followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. //[CLSCompliant (false)] /// /// @@ -1670,16 +1665,16 @@ public static void WriteLine (ulong value) // // Summary: - // Writes the text representation of the specified object, followed by the current - // line terminator, to the standard output stream. + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1691,16 +1686,16 @@ public static void WriteLine (object value) // // Summary: - // Writes the specified string value, followed by the current line terminator, to - // the standard output stream. + // Writes the specified string value, followed by the current line terminator, to + // the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1712,25 +1707,25 @@ public static void WriteLine (string value) // // Summary: - // Writes the text representation of the specified object, followed by the current - // line terminator, to the standard output stream using the specified format information. + // Writes the text representation of the specified object, followed by the current + // line terminator, to the standard output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // An object to write using format. + // arg0: + // An object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1743,31 +1738,31 @@ public static void WriteLine (string format, object arg0) // // Summary: - // Writes the text representation of the specified objects, followed by the current - // line terminator, to the standard output stream using the specified format information. + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // - // arg2: - // The third object to write using format. + // arg2: + // The third object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1796,26 +1791,26 @@ public static void WriteLine (string format, object arg0, object arg1, object ar // // Summary: - // Writes the text representation of the specified array of objects, followed by - // the current line terminator, to the standard output stream using the specified - // format information. + // Writes the text representation of the specified array of objects, followed by + // the current line terminator, to the standard output stream using the specified + // format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg: - // An array of objects to write using format. + // arg: + // An array of objects to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format or arg is null. + // T:System.ArgumentNullException: + // format or arg is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1828,31 +1823,31 @@ public static void WriteLine (string format, params object [] arg) // // Summary: - // Writes the specified subarray of Unicode characters, followed by the current - // line terminator, to the standard output stream. + // Writes the specified subarray of Unicode characters, followed by the current + // line terminator, to the standard output stream. // // Parameters: - // buffer: - // An array of Unicode characters. + // buffer: + // An array of Unicode characters. // - // index: - // The starting position in buffer. + // index: + // The starting position in buffer. // - // count: - // The number of characters to write. + // count: + // The number of characters to write. // // Exceptions: - // T:System.ArgumentNullException: - // buffer is null. + // T:System.ArgumentNullException: + // buffer is null. // - // T:System.ArgumentOutOfRangeException: - // index or count is less than zero. + // T:System.ArgumentOutOfRangeException: + // index or count is less than zero. // - // T:System.ArgumentException: - // index plus count specify a position that is not within buffer. + // T:System.ArgumentException: + // index plus count specify a position that is not within buffer. // - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1866,16 +1861,16 @@ public static void WriteLine (char [] buffer, int index, int count) // // Summary: - // Writes the text representation of the specified System.Decimal value, followed - // by the current line terminator, to the standard output stream. + // Writes the text representation of the specified System.Decimal value, followed + // by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1887,16 +1882,16 @@ public static void WriteLine (decimal value) // // Summary: - // Writes the specified array of Unicode characters, followed by the current line - // terminator, to the standard output stream. + // Writes the specified array of Unicode characters, followed by the current line + // terminator, to the standard output stream. // // Parameters: - // buffer: - // A Unicode character array. + // buffer: + // A Unicode character array. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1908,16 +1903,16 @@ public static void WriteLine (char [] buffer) // // Summary: - // Writes the specified Unicode character, followed by the current line terminator, - // value to the standard output stream. + // Writes the specified Unicode character, followed by the current line terminator, + // value to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1929,16 +1924,16 @@ public static void WriteLine (char value) // // Summary: - // Writes the text representation of the specified Boolean value, followed by the - // current line terminator, to the standard output stream. + // Writes the text representation of the specified Boolean value, followed by the + // current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// @@ -1950,28 +1945,28 @@ public static void WriteLine (bool value) // // Summary: - // Writes the text representation of the specified objects, followed by the current - // line terminator, to the standard output stream using the specified format information. + // Writes the text representation of the specified objects, followed by the current + // line terminator, to the standard output stream using the specified format information. // // Parameters: - // format: - // A composite format string (see Remarks). + // format: + // A composite format string (see Remarks). // - // arg0: - // The first object to write using format. + // arg0: + // The first object to write using format. // - // arg1: - // The second object to write using format. + // arg1: + // The second object to write using format. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. // - // T:System.ArgumentNullException: - // format is null. + // T:System.ArgumentNullException: + // format is null. // - // T:System.FormatException: - // The format specification in format is invalid. + // T:System.FormatException: + // The format specification in format is invalid. /// /// /// @@ -1985,16 +1980,16 @@ public static void WriteLine (string format, object arg0, object arg1) // // Summary: - // Writes the text representation of the specified double-precision floating-point - // value, followed by the current line terminator, to the standard output stream. + // Writes the text representation of the specified double-precision floating-point + // value, followed by the current line terminator, to the standard output stream. // // Parameters: - // value: - // The value to write. + // value: + // The value to write. // // Exceptions: - // T:System.IO.IOException: - // An I/O error occurred. + // T:System.IO.IOException: + // An I/O error occurred. /// /// /// From ce37be82c88a3a0f6e03405730f38cfe78de2f5b Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:12:12 -0700 Subject: [PATCH 26/36] fixed spaces->tabs --- Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index cb1d772a1b..2a006c5480 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -1,9 +1,6 @@ // // FakeDriver.cs: A fake ConsoleDriver for unit tests. // -// Authors: -// Charlie Kindel (github.com/tig) -// using System; using System.Collections.Generic; using System.Diagnostics; @@ -230,8 +227,8 @@ void SetColor (int color) { redrawColor = color; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (color & 0xffff)) { FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff); } @@ -620,8 +617,8 @@ public override bool GetColors (int value, out Color foreground, out Color backg foreground = default; background = default; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) - .OfType () - .Select (s => (int)s); + .OfType () + .Select (s => (int)s); if (values.Contains (value & 0xffff)) { hasColor = true; background = (Color)(ConsoleColor)(value & 0xffff); From 60524c7b50e0c2e9052b6182a9acc70aa14c1f2f Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:13:58 -0700 Subject: [PATCH 27/36] fixed spaces->tabs --- .../CursesDriver/UnixMainLoop.cs | 30 ++------------- .../CursesDriver/UnmanagedLibrary.cs | 37 +++++++------------ 2 files changed, 17 insertions(+), 50 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index 30ebd56070..15d7f7c1f4 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -1,30 +1,6 @@ // // mainloop.cs: Simple managed mainloop implementation. // -// Authors: -// Miguel de Icaza (miguel.de.icaza@gmail.com) -// -// Copyright (C) 2011 Novell (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -52,7 +28,7 @@ struct Pollfd { } /// - /// Condition on which to wake up from file descriptor activity. These match the Linux/BSD poll definitions. + /// Condition on which to wake up from file descriptor activity. These match the Linux/BSD poll definitions. /// [Flags] public enum Condition : short { @@ -127,10 +103,10 @@ void IMainLoopDriver.Setup (MainLoop mainLoop) } /// - /// Removes an active watch from the mainloop. + /// Removes an active watch from the mainloop. /// /// - /// The token parameter is the value returned from AddWatch + /// The token parameter is the value returned from AddWatch /// public void RemoveWatch (object token) { diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs index b1b60c3a56..a07a3fd895 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs @@ -6,7 +6,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,11 +17,7 @@ using System; using System.IO; -using System.Reflection; using System.Runtime.InteropServices; -using System.Threading; - - namespace Unix.Terminal { /// @@ -45,7 +41,7 @@ internal class UnmanagedLibrary { static bool IsNetCore; public static bool IsMacOSPlatform => IsMacOS; - + [DllImport ("libc")] static extern int uname (IntPtr buf); @@ -105,11 +101,11 @@ static UnmanagedLibrary () // public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath) { - if (isFullPath){ + if (isFullPath) { this.libraryPath = FirstValidLibraryPath (libraryPathAlternatives); this.handle = PlatformSpecificLoadLibrary (this.libraryPath); } else { - foreach (var lib in libraryPathAlternatives){ + foreach (var lib in libraryPathAlternatives) { this.handle = PlatformSpecificLoadLibrary (lib); if (this.handle != IntPtr.Zero) break; @@ -164,13 +160,13 @@ public IntPtr LoadSymbol (string symbolName) } public T GetNativeMethodDelegate (string methodName) - where T : class + where T : class { var ptr = LoadSymbol (methodName); if (ptr == IntPtr.Zero) { throw new MissingMethodException (string.Format ("The native method \"{0}\" does not exist", methodName)); } - return Marshal.GetDelegateForFunctionPointer(ptr); // non-generic version is obsolete + return Marshal.GetDelegateForFunctionPointer (ptr); // non-generic version is obsolete } /// @@ -209,12 +205,11 @@ static string FirstValidLibraryPath (string [] libraryPathAlternatives) } } throw new FileNotFoundException ( - String.Format ("Error loading native library. Not found in any of the possible locations: {0}", + String.Format ("Error loading native library. Not found in any of the possible locations: {0}", string.Join (",", libraryPathAlternatives))); } - static class Windows - { + static class Windows { [DllImport ("kernel32.dll")] internal static extern IntPtr LoadLibrary (string filename); @@ -222,8 +217,7 @@ static class Windows internal static extern IntPtr GetProcAddress (IntPtr hModule, string procName); } - static class Linux - { + static class Linux { [DllImport ("libdl.so")] internal static extern IntPtr dlopen (string filename, int flags); @@ -231,8 +225,7 @@ static class Linux internal static extern IntPtr dlsym (IntPtr handle, string symbol); } - static class MacOSX - { + static class MacOSX { [DllImport ("libSystem.dylib")] internal static extern IntPtr dlopen (string filename, int flags); @@ -247,8 +240,7 @@ static class MacOSX /// dlopen and dlsym from the current process as on Linux /// Mono sure is linked against these symbols. /// - static class Mono - { + static class Mono { [DllImport ("__Internal")] internal static extern IntPtr dlopen (string filename, int flags); @@ -261,13 +253,12 @@ static class Mono /// dlopen and dlsym from the "libcoreclr.so", /// to avoid the dependency on libc-dev Linux. /// - static class CoreCLR - { + static class CoreCLR { #if NET6_0 // Custom resolver to support true single-file apps // (those which run directly from bundle; in-memory). - // -1 on Unix means self-referencing binary (libcoreclr.so) - // 0 means fallback to CoreCLR's internal resolution + // -1 on Unix means self-referencing binary (libcoreclr.so) + // 0 means fallback to CoreCLR's internal resolution // Note: meaning of -1 stay the same even for non-single-file form factors. static CoreCLR() => NativeLibrary.SetDllImportResolver(typeof(CoreCLR).Assembly, (string libraryName, Assembly assembly, DllImportSearchPath? searchPath) => From 89fd87e436f6b0f9955b87cf4af6e056c7958374 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:15:00 -0700 Subject: [PATCH 28/36] fixed build error --- Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs index a07a3fd895..cde4337447 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs @@ -17,7 +17,9 @@ using System; using System.IO; +using System.Reflection; using System.Runtime.InteropServices; +using System.Threading; namespace Unix.Terminal { /// From c95ce324e09dbf5c09d971c62a6c030b1345ff50 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Mon, 23 Jan 2023 17:29:18 -0700 Subject: [PATCH 29/36] removed old comments --- Terminal.Gui/Core/ConsoleDriver.cs | 72 +----------------------------- 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 4b962d3c43..314f5fd857 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -1,10 +1,6 @@ // // ConsoleDriver.cs: Definition for the Console Driver API // -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// -// Define this to enable diagnostics drawing for Window Frames using NStack; using System; using System.Collections.Generic; @@ -12,7 +8,6 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using Unix.Terminal; namespace Terminal.Gui { /// @@ -561,72 +556,7 @@ public enum CursorVisibility { /// Works under Xterm-like terminal otherwise this is equivalent to BoxFix = 0x02020164, } - - ///// - ///// Special characters that can be drawn with - ///// - //public enum SpecialChar { - // /// - // /// Horizontal line character. - // /// - // HLine, - - // /// - // /// Vertical line character. - // /// - // VLine, - - // /// - // /// Stipple pattern - // /// - // Stipple, - - // /// - // /// Diamond character - // /// - // Diamond, - - // /// - // /// Upper left corner - // /// - // ULCorner, - - // /// - // /// Lower left corner - // /// - // LLCorner, - - // /// - // /// Upper right corner - // /// - // URCorner, - - // /// - // /// Lower right corner - // /// - // LRCorner, - - // /// - // /// Left tee - // /// - // LeftTee, - - // /// - // /// Right tee - // /// - // RightTee, - - // /// - // /// Top tee - // /// - // TopTee, - - // /// - // /// The bottom tee. - // /// - // BottomTee, - //} - + /// /// ConsoleDriver is an abstract class that defines the requirements for a console driver. /// There are currently three implementations: (for Unix and Mac), , and that uses the .NET Console API. From 436715f13fceb257ca3f22d960b952807ce4d349 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 01:50:35 +0000 Subject: [PATCH 30/36] Resolving merge conflicts. --- .devcontainer/devcontainer.json | 44 +- ReactiveExample/ReactiveExample.csproj | 6 +- .../CursesDriver/CursesDriver.cs | 503 ++----- .../ConsoleDrivers/CursesDriver/constants.cs | 4 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 28 +- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 1322 +++++------------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 54 +- Terminal.Gui/Core/Application.cs | 42 +- .../Core/Autocomplete/Autocomplete.cs | 67 +- .../Core/Autocomplete/IAutocomplete.cs | 3 +- Terminal.Gui/Core/ConsoleDriver.cs | 365 ++++- Terminal.Gui/Core/ConsoleKeyMapping.cs | 19 + Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs | 109 ++ Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs | 907 +++++++++++ Terminal.Gui/Core/Event.cs | 2 +- Terminal.Gui/Core/Graphs/LineCanvas.cs | 191 ++- Terminal.Gui/Core/TextFormatter.cs | 4 +- Terminal.Gui/Core/Toplevel.cs | 22 +- Terminal.Gui/Core/View.cs | 99 +- Terminal.Gui/Terminal.Gui.csproj | 8 +- Terminal.Gui/Views/TextField.cs | 10 +- Terminal.Gui/Views/TextView.cs | 38 +- UICatalog/Properties/launchSettings.json | 16 +- UICatalog/Scenarios/ASCIICustomButton.cs | 313 ++++ UICatalog/Scenarios/Animation.cs | 234 +++ UICatalog/Scenarios/BasicColors.cs | 5 +- UICatalog/Scenarios/LineDrawing.cs | 15 +- UICatalog/Scenarios/Snake.cs | 353 +++++ .../Scenarios/Spinning_globe_dark_small.gif | Bin 0 -> 22869 bytes .../Scenarios/spinning-globe-attribution.txt | 3 + UICatalog/UICatalog.cs | 4 +- UICatalog/UICatalog.csproj | 6 +- UnitTests/Application/ApplicationTests.cs | 2 +- UnitTests/Core/BorderTests.cs | 80 +- UnitTests/Core/EscSeqReqTests.cs | 78 + UnitTests/Core/EscSeqUtilsTests.cs | 870 +++++++++++ UnitTests/{ => Core}/LineCanvasTests.cs | 135 +- UnitTests/Drivers/AttributeTests.cs | 74 +- UnitTests/Drivers/ColorTests.cs | 38 + UnitTests/Drivers/ConsoleDriverTests.cs | 5 +- UnitTests/Drivers/KeyTests.cs | 1 + UnitTests/TopLevels/ToplevelTests.cs | 67 +- UnitTests/UICatalog/ScenarioTests.cs | 5 + UnitTests/UnitTests.csproj | 14 +- UnitTests/Views/AutocompleteTests.cs | 86 ++ UnitTests/Views/ScrollViewTests.cs | 228 ++- UnitTests/Views/TextFieldTests.cs | 119 +- UnitTests/Views/ViewTests.cs | 34 +- 48 files changed, 4961 insertions(+), 1671 deletions(-) create mode 100644 Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs create mode 100644 Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs create mode 100644 UICatalog/Scenarios/ASCIICustomButton.cs create mode 100644 UICatalog/Scenarios/Animation.cs create mode 100644 UICatalog/Scenarios/Snake.cs create mode 100644 UICatalog/Scenarios/Spinning_globe_dark_small.gif create mode 100644 UICatalog/Scenarios/spinning-globe-attribution.txt create mode 100644 UnitTests/Core/EscSeqReqTests.cs create mode 100644 UnitTests/Core/EscSeqUtilsTests.cs rename UnitTests/{ => Core}/LineCanvasTests.cs (61%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f854f34f1c..5ca55ca347 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,25 +1,29 @@ { "name": "Terminal.Gui Codespace", - "image": "mcr.microsoft.com/vscode/devcontainers/dotnet:6.0", - "settings": { - "terminal.integrated.defaultProfile.linux": "pwsh" - }, - "extensions": [ - "eamodio.gitlens", - "ms-dotnettools.csharp", - "VisualStudioExptTeam.vscodeintellicode", - "ms-vscode.powershell", - "cschleiden.vscode-github-actions", - "redhat.vscode-yaml", - "bierner.markdown-preview-github-styles", - "ban.spellright", - "jmrog.vscode-nuget-package-manager", - "coenraads.bracket-pair-colorizer", - "vscode-icons-team.vscode-icons", - "editorconfig.editorconfig", - "formulahendry.dotnet-test-explorer" - ], - "postCreateCommand": "dotnet restore && dotnet clean && dotnet build --configuration Release --no-restore && dotnet test --configuration Debug --no-restore --verbosity normal --collect:'XPlat Code Coverage' --settings UnitTests/coverlet.runsettings" + "image": "mcr.microsoft.com/vscode/devcontainers/dotnet:7.0", + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.defaultProfile.linux": "pwsh" + }, + "extensions": [ + "eamodio.gitlens", + "ms-dotnettools.csharp", + "VisualStudioExptTeam.vscodeintellicode", + "ms-vscode.powershell", + "cschleiden.vscode-github-actions", + "redhat.vscode-yaml", + "bierner.markdown-preview-github-styles", + "ban.spellright", + "jmrog.vscode-nuget-package-manager", + "coenraads.bracket-pair-colorizer", + "vscode-icons-team.vscode-icons", + "editorconfig.editorconfig", + "formulahendry.dotnet-test-explorer" + ], + "postCreateCommand": "dotnet restore && dotnet clean && dotnet build --configuration Release --no-restore && dotnet test --configuration Debug --no-restore --verbosity normal --collect:'XPlat Code Coverage' --settings UnitTests/coverlet.runsettings" + } + } } // Built with ❤ by [Pipeline Foundation](https://pipeline.foundation) diff --git a/ReactiveExample/ReactiveExample.csproj b/ReactiveExample/ReactiveExample.csproj index 0c174f748b..04793513df 100644 --- a/ReactiveExample/ReactiveExample.csproj +++ b/ReactiveExample/ReactiveExample.csproj @@ -3,7 +3,7 @@ Exe net6.0 - + 1.0 1.0 @@ -11,8 +11,8 @@ 1.0 - - + + diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 23a84aab69..418d11970a 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -76,14 +76,14 @@ public override void AddRune (Rune rune) var c = sn [0]; Curses.mvaddch (crow, ccol - 1, (int)(uint)c); contents [crow, ccol - 1, 0] = c; - contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 1] = CurrentAttribute; contents [crow, ccol - 1, 2] = 1; } else { if (runeWidth < 2 && ccol > 0 && Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) { - var curAtttib = currentAttribute; + var curAtttib = CurrentAttribute; Curses.attrset (contents [crow, ccol - 1, 1]); Curses.mvaddch (crow, ccol - 1, (int)(uint)' '); contents [crow, ccol - 1, 0] = (int)(uint)' '; @@ -93,7 +93,7 @@ public override void AddRune (Rune rune) } else if (runeWidth < 2 && ccol <= Clip.Right - 1 && Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) { - var curAtttib = currentAttribute; + var curAtttib = CurrentAttribute; Curses.attrset (contents [crow, ccol + 1, 1]); Curses.mvaddch (crow, ccol + 1, (int)(uint)' '); contents [crow, ccol + 1, 0] = (int)(uint)' '; @@ -108,25 +108,28 @@ public override void AddRune (Rune rune) Curses.addch ((int)(uint)rune); contents [crow, ccol, 0] = (int)(uint)rune; } - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 1; } - } else + } else { needMove = true; + } if (runeWidth < 0 || runeWidth > 0) { ccol++; } + if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 0; } ccol++; } - if (sync) + if (sync) { UpdateScreen (); + } } public override void AddStr (ustring str) @@ -157,10 +160,7 @@ private void ProcessWinChange () public override void End () { - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) { - StopReportingMouseMoves (); - } - + StopReportingMouseMoves (); SetCursorVisibility (CursorVisibility.Default); Curses.endwin (); @@ -168,12 +168,10 @@ public override void End () public override void UpdateScreen () => window.redrawwin (); - Attribute currentAttribute; - public override void SetAttribute (Attribute c) { - currentAttribute = c; - Curses.attrset (currentAttribute); + base.SetAttribute (c); + Curses.attrset (CurrentAttribute); } public Curses.Window window; @@ -209,6 +207,7 @@ public override Attribute MakeColor (Color fore, Color back) public override void SetColors (ConsoleColor foreground, ConsoleColor background) { + // BUGBUG: This code is never called ?? See Issue #2300 int f = (short)foreground; int b = (short)background; var v = colorPairs [f, b]; @@ -226,6 +225,7 @@ public override void SetColors (ConsoleColor foreground, ConsoleColor background Dictionary rawPairs = new Dictionary (); public override void SetColors (short foreColorId, short backgroundColorId) { + // BUGBUG: This code is never called ?? See Issue #2300 int key = ((ushort)foreColorId << 16) | (ushort)backgroundColorId; if (!rawPairs.TryGetValue (key, out var v)) { v = MakeColor (foreColorId, backgroundColorId); @@ -310,305 +310,6 @@ static Key MapCursesKey (int cursesKey) } } - Curses.Event? lastMouseButtonPressed; - bool isButtonPressed; - bool cancelButtonClicked; - bool isReportMousePosition; - Point point; - int buttonPressedCount; - - MouseEvent ToDriverMouse (Curses.MouseEvent cev) - { - MouseFlags mouseFlag = MouseFlags.AllEvents; - - if (lastMouseButtonPressed != null && cev.ButtonState != Curses.Event.ReportMousePosition) { - lastMouseButtonPressed = null; - isButtonPressed = false; - } - - if (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed) { - - isButtonPressed = true; - buttonPressedCount++; - } else { - buttonPressedCount = 0; - } - //System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}"); - - if (buttonPressedCount == 2 - && (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed)) { - - switch (cev.ButtonState) { - case Curses.Event.Button1Pressed: - mouseFlag = MouseFlags.Button1DoubleClicked; - break; - - case Curses.Event.Button2Pressed: - mouseFlag = MouseFlags.Button2DoubleClicked; - break; - - case Curses.Event.Button3Pressed: - mouseFlag = MouseFlags.Button3DoubleClicked; - break; - } - cancelButtonClicked = true; - - } else if (buttonPressedCount == 3 - && (cev.ButtonState == Curses.Event.Button1Pressed - || cev.ButtonState == Curses.Event.Button2Pressed - || cev.ButtonState == Curses.Event.Button3Pressed)) { - - switch (cev.ButtonState) { - case Curses.Event.Button1Pressed: - mouseFlag = MouseFlags.Button1TripleClicked; - break; - - case Curses.Event.Button2Pressed: - mouseFlag = MouseFlags.Button2TripleClicked; - break; - - case Curses.Event.Button3Pressed: - mouseFlag = MouseFlags.Button3TripleClicked; - break; - } - buttonPressedCount = 0; - - } else if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked || - cev.ButtonState == Curses.Event.Button3Clicked) && - lastMouseButtonPressed == null) { - - isButtonPressed = false; - mouseFlag = ProcessButtonClickedEvent (cev); - - } else if (((cev.ButtonState == Curses.Event.Button1Pressed || cev.ButtonState == Curses.Event.Button2Pressed || - cev.ButtonState == Curses.Event.Button3Pressed) && lastMouseButtonPressed == null) || - isButtonPressed && lastMouseButtonPressed != null && cev.ButtonState == Curses.Event.ReportMousePosition) { - - mouseFlag = MapCursesButton (cev.ButtonState); - if (cev.ButtonState != Curses.Event.ReportMousePosition) - lastMouseButtonPressed = cev.ButtonState; - isButtonPressed = true; - isReportMousePosition = false; - - if (cev.ButtonState == Curses.Event.ReportMousePosition) { - mouseFlag = MapCursesButton ((Curses.Event)lastMouseButtonPressed) | MouseFlags.ReportMousePosition; - cancelButtonClicked = true; - } - point = new Point () { - X = cev.X, - Y = cev.Y - }; - - if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag)); - return false; - }); - } - - - } else if ((cev.ButtonState == Curses.Event.Button1Released || cev.ButtonState == Curses.Event.Button2Released || - cev.ButtonState == Curses.Event.Button3Released)) { - - mouseFlag = ProcessButtonReleasedEvent (cev); - isButtonPressed = false; - - } else if (cev.ButtonState == Curses.Event.ButtonWheeledUp) { - - mouseFlag = MouseFlags.WheeledUp; - - } else if (cev.ButtonState == Curses.Event.ButtonWheeledDown) { - - mouseFlag = MouseFlags.WheeledDown; - - } else if ((cev.ButtonState & (Curses.Event.ButtonWheeledUp & Curses.Event.ButtonShift)) != 0) { - - mouseFlag = MouseFlags.WheeledLeft; - - } else if ((cev.ButtonState & (Curses.Event.ButtonWheeledDown & Curses.Event.ButtonShift)) != 0) { - - mouseFlag = MouseFlags.WheeledRight; - - } else if (cev.ButtonState == Curses.Event.ReportMousePosition) { - if (cev.X != point.X || cev.Y != point.Y) { - mouseFlag = MouseFlags.ReportMousePosition; - isReportMousePosition = true; - point = new Point (); - } else { - mouseFlag = 0; - } - - } else { - mouseFlag = 0; - var eFlags = cev.ButtonState; - foreach (Enum value in Enum.GetValues (eFlags.GetType ())) { - if (eFlags.HasFlag (value)) { - mouseFlag |= MapCursesButton ((Curses.Event)value); - } - } - } - - mouseFlag = SetControlKeyStates (cev, mouseFlag); - - return new MouseEvent () { - X = cev.X, - Y = cev.Y, - //Flags = MapCursesButton (cev.ButtonState) - Flags = mouseFlag - }; - } - - MouseFlags ProcessButtonClickedEvent (Curses.MouseEvent cev) - { - lastMouseButtonPressed = cev.ButtonState; - var mf = GetButtonState (cev, true); - mouseHandler (ProcessButtonState (cev, mf)); - if (lastMouseButtonPressed != null && lastMouseButtonPressed == cev.ButtonState) { - mf = GetButtonState (cev, false); - mouseHandler (ProcessButtonState (cev, mf)); - if (lastMouseButtonPressed != null && lastMouseButtonPressed == cev.ButtonState) { - mf = MapCursesButton (cev.ButtonState); - } - } - lastMouseButtonPressed = null; - isButtonPressed = false; - return mf; - } - - MouseFlags ProcessButtonReleasedEvent (Curses.MouseEvent cev) - { - var mf = MapCursesButton (cev.ButtonState); - if (!cancelButtonClicked && lastMouseButtonPressed == null && !isReportMousePosition) { - mouseHandler (ProcessButtonState (cev, mf)); - mf = GetButtonState (cev); - } else if (isReportMousePosition) { - mf = MouseFlags.ReportMousePosition; - } - cancelButtonClicked = false; - return mf; - } - - async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag) - { - while (isButtonPressed) { - await Task.Delay (100); - var me = new MouseEvent () { - X = point.X, - Y = point.Y, - Flags = mouseFlag - }; - - var view = Application.WantContinuousButtonPressedView; - if (view == null) - break; - if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { - Application.MainLoop.Invoke (() => mouseHandler (me)); - } - } - } - - MouseFlags GetButtonState (Curses.MouseEvent cev, bool pressed = false) - { - MouseFlags mf = default; - switch (cev.ButtonState) { - case Curses.Event.Button1Clicked: - if (pressed) - mf = MouseFlags.Button1Pressed; - else - mf = MouseFlags.Button1Released; - break; - - case Curses.Event.Button2Clicked: - if (pressed) - mf = MouseFlags.Button2Pressed; - else - mf = MouseFlags.Button2Released; - break; - - case Curses.Event.Button3Clicked: - if (pressed) - mf = MouseFlags.Button3Pressed; - else - mf = MouseFlags.Button3Released; - break; - - case Curses.Event.Button1Released: - mf = MouseFlags.Button1Clicked; - break; - - case Curses.Event.Button2Released: - mf = MouseFlags.Button2Clicked; - break; - - case Curses.Event.Button3Released: - mf = MouseFlags.Button3Clicked; - break; - - } - return mf; - } - - MouseEvent ProcessButtonState (Curses.MouseEvent cev, MouseFlags mf) - { - return new MouseEvent () { - X = cev.X, - Y = cev.Y, - Flags = mf - }; - } - - MouseFlags MapCursesButton (Curses.Event cursesButton) - { - switch (cursesButton) { - case Curses.Event.Button1Pressed: return MouseFlags.Button1Pressed; - case Curses.Event.Button1Released: return MouseFlags.Button1Released; - case Curses.Event.Button1Clicked: return MouseFlags.Button1Clicked; - case Curses.Event.Button1DoubleClicked: return MouseFlags.Button1DoubleClicked; - case Curses.Event.Button1TripleClicked: return MouseFlags.Button1TripleClicked; - case Curses.Event.Button2Pressed: return MouseFlags.Button2Pressed; - case Curses.Event.Button2Released: return MouseFlags.Button2Released; - case Curses.Event.Button2Clicked: return MouseFlags.Button2Clicked; - case Curses.Event.Button2DoubleClicked: return MouseFlags.Button2DoubleClicked; - case Curses.Event.Button2TrippleClicked: return MouseFlags.Button2TripleClicked; - case Curses.Event.Button3Pressed: return MouseFlags.Button3Pressed; - case Curses.Event.Button3Released: return MouseFlags.Button3Released; - case Curses.Event.Button3Clicked: return MouseFlags.Button3Clicked; - case Curses.Event.Button3DoubleClicked: return MouseFlags.Button3DoubleClicked; - case Curses.Event.Button3TripleClicked: return MouseFlags.Button3TripleClicked; - case Curses.Event.ButtonWheeledUp: return MouseFlags.WheeledUp; - case Curses.Event.ButtonWheeledDown: return MouseFlags.WheeledDown; - case Curses.Event.Button4Pressed: return MouseFlags.Button4Pressed; - case Curses.Event.Button4Released: return MouseFlags.Button4Released; - case Curses.Event.Button4Clicked: return MouseFlags.Button4Clicked; - case Curses.Event.Button4DoubleClicked: return MouseFlags.Button4DoubleClicked; - case Curses.Event.Button4TripleClicked: return MouseFlags.Button4TripleClicked; - case Curses.Event.ButtonShift: return MouseFlags.ButtonShift; - case Curses.Event.ButtonCtrl: return MouseFlags.ButtonCtrl; - case Curses.Event.ButtonAlt: return MouseFlags.ButtonAlt; - case Curses.Event.ReportMousePosition: return MouseFlags.ReportMousePosition; - case Curses.Event.AllEvents: return MouseFlags.AllEvents; - default: return 0; - } - } - - static MouseFlags SetControlKeyStates (Curses.MouseEvent cev, MouseFlags mouseFlag) - { - if ((cev.ButtonState & Curses.Event.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0) - mouseFlag |= MouseFlags.ButtonCtrl; - - if ((cev.ButtonState & Curses.Event.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0) - mouseFlag |= MouseFlags.ButtonShift; - - if ((cev.ButtonState & Curses.Event.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0) - mouseFlag |= MouseFlags.ButtonAlt; - return mouseFlag; - } - - KeyModifiers keyModifiers; KeyModifiers MapKeyModifiers (Key key) @@ -642,9 +343,18 @@ void ProcessInput () ProcessWinChange (); } if (wch == Curses.KeyMouse) { - Curses.getmouse (out Curses.MouseEvent ev); - //System.Diagnostics.Debug.WriteLine ($"ButtonState: {ev.ButtonState}; ID: {ev.ID}; X: {ev.X}; Y: {ev.Y}; Z: {ev.Z}"); - mouseHandler (ToDriverMouse (ev)); + int wch2 = wch; + + while (wch2 == Curses.KeyMouse) { + KeyEvent key = null; + ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false), + new ConsoleKeyInfo ('<', 0, false, false, false) + }; + code = 0; + GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + } return; } k = MapCursesKey (wch); @@ -680,7 +390,7 @@ void ProcessInput () k = Key.AltMask | MapCursesKey (wch); } if (code == 0) { - KeyEvent key; + KeyEvent key = null; // The ESC-number handling, debatable. // Simulates the AltMask itself by pressing Alt + Space. @@ -692,55 +402,13 @@ void ProcessInput () k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64)); } else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) { k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0)); - } else if (wch2 == 27) { - k = (Key)wch2; - } else if (wch2 == Curses.KEY_CODE_SEQ) { - int [] c = null; - while (code == 0) { - code = Curses.get_wch (out wch2); - if (wch2 > 0) { - Array.Resize (ref c, c == null ? 1 : c.Length + 1); - c [c.Length - 1] = wch2; - } - } - if (c [0] == 49 && c [1] == 59 && c [2] == 55 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Alt+(F1 - F4) - wch2 = c [3] + 185; - k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Alt+(F5 - F8) - wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215; - k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Alt+(F9 - F12) - wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; - k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Shift+Alt+(F1 - F4) - wch2 = c [3] + 185; - k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Shift+Alt+(F5 - F8) - wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215; - k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Shift+Alt+(F9 - F12) - wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; - k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [1] == 59 && c [2] == 52 && c [3] == 83) { // Shift+Alt+(F4) - wch2 = 268; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Shift+Alt+(F5 - F8) - wch2 = c [1] < 55 ? c [1] + 216 : c [1] + 215; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Shift+Alt+(F9 - F12) - wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; - k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 54 && c [1] == 59 && c [2] == 56 && c [3] == 126) { // Shift+Ctrl+Alt+KeyNPage - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageDown; - } else if (c [0] == 53 && c [1] == 59 && c [2] == 56 && c [3] == 126) { // Shift+Ctrl+Alt+KeyPPage - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageUp; - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 72) { // Shift+Ctrl+Alt+KeyHome - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.Home; - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 70) { // Shift+Ctrl+Alt+KeyEnd - k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.End; - } else { - k = MapCursesKey (wch2); - } + } else if (wch2 == Curses.KeyCSI) { + ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { + new ConsoleKeyInfo ((char)Key.Esc, 0, false, false, false), + new ConsoleKeyInfo ('[', 0, false, false, false) + }; + GetEscSeq (ref code, ref k, ref wch2, ref key, ref cki); + return; } else { // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. if (((Key)wch2 & Key.CtrlMask) != 0) { @@ -795,6 +463,52 @@ void ProcessInput () //} } + void GetEscSeq (ref int code, ref Key k, ref int wch2, ref KeyEvent key, ref ConsoleKeyInfo [] cki) + { + ConsoleKey ck = 0; + ConsoleModifiers mod = 0; + while (code == 0) { + code = Curses.get_wch (out wch2); + var consoleKeyInfo = new ConsoleKeyInfo ((char)wch2, 0, false, false, false); + if (wch2 == 0 || wch2 == 27 || wch2 == Curses.KeyMouse) { + EscSeqUtils.DecodeEscSeq (null, ref consoleKeyInfo, ref ck, cki, ref mod, out _, out _, out _, out _, out bool isKeyMouse, out List mouseFlags, out Point pos, out _, ProcessContinuousButtonPressed); + if (isKeyMouse) { + foreach (var mf in mouseFlags) { + ProcessMouseEvent (mf, pos); + } + cki = null; + if (wch2 == 27) { + cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), cki); + } + } else { + k = ConsoleKeyMapping.MapConsoleKeyToKey (consoleKeyInfo.Key, out _); + k = ConsoleKeyMapping.MapKeyModifiers (consoleKeyInfo, k); + key = new KeyEvent (k, MapKeyModifiers (k)); + keyDownHandler (key); + keyHandler (key); + } + } else { + cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + } + } + } + + void ProcessMouseEvent (MouseFlags mouseFlag, Point pos) + { + var me = new MouseEvent () { + Flags = mouseFlag, + X = pos.X, + Y = pos.Y + }; + mouseHandler (me); + } + + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) + { + ProcessMouseEvent (mouseFlag, pos); + } + Action keyHandler; Action keyDownHandler; Action keyUpHandler; @@ -821,14 +535,12 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle }; } - Curses.Event oldMouseEvents, reportableMouseEvents; public override void Init (Action terminalResized) { if (window != null) return; try { - //Set cursor key to application. window = Curses.initscr (); Curses.set_escdelay (10); } catch (Exception e) { @@ -875,39 +587,21 @@ public override void Init (Action terminalResized) Curses.noecho (); Curses.Window.Standard.keypad (true); - reportableMouseEvents = Curses.mousemask (Curses.Event.AllEvents | Curses.Event.ReportMousePosition, out oldMouseEvents); TerminalResized = terminalResized; - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) - StartReportingMouseMoves (); + StartReportingMouseMoves (); - ResizeScreen (); - UpdateOffScreen (); - - //HLine = Curses.ACS_HLINE; - //VLine = Curses.ACS_VLINE; - //Stipple = Curses.ACS_CKBOARD; - //Diamond = Curses.ACS_DIAMOND; - //ULCorner = Curses.ACS_ULCORNER; - //LLCorner = Curses.ACS_LLCORNER; - //URCorner = Curses.ACS_URCORNER; - //LRCorner = Curses.ACS_LRCORNER; - //LeftTee = Curses.ACS_LTEE; - //RightTee = Curses.ACS_RTEE; - //TopTee = Curses.ACS_TTEE; - //BottomTee = Curses.ACS_BTEE; - //RightArrow = Curses.ACS_RARROW; - //LeftArrow = Curses.ACS_LARROW; - //UpArrow = Curses.ACS_UARROW; - //DownArrow = Curses.ACS_DARROW; + CurrentAttribute = MakeColor (Color.White, Color.Black); if (Curses.HasColors) { Curses.StartColor (); Curses.UseDefaultColors (); - CreateColors (); + InitalizeColorSchemes (); } else { - CreateColors (false); + InitalizeColorSchemes (false); + // BUGBUG: This is a hack to make the colors work on the Mac? + // The new Theme support overwrites these colors, so this is not needed? Colors.TopLevel.Normal = Curses.COLOR_GREEN; Colors.TopLevel.Focus = Curses.COLOR_WHITE; Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW; @@ -934,6 +628,10 @@ public override void Init (Action terminalResized) Colors.Error.HotFocus = Curses.A_REVERSE; Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY; } + + ResizeScreen (); + UpdateOffScreen (); + } public override void ResizeScreen () @@ -1059,23 +757,23 @@ public override Attribute MakeAttribute (Color fore, Color back) public override void Suspend () { - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) - StopReportingMouseMoves (); + StopReportingMouseMoves (); Platform.Suspend (); Curses.Window.Standard.redrawwin (); Curses.refresh (); - if (reportableMouseEvents.HasFlag (Curses.Event.ReportMousePosition)) - StartReportingMouseMoves (); + StartReportingMouseMoves (); } public override void StartReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003h"); + Console.Out.Write (EscSeqUtils.EnableMouseEvents); + Console.Out.Flush (); } public override void StopReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003l"); + Console.Out.Write (EscSeqUtils.DisableMouseEvents); + Console.Out.Flush (); } //int lastMouseInterval; @@ -1095,11 +793,6 @@ public override void CookMouse () //Curses.mouseinterval (lastMouseInterval); } - public override Attribute GetAttribute () - { - return currentAttribute; - } - /// public override bool GetCursorVisibility (out CursorVisibility visibility) { @@ -1281,6 +974,7 @@ public CursesClipboard () bool CheckSupport () { +#pragma warning disable RCS1075 // Avoid empty catch clause that catches System.Exception. try { var (exitCode, result) = ClipboardProcessRunner.Bash ("which xclip", waitForOutput: true); if (exitCode == 0 && result.FileExists ()) { @@ -1290,6 +984,7 @@ bool CheckSupport () } catch (Exception) { // Permissions issue. } +#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception. return false; } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs index b9a63834a6..fb9bc326bb 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs @@ -53,7 +53,6 @@ public partial class Curses { public const int COLOR_WHITE = unchecked((int)0x7); public const int COLOR_GRAY = unchecked((int)0x8); public const int KEY_CODE_YES = unchecked((int)0x100); - public const int KEY_CODE_SEQ = unchecked((int)0x5b); public const int ERR = unchecked((int)0xffffffff); public const int TIOCGWINSZ = unchecked((int)0x5413); public const int TIOCGWINSZ_MAC = unchecked((int)0x40087468); @@ -69,7 +68,7 @@ public enum Event : long { Button2Released = unchecked((int)0x20), Button2Clicked = unchecked((int)0x80), Button2DoubleClicked = unchecked((int)0x100), - Button2TrippleClicked = unchecked((int)0x200), + Button2TripleClicked = unchecked((int)0x200), Button3Pressed = unchecked((int)0x800), Button3Released = unchecked((int)0x400), Button3Clicked = unchecked((int)0x1000), @@ -106,6 +105,7 @@ public enum Event : long { public const int KeyPPage = unchecked((int)0x153); public const int KeyHome = unchecked((int)0x106); public const int KeyMouse = unchecked((int)0x199); + public const int KeyCSI = unchecked((int)0x5b); public const int KeyEnd = unchecked((int)0x168); public const int KeyDeleteChar = unchecked((int)0x14a); public const int KeyInsertChar = unchecked((int)0x14b); diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index f49132c265..824a638292 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -144,7 +144,7 @@ public override void AddRune (Rune rune) } var c = sn [0]; contents [crow, ccol - 1, 0] = c; - contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 1] = CurrentAttribute; contents [crow, ccol - 1, 2] = 1; } else { @@ -165,20 +165,22 @@ public override void AddRune (Rune rune) } else { contents [crow, ccol, 0] = (int)(uint)rune; } - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 1; dirtyLine [crow] = true; } - } else + } else { needMove = true; + } if (runeWidth < 0 || runeWidth > 0) { ccol++; } + if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 0; } ccol++; @@ -189,8 +191,9 @@ public override void AddRune (Rune rune) // if (crow + 1 < Rows) // crow++; //} - if (sync) + if (sync) { UpdateScreen (); + } } public override void AddStr (ustring str) @@ -228,11 +231,10 @@ public override void Init (Action terminalResized) rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT; FakeConsole.Clear (); ResizeScreen (); + // Call InitalizeColorSchemes before UpdateOffScreen as it references Colors + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitalizeColorSchemes (); UpdateOffScreen (); - - CreateColors (); - - //MockConsole.Clear (); } public override Attribute MakeAttribute (Color fore, Color back) @@ -303,10 +305,9 @@ public override void Refresh () UpdateCursor (); } - Attribute currentAttribute; public override void SetAttribute (Attribute c) { - currentAttribute = c; + base.SetAttribute (c); } public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) @@ -495,11 +496,6 @@ void ProcessInput (ConsoleKeyInfo consoleKey) keyUpHandler (new KeyEvent (map, keyModifiers)); } - public override Attribute GetAttribute () - { - return currentAttribute; - } - /// public override bool GetCursorVisibility (out CursorVisibility visibility) { diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 81546197e1..c6eaf503fd 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1,5 +1,4 @@ -//#define PROCESS_REQUEST -// +// // NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient. // // Authors: @@ -7,6 +6,7 @@ // using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading; @@ -111,27 +111,37 @@ internal class NetEvents { ManualResetEventSlim winChange = new ManualResetEventSlim (false); Queue inputResultQueue = new Queue (); ConsoleDriver consoleDriver; + volatile ConsoleKeyInfo [] cki = null; + static volatile bool isEscSeq; int lastWindowHeight; - int largestWindowHeight; + bool stopTasks; #if PROCESS_REQUEST - bool neededProcessRequest; + bool neededProcessRequest; #endif - public int NumberOfCSI { get; } + public bool IsTerminalWithOptions { get; set; } + public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc (); - public NetEvents (ConsoleDriver consoleDriver, int numberOfCSI = 1) + public NetEvents (ConsoleDriver consoleDriver) { if (consoleDriver == null) { throw new ArgumentNullException ("Console driver instance must be provided."); } this.consoleDriver = consoleDriver; - NumberOfCSI = numberOfCSI; Task.Run (ProcessInputResultQueue); Task.Run (CheckWinChange); } + internal void StopTasks () + { + stopTasks = true; + } + public InputResult? ReadConsoleInput () { while (true) { + if (stopTasks) { + return null; + } waitForStart.Set (); winChange.Set (); @@ -140,7 +150,7 @@ public NetEvents (ConsoleDriver consoleDriver, int numberOfCSI = 1) inputReady.Reset (); } #if PROCESS_REQUEST - neededProcessRequest = false; + neededProcessRequest = false; #endif if (inputResultQueue.Count > 0) { return inputResultQueue.Dequeue (); @@ -155,16 +165,53 @@ void ProcessInputResultQueue () waitForStart.Reset (); if (inputResultQueue.Count == 0) { - GetConsoleInputType (Console.ReadKey (true)); + GetConsoleKey (); } inputReady.Set (); } } + void GetConsoleKey () + { + ConsoleKey key = 0; + ConsoleModifiers mod = 0; + ConsoleKeyInfo newConsoleKeyInfo = default; + + while (true) { + ConsoleKeyInfo consoleKeyInfo = Console.ReadKey (true); + if ((consoleKeyInfo.KeyChar == (char)Key.Esc && !isEscSeq) + || (consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq)) { + if (cki == null && consoleKeyInfo.KeyChar != (char)Key.Esc && isEscSeq) { + cki = EscSeqUtils.ResizeArray (new ConsoleKeyInfo ((char)Key.Esc, 0, + false, false, false), cki); + } + isEscSeq = true; + newConsoleKeyInfo = consoleKeyInfo; + cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki); + if (!Console.KeyAvailable) { + DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod); + cki = null; + isEscSeq = false; + break; + } + } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq) { + DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod); + cki = null; + break; + } else { + GetConsoleInputType (consoleKeyInfo); + break; + } + } + } + void CheckWinChange () { while (true) { + if (stopTasks) { + return; + } winChange.Wait (); winChange.Reset (); WaitWinChange (); @@ -177,29 +224,56 @@ void WaitWinChange () while (true) { // HACK: Sleep for 10ms to mitigate high CPU usage (see issue #1502). 10ms was tested to address the problem, but may not be correct. Thread.Sleep (10); - if (!consoleDriver.EnableConsoleScrolling) { - if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) { - var w = Math.Max (Console.WindowWidth, 0); - var h = Math.Max (Console.WindowHeight, 0); - GetWindowSizeEvent (new Size (w, h)); - return; + if (stopTasks) { + return; + } + switch (IsTerminalWithOptions) { + case false: + int buffHeight, buffWidth; + if (((NetDriver)consoleDriver).IsWinPlatform) { + buffHeight = Math.Max (Console.BufferHeight, 0); + buffWidth = Math.Max (Console.BufferWidth, 0); + } else { + buffHeight = consoleDriver.Rows; + buffWidth = consoleDriver.Cols; } - } else { - largestWindowHeight = Math.Max (Console.BufferHeight, largestWindowHeight); - if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows - || Console.WindowHeight != lastWindowHeight) { - lastWindowHeight = Math.Max (Console.WindowHeight, 0); - GetWindowSizeEvent (new Size (Console.BufferWidth, largestWindowHeight)); + if (IsWinChanged ( + Math.Max (Console.WindowHeight, 0), + Math.Max (Console.WindowWidth, 0), + buffHeight, + buffWidth)) { + return; } -#if PROCESS_REQUEST - if (!neededProcessRequest) { - Console.Out.Write ("\x1b[6n"); - neededProcessRequest = true; - } -#endif + break; + case true: + //Request the size of the text area in characters. + EscSeqReqProc.Add ("t"); + Console.Out.Write ("\x1b[18t"); + break; + } + } + } + + bool IsWinChanged (int winHeight, int winWidth, int buffHeight, int buffWidth) + { + if (!consoleDriver.EnableConsoleScrolling) { + if (winWidth != consoleDriver.Cols || winHeight != consoleDriver.Rows) { + var w = Math.Max (winWidth, 0); + var h = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (w, h)); + return true; + } + } else { + if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight + || buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) { + + lastWindowHeight = Math.Max (winHeight, 0); + GetWindowSizeEvent (new Size (winWidth, lastWindowHeight)); + return true; } } + return false; } void GetWindowSizeEvent (Size size) @@ -219,65 +293,8 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) InputResult inputResult = new InputResult { EventType = EventType.Key }; - ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; - ConsoleKey key = 0; MouseEvent mouseEvent = new MouseEvent (); - var keyChar = consoleKeyInfo.KeyChar; - switch ((uint)keyChar) { - case 0: - if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. - newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - } - break; - case uint n when (n >= '\u0001' && n <= '\u001a'): - if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { - key = ConsoleKey.Enter; - newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - } else if (consoleKeyInfo.Key == 0) { - key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); - newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - key, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - true); - } - break; - case 27: - //case 91: - ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { consoleKeyInfo }; - ConsoleModifiers mod = consoleKeyInfo.Modifiers; - int delay = 0; - while (delay < 100) { - if (Console.KeyAvailable) { - do { - var result = Console.ReadKey (true); - Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1); - cki [cki.Length - 1] = result; - } while (Console.KeyAvailable); - break; - } - Thread.Sleep (50); - delay += 50; - } - SplitCSI (cki, ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, ref mod); - return; - case 127: - newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, - (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); - break; - default: - newConsoleKeyInfo = consoleKeyInfo; - break; - } + ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo); if (inputResult.EventType == EventType.Key) { inputResult.ConsoleKeyInfo = newConsoleKeyInfo; } else { @@ -287,808 +304,234 @@ void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo) inputResultQueue.Enqueue (inputResult); } - void SplitCSI (ConsoleKeyInfo [] cki, ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ref MouseEvent mouseEvent, ref ConsoleModifiers mod) - { - ConsoleKeyInfo [] splitedCki = new ConsoleKeyInfo [] { }; - int length = 0; - var kChar = GetKeyCharArray (cki); - var nCSI = GetNumberOfCSI (kChar); - int curCSI = 0; - char previousKChar = '\0'; - if (nCSI > 1) { - for (int i = 0; i < cki.Length; i++) { - var ck = cki [i]; - if (NumberOfCSI > 0 && nCSI - curCSI > NumberOfCSI) { - if (i + 1 < cki.Length && cki [i + 1].KeyChar == '\x1b' && previousKChar != '\0') { - curCSI++; - previousKChar = '\0'; - } else { - previousKChar = ck.KeyChar; - } - continue; - } - if (ck.KeyChar == '\x1b') { - if (ck.KeyChar == 'R') { - ResizeArray (ck); - } - if (splitedCki.Length > 1) { - DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, splitedCki, ref mod); - } - splitedCki = new ConsoleKeyInfo [] { }; - length = 0; - } - ResizeArray (ck); - if (i == cki.Length - 1 && splitedCki.Length > 0) { - DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, splitedCki, ref mod); - } - } - } else { - DecodeCSI (ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, cki, ref mod); - } - - void ResizeArray (ConsoleKeyInfo ck) - { - length++; - Array.Resize (ref splitedCki, length); - splitedCki [length - 1] = ck; - } - } - - char [] GetKeyCharArray (ConsoleKeyInfo [] cki) + void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) { - char [] kChar = new char [] { }; - var length = 0; - foreach (var kc in cki) { - length++; - Array.Resize (ref kChar, length); - kChar [length - 1] = kc.KeyChar; - } - - return kChar; - } - - int GetNumberOfCSI (char [] csi) - { - int nCSI = 0; - for (int i = 0; i < csi.Length; i++) { - if (csi [i] == '\x1b' || (csi [i] == '[' && (i == 0 || (i > 0 && csi [i - 1] != '\x1b')))) { - nCSI++; - } - } + string c1Control, code, terminating; + string [] values; + // isKeyMouse is true if it's CSI<, false otherwise + bool isKeyMouse; + bool isReq; + List mouseFlags; + Point pos; + EscSeqUtils.DecodeEscSeq (EscSeqReqProc, ref newConsoleKeyInfo, ref key, cki, ref mod, out c1Control, out code, out values, out terminating, out isKeyMouse, out mouseFlags, out pos, out isReq, ProcessContinuousButtonPressed); - return nCSI; - } - - void DecodeCSI (ref InputResult inputResult, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ref MouseEvent mouseEvent, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod) - { - switch (cki.Length) { - case 2: - if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { - key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); - newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, - key, - false, - true, - true); - } else { - if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { - key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; - } else { - key = (ConsoleKey)cki [1].KeyChar; - } - newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, - (ConsoleKey)Math.Min ((uint)key, 255), - false, - true, - false); - } - break; - case 3: - if (cki [1].KeyChar == '[' || cki [1].KeyChar == 79) { - key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case 4: - if (cki [1].KeyChar == '[' && cki [3].KeyChar == 126) { - key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); + if (isKeyMouse) { + foreach (var mf in mouseFlags) { + GetMouseEvent (MapMouseFlags (mf), pos); } - break; - case 5: - if (cki [1].KeyChar == '[' && (cki [2].KeyChar == 49 || cki [2].KeyChar == 50) - && cki [4].KeyChar == 126) { - key = GetConsoleKey (cki [3].KeyChar, ref mod, cki.Length); - } else if (cki [1].KeyChar == 49 && cki [2].KeyChar == ';') { // For WSL - mod |= GetConsoleModifiers (cki [3].KeyChar); - key = ConsoleKey.End; - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case 6: - if (cki [1].KeyChar == '[' && cki [2].KeyChar == 49 && cki [3].KeyChar == ';') { - mod |= GetConsoleModifiers (cki [4].KeyChar); - key = GetConsoleKey (cki [5].KeyChar, ref mod, cki.Length); - } else if (cki [1].KeyChar == '[' && cki [3].KeyChar == ';') { - mod |= GetConsoleModifiers (cki [4].KeyChar); - key = GetConsoleKey (cki [2].KeyChar, ref mod, cki.Length); - } - newConsoleKeyInfo = new ConsoleKeyInfo ('\0', - key, - (mod & ConsoleModifiers.Shift) != 0, - (mod & ConsoleModifiers.Alt) != 0, - (mod & ConsoleModifiers.Control) != 0); - break; - case 7: - GetRequestEvent (GetKeyCharArray (cki)); return; - case int n when n >= 8: - GetMouseEvent (cki); + } else if (isReq) { + GetRequestEvent (c1Control, code, values, terminating); return; } - if (inputResult.EventType == EventType.Key) { - inputResult.ConsoleKeyInfo = newConsoleKeyInfo; - } else { - inputResult.MouseEvent = mouseEvent; - } + InputResult inputResult = new InputResult { + EventType = EventType.Key, + ConsoleKeyInfo = newConsoleKeyInfo + }; inputResultQueue.Enqueue (inputResult); } - Point lastCursorPosition; - - void GetRequestEvent (char [] kChar) + void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos) { - EventType eventType = new EventType (); - Point point = new Point (); - int foundPoint = 0; - string value = ""; - for (int i = 0; i < kChar.Length; i++) { - var c = kChar [i]; - if (c == '\u001b' || c == '[') { - foundPoint++; - } else if (foundPoint == 1 && c != ';' && c != '?') { - value += c.ToString (); - } else if (c == '?') { - foundPoint++; - } else if (c == ';') { - if (foundPoint >= 1) { - point.Y = int.Parse (value) - 1; - } - value = ""; - foundPoint++; - } else if (foundPoint > 0 && i < kChar.Length - 1) { - value += c.ToString (); - } else if (i == kChar.Length - 1) { - point.X = int.Parse (value) + Console.WindowTop - 1; - - switch (c) { - case 'R': - if (lastCursorPosition.Y != point.Y) { - lastCursorPosition = point; - eventType = EventType.WindowPosition; - var winPositionEv = new WindowPositionEvent () { - CursorPosition = point - }; - inputResultQueue.Enqueue (new InputResult () { - EventType = eventType, - WindowPositionEvent = winPositionEv - }); - } else { - return; - } - break; - case 'c': // CSI?1;0c ("VT101 with No Options") - break; - default: - throw new NotImplementedException (); - } - } - } - - inputReady.Set (); + GetMouseEvent (MapMouseFlags (mouseFlag), pos); } - MouseEvent lastMouseEvent; - bool isButtonPressed; - bool isButtonClicked; - bool isButtonDoubleClicked; - bool isButtonTripleClicked; - bool isProcContBtnPressedRuning; - int buttonPressedCount; - //bool isButtonReleased; - - void GetMouseEvent (ConsoleKeyInfo [] cki) + MouseButtonState MapMouseFlags (MouseFlags mouseFlags) { - MouseEvent mouseEvent = new MouseEvent (); - MouseButtonState buttonState = 0; - Point point = new Point (); - int buttonCode = 0; - bool foundButtonCode = false; - int foundPoint = 0; - string value = ""; - var kChar = GetKeyCharArray (cki); - //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); - for (int i = 0; i < kChar.Length; i++) { - var c = kChar [i]; - if (c == '<') { - foundButtonCode = true; - } else if (foundButtonCode && c != ';') { - value += c.ToString (); - } else if (c == ';') { - if (foundButtonCode) { - foundButtonCode = false; - buttonCode = int.Parse (value); - } - if (foundPoint == 1) { - point.X = int.Parse (value) - 1; - } - value = ""; - foundPoint++; - } else if (foundPoint > 0 && c != 'm' && c != 'M') { - value += c.ToString (); - } else if (c == 'm' || c == 'M') { - point.Y = int.Parse (value) + Console.WindowTop - 1; - - //if (c == 'M') { - // isButtonPressed = true; - //} else if (c == 'm') { - // isButtonPressed = false; - //} - - //System.Diagnostics.Debug.WriteLine ($"buttonCode: {buttonCode}"); - - switch (buttonCode) { - case 0: - case 8: - case 16: - case 24: - case 32: - case 36: - case 40: - case 48: - case 56: - buttonState = c == 'M' ? MouseButtonState.Button1Pressed - : MouseButtonState.Button1Released; + MouseButtonState mbs = default; + foreach (var flag in Enum.GetValues (mouseFlags.GetType ())) { + if (mouseFlags.HasFlag ((MouseFlags)flag)) { + switch (flag) { + case MouseFlags.Button1Pressed: + mbs |= MouseButtonState.Button1Pressed; break; - case 1: - case 9: - case 17: - case 25: - case 33: - case 37: - case 41: - case 45: - case 49: - case 53: - case 57: - case 61: - buttonState = c == 'M' ? MouseButtonState.Button2Pressed - : MouseButtonState.Button2Released; + case MouseFlags.Button1Released: + mbs |= MouseButtonState.Button1Released; break; - case 2: - case 10: - case 14: - case 18: - case 22: - case 26: - case 30: - case 34: - case 42: - case 46: - case 50: - case 54: - case 58: - case 62: - buttonState = c == 'M' ? MouseButtonState.Button3Pressed - : MouseButtonState.Button3Released; + case MouseFlags.Button1Clicked: + mbs |= MouseButtonState.Button1Clicked; break; - case 35: - case 39: - case 43: - case 47: - case 55: - case 59: - case 63: - buttonState = MouseButtonState.ReportMousePosition; + case MouseFlags.Button1DoubleClicked: + mbs |= MouseButtonState.Button1DoubleClicked; break; - case 64: - buttonState = MouseButtonState.ButtonWheeledUp; + case MouseFlags.Button1TripleClicked: + mbs |= MouseButtonState.Button1TripleClicked; break; - case 65: - buttonState = MouseButtonState.ButtonWheeledDown; + case MouseFlags.Button2Pressed: + mbs |= MouseButtonState.Button2Pressed; break; - case 68: - case 72: - case 80: - buttonState = MouseButtonState.ButtonWheeledLeft; // Shift/Ctrl+ButtonWheeledUp + case MouseFlags.Button2Released: + mbs |= MouseButtonState.Button2Released; break; - case 69: - case 73: - case 81: - buttonState = MouseButtonState.ButtonWheeledRight; // Shift/Ctrl+ButtonWheeledDown + case MouseFlags.Button2Clicked: + mbs |= MouseButtonState.Button2Clicked; break; - } - // Modifiers. - switch (buttonCode) { - case 8: - case 9: - case 10: - case 43: - buttonState |= MouseButtonState.ButtonAlt; + case MouseFlags.Button2DoubleClicked: + mbs |= MouseButtonState.Button2DoubleClicked; + break; + case MouseFlags.Button2TripleClicked: + mbs |= MouseButtonState.Button2TripleClicked; + break; + case MouseFlags.Button3Pressed: + mbs |= MouseButtonState.Button3Pressed; break; - case 14: - case 47: - buttonState |= MouseButtonState.ButtonAlt | MouseButtonState.ButtonShift; + case MouseFlags.Button3Released: + mbs |= MouseButtonState.Button3Released; break; - case 16: - case 17: - case 18: - case 51: - buttonState |= MouseButtonState.ButtonCtrl; + case MouseFlags.Button3Clicked: + mbs |= MouseButtonState.Button3Clicked; break; - case 22: - case 55: - buttonState |= MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift; + case MouseFlags.Button3DoubleClicked: + mbs |= MouseButtonState.Button3DoubleClicked; break; - case 24: - case 25: - case 26: - case 59: - buttonState |= MouseButtonState.ButtonAlt | MouseButtonState.ButtonCtrl; + case MouseFlags.Button3TripleClicked: + mbs |= MouseButtonState.Button3TripleClicked; break; - case 30: - case 63: - buttonState |= MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift | MouseButtonState.ButtonAlt; + case MouseFlags.WheeledUp: + mbs |= MouseButtonState.ButtonWheeledUp; break; - case 32: - case 33: - case 34: - buttonState |= MouseButtonState.ReportMousePosition; + case MouseFlags.WheeledDown: + mbs |= MouseButtonState.ButtonWheeledDown; break; - case 36: - case 37: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonShift; + case MouseFlags.WheeledLeft: + mbs |= MouseButtonState.ButtonWheeledLeft; break; - case 39: - case 68: - case 69: - buttonState |= MouseButtonState.ButtonShift; + case MouseFlags.WheeledRight: + mbs |= MouseButtonState.ButtonWheeledRight; break; - case 40: - case 41: - case 42: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonAlt; + case MouseFlags.Button4Pressed: + mbs |= MouseButtonState.Button4Pressed; break; - case 45: - case 46: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonAlt | MouseButtonState.ButtonShift; + case MouseFlags.Button4Released: + mbs |= MouseButtonState.Button4Released; break; - case 48: - case 49: - case 50: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl; + case MouseFlags.Button4Clicked: + mbs |= MouseButtonState.Button4Clicked; break; - case 53: - case 54: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift; + case MouseFlags.Button4DoubleClicked: + mbs |= MouseButtonState.Button4DoubleClicked; break; - case 56: - case 57: - case 58: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonAlt; + case MouseFlags.Button4TripleClicked: + mbs |= MouseButtonState.Button4TripleClicked; break; - case 61: - case 62: - buttonState |= MouseButtonState.ReportMousePosition | MouseButtonState.ButtonCtrl | MouseButtonState.ButtonShift | MouseButtonState.ButtonAlt; + case MouseFlags.ButtonShift: + mbs |= MouseButtonState.ButtonShift; + break; + case MouseFlags.ButtonCtrl: + mbs |= MouseButtonState.ButtonCtrl; + break; + case MouseFlags.ButtonAlt: + mbs |= MouseButtonState.ButtonAlt; + break; + case MouseFlags.ReportMousePosition: + mbs |= MouseButtonState.ReportMousePosition; + break; + case MouseFlags.AllEvents: + mbs |= MouseButtonState.AllEvents; break; } } } - mouseEvent.Position.X = point.X; - mouseEvent.Position.Y = point.Y; - mouseEvent.ButtonState = buttonState; - //System.Diagnostics.Debug.WriteLine ($"ButtonState: {mouseEvent.ButtonState} X: {mouseEvent.Position.X} Y: {mouseEvent.Position.Y}"); - - if (isButtonDoubleClicked) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); - return false; - }); - } + return mbs; + } - if ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0) { + Point lastCursorPosition; - if ((buttonState & MouseButtonState.ReportMousePosition) == 0) { - buttonPressedCount++; + void GetRequestEvent (string c1Control, string code, string [] values, string terminating) + { + EventType eventType = new EventType (); + switch (terminating) { + case "R": // Reports cursor position as CSI r ; c R + Point point = new Point { + X = int.Parse (values [1]) - 1, + Y = int.Parse (values [0]) - 1 + }; + if (lastCursorPosition.Y != point.Y) { + lastCursorPosition = point; + eventType = EventType.WindowPosition; + var winPositionEv = new WindowPositionEvent () { + CursorPosition = point + }; + inputResultQueue.Enqueue (new InputResult () { + EventType = eventType, + WindowPositionEvent = winPositionEv + }); } else { - buttonPressedCount = 0; + return; + } + break; + case "c": + try { + var parent = EscSeqUtils.GetParentProcess (Process.GetCurrentProcess ()); + if (parent == null) { Debug.WriteLine ("Not supported!"); } + } catch (Exception ex) { + Debug.WriteLine (ex.Message); } - //System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}"); - isButtonPressed = true; - } else { - isButtonPressed = false; - buttonPressedCount = 0; - } - - if (buttonPressedCount == 2 && !isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { - - isButtonDoubleClicked = true; - ProcessButtonDoubleClicked (mouseEvent); - inputReady.Set (); - return; - } else if (buttonPressedCount == 3 && isButtonDoubleClicked - && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed - || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) { - - isButtonDoubleClicked = false; - isButtonTripleClicked = true; - buttonPressedCount = 0; - ProcessButtonTripleClicked (mouseEvent); - lastMouseEvent = mouseEvent; - inputReady.Set (); - return; - } - - //System.Diagnostics.Debug.WriteLine ($"isButtonClicked: {isButtonClicked} isButtonDoubleClicked: {isButtonDoubleClicked} isButtonTripleClicked: {isButtonTripleClicked}"); - if ((isButtonClicked || isButtonDoubleClicked || isButtonTripleClicked) - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - - //isButtonClicked = false; - //isButtonDoubleClicked = false; - isButtonTripleClicked = false; - buttonPressedCount = 0; - return; - } - - if (isButtonClicked && !isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - - isButtonClicked = false; - isButtonDoubleClicked = true; - ProcessButtonDoubleClicked (mouseEvent); - Application.MainLoop.AddIdle (() => { - Task.Run (async () => { - await Task.Delay (600); - isButtonDoubleClicked = false; - }); - return false; - }); - inputReady.Set (); - return; - } - if (isButtonDoubleClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Pressed) != 0 - || (buttonState & MouseButtonState.Button2Pressed) != 0 - || (buttonState & MouseButtonState.Button3Pressed) != 0 - || (buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - - isButtonDoubleClicked = false; - isButtonTripleClicked = true; - ProcessButtonTripleClicked (mouseEvent); - inputReady.Set (); - return; - } - - //if (!isButtonPressed && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked - // && !isButtonReleased && lastMouseEvent.ButtonState != 0 - // && ((buttonState & MouseButtonState.Button1Released) == 0 - // && (buttonState & MouseButtonState.Button2Released) == 0 - // && (buttonState & MouseButtonState.Button3Released) == 0)) { - // ProcessButtonReleased (lastMouseEvent); - // inputReady.Set (); - // return; - //} - - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = mouseEvent - }); - - if (!isButtonClicked && !lastMouseEvent.ButtonState.HasFlag (MouseButtonState.ReportMousePosition) - && lastMouseEvent.Position != default && lastMouseEvent.Position == point - && ((buttonState & MouseButtonState.Button1Released) != 0 - || (buttonState & MouseButtonState.Button2Released) != 0 - || (buttonState & MouseButtonState.Button3Released) != 0)) { - isButtonClicked = true; - ProcessButtonClicked (mouseEvent); - Application.MainLoop.AddIdle (() => { - Task.Run (async () => { - await Task.Delay (300); - isButtonClicked = false; - }); - return false; - }); - inputReady.Set (); - return; - } - lastMouseEvent = mouseEvent; - if (isButtonPressed && !isButtonClicked && !isButtonDoubleClicked && !isButtonTripleClicked && !isProcContBtnPressedRuning) { - //isButtonReleased = false; - if ((buttonState & MouseButtonState.ReportMousePosition) != 0) { - point = new Point (); + if (c1Control == "CSI" && values.Length == 2 + && values [0] == "1" && values [1] == "0") { + // Reports CSI?1;0c ("VT101 with No Options") + IsTerminalWithOptions = false; } else { - point = new Point () { - X = mouseEvent.Position.X, - Y = mouseEvent.Position.Y - }; + IsTerminalWithOptions = true; } - if ((buttonState & MouseButtonState.ReportMousePosition) == 0) { - Application.MainLoop.AddIdle (() => { - Task.Run (async () => await ProcessContinuousButtonPressedAsync ()); - return false; - }); + break; + case "t": + switch (values [0]) { + case "8": + IsWinChanged ( + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0), + Math.Max (int.Parse (values [1]), 0), + Math.Max (int.Parse (values [2]), 0)); + break; + default: + SetRequestedEvent (c1Control, code, values, terminating); + break; } + break; + default: + SetRequestedEvent (c1Control, code, values, terminating); + break; } inputReady.Set (); } - void ProcessButtonClicked (MouseEvent mouseEvent) - { - var me = new MouseEvent () { - Position = mouseEvent.Position, - ButtonState = mouseEvent.ButtonState - }; - if ((mouseEvent.ButtonState & MouseButtonState.Button1Released) != 0) { - me.ButtonState &= ~MouseButtonState.Button1Released; - me.ButtonState |= MouseButtonState.Button1Clicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Released) != 0) { - me.ButtonState &= ~MouseButtonState.Button2Released; - me.ButtonState |= MouseButtonState.Button2Clicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Released) != 0) { - me.ButtonState &= ~MouseButtonState.Button3Released; - me.ButtonState |= MouseButtonState.Button3Clicked; - } - //isButtonReleased = true; - - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = me - }); - } - - async Task ProcessButtonDoubleClickedAsync () - { - await Task.Delay (300); - isButtonDoubleClicked = false; - buttonPressedCount = 0; - } - - void ProcessButtonDoubleClicked (MouseEvent mouseEvent) + void SetRequestedEvent (string c1Control, string code, string [] values, string terminating) { - var me = new MouseEvent () { - Position = mouseEvent.Position, - ButtonState = mouseEvent.ButtonState + EventType eventType = EventType.RequestResponse; + var requestRespEv = new RequestResponseEvent () { + ResultTuple = (c1Control, code, values, terminating) }; - if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button1Pressed; - me.ButtonState |= MouseButtonState.Button1DoubleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button2Pressed; - me.ButtonState |= MouseButtonState.Button2DoubleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button3Pressed; - me.ButtonState |= MouseButtonState.Button3DoubleClicked; - } - //isButtonReleased = true; - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = me + EventType = eventType, + RequestResponseEvent = requestRespEv }); } - void ProcessButtonTripleClicked (MouseEvent mouseEvent) + void GetMouseEvent (MouseButtonState buttonState, Point pos) { - var me = new MouseEvent () { - Position = mouseEvent.Position, - ButtonState = mouseEvent.ButtonState + MouseEvent mouseEvent = new MouseEvent () { + Position = pos, + ButtonState = buttonState, }; - if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button1Pressed; - me.ButtonState |= MouseButtonState.Button1TripleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button2Pressed; - me.ButtonState |= MouseButtonState.Button2TrippleClicked; - } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) { - me.ButtonState &= ~MouseButtonState.Button3Pressed; - me.ButtonState |= MouseButtonState.Button3TripleClicked; - } - //isButtonReleased = true; inputResultQueue.Enqueue (new InputResult () { EventType = EventType.Mouse, - MouseEvent = me + MouseEvent = mouseEvent }); - } - - async Task ProcessContinuousButtonPressedAsync () - { - isProcContBtnPressedRuning = true; - await Task.Delay (200); - while (isButtonPressed) { - await Task.Delay (100); - var view = Application.WantContinuousButtonPressedView; - if (view == null) { - break; - } - if (isButtonPressed && (lastMouseEvent.ButtonState & MouseButtonState.ReportMousePosition) == 0) { - inputResultQueue.Enqueue (new InputResult () { - EventType = EventType.Mouse, - MouseEvent = lastMouseEvent - }); - inputReady.Set (); - } - } - isProcContBtnPressedRuning = false; - //isButtonPressed = false; - } - - //void ProcessButtonReleased (MouseEvent mouseEvent) - //{ - // var me = new MouseEvent () { - // Position = mouseEvent.Position, - // ButtonState = mouseEvent.ButtonState - // }; - // if ((mouseEvent.ButtonState & MouseButtonState.Button1Pressed) != 0) { - // me.ButtonState &= ~(MouseButtonState.Button1Pressed | MouseButtonState.ReportMousePosition); - // me.ButtonState |= MouseButtonState.Button1Released; - // } else if ((mouseEvent.ButtonState & MouseButtonState.Button2Pressed) != 0) { - // me.ButtonState &= ~(MouseButtonState.Button2Pressed | MouseButtonState.ReportMousePosition); - // me.ButtonState |= MouseButtonState.Button2Released; - // } else if ((mouseEvent.ButtonState & MouseButtonState.Button3Pressed) != 0) { - // me.ButtonState &= ~(MouseButtonState.Button3Pressed | MouseButtonState.ReportMousePosition); - // me.ButtonState |= MouseButtonState.Button3Released; - // } - // isButtonReleased = true; - // lastMouseEvent = me; - - // inputResultQueue.Enqueue (new InputResult () { - // EventType = EventType.Mouse, - // MouseEvent = me - // }); - //} - - ConsoleModifiers GetConsoleModifiers (uint keyChar) - { - switch (keyChar) { - case 50: - return ConsoleModifiers.Shift; - case 51: - return ConsoleModifiers.Alt; - case 52: - return ConsoleModifiers.Shift | ConsoleModifiers.Alt; - case 53: - return ConsoleModifiers.Control; - case 54: - return ConsoleModifiers.Shift | ConsoleModifiers.Control; - case 55: - return ConsoleModifiers.Alt | ConsoleModifiers.Control; - case 56: - return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control; - default: - return 0; - } - } - - ConsoleKey GetConsoleKey (char keyChar, ref ConsoleModifiers mod, int length) - { - ConsoleKey key; - switch (keyChar) { - case 'A': - key = ConsoleKey.UpArrow; - break; - case 'B': - key = ConsoleKey.DownArrow; - break; - case 'C': - key = ConsoleKey.RightArrow; - break; - case 'D': - key = ConsoleKey.LeftArrow; - break; - case 'F': - key = ConsoleKey.End; - break; - case 'H': - key = ConsoleKey.Home; - break; - case 'P': - key = ConsoleKey.F1; - break; - case 'Q': - key = ConsoleKey.F2; - break; - case 'R': - key = ConsoleKey.F3; - break; - case 'S': - key = ConsoleKey.F4; - break; - case 'Z': - key = ConsoleKey.Tab; - mod |= ConsoleModifiers.Shift; - break; - case '0': - key = ConsoleKey.F9; - break; - case '1': - key = ConsoleKey.F10; - break; - case '2': - key = ConsoleKey.Insert; - break; - case '3': - if (length == 5) { - key = ConsoleKey.F11; - } else { - key = ConsoleKey.Delete; - } - break; - case '4': - key = ConsoleKey.F12; - break; - case '5': - if (length == 5) { - key = ConsoleKey.F5; - } else { - key = ConsoleKey.PageUp; - } - break; - case '6': - key = ConsoleKey.PageDown; - break; - case '7': - key = ConsoleKey.F6; - break; - case '8': - key = ConsoleKey.F7; - break; - case '9': - key = ConsoleKey.F8; - break; - default: - key = 0; - break; - } - return key; + inputReady.Set (); } public enum EventType { Key = 1, Mouse = 2, WindowSize = 3, - WindowPosition = 4 + WindowPosition = 4, + RequestResponse = 5 } [Flags] @@ -1102,7 +545,7 @@ public enum MouseButtonState { Button2Released = 0x40, Button2Clicked = 0x80, Button2DoubleClicked = 0x100, - Button2TrippleClicked = 0x200, + Button2TripleClicked = 0x200, Button3Pressed = 0x400, Button3Released = 0x800, Button3Clicked = 0x1000, @@ -1121,7 +564,7 @@ public enum MouseButtonState { ButtonCtrl = 0x2000000, ButtonAlt = 0x4000000, ReportMousePosition = 0x8000000, - AllEvents = Button1Pressed | Button1Released | Button1Clicked | Button1DoubleClicked | Button1TripleClicked | Button2Pressed | Button2Released | Button2Clicked | Button2DoubleClicked | Button2TrippleClicked | Button3Pressed | Button3Released | Button3Clicked | Button3DoubleClicked | Button3TripleClicked | ButtonWheeledUp | ButtonWheeledDown | ButtonWheeledLeft | ButtonWheeledRight | Button4Pressed | Button4Released | Button4Clicked | Button4DoubleClicked | Button4TripleClicked | ReportMousePosition + AllEvents = -1 } public struct MouseEvent { @@ -1139,12 +582,17 @@ public struct WindowPositionEvent { public Point CursorPosition; } + public struct RequestResponseEvent { + public (string c1Control, string code, string [] values, string terminating) ResultTuple; + } + public struct InputResult { public EventType EventType; public ConsoleKeyInfo ConsoleKeyInfo; public MouseEvent MouseEvent; public WindowSizeEvent WindowSizeEvent; public WindowPositionEvent WindowPositionEvent; + public RequestResponseEvent RequestResponseEvent; } } @@ -1242,7 +690,7 @@ public override void AddRune (Rune rune) } var c = sn [0]; contents [crow, ccol - 1, 0] = c; - contents [crow, ccol - 1, 1] = currentAttribute; + contents [crow, ccol - 1, 1] = CurrentAttribute; contents [crow, ccol - 1, 2] = 1; } else { @@ -1263,7 +711,7 @@ public override void AddRune (Rune rune) } else { contents [crow, ccol, 0] = (int)(uint)rune; } - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 1; } @@ -1273,19 +721,15 @@ public override void AddRune (Rune rune) if (runeWidth < 0 || runeWidth > 0) { ccol++; } + if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 0; } ccol++; } - //if (ccol == Cols) { - // ccol = 0; - // if (crow + 1 < Rows) - // crow++; - //} if (sync) { UpdateScreen (); } @@ -1299,6 +743,8 @@ public override void AddStr (ustring str) public override void End () { + mainLoop.netEvents.StopTasks (); + if (IsWinPlatform) { NetWinConsole.Cleanup (); } @@ -1307,10 +753,14 @@ public override void End () Console.ResetColor (); //Disable alternative screen buffer. - Console.Out.Write ("\x1b[?1047l"); + Console.Out.Write ("\x1b[?1049l"); + Console.Out.Flush (); //Set cursor key to cursor. Console.Out.Write ("\x1b[?25h"); + Console.Out.Flush (); + + Console.Out.Close (); } public override Attribute MakeColor (Color foreground, Color background) @@ -1333,10 +783,12 @@ public override void Init (Action terminalResized) TerminalResized = terminalResized; //Enable alternative screen buffer. - Console.Out.Write ("\x1b[?1047h"); + Console.Out.Write ("\x1b[?1049h"); + Console.Out.Flush (); //Set cursor key to application. Console.Out.Write ("\x1b[?25l"); + Console.Out.Flush (); Console.TreatControlCAsInput = true; @@ -1347,24 +799,25 @@ public override void Init (Action terminalResized) } cols = Console.WindowWidth; - rows = Console.WindowHeight; + rows = largestBufferHeight; + + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitalizeColorSchemes (); ResizeScreen (); UpdateOffScreen (); StartReportingMouseMoves (); - - CreateColors (); } public override void ResizeScreen () { if (!EnableConsoleScrolling) { if (Console.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { - // Not supported on Unix. - if (IsWinPlatform) { + // Not supported on Unix. + if (IsWinPlatform) { + // Can raise an exception while is still resizing. + try { #pragma warning disable CA1416 Console.CursorTop = 0; Console.CursorLeft = 0; @@ -1375,39 +828,36 @@ public override void ResizeScreen () } Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 - } else { - //Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t"); - Console.Out.Write ($"\x1b[0;0" + - $";{Rows};{Cols}w"); + } catch (System.IO.IOException) { + setClip (); + } catch (ArgumentOutOfRangeException) { + setClip (); } - } catch (System.IO.IOException) { - setClip (); - } catch (ArgumentOutOfRangeException) { - setClip (); + } else { + Console.Out.Write ($"\x1b[8;{Rows};{Cols}t"); } } } else { - if (IsWinPlatform && Console.WindowHeight > 0) { - // Can raise an exception while is still resizing. - try { + if (IsWinPlatform) { + if (Console.WindowHeight > 0) { + // Can raise an exception while is still resizing. + try { #pragma warning disable CA1416 - Console.CursorTop = 0; - Console.CursorLeft = 0; - //Console.WindowTop = 0; - //Console.WindowLeft = 0; - if (Console.WindowHeight > Rows) { - Console.SetWindowSize (Cols, Rows); - } - Console.SetBufferSize (Cols, Rows); + Console.CursorTop = 0; + Console.CursorLeft = 0; + if (Console.WindowHeight > Rows) { + Console.SetWindowSize (Cols, Rows); + } + Console.SetBufferSize (Cols, Rows); #pragma warning restore CA1416 - } catch (System.IO.IOException) { - setClip (); - } catch (ArgumentOutOfRangeException) { - setClip (); + } catch (System.IO.IOException) { + setClip (); + } catch (ArgumentOutOfRangeException) { + setClip (); + } } } else { - Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" + - $";{Rows};{Cols}w"); + Console.Out.Write ($"\x1b[30;{Rows};{Cols}t"); } } setClip (); @@ -1415,14 +865,6 @@ public override void ResizeScreen () void setClip () { Clip = new Rect (0, 0, Cols, Rows); - //if (!EnableConsoleScrolling) { - // // ANSI ESC "[xJ" Clears part of the screen. - // // If n is 0 (or missing), clear from cursor to end of screen. - // // If n is 1, clear from cursor to beginning of the screen. - // // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // Console.Out.Write ("\x1b[0J"); - //} } } @@ -1457,11 +899,9 @@ public override void Refresh () UpdateCursor (); } - int redrawAttr = -1; - public override void UpdateScreen () { - if (Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3 + if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3 || (!EnableConsoleScrolling && Rows != Console.WindowHeight) || (EnableConsoleScrolling && Rows != largestBufferHeight)) { return; @@ -1472,15 +912,10 @@ public override void UpdateScreen () int rows = Rows; int cols = Cols; System.Text.StringBuilder output = new System.Text.StringBuilder (); + int redrawAttr = -1; var lastCol = -1; - var successSetCurPos = true; Console.CursorVisible = false; - if (IsWinPlatform) { - successSetCurPos = SetCursorPosition (0, 0); - } else { - SetVirtualCursorPosition (0, 0); - } for (int row = top; row < rows; row++) { if (Console.WindowHeight < 1) { @@ -1489,9 +924,10 @@ public override void UpdateScreen () if (!dirtyLine [row]) { continue; } - if (successSetCurPos) { - dirtyLine [row] = false; + if (!SetCursorPosition (0, row)) { + return; } + dirtyLine [row] = false; output.Clear (); for (int col = left; col < cols; col++) { lastCol = -1; @@ -1499,9 +935,7 @@ public override void UpdateScreen () for (; col < cols; col++) { if (contents [row, col, 2] == 0) { if (output.Length > 0) { - //Console.CursorLeft = lastCol; - //Console.CursorTop = row; - SetVirtualCursorPosition (lastCol, row); + SetCursorPosition (lastCol, row); Console.Write (output); output.Clear (); lastCol += outputWidth; @@ -1519,6 +953,7 @@ public override void UpdateScreen () var attr = contents [row, col, 1]; if (attr != redrawAttr) { + redrawAttr = attr; output.Append (WriteAttributes (attr)); } outputWidth++; @@ -1529,23 +964,21 @@ public override void UpdateScreen () } else { output.Append ((char)rune); } - if (successSetCurPos) { - contents [row, col, 2] = 0; - } + contents [row, col, 2] = 0; } } if (output.Length > 0) { - //Console.CursorLeft = lastCol; - //Console.CursorTop = row; - SetVirtualCursorPosition (lastCol, row); + SetCursorPosition (lastCol, row); Console.Write (output); } } + SetCursorPosition (0, 0); } void SetVirtualCursorPosition (int col, int row) { Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); + Console.Out.Flush (); } System.Text.StringBuilder WriteAttributes (int attr) @@ -1555,7 +988,6 @@ System.Text.StringBuilder WriteAttributes (int attr) int fg = 0; System.Text.StringBuilder sb = new System.Text.StringBuilder (); - redrawAttr = attr; IEnumerable values = Enum.GetValues (typeof (ConsoleColor)) .OfType () .Select (s => (int)s); @@ -1611,50 +1043,92 @@ int MapColors (ConsoleColor color, bool isForeground = true) bool SetCursorPosition (int col, int row) { - // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. - try { - Console.SetCursorPosition (col, row); + if (IsWinPlatform) { + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try { + Console.SetCursorPosition (col, row); + return true; + } catch (Exception) { + return false; + } + } else { + SetVirtualCursorPosition (col, row); return true; - } catch (Exception) { - return false; } } + private void SetWindowPosition (int col, int row) + { + if (IsWinPlatform && EnableConsoleScrolling) { + var winTop = Math.Max (Rows - Console.WindowHeight - row, 0); + winTop = Math.Min (winTop, Rows - Console.WindowHeight + 1); + winTop = Math.Max (winTop, 0); + if (winTop != Console.WindowTop) { + try { + if (!EnsureBufferSize ()) { + return; + } +#pragma warning disable CA1416 + Console.SetWindowPosition (col, winTop); +#pragma warning restore CA1416 + } catch (System.IO.IOException) { + + } catch (System.ArgumentOutOfRangeException) { } + } + } + top = Console.WindowTop; + left = Console.WindowLeft; + } + + private bool EnsureBufferSize () + { +#pragma warning disable CA1416 + if (IsWinPlatform && Console.BufferHeight < Rows) { + try { + Console.SetBufferSize (Console.WindowWidth, Rows); + } catch (Exception) { + return false; + } + } +#pragma warning restore CA1416 + return true; + } + private CursorVisibility? savedCursorVisibility; public override void UpdateCursor () { - if (!EnsureCursorVisibility ()) - return; + EnsureCursorVisibility (); + //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); - // Prevents the exception of size changing during resizing. - try { - if (ccol >= 0 && ccol < Console.BufferWidth && crow >= 0 && crow < Console.BufferHeight) { - Console.SetCursorPosition (ccol, crow); - } - } catch (System.IO.IOException) { - } catch (ArgumentOutOfRangeException) { + if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) { + SetCursorPosition (ccol, crow); + SetWindowPosition (0, crow); } + //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}"); + //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}"); } public override void StartReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003h\x1b[?1015h\x1b[?1006h"); + Console.Out.Write (EscSeqUtils.EnableMouseEvents); + Console.Out.Flush (); } public override void StopReportingMouseMoves () { - Console.Out.Write ("\x1b[?1003l\x1b[?1015l\x1b[?1006l"); + Console.Out.Write (EscSeqUtils.DisableMouseEvents); + Console.Out.Flush (); } public override void Suspend () { } - Attribute currentAttribute; + public override void SetAttribute (Attribute c) { - currentAttribute = c; + base.SetAttribute (c); } public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo) @@ -1799,6 +1273,7 @@ Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) Action keyDownHandler; Action keyUpHandler; Action mouseHandler; + NetMainLoop mainLoop; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { @@ -1807,10 +1282,14 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle this.keyUpHandler = keyUpHandler; this.mouseHandler = mouseHandler; - var mLoop = mainLoop.Driver as NetMainLoop; + var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop; // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called. mLoop.ProcessInput = (e) => ProcessInput (e); + + // Check if terminal supports requests + this.mainLoop.netEvents.EscSeqReqProc.Add ("c"); + Console.Out.Write ("\x1b[0c"); } void ProcessInput (NetEvents.InputResult inputEvent) @@ -1841,25 +1320,29 @@ void ProcessInput (NetEvents.InputResult inputEvent) case NetEvents.EventType.WindowSize: ChangeWin (inputEvent.WindowSizeEvent.Size); break; + case NetEvents.EventType.RequestResponse: + Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple; + break; } } + volatile bool winChanging; + void ChangeWin (Size size) { - Size newSize; - if (!EnableConsoleScrolling) { + winChanging = true; + if (!HeightAsBuffer) { largestBufferHeight = Math.Max (size.Height, 0); - newSize = new Size (size.Width, largestBufferHeight); - top = 0; - left = 0; } else { largestBufferHeight = Math.Max (size.Height, largestBufferHeight); - newSize = new Size (Math.Max (size.Width, 0), largestBufferHeight); } - cols = newSize.Width; - rows = newSize.Height; + top = 0; + left = 0; + cols = size.Width; + rows = largestBufferHeight; ResizeScreen (); UpdateOffScreen (); + winChanging = false; TerminalResized?.Invoke (); } @@ -1896,7 +1379,7 @@ MouseEvent ToDriverMouse (NetEvents.MouseEvent me) if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) { mouseFlag |= MouseFlags.Button2DoubleClicked; } - if ((me.ButtonState & NetEvents.MouseButtonState.Button2TrippleClicked) != 0) { + if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) { mouseFlag |= MouseFlags.Button2TripleClicked; } if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) { @@ -1961,11 +1444,6 @@ MouseEvent ToDriverMouse (NetEvents.MouseEvent me) }; } - public override Attribute GetAttribute () - { - return currentAttribute; - } - /// public override bool GetCursorVisibility (out CursorVisibility visibility) { @@ -2064,7 +1542,7 @@ internal class NetMainLoop : IMainLoopDriver { Queue inputResult = new Queue (); MainLoop mainLoop; CancellationTokenSource tokenSource = new CancellationTokenSource (); - NetEvents netEvents; + internal NetEvents netEvents; /// /// Invoked when a Key is pressed. diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index d82971c5bf..6ed98fa927 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -92,6 +92,10 @@ public void SetInitialCursorVisibility () public bool GetCursorVisibility (out CursorVisibility visibility) { + if (ScreenBuffer == IntPtr.Zero) { + visibility = CursorVisibility.Invisible; + return false; + } if (!GetConsoleCursorInfo (ScreenBuffer, out ConsoleCursorInfo info)) { var err = Marshal.GetLastWin32Error (); if (err != 0) { @@ -259,6 +263,9 @@ internal Size SetConsoleOutputWindow (out Point position) position = new Point (csbi.srWindow.Left, csbi.srWindow.Top); SetConsoleOutputWindow (csbi); var winRect = new SmallRect (0, 0, (short)(sz.Width - 1), (short)Math.Max (sz.Height - 1, 0)); + if (!SetConsoleScreenBufferInfoEx (OutputHandle, ref csbi)) { + throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); + } if (!SetConsoleWindowInfo (OutputHandle, true, ref winRect)) { throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ()); } @@ -1453,13 +1460,13 @@ public override void Init (Action terminalResized) var winSize = WinConsole.GetConsoleOutputWindow (out Point pos); cols = winSize.Width; rows = winSize.Height; - WindowsConsole.SmallRect.MakeEmpty (ref damageRegion); + CurrentAttribute = MakeColor (Color.White, Color.Black); + InitalizeColorSchemes (); + ResizeScreen (); UpdateOffScreen (); - - CreateColors (); } catch (Win32Exception e) { throw new InvalidOperationException ("The Windows Console output window is not available.", e); } @@ -1477,15 +1484,13 @@ public override void ResizeScreen () }; WinConsole.ForceRefreshCursorVisibility (); if (!EnableConsoleScrolling) { - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer - Console.Out.Write ("\x1b[0J"); - - // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer + Console.Out.Write ("\x1b[3J"); } } @@ -1537,8 +1542,8 @@ public override void AddRune (Rune rune) var prevPosition = crow * Cols + (ccol - 1); OutputBuffer [prevPosition].Char.UnicodeChar = c; contents [crow, ccol - 1, 0] = c; - OutputBuffer [prevPosition].Attributes = (ushort)currentAttribute; - contents [crow, ccol - 1, 1] = currentAttribute; + OutputBuffer [prevPosition].Attributes = (ushort)CurrentAttribute; + contents [crow, ccol - 1, 1] = CurrentAttribute; contents [crow, ccol - 1, 2] = 1; WindowsConsole.SmallRect.Update (ref damageRegion, (short)(ccol - 1), (short)crow); } else { @@ -1564,8 +1569,8 @@ public override void AddRune (Rune rune) OutputBuffer [position].Char.UnicodeChar = (char)rune; contents [crow, ccol, 0] = (int)(uint)rune; } - OutputBuffer [position].Attributes = (ushort)currentAttribute; - contents [crow, ccol, 1] = currentAttribute; + OutputBuffer [position].Attributes = (ushort)CurrentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 1; WindowsConsole.SmallRect.Update (ref damageRegion, (short)ccol, (short)crow); } @@ -1574,20 +1579,22 @@ public override void AddRune (Rune rune) if (runeWidth < 0 || runeWidth > 0) { ccol++; } + if (runeWidth > 1) { if (validClip && ccol < Clip.Right) { position = GetOutputBufferPosition (); - OutputBuffer [position].Attributes = (ushort)currentAttribute; + OutputBuffer [position].Attributes = (ushort)CurrentAttribute; OutputBuffer [position].Char.UnicodeChar = (char)0x00; contents [crow, ccol, 0] = (int)(uint)0x00; - contents [crow, ccol, 1] = currentAttribute; + contents [crow, ccol, 1] = CurrentAttribute; contents [crow, ccol, 2] = 0; } ccol++; } - if (sync) + if (sync) { UpdateScreen (); + } } public override void AddStr (ustring str) @@ -1596,11 +1603,9 @@ public override void AddStr (ustring str) AddRune (rune); } - Attribute currentAttribute; - public override void SetAttribute (Attribute c) { - currentAttribute = c; + base.SetAttribute (c); } public override Attribute MakeColor (Color foreground, Color background) @@ -1714,11 +1719,6 @@ public override void End () // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526 } - public override Attribute GetAttribute () - { - return currentAttribute; - } - /// public override bool GetCursorVisibility (out CursorVisibility visibility) { diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index cc1a240f7b..27dbc86162 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -57,7 +57,7 @@ namespace Terminal.Gui { /// /// public static class Application { - static Stack toplevels = new Stack (); + static readonly Stack toplevels = new Stack (); /// /// The current in use. @@ -111,6 +111,8 @@ public static Toplevel MdiTop { /// public static View WantContinuousButtonPressedView { get; private set; } + private static bool? _heightAsBuffer; + /// /// The current used in the terminal. /// @@ -131,13 +133,14 @@ public static Toplevel MdiTop { public static bool EnableConsoleScrolling { get { if (Driver == null) { - throw new ArgumentNullException ("The driver must be initialized first."); + return _heightAsBuffer.HasValue && _heightAsBuffer.Value; } return Driver.EnableConsoleScrolling; } set { + _heightAsBuffer = value; if (Driver == null) { - throw new ArgumentNullException ("The driver must be initialized first."); + return; } Driver.EnableConsoleScrolling = value; } @@ -155,7 +158,7 @@ public static bool HeightAsBuffer { static Key alternateForwardKey = Key.PageDown | Key.CtrlMask; /// - /// Alternative key to navigate forwards through all views. Ctrl+Tab is always used. + /// Alternative key to navigate forwards through views. Ctrl+Tab is the primary key. /// public static Key AlternateForwardKey { get => alternateForwardKey; @@ -170,7 +173,7 @@ public static Key AlternateForwardKey { static void OnAlternateForwardKeyChanged (Key oldKey) { - foreach (var top in toplevels) { + foreach (var top in toplevels.ToArray()) { top.OnAlternateForwardKeyChanged (oldKey); } } @@ -178,7 +181,7 @@ static void OnAlternateForwardKeyChanged (Key oldKey) static Key alternateBackwardKey = Key.PageUp | Key.CtrlMask; /// - /// Alternative key to navigate backwards through all views. Shift+Ctrl+Tab is always used. + /// Alternative key to navigate backwards through views. Shift+Ctrl+Tab is the primary key. /// public static Key AlternateBackwardKey { get => alternateBackwardKey; @@ -193,7 +196,7 @@ public static Key AlternateBackwardKey { static void OnAlternateBackwardKeyChanged (Key oldKey) { - foreach (var top in toplevels) { + foreach (var top in toplevels.ToArray()) { top.OnAlternateBackwardKeyChanged (oldKey); } } @@ -223,7 +226,8 @@ public static Key QuitKey { static void OnQuitKeyChanged (Key oldKey) { - foreach (var top in toplevels) { + // Duplicate the list so if it changes during enumeration we're safe + foreach (var top in toplevels.ToArray()) { top.OnQuitKeyChanged (oldKey); } } @@ -235,7 +239,7 @@ static void OnQuitKeyChanged (Key oldKey) public static MainLoop MainLoop { get; private set; } /// - /// Disable or enable the mouse in this + /// Disable or enable the mouse. The mouse is enabled by default. /// public static bool IsMouseDisabled { get; set; } @@ -289,7 +293,7 @@ public static Rect MakeCenteredRect (Size size) // users use async/await on their code // class MainLoopSyncContext : SynchronizationContext { - MainLoop mainLoop; + readonly MainLoop mainLoop; public MainLoopSyncContext (MainLoop mainLoop) { @@ -328,9 +332,9 @@ public override void Send (SendOrPostCallback d, object state) } /// - /// If set, it forces the use of the System.Console-based driver. + /// If , forces the use of the System.Console-based (see ) driver. The default is . /// - public static bool UseSystemConsole; + public static bool UseSystemConsole { get; set; } = false; // For Unit testing - ignores UseSystemConsole internal static bool ForceFakeConsole; @@ -445,6 +449,7 @@ internal static void InternalInit (Func topLevelFactory, ConsoleDriver MainLoop = new MainLoop (mainLoopDriver); try { + Driver.HeightAsBuffer = HeightAsBuffer; Driver.Init (TerminalResized); } catch (InvalidOperationException ex) { // This is a case where the driver is unable to initialize the console. @@ -956,6 +961,8 @@ public static RunState Begin (Toplevel toplevel) if (Top != null && toplevel != Top && !toplevels.Contains (Top)) { Top.Dispose (); Top = null; + } else if (Top != null && toplevel != Top && toplevels.Contains (Top)) { + Top.OnLeave (toplevel); } if (string.IsNullOrEmpty (toplevel.Id.ToString ())) { var count = 1; @@ -1009,9 +1016,7 @@ public static RunState Begin (Toplevel toplevel) toplevel.PositionToplevels (); toplevel.WillPresent (); if (refreshDriver) { - if (MdiTop != null) { - MdiTop.OnChildLoaded (toplevel); - } + MdiTop?.OnChildLoaded (toplevel); toplevel.OnLoaded (); Redraw (toplevel); toplevel.PositionCursor (); @@ -1066,6 +1071,7 @@ public static void End (RunState runState) MdiTop.OnAllChildClosed (); } else { SetCurrentAsTop (); + Current.OnEnter (Current); } Refresh (); } @@ -1134,12 +1140,6 @@ static void Redraw (View view) Driver.Refresh (); } - static void Refresh (View view) - { - view.Redraw (view.Bounds); - Driver.Refresh (); - } - /// /// Triggers a refresh of the entire display. /// diff --git a/Terminal.Gui/Core/Autocomplete/Autocomplete.cs b/Terminal.Gui/Core/Autocomplete/Autocomplete.cs index f351b8424e..61ccc01ea1 100644 --- a/Terminal.Gui/Core/Autocomplete/Autocomplete.cs +++ b/Terminal.Gui/Core/Autocomplete/Autocomplete.cs @@ -324,6 +324,7 @@ public virtual bool ProcessKey (KeyEvent kb) if (IsWordChar ((char)kb.Key)) { Visible = true; closed = false; + return false; } if (kb.Key == Reopen) { @@ -332,6 +333,9 @@ public virtual bool ProcessKey (KeyEvent kb) if (closed || Suggestions.Count == 0) { Visible = false; + if (!closed) { + Close (); + } return false; } @@ -345,6 +349,17 @@ public virtual bool ProcessKey (KeyEvent kb) return true; } + if (kb.Key == Key.CursorLeft || kb.Key == Key.CursorRight) { + GenerateSuggestions (kb.Key == Key.CursorLeft ? -1 : 1); + if (Suggestions.Count == 0) { + Visible = false; + if (!closed) { + Close (); + } + } + return false; + } + if (kb.Key == SelectionKey) { return Select (); } @@ -368,6 +383,9 @@ public virtual bool ProcessKey (KeyEvent kb) public virtual bool MouseEvent (MouseEvent me, bool fromHost = false) { if (fromHost) { + if (!Visible) { + return false; + } GenerateSuggestions (); if (Visible && Suggestions.Count == 0) { Visible = false; @@ -444,7 +462,8 @@ public virtual void ClearSuggestions () /// Populates with all strings in that /// match with the current cursor position/text in the /// - public virtual void GenerateSuggestions () + /// The column offset. + public virtual void GenerateSuggestions (int columnOffset = 0) { // if there is nothing to pick from if (AllSuggestions.Count == 0) { @@ -452,7 +471,7 @@ public virtual void GenerateSuggestions () return; } - var currentWord = GetCurrentWord (); + var currentWord = GetCurrentWord (columnOffset); if (string.IsNullOrWhiteSpace (currentWord)) { ClearSuggestions (); @@ -524,11 +543,12 @@ protected virtual bool InsertSelection (string accepted) /// /// Returns the currently selected word from the . /// - /// When overriding this method views can make use of + /// When overriding this method views can make use of /// /// + /// The column offset. /// - protected abstract string GetCurrentWord (); + protected abstract string GetCurrentWord (int columnOffset = 0); /// /// @@ -536,37 +556,40 @@ protected virtual bool InsertSelection (string accepted) /// or null. Also returns null if the is positioned in the middle of a word. /// /// - /// Use this method to determine whether autocomplete should be shown when the cursor is at - /// a given point in a line and to get the word from which suggestions should be generated. + /// + /// Use this method to determine whether autocomplete should be shown when the cursor is at + /// a given point in a line and to get the word from which suggestions should be generated. + /// Use the to indicate if search the word at left (negative), + /// at right (positive) or at the current column (zero) which is the default. + /// /// /// /// + /// /// - protected virtual string IdxToWord (List line, int idx) + protected virtual string IdxToWord (List line, int idx, int columnOffset = 0) { StringBuilder sb = new StringBuilder (); + var endIdx = idx; - // do not generate suggestions if the cursor is positioned in the middle of a word - bool areMidWord; - - if (idx == line.Count) { - // the cursor positioned at the very end of the line - areMidWord = false; - } else { - // we are in the middle of a word if the cursor is over a letter/number - areMidWord = IsWordChar (line [idx]); + // get the ending word index + while (endIdx < line.Count) { + if (IsWordChar (line [endIdx])) { + endIdx++; + } else { + break; + } } - // if we are in the middle of a word then there is no way to autocomplete that word - if (areMidWord) { + // It isn't a word char then there is no way to autocomplete that word + if (endIdx == idx && columnOffset != 0) { return null; } // we are at the end of a word. Work out what has been typed so far - while (idx-- > 0) { - - if (IsWordChar (line [idx])) { - sb.Insert (0, (char)line [idx]); + while (endIdx-- > 0) { + if (IsWordChar (line [endIdx])) { + sb.Insert (0, (char)line [endIdx]); } else { break; } diff --git a/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs b/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs index 32e7046e7c..2e3194eb3e 100644 --- a/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs +++ b/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs @@ -109,6 +109,7 @@ public interface IAutocomplete { /// Populates with all strings in that /// match with the current cursor position/text in the . /// - void GenerateSuggestions (); + /// The column offset. Current (zero - default), left (negative), right (positive). + void GenerateSuggestions (int columnOffset = 0); } } diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index d08916d3ef..421c17f25c 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -1,9 +1,10 @@ // -// ConsoleDriver.cs: Definition for the Console Driver API +// ConsoleDriver.cs: Base class for Terminal.Gui ConsoleDriver implementations. // using NStack; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -11,8 +12,11 @@ namespace Terminal.Gui { /// - /// Basic colors that can be used to set the foreground and background colors in console applications. + /// Colors that can be used to set the foreground and background colors in console applications. /// + /// + /// The value indicates either no-color has been set or the color is invalid. + /// public enum Color { /// /// The black color. @@ -81,22 +85,104 @@ public enum Color { } /// - /// Attributes are used as elements that contain both a foreground and a background or platform specific features + /// + /// + public class TrueColor { + /// + /// Red color component. + /// + public int Red { get; } + /// + /// Green color component. + /// + public int Green { get; } + /// + /// Blue color component. + /// + public int Blue { get; } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// + /// + public TrueColor (int red, int green, int blue) + { + Red = red; + Green = green; + Blue = blue; + } + + /// + /// + /// + /// + public Color ToConsoleColor () + { + var trueColorMap = new Dictionary () { + { new TrueColor (0,0,0),Color.Black}, + { new TrueColor (0, 0, 0x80),Color.Blue}, + { new TrueColor (0, 0x80, 0),Color.Green}, + { new TrueColor (0, 0x80, 0x80),Color.Cyan}, + { new TrueColor (0x80, 0, 0),Color.Red}, + { new TrueColor (0x80, 0, 0x80),Color.Magenta}, + { new TrueColor (0xC1, 0x9C, 0x00),Color.Brown}, // TODO confirm this + { new TrueColor (0xC0, 0xC0, 0xC0),Color.Gray}, + { new TrueColor (0x80, 0x80, 0x80),Color.DarkGray}, + { new TrueColor (0, 0, 0xFF),Color.BrightBlue}, + { new TrueColor (0, 0xFF, 0),Color.BrightGreen}, + { new TrueColor (0, 0xFF, 0xFF),Color.BrightCyan}, + { new TrueColor (0xFF, 0, 0),Color.BrightRed}, + { new TrueColor (0xFF, 0, 0xFF),Color.BrightMagenta }, + { new TrueColor (0xFF, 0xFF, 0),Color.BrightYellow}, + { new TrueColor (0xFF, 0xFF, 0xFF),Color.White}, + }; + // Iterate over all colors in the map + var distances = trueColorMap.Select ( + k => Tuple.Create ( + // the candidate we are considering matching against (RGB) + k.Key, + + CalculateDistance (k.Key, this) + )); + + // get the closest + var match = distances.OrderBy (t => t.Item2).First (); + return trueColorMap [match.Item1]; + } + + private float CalculateDistance (TrueColor color1, TrueColor color2) + { + // use RGB distance + return + Math.Abs (color1.Red - color2.Red) + + Math.Abs (color1.Green - color2.Green) + + Math.Abs (color1.Blue - color2.Blue); + } + } + + /// + /// Attributes are used as elements that contain both a foreground and a background or platform specific features. /// /// - /// s are needed to map colors to terminal capabilities that might lack colors, on color - /// scenarios, they encode both the foreground and the background color and are used in the - /// class to define color schemes that can be used in your application. + /// s are needed to map colors to terminal capabilities that might lack colors. + /// They encode both the foreground and the background color and are used in the + /// class to define color schemes that can be used in an application. /// public struct Attribute { /// - /// The color attribute value. + /// The -specific color attribute value. If is + /// the value of this property is invalid (typically because the Attribute was created before a driver was loaded) + /// and the attribute should be re-made (see ) before it is used. /// public int Value { get; } + /// /// The foreground color. /// public Color Foreground { get; } + /// /// The background color. /// @@ -112,8 +198,10 @@ public Attribute (int value) Color foreground = default; Color background = default; + Initialized = false; if (Application.Driver != null) { Application.Driver.GetColors (value, out foreground, out background); + Initialized = true; } Value = value; Foreground = foreground; @@ -131,6 +219,7 @@ public Attribute (int value, Color foreground, Color background) Value = value; Foreground = foreground; Background = background; + Initialized = true; } /// @@ -140,7 +229,9 @@ public Attribute (int value, Color foreground, Color background) /// Background public Attribute (Color foreground = new Color (), Color background = new Color ()) { - Value = Make (foreground, background).Value; + var make = Make (foreground, background); + Initialized = make.Initialized; + Value = make.Value; Foreground = foreground; Background = background; } @@ -153,29 +244,42 @@ public Attribute (int value, Color foreground, Color background) public Attribute (Color color) : this (color, color) { } /// - /// Implicit conversion from an to the underlying Int32 representation + /// Implicit conversion from an to the underlying, driver-specific, Int32 representation + /// of the color. /// - /// The integer value stored in the attribute. + /// The driver-specific color value stored in the attribute. /// The attribute to convert - public static implicit operator int (Attribute c) => c.Value; + public static implicit operator int (Attribute c) + { + if (!c.Initialized) throw new InvalidOperationException ("Attribute: Attributes must be initialized by a driver before use."); + return c.Value; + } /// - /// Implicitly convert an integer value into an + /// Implicitly convert an driver-specific color value into an /// - /// An attribute with the specified integer value. + /// An attribute with the specified driver-specific color value. /// value public static implicit operator Attribute (int v) => new Attribute (v); /// - /// Creates an from the specified foreground and background. + /// Creates an from the specified foreground and background colors. /// - /// The make. + /// + /// If a has not been loaded (Application.Driver == null) this + /// method will return an attribute with set to . + /// + /// The new attribute. /// Foreground color to use. /// Background color to use. public static Attribute Make (Color foreground, Color background) { - if (Application.Driver == null) - throw new InvalidOperationException ("The Application has not been initialized"); + if (Application.Driver == null) { + // Create the attribute, but show it's not been initialized + return new Attribute (-1, foreground, background) { + Initialized = false + }; + } return Application.Driver.MakeAttribute (foreground, background); } @@ -189,45 +293,110 @@ public static Attribute Get () throw new InvalidOperationException ("The Application has not been initialized"); return Application.Driver.GetAttribute (); } + + /// + /// If the attribute has been initialized by a and + /// thus has that is valid for that driver. If the + /// and colors may have been set '-1' but + /// the attribute has not been mapped to a specific color value. + /// + /// + /// Attributes that have not been initialized must eventually be initialized before being passed to a driver. + /// + public bool Initialized { get; internal set; } + + /// + /// Returns if the Attribute is valid (both foreground and background have valid color values). + /// + /// + public bool HasValidColors { get => (int)Foreground > -1 && (int)Background > -1; } } /// - /// Color scheme definitions, they cover some common scenarios and are used - /// typically in containers such as and to set the scheme that is used by all the - /// views contained inside. + /// Defines the color s for common visible elements in a . + /// Containers such as and use to determine + /// the colors used by sub-views. /// + /// + /// See also: . + /// public class ColorScheme : IEquatable { - Attribute _normal; - Attribute _focus; - Attribute _hotNormal; - Attribute _hotFocus; - Attribute _disabled; - internal string caller = ""; + Attribute _normal = new Attribute (Color.White, Color.Black); + Attribute _focus = new Attribute (Color.White, Color.Black); + Attribute _hotNormal = new Attribute (Color.White, Color.Black); + Attribute _hotFocus = new Attribute (Color.White, Color.Black); + Attribute _disabled = new Attribute (Color.White, Color.Black); /// - /// The default color for text, when the view is not focused. + /// Used by and to track which ColorScheme + /// is being accessed. /// - public Attribute Normal { get { return _normal; } set { _normal = value; } } + internal string schemeBeingSet = ""; /// - /// The color for text when the view has the focus. + /// The foreground and background color for text when the view is not focused, hot, or disabled. /// - public Attribute Focus { get { return _focus; } set { _focus = value; } } + public Attribute Normal { + get { return _normal; } + set { + if (!value.HasValidColors) { + return; + } + _normal = value; + } + } /// - /// The color for the hotkey when a view is not focused + /// The foreground and background color for text when the view has the focus. /// - public Attribute HotNormal { get { return _hotNormal; } set { _hotNormal = value; } } + public Attribute Focus { + get { return _focus; } + set { + if (!value.HasValidColors) { + return; + } + _focus = value; + } + } /// - /// The color for the hotkey when the view is focused. + /// The foreground and background color for text when the view is highlighted (hot). /// - public Attribute HotFocus { get { return _hotFocus; } set { _hotFocus = value; } } + public Attribute HotNormal { + get { return _hotNormal; } + set { + if (!value.HasValidColors) { + return; + } + _hotNormal = value; + } + } /// - /// The default color for text, when the view is disabled. + /// The foreground and background color for text when the view is highlighted (hot) and has focus. /// - public Attribute Disabled { get { return _disabled; } set { _disabled = value; } } + public Attribute HotFocus { + get { return _hotFocus; } + set { + if (!value.HasValidColors) { + return; + } + _hotFocus = value; + } + } + + /// + /// The default foreground and background color for text, when the view is disabled. + /// + public Attribute Disabled { + get { return _disabled; } + set { + if (!value.HasValidColors) { + return; + } + _disabled = value; + } + } /// /// Compares two objects for equality. @@ -290,20 +459,67 @@ public override int GetHashCode () { return !(left == right); } + + internal void Initialize () + { + // If the new scheme was created before a driver was loaded, we need to re-make + // the attributes + if (!_normal.Initialized) { + _normal = new Attribute (_normal.Foreground, _normal.Background); + } + if (!_focus.Initialized) { + _focus = new Attribute (_focus.Foreground, _focus.Background); + } + if (!_hotNormal.Initialized) { + _hotNormal = new Attribute (_hotNormal.Foreground, _hotNormal.Background); + } + if (!_hotFocus.Initialized) { + _hotFocus = new Attribute (_hotFocus.Foreground, _hotFocus.Background); + } + if (!_disabled.Initialized) { + _disabled = new Attribute (_disabled.Foreground, _disabled.Background); + } + } } /// /// The default s for the application. /// + /// + /// This property can be set in a Theme to change the default for the application. + /// public static class Colors { + private class SchemeNameComparerIgnoreCase : IEqualityComparer { + public bool Equals (string x, string y) + { + if (x != null && y != null) { + return x.ToLowerInvariant () == y.ToLowerInvariant (); + } + return false; + } + + public int GetHashCode (string obj) + { + return obj.ToLowerInvariant ().GetHashCode (); + } + } + static Colors () + { + ColorSchemes = Create (); + } + + /// + /// Creates a new dictionary of new objects. + /// + public static Dictionary Create () { // Use reflection to dynamically create the default set of ColorSchemes from the list defined // by the class. - ColorSchemes = typeof (Colors).GetProperties () + return typeof (Colors).GetProperties () .Where (p => p.PropertyType == typeof (ColorScheme)) - .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) // (ColorScheme)p.GetValue (p))) - .ToDictionary (t => t.Key, t => t.Value); + .Select (p => new KeyValuePair (p.Name, new ColorScheme ())) + .ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ()); } /// @@ -356,21 +572,21 @@ static Colors () /// public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); } - static ColorScheme GetColorScheme ([CallerMemberName] string callerMemberName = null) + static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null) { - return ColorSchemes [callerMemberName]; + return ColorSchemes [schemeBeingSet]; } - static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string callerMemberName = null) + static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null) { - ColorSchemes [callerMemberName] = colorScheme; - colorScheme.caller = callerMemberName; + ColorSchemes [schemeBeingSet] = colorScheme; + colorScheme.schemeBeingSet = schemeBeingSet; } /// /// Provides the defined s. /// - public static Dictionary ColorSchemes { get; } + public static Dictionary ColorSchemes { get; private set; } } /// @@ -607,13 +823,35 @@ public bool IsValidContent (int col, int row, Rect clip) => public abstract void UpdateScreen (); /// - /// Selects the specified attribute as the attribute to use for future calls to AddRune, AddString. + /// The current attribute the driver is using. /// + public virtual Attribute CurrentAttribute { + get => currentAttribute; + set { + if (!value.Initialized && value.HasValidColors && Application.Driver != null) { + CurrentAttribute = Application.Driver.MakeAttribute (value.Foreground, value.Background); + return; + } + if (!value.Initialized) Debug.WriteLine ("ConsoleDriver.CurrentAttribute: Attributes must be initialized before use."); + + currentAttribute = value; + } + } + + /// + /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString. + /// + /// + /// Implementations should call base.SetAttribute(c). + /// /// C. - public abstract void SetAttribute (Attribute c); + public virtual void SetAttribute (Attribute c) + { + CurrentAttribute = c; + } /// - /// Set Colors from limit sets of colors. + /// Set Colors from limit sets of colors. Not implemented by any driver: See Issue #2300. /// /// Foreground. /// Background. @@ -623,7 +861,7 @@ public bool IsValidContent (int col, int row, Rect clip) => // that independently with the R, G, B values. /// /// Advanced uses - set colors to any pre-set pairs, you would need to init_color - /// that independently with the R, G, B values. + /// that independently with the R, G, B values. Not implemented by any driver: See Issue #2300. /// /// Foreground color identifier. /// Background color identifier. @@ -946,12 +1184,13 @@ public Rect Clip { public abstract void StopReportingMouseMoves (); /// - /// Disables the cooked event processing from the mouse driver. At startup, it is assumed mouse events are cooked. + /// Disables the cooked event processing from the mouse driver. + /// At startup, it is assumed mouse events are cooked. Not implemented by any driver: See Issue #2300. /// public abstract void UncookMouse (); /// - /// Enables the cooked event processing from the mouse driver + /// Enables the cooked event processing from the mouse driver. Not implemented by any driver: See Issue #2300. /// public abstract void CookMouse (); @@ -1144,6 +1383,7 @@ public Rect Clip { /// Lower right rounded corner /// public Rune LRRCorner = '\u256f'; + private Attribute currentAttribute; /// /// Make the attribute for the foreground and background colors. @@ -1157,7 +1397,7 @@ public Rect Clip { /// Gets the current . /// /// The current attribute. - public abstract Attribute GetAttribute (); + public Attribute GetAttribute () => CurrentAttribute; /// /// Make the for the . @@ -1168,21 +1408,24 @@ public Rect Clip { public abstract Attribute MakeColor (Color foreground, Color background); /// - /// Create all with the for the console driver. + /// Ensures all s in are correctly + /// initialized by the driver. /// - /// Flag indicating if colors are supported. - public void CreateColors (bool hasColors = true) + /// Flag indicating if colors are supported (not used). + public void InitalizeColorSchemes (bool supportsColors = true) { - Colors.TopLevel = new ColorScheme (); - Colors.Base = new ColorScheme (); - Colors.Dialog = new ColorScheme (); - Colors.Menu = new ColorScheme (); - Colors.Error = new ColorScheme (); + // Ensure all Attributes are initialized by the driver + foreach (var s in Colors.ColorSchemes) { + s.Value.Initialize (); + } - if (!hasColors) { + if (!supportsColors) { return; } + + // Define the default color theme only if the user has not defined one. + Colors.TopLevel.Normal = MakeColor (Color.BrightGreen, Color.Black); Colors.TopLevel.Focus = MakeColor (Color.White, Color.Cyan); Colors.TopLevel.HotNormal = MakeColor (Color.Brown, Color.Black); diff --git a/Terminal.Gui/Core/ConsoleKeyMapping.cs b/Terminal.Gui/Core/ConsoleKeyMapping.cs index d7bc3d5849..441adac483 100644 --- a/Terminal.Gui/Core/ConsoleKeyMapping.cs +++ b/Terminal.Gui/Core/ConsoleKeyMapping.cs @@ -334,6 +334,25 @@ public static Key MapConsoleKeyToKey (ConsoleKey consoleKey, out bool isMappable return (Key)consoleKey; } + /// + /// Maps a to a . + /// + /// The console key info. + /// The key. + /// The with or the + public static Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + keyMod = Key.ShiftMask; + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + keyMod |= Key.CtrlMask; + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + keyMod |= Key.AltMask; + + return keyMod != Key.Null ? keyMod | key : key; + } + private static HashSet scanCodes = new HashSet { new ScanCodeMapping (1,27,0,27), // Escape new ScanCodeMapping (1,27,ConsoleModifiers.Shift,27), diff --git a/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs new file mode 100644 index 0000000000..a5e305c6de --- /dev/null +++ b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; + +namespace Terminal.Gui { + /// + /// Represents a state of escape sequence request. + /// + /// + /// This is needed because there are some escape sequence requests responses that are equal + /// with some normal escape sequences and thus, will be only considered the responses to the + /// requests that were registered with this object. + /// + public class EscSeqReqStat { + /// + /// Gets the terminating. + /// + public string Terminating { get; } + /// + /// Gets the number of requests. + /// + public int NumOfReq { get; } + /// + /// Gets information about unfinished requests. + /// + public int Unfinished { get; set; } + + /// + /// Creates a new state of escape sequence request. + /// + /// The terminating. + /// The number of requests. + public EscSeqReqStat (string terminating, int numOfReq) + { + Terminating = terminating; + NumOfReq = Unfinished = numOfReq; + } + } + + /// + /// Manages a list of escape sequence requests. + /// + public class EscSeqReqProc { + /// + /// Gets the list. + /// + public List EscSeqReqStats { get; } = new List (); + + /// + /// Adds a new instance to the list. + /// + /// The terminating. + /// The number of requests. + public void Add (string terminating, int numOfReq = 1) + { + lock (EscSeqReqStats) { + var found = EscSeqReqStats.Find (x => x.Terminating == terminating); + if (found == null) { + EscSeqReqStats.Add (new EscSeqReqStat (terminating, numOfReq)); + } else if (found != null && found.Unfinished < found.NumOfReq) { + found.Unfinished = Math.Min (found.Unfinished + numOfReq, found.NumOfReq); + } + } + } + + /// + /// Removes a instance from the list. + /// + /// The terminating. + public void Remove (string terminating) + { + lock (EscSeqReqStats) { + var found = EscSeqReqStats.Find (x => x.Terminating == terminating); + if (found == null) { + return; + } + if (found != null && found.Unfinished == 0) { + EscSeqReqStats.Remove (found); + } else if (found != null && found.Unfinished > 0) { + found.Unfinished--; + if (found.Unfinished == 0) { + EscSeqReqStats.Remove (found); + } + } + } + } + + /// + /// Indicates if a with the exist + /// in the list. + /// + /// + /// if exist, otherwise. + public bool Requested (string terminating) + { + lock (EscSeqReqStats) { + var found = EscSeqReqStats.Find (x => x.Terminating == terminating); + if (found == null) { + return false; + } + if (found != null && found.Unfinished > 0) { + return true; + } else { + EscSeqReqStats.Remove (found); + } + return false; + } + } + } +} diff --git a/Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs new file mode 100644 index 0000000000..bfee2030fb --- /dev/null +++ b/Terminal.Gui/Core/EscSeqUtils/EscSeqUtils.cs @@ -0,0 +1,907 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Terminal.Gui { + /// + /// Provides a platform-independent API for managing ANSI escape sequence codes. + /// + public static class EscSeqUtils { + /// + /// Represents the escape key. + /// + public static readonly char KeyEsc = (char)Key.Esc; + /// + /// Represents the CSI (Control Sequence Introducer). + /// + public static readonly string KeyCSI = $"{KeyEsc}["; + /// + /// Represents the CSI for enable any mouse event tracking. + /// + public static readonly string CSI_EnableAnyEventMouse = KeyCSI + "?1003h"; + /// + /// Represents the CSI for enable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_EnableSgrExtModeMouse = KeyCSI + "?1006h"; + /// + /// Represents the CSI for enable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_EnableUrxvtExtModeMouse = KeyCSI + "?1015h"; + /// + /// Represents the CSI for disable any mouse event tracking. + /// + public static readonly string CSI_DisableAnyEventMouse = KeyCSI + "?1003l"; + /// + /// Represents the CSI for disable SGR (Select Graphic Rendition). + /// + public static readonly string CSI_DisableSgrExtModeMouse = KeyCSI + "?1006l"; + /// + /// Represents the CSI for disable URXVT (Unicode Extended Virtual Terminal). + /// + public static readonly string CSI_DisableUrxvtExtModeMouse = KeyCSI + "?1015l"; + + /// + /// Control sequence for enable mouse events. + /// + public static string EnableMouseEvents { get; set; } = + CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse; + /// + /// Control sequence for disable mouse events. + /// + public static string DisableMouseEvents { get; set; } = + CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse; + + /// + /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences. + /// + /// The . + /// The modified. + public static ConsoleKeyInfo GetConsoleInputKey (ConsoleKeyInfo consoleKeyInfo) + { + ConsoleKeyInfo newConsoleKeyInfo = consoleKeyInfo; + ConsoleKey key; + var keyChar = consoleKeyInfo.KeyChar; + switch ((uint)keyChar) { + case 0: + if (consoleKeyInfo.Key == (ConsoleKey)64) { // Ctrl+Space in Windows. + newConsoleKeyInfo = new ConsoleKeyInfo (' ', ConsoleKey.Spacebar, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + } + break; + case uint n when (n >= '\u0001' && n <= '\u001a'): + if (consoleKeyInfo.Key == 0 && consoleKeyInfo.KeyChar == '\r') { + key = ConsoleKey.Enter; + newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + } else if (consoleKeyInfo.Key == 0) { + key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + (uint)ConsoleKey.A - 1); + newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, + key, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + true); + } + break; + case 127: + newConsoleKeyInfo = new ConsoleKeyInfo (consoleKeyInfo.KeyChar, ConsoleKey.Backspace, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0, + (consoleKeyInfo.Modifiers & ConsoleModifiers.Control) != 0); + break; + default: + newConsoleKeyInfo = consoleKeyInfo; + break; + } + + return newConsoleKeyInfo; + } + + /// + /// A helper to resize the as needed. + /// + /// The . + /// The array to resize. + /// The resized. + public static ConsoleKeyInfo [] ResizeArray (ConsoleKeyInfo consoleKeyInfo, ConsoleKeyInfo [] cki) + { + Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1); + cki [cki.Length - 1] = consoleKeyInfo; + return cki; + } + + /// + /// Decodes a escape sequence to been processed in the appropriate manner. + /// + /// The which may contain a request. + /// The which may changes. + /// The which may changes. + /// The array. + /// The which may changes. + /// The control returned by the method. + /// The code returned by the method. + /// The values returned by the method. + /// The terminating returned by the method. + /// Indicates if the escape sequence is a mouse key. + /// The button state. + /// The position. + /// Indicates if the escape sequence is a response to a request. + /// The handler that will process the event. + public static void DecodeEscSeq (EscSeqReqProc escSeqReqProc, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminating, out bool isKeyMouse, out List buttonState, out Point pos, out bool isReq, Action continuousButtonPressedHandler) + { + char [] kChars = GetKeyCharArray (cki); + (c1Control, code, values, terminating) = GetEscapeResult (kChars); + isKeyMouse = false; + buttonState = new List () { 0 }; + pos = default; + isReq = false; + switch (c1Control) { + case "ESC": + if (values == null && string.IsNullOrEmpty (terminating)) { + key = ConsoleKey.Escape; + newConsoleKeyInfo = new ConsoleKeyInfo (cki [0].KeyChar, key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + } else if ((uint)cki [1].KeyChar >= 1 && (uint)cki [1].KeyChar <= 26) { + key = (ConsoleKey)(char)(cki [1].KeyChar + (uint)ConsoleKey.A - 1); + newConsoleKeyInfo = new ConsoleKeyInfo (cki [1].KeyChar, + key, + false, + true, + true); + } else { + if (cki [1].KeyChar >= 97 && cki [1].KeyChar <= 122) { + key = (ConsoleKey)cki [1].KeyChar.ToString ().ToUpper () [0]; + } else { + key = (ConsoleKey)cki [1].KeyChar; + } + newConsoleKeyInfo = new ConsoleKeyInfo ((char)key, + (ConsoleKey)Math.Min ((uint)key, 255), + false, + true, + false); + } + break; + case "SS3": + key = GetConsoleKey (terminating [0], values [0], ref mod); + newConsoleKeyInfo = new ConsoleKeyInfo ('\0', + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + break; + case "CSI": + if (!string.IsNullOrEmpty (code) && code == "<") { + GetMouse (cki, out buttonState, out pos, continuousButtonPressedHandler); + isKeyMouse = true; + return; + } else if (escSeqReqProc != null && escSeqReqProc.Requested (terminating)) { + isReq = true; + escSeqReqProc.Remove (terminating); + return; + } + key = GetConsoleKey (terminating [0], values [0], ref mod); + if (key != 0 && values.Length > 1) { + mod |= GetConsoleModifiers (values [1]); + } + newConsoleKeyInfo = new ConsoleKeyInfo ('\0', + key, + (mod & ConsoleModifiers.Shift) != 0, + (mod & ConsoleModifiers.Alt) != 0, + (mod & ConsoleModifiers.Control) != 0); + break; + } + } + + /// + /// Gets all the needed information about a escape sequence. + /// + /// The array with all chars. + /// + /// The c1Control returned by , code, values and terminating. + /// + public static (string c1Control, string code, string [] values, string terminating) GetEscapeResult (char [] kChar) + { + if (kChar == null || kChar.Length == 0) { + return (null, null, null, null); + } + if (kChar [0] != '\x1b') { + throw new InvalidOperationException ("Invalid escape character!"); + } + if (kChar.Length == 1) { + return ("ESC", null, null, null); + } + if (kChar.Length == 2) { + return ("ESC", null, null, kChar [1].ToString ()); + } + string c1Control = GetC1ControlChar (kChar [1]); + string code = null; + int nSep = kChar.Count (x => x == ';') + 1; + string [] values = new string [nSep]; + int valueIdx = 0; + string terminating = ""; + for (int i = 2; i < kChar.Length; i++) { + var c = kChar [i]; + if (char.IsDigit (c)) { + values [valueIdx] += c.ToString (); + } else if (c == ';') { + valueIdx++; + } else if (valueIdx == nSep - 1 || i == kChar.Length - 1) { + terminating += c.ToString (); + } else { + code += c.ToString (); + } + } + + return (c1Control, code, values, terminating); + } + + /// + /// Gets the c1Control used in the called escape sequence. + /// + /// The char used. + /// The c1Control. + public static string GetC1ControlChar (char c) + { + // These control characters are used in the vtXXX emulation. + switch (c) { + case 'D': + return "IND"; // Index + case 'E': + return "NEL"; // Next Line + case 'H': + return "HTS"; // Tab Set + case 'M': + return "RI"; // Reverse Index + case 'N': + return "SS2"; // Single Shift Select of G2 Character Set: affects next character only + case 'O': + return "SS3"; // Single Shift Select of G3 Character Set: affects next character only + case 'P': + return "DCS"; // Device Control String + case 'V': + return "SPA"; // Start of Guarded Area + case 'W': + return "EPA"; // End of Guarded Area + case 'X': + return "SOS"; // Start of String + case 'Z': + return "DECID"; // Return Terminal ID Obsolete form of CSI c (DA) + case '[': + return "CSI"; // Control Sequence Introducer + case '\\': + return "ST"; // String Terminator + case ']': + return "OSC"; // Operating System Command + case '^': + return "PM"; // Privacy Message + case '_': + return "APC"; // Application Program Command + default: + return ""; // Not supported + } + } + + /// + /// Gets the from the value. + /// + /// The value. + /// The or zero. + public static ConsoleModifiers GetConsoleModifiers (string value) + { + switch (value) { + case "2": + return ConsoleModifiers.Shift; + case "3": + return ConsoleModifiers.Alt; + case "4": + return ConsoleModifiers.Shift | ConsoleModifiers.Alt; + case "5": + return ConsoleModifiers.Control; + case "6": + return ConsoleModifiers.Shift | ConsoleModifiers.Control; + case "7": + return ConsoleModifiers.Alt | ConsoleModifiers.Control; + case "8": + return ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control; + default: + return 0; + } + } + + /// + /// Gets the depending on terminating and value. + /// + /// The terminating. + /// The value. + /// The which may changes. + /// The and probably the . + public static ConsoleKey GetConsoleKey (char terminating, string value, ref ConsoleModifiers mod) + { + ConsoleKey key; + switch (terminating) { + case 'A': + key = ConsoleKey.UpArrow; + break; + case 'B': + key = ConsoleKey.DownArrow; + break; + case 'C': + key = ConsoleKey.RightArrow; + break; + case 'D': + key = ConsoleKey.LeftArrow; + break; + case 'F': + key = ConsoleKey.End; + break; + case 'H': + key = ConsoleKey.Home; + break; + case 'P': + key = ConsoleKey.F1; + break; + case 'Q': + key = ConsoleKey.F2; + break; + case 'R': + key = ConsoleKey.F3; + break; + case 'S': + key = ConsoleKey.F4; + break; + case 'Z': + key = ConsoleKey.Tab; + mod |= ConsoleModifiers.Shift; + break; + case '~': + switch (value) { + case "2": + key = ConsoleKey.Insert; + break; + case "3": + key = ConsoleKey.Delete; + break; + case "5": + key = ConsoleKey.PageUp; + break; + case "6": + key = ConsoleKey.PageDown; + break; + case "15": + key = ConsoleKey.F5; + break; + case "17": + key = ConsoleKey.F6; + break; + case "18": + key = ConsoleKey.F7; + break; + case "19": + key = ConsoleKey.F8; + break; + case "20": + key = ConsoleKey.F9; + break; + case "21": + key = ConsoleKey.F10; + break; + case "23": + key = ConsoleKey.F11; + break; + case "24": + key = ConsoleKey.F12; + break; + default: + key = 0; + break; + } + break; + default: + key = 0; + break; + } + + return key; + } + + /// + /// A helper to get only the from the array. + /// + /// + /// The char array of the escape sequence. + public static char [] GetKeyCharArray (ConsoleKeyInfo [] cki) + { + char [] kChar = new char [] { }; + var length = 0; + foreach (var kc in cki) { + length++; + Array.Resize (ref kChar, length); + kChar [length - 1] = kc.KeyChar; + } + + return kChar; + } + + private static MouseFlags? lastMouseButtonPressed; + //private static MouseFlags? lastMouseButtonReleased; + private static bool isButtonPressed; + //private static bool isButtonReleased; + private static bool isButtonClicked; + private static bool isButtonDoubleClicked; + private static bool isButtonTripleClicked; + private static Point point; + + /// + /// Gets the mouse button flags and the position. + /// + /// The array. + /// The mouse button flags. + /// The mouse position. + /// The handler that will process the event. + public static void GetMouse (ConsoleKeyInfo [] cki, out List mouseFlags, out Point pos, Action continuousButtonPressedHandler) + { + MouseFlags buttonState = 0; + pos = new Point (); + int buttonCode = 0; + bool foundButtonCode = false; + int foundPoint = 0; + string value = ""; + var kChar = GetKeyCharArray (cki); + //System.Diagnostics.Debug.WriteLine ($"kChar: {new string (kChar)}"); + for (int i = 0; i < kChar.Length; i++) { + var c = kChar [i]; + if (c == '<') { + foundButtonCode = true; + } else if (foundButtonCode && c != ';') { + value += c.ToString (); + } else if (c == ';') { + if (foundButtonCode) { + foundButtonCode = false; + buttonCode = int.Parse (value); + } + if (foundPoint == 1) { + pos.X = int.Parse (value) - 1; + } + value = ""; + foundPoint++; + } else if (foundPoint > 0 && c != 'm' && c != 'M') { + value += c.ToString (); + } else if (c == 'm' || c == 'M') { + //pos.Y = int.Parse (value) + Console.WindowTop - 1; + pos.Y = int.Parse (value) - 1; + + switch (buttonCode) { + case 0: + case 8: + case 16: + case 24: + case 32: + case 36: + case 40: + case 48: + case 56: + buttonState = c == 'M' ? MouseFlags.Button1Pressed + : MouseFlags.Button1Released; + break; + case 1: + case 9: + case 17: + case 25: + case 33: + case 37: + case 41: + case 45: + case 49: + case 53: + case 57: + case 61: + buttonState = c == 'M' ? MouseFlags.Button2Pressed + : MouseFlags.Button2Released; + break; + case 2: + case 10: + case 14: + case 18: + case 22: + case 26: + case 30: + case 34: + case 42: + case 46: + case 50: + case 54: + case 58: + case 62: + buttonState = c == 'M' ? MouseFlags.Button3Pressed + : MouseFlags.Button3Released; + break; + case 35: + //// Needed for Windows OS + //if (isButtonPressed && c == 'm' + // && (lastMouseEvent.ButtonState == MouseFlags.Button1Pressed + // || lastMouseEvent.ButtonState == MouseFlags.Button2Pressed + // || lastMouseEvent.ButtonState == MouseFlags.Button3Pressed)) { + + // switch (lastMouseEvent.ButtonState) { + // case MouseFlags.Button1Pressed: + // buttonState = MouseFlags.Button1Released; + // break; + // case MouseFlags.Button2Pressed: + // buttonState = MouseFlags.Button2Released; + // break; + // case MouseFlags.Button3Pressed: + // buttonState = MouseFlags.Button3Released; + // break; + // } + //} else { + // buttonState = MouseFlags.ReportMousePosition; + //} + //break; + case 39: + case 43: + case 47: + case 51: + case 55: + case 59: + case 63: + buttonState = MouseFlags.ReportMousePosition; + break; + case 64: + buttonState = MouseFlags.WheeledUp; + break; + case 65: + buttonState = MouseFlags.WheeledDown; + break; + case 68: + case 72: + case 80: + buttonState = MouseFlags.WheeledLeft; // Shift/Ctrl+WheeledUp + break; + case 69: + case 73: + case 81: + buttonState = MouseFlags.WheeledRight; // Shift/Ctrl+WheeledDown + break; + } + // Modifiers. + switch (buttonCode) { + case 8: + case 9: + case 10: + case 43: + buttonState |= MouseFlags.ButtonAlt; + break; + case 14: + case 47: + buttonState |= MouseFlags.ButtonAlt | MouseFlags.ButtonShift; + break; + case 16: + case 17: + case 18: + case 51: + buttonState |= MouseFlags.ButtonCtrl; + break; + case 22: + case 55: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; + break; + case 24: + case 25: + case 26: + case 59: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; + break; + case 30: + case 63: + buttonState |= MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; + break; + case 32: + case 33: + case 34: + buttonState |= MouseFlags.ReportMousePosition; + break; + case 36: + case 37: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonShift; + break; + case 39: + case 68: + case 69: + buttonState |= MouseFlags.ButtonShift; + break; + case 40: + case 41: + case 42: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt; + break; + case 45: + case 46: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonAlt | MouseFlags.ButtonShift; + break; + case 48: + case 49: + case 50: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl; + break; + case 53: + case 54: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift; + break; + case 56: + case 57: + case 58: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonAlt; + break; + case 61: + case 62: + buttonState |= MouseFlags.ReportMousePosition | MouseFlags.ButtonCtrl | MouseFlags.ButtonShift | MouseFlags.ButtonAlt; + break; + } + } + } + + mouseFlags = new List () { MouseFlags.AllEvents }; + + if (lastMouseButtonPressed != null && !isButtonPressed && !buttonState.HasFlag (MouseFlags.ReportMousePosition) + && !buttonState.HasFlag (MouseFlags.Button1Released) + && !buttonState.HasFlag (MouseFlags.Button2Released) + && !buttonState.HasFlag (MouseFlags.Button3Released) + && !buttonState.HasFlag (MouseFlags.Button4Released)) { + + lastMouseButtonPressed = null; + isButtonPressed = false; + } + + if (!isButtonClicked && !isButtonDoubleClicked && ((buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed) && lastMouseButtonPressed == null) || + isButtonPressed && lastMouseButtonPressed != null && buttonState.HasFlag (MouseFlags.ReportMousePosition)) { + + mouseFlags [0] = buttonState; + lastMouseButtonPressed = buttonState; + isButtonPressed = true; + + if ((mouseFlags [0] & MouseFlags.ReportMousePosition) == 0) { + point = new Point () { + X = pos.X, + Y = pos.Y + }; + + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessContinuousButtonPressedAsync (buttonState, continuousButtonPressedHandler)); + return false; + }); + } else if (mouseFlags [0] == MouseFlags.ReportMousePosition) { + isButtonPressed = false; + } + + } else if (isButtonDoubleClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { + + mouseFlags [0] = GetButtonTripleClicked (buttonState); + isButtonDoubleClicked = false; + isButtonTripleClicked = true; + + } else if (isButtonClicked && (buttonState == MouseFlags.Button1Pressed || buttonState == MouseFlags.Button2Pressed || + buttonState == MouseFlags.Button3Pressed || buttonState == MouseFlags.Button4Pressed)) { + + mouseFlags [0] = GetButtonDoubleClicked (buttonState); + isButtonClicked = false; + isButtonDoubleClicked = true; + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); + return false; + }); + + } + //else if (isButtonReleased && !isButtonClicked && buttonState == MouseFlags.ReportMousePosition) { + // mouseFlag [0] = GetButtonClicked ((MouseFlags)lastMouseButtonReleased); + // lastMouseButtonReleased = null; + // isButtonReleased = false; + // isButtonClicked = true; + // Application.MainLoop.AddIdle (() => { + // Task.Run (async () => await ProcessButtonClickedAsync ()); + // return false; + // }); + + //} + else if (!isButtonClicked && !isButtonDoubleClicked && (buttonState == MouseFlags.Button1Released || buttonState == MouseFlags.Button2Released || + buttonState == MouseFlags.Button3Released || buttonState == MouseFlags.Button4Released)) { + + mouseFlags [0] = buttonState; + isButtonPressed = false; + + if (isButtonTripleClicked) { + isButtonTripleClicked = false; + } else if (pos.X == point.X && pos.Y == point.Y) { + mouseFlags.Add (GetButtonClicked (buttonState)); + isButtonClicked = true; + Application.MainLoop.AddIdle (() => { + Task.Run (async () => await ProcessButtonClickedAsync ()); + return false; + }); + } + + point = pos; + + //if ((lastMouseButtonPressed & MouseFlags.ReportMousePosition) == 0) { + // lastMouseButtonReleased = buttonState; + // isButtonPressed = false; + // isButtonReleased = true; + //} else { + // lastMouseButtonPressed = null; + // isButtonPressed = false; + //} + + } else if (buttonState == MouseFlags.WheeledUp) { + + mouseFlags [0] = MouseFlags.WheeledUp; + + } else if (buttonState == MouseFlags.WheeledDown) { + + mouseFlags [0] = MouseFlags.WheeledDown; + + } else if (buttonState == MouseFlags.WheeledLeft) { + + mouseFlags [0] = MouseFlags.WheeledLeft; + + } else if (buttonState == MouseFlags.WheeledRight) { + + mouseFlags [0] = MouseFlags.WheeledRight; + + } else if (buttonState == MouseFlags.ReportMousePosition) { + mouseFlags [0] = MouseFlags.ReportMousePosition; + + } else { + mouseFlags [0] = buttonState; + //foreach (var flag in buttonState.GetUniqueFlags()) { + // mouseFlag [0] |= flag; + //} + } + + mouseFlags [0] = SetControlKeyStates (buttonState, mouseFlags [0]); + //buttonState = mouseFlags; + + //System.Diagnostics.Debug.WriteLine ($"buttonState: {buttonState} X: {pos.X} Y: {pos.Y}"); + //foreach (var mf in mouseFlags) { + // System.Diagnostics.Debug.WriteLine ($"mouseFlags: {mf} X: {pos.X} Y: {pos.Y}"); + //} + } + + private static async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag, Action continuousButtonPressedHandler) + { + while (isButtonPressed) { + await Task.Delay (100); + //var me = new MouseEvent () { + // X = point.X, + // Y = point.Y, + // Flags = mouseFlag + //}; + + var view = Application.WantContinuousButtonPressedView; + if (view == null) + break; + if (isButtonPressed && lastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) { + Application.MainLoop.Invoke (() => continuousButtonPressedHandler (mouseFlag, point)); + } + } + } + + private static async Task ProcessButtonClickedAsync () + { + await Task.Delay (300); + isButtonClicked = false; + } + + private static async Task ProcessButtonDoubleClickedAsync () + { + await Task.Delay (300); + isButtonDoubleClicked = false; + } + + private static MouseFlags GetButtonClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Released: + mf = MouseFlags.Button1Clicked; + break; + + case MouseFlags.Button2Released: + mf = MouseFlags.Button2Clicked; + break; + + case MouseFlags.Button3Released: + mf = MouseFlags.Button3Clicked; + break; + } + return mf; + } + + private static MouseFlags GetButtonDoubleClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Pressed: + mf = MouseFlags.Button1DoubleClicked; + break; + + case MouseFlags.Button2Pressed: + mf = MouseFlags.Button2DoubleClicked; + break; + + case MouseFlags.Button3Pressed: + mf = MouseFlags.Button3DoubleClicked; + break; + } + return mf; + } + + private static MouseFlags GetButtonTripleClicked (MouseFlags mouseFlag) + { + MouseFlags mf = default; + switch (mouseFlag) { + case MouseFlags.Button1Pressed: + mf = MouseFlags.Button1TripleClicked; + break; + + case MouseFlags.Button2Pressed: + mf = MouseFlags.Button2TripleClicked; + break; + + case MouseFlags.Button3Pressed: + mf = MouseFlags.Button3TripleClicked; + break; + } + return mf; + } + + private static MouseFlags SetControlKeyStates (MouseFlags buttonState, MouseFlags mouseFlag) + { + if ((buttonState & MouseFlags.ButtonCtrl) != 0 && (mouseFlag & MouseFlags.ButtonCtrl) == 0) + mouseFlag |= MouseFlags.ButtonCtrl; + + if ((buttonState & MouseFlags.ButtonShift) != 0 && (mouseFlag & MouseFlags.ButtonShift) == 0) + mouseFlag |= MouseFlags.ButtonShift; + + if ((buttonState & MouseFlags.ButtonAlt) != 0 && (mouseFlag & MouseFlags.ButtonAlt) == 0) + mouseFlag |= MouseFlags.ButtonAlt; + return mouseFlag; + } + + /// + /// Get the terminal that holds the console driver. + /// + /// The process. + /// If supported the executable console process, null otherwise. + public static Process GetParentProcess (Process process) + { + if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) { + return null; + } + + string query = "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = " + process.Id; + using (ManagementObjectSearcher mos = new ManagementObjectSearcher (query)) { + foreach (ManagementObject mo in mos.Get ()) { + if (mo ["ParentProcessId"] != null) { + try { + var id = Convert.ToInt32 (mo ["ParentProcessId"]); + return Process.GetProcessById (id); + } catch { + } + } + } + } + return null; + } + } +} diff --git a/Terminal.Gui/Core/Event.cs b/Terminal.Gui/Core/Event.cs index 4203faa860..a46bbf7e53 100644 --- a/Terminal.Gui/Core/Event.cs +++ b/Terminal.Gui/Core/Event.cs @@ -749,7 +749,7 @@ public enum MouseFlags { /// WheeledUp = unchecked((int)0x10000000), /// - /// Vertical button wheeled up. + /// Vertical button wheeled down. /// WheeledDown = unchecked((int)0x20000000), /// diff --git a/Terminal.Gui/Core/Graphs/LineCanvas.cs b/Terminal.Gui/Core/Graphs/LineCanvas.cs index 04583518eb..1b1cbe76bf 100644 --- a/Terminal.Gui/Core/Graphs/LineCanvas.cs +++ b/Terminal.Gui/Core/Graphs/LineCanvas.cs @@ -11,9 +11,25 @@ namespace Terminal.Gui.Graphs { /// public class LineCanvas { - + private List lines = new List (); + Dictionary runeResolvers = new Dictionary { + {IntersectionRuneType.ULCorner,new ULIntersectionRuneResolver()}, + {IntersectionRuneType.URCorner,new URIntersectionRuneResolver()}, + {IntersectionRuneType.LLCorner,new LLIntersectionRuneResolver()}, + {IntersectionRuneType.LRCorner,new LRIntersectionRuneResolver()}, + + {IntersectionRuneType.TopTee,new TopTeeIntersectionRuneResolver()}, + {IntersectionRuneType.LeftTee,new LeftTeeIntersectionRuneResolver()}, + {IntersectionRuneType.RightTee,new RightTeeIntersectionRuneResolver()}, + {IntersectionRuneType.BottomTee,new BottomTeeIntersectionRuneResolver()}, + + + {IntersectionRuneType.Crosshair,new CrosshairIntersectionRuneResolver()}, + // TODO: Add other resolvers + }; + /// /// Add a new line to the canvas starting at . /// Use positive for Right and negative for Left @@ -32,21 +48,22 @@ public void AddLine (Point from, int length, Orientation orientation, BorderStyl } /// /// Evaluate all currently defined lines that lie within - /// and generate a 'bitmap' that + /// and map that /// shows what characters (if any) should be rendered at each /// point so that all lines connect up correctly with appropriate /// intersection symbols. /// /// /// - /// Map as 2D array where first index is rows and second is column - public Rune? [,] GenerateImage (Rect inArea) + /// Mapping of all the points within to + /// line or intersection runes which should be drawn there. + public Dictionary GenerateImage (Rect inArea) { - Rune? [,] canvas = new Rune? [inArea.Height, inArea.Width]; + var map = new Dictionary(); // walk through each pixel of the bitmap - for (int y = 0; y < inArea.Height; y++) { - for (int x = 0; x < inArea.Width; x++) { + for (int y = inArea.Y; y < inArea.Height; y++) { + for (int x = inArea.X; x < inArea.Width; x++) { var intersects = lines .Select (l => l.Intersects (x, y)) @@ -54,33 +71,118 @@ public void AddLine (Point from, int length, Orientation orientation, BorderStyl .ToArray (); // TODO: use Driver and LineStyle to map - canvas [y, x] = GetRuneForIntersects (Application.Driver, intersects); + var rune = GetRuneForIntersects (Application.Driver, intersects); + if(rune != null) + { + map.Add(new Point(x,y),rune.Value); + } } } - return canvas; + return map; } - /// - /// Draws all the lines that lie within the onto - /// the client area. This method should be called from - /// . - /// - /// - /// - public void Draw (View view, Rect bounds) - { - var runes = GenerateImage (bounds); + private abstract class IntersectionRuneResolver { + readonly Rune round; + readonly Rune doubleH; + readonly Rune doubleV; + readonly Rune doubleBoth; + readonly Rune normal; - for (int y = bounds.Y; y < bounds.Height; y++) { - for (int x = bounds.X; x < bounds.Width; x++) { - var rune = runes [y, x]; + public IntersectionRuneResolver (Rune round, Rune doubleH, Rune doubleV, Rune doubleBoth, Rune normal) + { + this.round = round; + this.doubleH = doubleH; + this.doubleV = doubleV; + this.doubleBoth = doubleBoth; + this.normal = normal; + } - if (rune.HasValue) { - view.AddRune (x, y, rune.Value); - } + public Rune? GetRuneForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects) + { + var useRounded = intersects.Any (i => i.Line.Style == BorderStyle.Rounded && i.Line.Length != 0); + + bool doubleHorizontal = intersects.Any (l => l.Line.Orientation == Orientation.Horizontal && l.Line.Style == BorderStyle.Double); + bool doubleVertical = intersects.Any (l => l.Line.Orientation == Orientation.Vertical && l.Line.Style == BorderStyle.Double); + + + if (doubleHorizontal) { + return doubleVertical ? doubleBoth : doubleH; } + + if (doubleVertical) { + return doubleV; + } + + return useRounded ? round : normal; + } + } + + private class ULIntersectionRuneResolver : IntersectionRuneResolver { + public ULIntersectionRuneResolver () : + base ('╭', '╒', '╓', '╔', '┌') + { + + } + } + private class URIntersectionRuneResolver : IntersectionRuneResolver { + + public URIntersectionRuneResolver () : + base ('╮', '╕', '╖', '╗', '┐') + { + + } + } + private class LLIntersectionRuneResolver : IntersectionRuneResolver { + + public LLIntersectionRuneResolver () : + base ('╰', '╘', '╙', '╚', '└') + { + + } + } + private class LRIntersectionRuneResolver : IntersectionRuneResolver { + public LRIntersectionRuneResolver () : + base ('╯', '╛', '╜', '╝', '┘') + { + + } + } + + private class TopTeeIntersectionRuneResolver : IntersectionRuneResolver { + public TopTeeIntersectionRuneResolver () : + base ('┬', '╤', '╥', '╦', '┬') + { + + } + } + private class LeftTeeIntersectionRuneResolver : IntersectionRuneResolver { + public LeftTeeIntersectionRuneResolver () : + base ('├', '╞', '╟', '╠', '├') + { + + } + } + private class RightTeeIntersectionRuneResolver : IntersectionRuneResolver { + public RightTeeIntersectionRuneResolver () : + base ('┤', '╡', '╢', '╣', '┤') + { + + } + } + private class BottomTeeIntersectionRuneResolver : IntersectionRuneResolver { + public BottomTeeIntersectionRuneResolver () : + base ('┴', '╧', '╨', '╩', '┴') + { + + } + } + private class CrosshairIntersectionRuneResolver : IntersectionRuneResolver { + public CrosshairIntersectionRuneResolver () : + base ('┼', '╪', '╫', '╬', '┼') + { + } } @@ -90,45 +192,34 @@ public void Draw (View view, Rect bounds) return null; var runeType = GetRuneTypeForIntersects (intersects); + + if (runeResolvers.ContainsKey (runeType)) { + return runeResolvers [runeType].GetRuneForIntersects (driver, intersects); + } + + // TODO: Remove these two once we have all of the below ported to IntersectionRuneResolvers var useDouble = intersects.Any (i => i.Line.Style == BorderStyle.Double && i.Line.Length != 0); var useRounded = intersects.Any (i => i.Line.Style == BorderStyle.Rounded && i.Line.Length != 0); + // TODO: maybe make these resolvers to for simplicity? + // or for dotted lines later on or that kind of thing? switch (runeType) { - case IntersectionRuneType.None: + case IntersectionRuneType.None: return null; - case IntersectionRuneType.Dot: + case IntersectionRuneType.Dot: return (Rune)'.'; - case IntersectionRuneType.ULCorner: - return useDouble ? driver.ULDCorner : useRounded ? driver.ULRCorner : driver.ULCorner; - case IntersectionRuneType.URCorner: - return useDouble ? driver.URDCorner : useRounded ? driver.URRCorner : driver.URCorner; - case IntersectionRuneType.LLCorner: - return useDouble ? driver.LLDCorner : useRounded ? driver.LLRCorner : driver.LLCorner; - case IntersectionRuneType.LRCorner: - return useDouble ? driver.LRDCorner : useRounded ? driver.LRRCorner : driver.LRCorner; - case IntersectionRuneType.TopTee: - return useDouble ? '╦' : driver.TopTee; - case IntersectionRuneType.BottomTee: - return useDouble ? '╩' : driver.BottomTee; - case IntersectionRuneType.RightTee: - return useDouble ? '╣' : driver.RightTee; - case IntersectionRuneType.LeftTee: - return useDouble ? '╠' : driver.LeftTee; - case IntersectionRuneType.Crosshair: - return useDouble ? '╬' : '┼'; - case IntersectionRuneType.HLine: + case IntersectionRuneType.HLine: return useDouble ? driver.HDLine : driver.HLine; - case IntersectionRuneType.VLine: + case IntersectionRuneType.VLine: return useDouble ? driver.VDLine : driver.VLine; - default: throw new ArgumentOutOfRangeException (nameof (runeType)); + default: throw new Exception ("Could not find resolver or switch case for " + nameof (runeType) + ":" + runeType); } - } private IntersectionRuneType GetRuneTypeForIntersects (IntersectionDefinition [] intersects) { - if(intersects.All(i=>i.Line.Length == 0)) { + if (intersects.All (i => i.Line.Length == 0)) { return IntersectionRuneType.Dot; } diff --git a/Terminal.Gui/Core/TextFormatter.cs b/Terminal.Gui/Core/TextFormatter.cs index 50fc9f5ae6..e6e1592e3d 100644 --- a/Terminal.Gui/Core/TextFormatter.cs +++ b/Terminal.Gui/Core/TextFormatter.cs @@ -1190,7 +1190,9 @@ public void Draw (Rect bounds, Attribute normalColor, Attribute hotColor, Rect c for (int line = 0; line < linesFormated.Count; line++) { if ((isVertical && line > bounds.Width) || (!isVertical && line > bounds.Height)) continue; - if ((isVertical && line > maxBounds.Left + maxBounds.Width - bounds.X) || (!isVertical && line > maxBounds.Top + maxBounds.Height - bounds.Y)) + if ((isVertical && line >= maxBounds.Left + maxBounds.Width) + || (!isVertical && line >= maxBounds.Top + maxBounds.Height)) + break; var runes = lines [line].ToRunes (); diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index fddabb692e..02691b84f2 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -821,7 +821,6 @@ public override bool MouseEvent (MouseEvent mouseEvent) if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && dragPosition.HasValue) { Application.UngrabMouse (); - Driver.UncookMouse (); dragPosition = null; } @@ -909,6 +908,12 @@ public override void PositionCursor () { if (!IsMdiContainer) { base.PositionCursor (); + if (Focused == null) { + EnsureFocus (); + if (Focused == null) { + Driver.SetCursorVisibility (CursorVisibility.Invisible); + } + } return; } @@ -921,6 +926,9 @@ public override void PositionCursor () } } base.PositionCursor (); + if (Focused == null) { + Driver.SetCursorVisibility (CursorVisibility.Invisible); + } } /// @@ -960,6 +968,18 @@ public virtual bool ShowChild (Toplevel top = null) } return false; } + + /// + public override bool OnEnter (View view) + { + return MostFocused?.OnEnter (view) ?? base.OnEnter (view); + } + + /// + public override bool OnLeave (View view) + { + return MostFocused?.OnLeave (view) ?? base.OnLeave (view); + } } /// diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 8734261c0b..018cf1384f 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -951,6 +951,10 @@ public virtual void Add (View view) view.tabIndex = tabIndexes.IndexOf (view); addingView = false; } + if (view.Enabled && !Enabled) { + view.oldEnabled = true; + view.Enabled = false; + } SetNeedsLayout (); SetNeedsDisplay (); OnAdded (view); @@ -1105,15 +1109,8 @@ public void BringSubviewForward (View subview) /// public void Clear () { - Rect containerBounds = GetContainerBounds (); - Rect viewBounds = Bounds; - if (!containerBounds.IsEmpty) { - viewBounds.Width = Math.Min (viewBounds.Width, containerBounds.Width); - viewBounds.Height = Math.Min (viewBounds.Height, containerBounds.Height); - } - - var h = viewBounds.Height; - var w = viewBounds.Width; + var h = Frame.Height; + var w = Frame.Width; for (var line = 0; line < h; line++) { Move (0, line); for (var col = 0; col < w; col++) @@ -1310,14 +1307,16 @@ public virtual void PositionCursor () return; } - if (focused?.Visible == true && focused?.Enabled == true && focused?.Frame.Width > 0 && focused.Frame.Height > 0) { + if (focused == null && SuperView != null) { + SuperView.EnsureFocus (); + } else if (focused?.Visible == true && focused?.Enabled == true && focused?.Frame.Width > 0 && focused.Frame.Height > 0) { focused.PositionCursor (); + } else if (focused?.Visible == true && focused?.Enabled == false) { + focused = null; + } else if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0) { + Move (TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition, 0); } else { - if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0) { - Move (TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition, 0); - } else { - Move (frame.X, frame.Y); - } + Move (frame.X, frame.Y); } } @@ -1447,8 +1446,9 @@ public View MostFocused { /// public virtual ColorScheme ColorScheme { get { - if (colorScheme == null) + if (colorScheme == null) { return SuperView?.ColorScheme; + } return colorScheme; } set { @@ -1510,13 +1510,13 @@ public virtual void Redraw (Rect bounds) var clipRect = new Rect (Point.Empty, frame.Size); if (ColorScheme != null) { - Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); + Driver.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ()); } - if (Border != null) { + if (!IgnoreBorderPropertyOnRedraw && Border != null) { Border.DrawContent (this); } else if (ustring.IsNullOrEmpty (TextFormatter.Text) && - (GetType ().IsNestedPublic) && !IsOverridden (this, "Redraw") && + (GetType ().IsNestedPublic && !IsOverridden (this, "Redraw") || GetType ().Name == "View") && (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) { Clear (); @@ -1524,15 +1524,15 @@ public virtual void Redraw (Rect bounds) } if (!ustring.IsNullOrEmpty (TextFormatter.Text)) { - Clear (); + Rect containerBounds = GetContainerBounds (); + Clear (ViewToScreen (GetNeedDisplay (containerBounds))); SetChildNeedsDisplay (); // Draw any Text if (TextFormatter != null) { TextFormatter.NeedsFormat = true; } - Rect containerBounds = GetContainerBounds (); - TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : GetNormalColor (), - HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled, + TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (), + HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (), containerBounds); } @@ -1568,14 +1568,35 @@ public virtual void Redraw (Rect bounds) ClearNeedsDisplay (); } + Rect GetNeedDisplay (Rect containerBounds) + { + Rect rect = NeedDisplay; + if (!containerBounds.IsEmpty) { + rect.Width = Math.Min (NeedDisplay.Width, containerBounds.Width); + rect.Height = Math.Min (NeedDisplay.Height, containerBounds.Height); + } + + return rect; + } + Rect GetContainerBounds () { var containerBounds = SuperView == null ? default : SuperView.ViewToScreen (SuperView.Bounds); var driverClip = Driver == null ? Rect.Empty : Driver.Clip; containerBounds.X = Math.Max (containerBounds.X, driverClip.X); containerBounds.Y = Math.Max (containerBounds.Y, driverClip.Y); - containerBounds.Width = Math.Min (containerBounds.Width, driverClip.Width); - containerBounds.Height = Math.Min (containerBounds.Height, driverClip.Height); + var lenOffset = (driverClip.X + driverClip.Width) - (containerBounds.X + containerBounds.Width); + if (containerBounds.X + containerBounds.Width > driverClip.X + driverClip.Width) { + containerBounds.Width = Math.Max (containerBounds.Width + lenOffset, 0); + } else { + containerBounds.Width = Math.Min (containerBounds.Width, driverClip.Width); + } + lenOffset = (driverClip.Y + driverClip.Height) - (containerBounds.Y + containerBounds.Height); + if (containerBounds.Y + containerBounds.Height > driverClip.Y + driverClip.Height) { + containerBounds.Height = Math.Max (containerBounds.Height + lenOffset, 0); + } else { + containerBounds.Height = Math.Min (containerBounds.Height, driverClip.Height); + } return containerBounds; } @@ -2602,7 +2623,13 @@ public override bool Enabled { get => base.Enabled; set { if (base.Enabled != value) { - base.Enabled = value; + if (value) { + if (SuperView == null || SuperView?.Enabled == true) { + base.Enabled = value; + } + } else { + base.Enabled = value; + } if (!value && HasFocus) { SetHasFocus (false, this); } @@ -2662,6 +2689,15 @@ public virtual Border Border { } } + /// + /// Get or sets whether the view will use (if is set) to draw + /// a border. If (the default), + /// will call + /// to draw the view's border. If no border is drawn (and the view is expected to draw the border + /// itself). + /// + public virtual bool IgnoreBorderPropertyOnRedraw { get; set; } + /// /// Pretty prints the View /// @@ -3085,6 +3121,17 @@ public virtual Attribute GetNormalColor () return Enabled ? ColorScheme.Normal : ColorScheme.Disabled; } + /// + /// Determines the current based on the value. + /// + /// if is + /// or if is . + /// If it's overridden can return other values. + public virtual Attribute GetFocusColor () + { + return Enabled ? ColorScheme.Focus : ColorScheme.Disabled; + } + /// /// Determines the current based on the value. /// diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index ce22fd3cd0..0bab49cbfd 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -10,9 +10,10 @@ - 1.9 - 1.9 - 1.9 + 1.0 + 1.0 + 1.0 + 1.0 @@ -52,6 +53,7 @@ + net472;netstandard2.0;net6.0 diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 14fc563940..f8c4e23daa 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -1064,8 +1064,8 @@ public override bool MouseEvent (MouseEvent ev) EnsureHasFocus (); int x = PositionCursor (ev); int sbw = x; - if (x == text.Count || (x > 0 && (char)Text [x - 1] != ' ' - || (x > 0 && (char)Text [x] == ' '))) { + if (x == text.Count || (x > 0 && (char)text [x - 1] != ' ') + || (x > 0 && (char)text [x] == ' ')) { sbw = WordBackward (x); } @@ -1346,12 +1346,12 @@ protected override void DeleteTextBackwards () } /// - protected override string GetCurrentWord () + protected override string GetCurrentWord (int columnOffset = 0) { var host = (TextField)HostControl; var currentLine = host.Text.ToRuneList (); - var cursorPosition = Math.Min (host.CursorPosition, currentLine.Count); - return IdxToWord (currentLine, cursorPosition); + var cursorPosition = Math.Min (host.CursorPosition + columnOffset, currentLine.Count); + return IdxToWord (currentLine, cursorPosition, columnOffset); } /// diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 3c9e54b09e..7cb797b11c 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2035,6 +2035,16 @@ public override bool OnEnter (View view) return base.OnEnter (view); } + /// + public override bool OnLeave (View view) + { + if (Application.MouseGrabView != null && Application.MouseGrabView == this) { + Application.UngrabMouse (); + } + + return base.OnLeave (view); + } + // Returns an encoded region start..end (top 32 bits are the row, low32 the column) void GetEncodedRegionBounds (out long start, out long end, int? startRow = null, int? startCol = null, int? cRow = null, int? cCol = null) @@ -2437,6 +2447,10 @@ public override void Redraw (Rect bounds) PositionCursor (); + if (clickWithSelecting) { + clickWithSelecting = false; + return; + } if (SelectedLength > 0) return; @@ -2667,8 +2681,10 @@ void Adjust () need = true; } else if ((wordWrap && leftColumn > 0) || (dSize.size + RightOffset < Frame.Width + offB.width && tSize.size + RightOffset < Frame.Width + offB.width)) { - leftColumn = 0; - need = true; + if (leftColumn > 0) { + leftColumn = 0; + need = true; + } } if (currentRow < topRow) { @@ -4269,6 +4285,7 @@ void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune) } bool isButtonShift; + bool clickWithSelecting; /// public override bool MouseEvent (MouseEvent ev) @@ -4362,6 +4379,7 @@ public override bool MouseEvent (MouseEvent ev) columnTrack = currentColumn; } else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed)) { if (shiftSelecting) { + clickWithSelecting = true; StopSelecting (); } ProcessMouseClick (ev, out _); @@ -4447,16 +4465,6 @@ void ProcessMouseClick (MouseEvent ev, out List line) line = r; } - /// - public override bool OnLeave (View view) - { - if (Application.MouseGrabView != null && Application.MouseGrabView == this) { - Application.UngrabMouse (); - } - - return base.OnLeave (view); - } - /// /// Allows clearing the items updating the original text. /// @@ -4475,12 +4483,12 @@ public void ClearHistoryChanges () public class TextViewAutocomplete : Autocomplete { /// - protected override string GetCurrentWord () + protected override string GetCurrentWord (int columnOffset = 0) { var host = (TextView)HostControl; var currentLine = host.GetCurrentLine (); - var cursorPosition = Math.Min (host.CurrentColumn, currentLine.Count); - return IdxToWord (currentLine, cursorPosition); + var cursorPosition = Math.Min (host.CurrentColumn + columnOffset, currentLine.Count); + return IdxToWord (currentLine, cursorPosition, columnOffset); } /// diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json index b4df9cb03a..4fe02b923b 100644 --- a/UICatalog/Properties/launchSettings.json +++ b/UICatalog/Properties/launchSettings.json @@ -3,6 +3,12 @@ "UICatalog": { "commandName": "Project" }, + "WSL : UICatalog": { + "commandName": "Executable", + "executablePath": "wsl", + "commandLineArgs": "dotnet UICatalog.dll", + "distributionName": "" + }, "UICatalog -usc": { "commandName": "Project", "commandLineArgs": "-usc" @@ -29,10 +35,6 @@ "commandName": "Project", "commandLineArgs": "WizardAsView" }, - "VkeyPacketSimulator": { - "commandName": "Project", - "commandLineArgs": "VkeyPacketSimulator" - }, "CollectionNavigatorTester": { "commandName": "Project", "commandLineArgs": "\"Search Collection Nav\"" @@ -48,12 +50,6 @@ "Windows & FrameViews": { "commandName": "Project", "commandLineArgs": "\"Windows & FrameViews\"" - }, - "WSL : UICatalog": { - "commandName": "Executable", - "executablePath": "wsl", - "commandLineArgs": "dotnet UICatalog.dll", - "distributionName": "" } } } \ No newline at end of file diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs new file mode 100644 index 0000000000..77cacf8b95 --- /dev/null +++ b/UICatalog/Scenarios/ASCIICustomButton.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Terminal.Gui; + +namespace UICatalog.Scenarios { + [ScenarioMetadata (Name: "ASCIICustomButtonTest", Description: "ASCIICustomButton sample")] + [ScenarioCategory ("Controls")] + public class ASCIICustomButtonTest : Scenario { + private static bool smallerWindow; + private ScrollViewTestWindow scrollViewTestWindow; + private MenuItem miSmallerWindow; + + public override void Init (ColorScheme colorScheme) + { + Application.Init (); + scrollViewTestWindow = new ScrollViewTestWindow (); + var menu = new MenuBar (new MenuBarItem [] { + new MenuBarItem("Window Size", new MenuItem [] { + miSmallerWindow = new MenuItem ("Smaller Window", "", ChangeWindowSize) { + CheckType = MenuItemCheckStyle.Checked + }, + null, + new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Key.Q | Key.CtrlMask) + }) + }); + Application.Top.Add (menu, scrollViewTestWindow); + Application.Run (); + } + + private void ChangeWindowSize () + { + smallerWindow = miSmallerWindow.Checked = !miSmallerWindow.Checked; + scrollViewTestWindow.Dispose (); + Application.Top.Remove (scrollViewTestWindow); + scrollViewTestWindow = new ScrollViewTestWindow (); + Application.Top.Add (scrollViewTestWindow); + } + + public override void Run () + { + } + + public class ASCIICustomButton : Button { + public string Description => $"Description of: {id}"; + + public event Action PointerEnter; + + private Label fill; + private FrameView border; + private string id; + + public ASCIICustomButton (string text, Pos x, Pos y, int width, int height) : base (text) + { + CustomInitialize ("", text, x, y, width, height); + } + + public ASCIICustomButton (string id, string text, Pos x, Pos y, int width, int height) : base (text) + { + CustomInitialize (id, text, x, y, width, height); + } + + private void CustomInitialize (string id, string text, Pos x, Pos y, int width, int height) + { + this.id = id; + X = x; + Y = y; + + Frame = new Rect { + Width = width, + Height = height + }; + + border = new FrameView () { + Width = width, + Height = height + }; + + AutoSize = false; + + var fillText = new System.Text.StringBuilder (); + for (int i = 0; i < Bounds.Height; i++) { + if (i > 0) { + fillText.AppendLine (""); + } + for (int j = 0; j < Bounds.Width; j++) { + fillText.Append ("█"); + } + } + + fill = new Label (fillText.ToString ()) { + Visible = false, + CanFocus = false + }; + + var title = new Label (text) { + X = Pos.Center (), + Y = Pos.Center (), + }; + + border.MouseClick += This_MouseClick; + border.Subviews [0].MouseClick += This_MouseClick; + fill.MouseClick += This_MouseClick; + title.MouseClick += This_MouseClick; + + Add (border, fill, title); + } + + private void This_MouseClick (MouseEventArgs obj) + { + OnMouseEvent (obj.MouseEvent); + } + + public override bool OnMouseEvent (MouseEvent mouseEvent) + { + Debug.WriteLine ($"{mouseEvent.Flags}"); + if (mouseEvent.Flags == MouseFlags.Button1Clicked) { + if (!HasFocus && SuperView != null) { + if (!SuperView.HasFocus) { + SuperView.SetFocus (); + } + SetFocus (); + SetNeedsDisplay (); + } + + OnClicked (); + return true; + } + return base.OnMouseEvent (mouseEvent); + } + + public override bool OnEnter (View view) + { + border.Visible = false; + fill.Visible = true; + PointerEnter.Invoke (this); + view = this; + return base.OnEnter (view); + } + + public override bool OnLeave (View view) + { + border.Visible = true; + fill.Visible = false; + if (view == null) + view = this; + return base.OnLeave (view); + } + } + + public class ScrollViewTestWindow : Window { + private List [Fact, AutoInitShutdown] - public void TestLineCanvas_Corner_NoOverlap() + public void TestLineCanvas_Corner_NoOverlap () { var v = GetCanvas (out var canvas); canvas.AddLine (new Point (0, 0), 1, Orientation.Horizontal, BorderStyle.Single); @@ -127,13 +130,14 @@ public void TestLineCanvas_Corner_Correct () │ │"; TestHelpers.AssertDriverContentsAre (looksLike, output); - + } - [Fact,AutoInitShutdown] + + [Fact, AutoInitShutdown] public void TestLineCanvas_Window () { var v = GetCanvas (out var canvas); - + // outer box canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, BorderStyle.Single); canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, BorderStyle.Single); @@ -168,10 +172,10 @@ public void TestLineCanvas_Window_Rounded () // outer box canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, BorderStyle.Rounded); - + // BorderStyle.Single is ignored because corner overlaps with the above line which is Rounded // this results in a rounded corner being used. - canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, BorderStyle.Single); + canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, BorderStyle.Single); canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, BorderStyle.Rounded); canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, BorderStyle.Single); @@ -218,7 +222,106 @@ public void TestLineCanvas_Window_Double () TestHelpers.AssertDriverContentsAre (looksLike, output); } - private View GetCanvas (out LineCanvas canvas) + + [Theory, AutoInitShutdown] + [InlineData (BorderStyle.Single)] + [InlineData (BorderStyle.Rounded)] + public void TestLineCanvas_Window_DoubleTop_SingleSides (BorderStyle thinStyle) + { + var v = GetCanvas (out var canvas); + + // outer box + canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, BorderStyle.Double); + canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, thinStyle); + canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, BorderStyle.Double); + canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, thinStyle); + + + canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, thinStyle); + canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, BorderStyle.Double); + + v.Redraw (v.Bounds); + + string looksLike = +@" +╒════╤═══╕ +│ │ │ +╞════╪═══╡ +│ │ │ +╘════╧═══╛ +"; + TestHelpers.AssertDriverContentsAre (looksLike, output); + } + + [Theory, AutoInitShutdown] + [InlineData (BorderStyle.Single)] + [InlineData (BorderStyle.Rounded)] + public void TestLineCanvas_Window_SingleTop_DoubleSides (BorderStyle thinStyle) + { + var v = GetCanvas (out var canvas); + + // outer box + canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, thinStyle); + canvas.AddLine (new Point (9, 0), 4, Orientation.Vertical, BorderStyle.Double); + canvas.AddLine (new Point (9, 4), -9, Orientation.Horizontal, thinStyle); + canvas.AddLine (new Point (0, 4), -4, Orientation.Vertical, BorderStyle.Double); + + + canvas.AddLine (new Point (5, 0), 4, Orientation.Vertical, BorderStyle.Double); + canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, thinStyle); + + v.Redraw (v.Bounds); + + string looksLike = +@" +╓────╥───╖ +║ ║ ║ +╟────╫───╢ +║ ║ ║ +╙────╨───╜ + +"; + TestHelpers.AssertDriverContentsAre (looksLike, output); + } + + [Fact, AutoInitShutdown] + public void TestLineCanvas_LeaveMargin_Top1_Left1 () + { + // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1) + var v = GetCanvas (out var canvas, 1, 1); + + // outer box + canvas.AddLine (new Point (0, 0), 8, Orientation.Horizontal, BorderStyle.Single); + canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, BorderStyle.Single); + canvas.AddLine (new Point (8, 3), -8, Orientation.Horizontal, BorderStyle.Single); + canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, BorderStyle.Single); + + + canvas.AddLine (new Point (5, 0), 3, Orientation.Vertical, BorderStyle.Single); + canvas.AddLine (new Point (0, 2), 8, Orientation.Horizontal, BorderStyle.Single); + + v.Redraw (v.Bounds); + + string looksLike = +@" + ┌────┬──┐ + │ │ │ + ├────┼──┤ + └────┴──┘ +"; + TestHelpers.AssertDriverContentsAre (looksLike, output); + } + + + /// + /// Creates a new into which a is rendered + /// at time. + /// + /// The you can draw into. + /// How far to offset drawing in X + /// How far to offset drawing in Y + /// + private View GetCanvas (out LineCanvas canvas, int offsetX = 0, int offsetY = 0) { var v = new View { Width = 10, @@ -226,8 +329,16 @@ private View GetCanvas (out LineCanvas canvas) Bounds = new Rect (0, 0, 10, 5) }; - var canvasCopy = canvas = new LineCanvas (); - v.DrawContentComplete += (r)=> canvasCopy.Draw (v, v.Bounds); + var canvasCopy = canvas = new LineCanvas (); + v.DrawContentComplete += (r) => { + foreach(var p in canvasCopy.GenerateImage(v.Bounds)) + { + v.AddRune( + offsetX + p.Key.X, + offsetY + p.Key.Y, + p.Value); + } + }; return v; } diff --git a/UnitTests/Drivers/AttributeTests.cs b/UnitTests/Drivers/AttributeTests.cs index ddc7b4695c..f2130f220d 100644 --- a/UnitTests/Drivers/AttributeTests.cs +++ b/UnitTests/Drivers/AttributeTests.cs @@ -85,7 +85,34 @@ public void Implicit_Assign () } [Fact] - public void Make_Asserts_IfNotInit () + public void Implicit_Assign_NoDriver () + { + + var attr = new Attribute (); + + var fg = new Color (); + fg = Color.Red; + + var bg = new Color (); + bg = Color.Blue; + + // Test conversion to int + attr = new Attribute (fg, bg); + int value_implicit = (int)attr.Value; + Assert.False (attr.Initialized); + + Assert.Equal (-1, value_implicit); + Assert.False (attr.Initialized); + + // Test conversion from int + attr = -1; + Assert.Equal (-1, attr.Value); + Assert.False (attr.Initialized); + + } + + [Fact] + public void Make_SetsNotInitialized_NoDriver () { var fg = new Color (); fg = Color.Red; @@ -93,7 +120,9 @@ public void Make_Asserts_IfNotInit () var bg = new Color (); bg = Color.Blue; - Assert.Throws (() => Attribute.Make (fg, bg)); + var a = Attribute.Make (fg, bg); + + Assert.False (a.Initialized); } [Fact] @@ -109,8 +138,8 @@ public void Make_Creates () var bg = new Color (); bg = Color.Blue; - var attr = Attribute.Make (fg, bg); - + var attr = Attribute.Make (fg, bg); + Assert.True (attr.Initialized); Assert.Equal (fg, attr.Foreground); Assert.Equal (bg, attr.Background); @@ -119,7 +148,23 @@ public void Make_Creates () } [Fact] - public void Get_Asserts_IfNotInit () + public void Make_Creates_NoDriver () + { + + var fg = new Color (); + fg = Color.Red; + + var bg = new Color (); + bg = Color.Blue; + + var attr = Attribute.Make (fg, bg); + Assert.False (attr.Initialized); + Assert.Equal (fg, attr.Foreground); + Assert.Equal (bg, attr.Background); + } + + [Fact] + public void Get_Asserts_NoDriver () { Assert.Throws (() => Attribute.Get ()); } @@ -163,5 +208,24 @@ public void GetColors_Based_On_Value () Assert.Equal (Color.Red, fg); Assert.Equal (Color.Green, bg); } + + [Fact] + public void IsValid_Tests () + { + var attr = new Attribute (); + Assert.True (attr.HasValidColors); + + attr = new Attribute (Color.Red, Color.Green); + Assert.True (attr.HasValidColors); + + attr = new Attribute (Color.Red, (Color)(-1)); + Assert.False (attr.HasValidColors); + + attr = new Attribute ((Color)(-1), Color.Green); + Assert.False (attr.HasValidColors); + + attr = new Attribute ((Color)(-1), (Color)(-1)); + Assert.False (attr.HasValidColors); + } } } diff --git a/UnitTests/Drivers/ColorTests.cs b/UnitTests/Drivers/ColorTests.cs index f42463c818..2ac8ad14de 100644 --- a/UnitTests/Drivers/ColorTests.cs +++ b/UnitTests/Drivers/ColorTests.cs @@ -35,5 +35,43 @@ public void SetColors_Changes_Colors (Type driverType) Application.Shutdown (); } + [Fact, AutoInitShutdown] + public void ColorScheme_New () + { + var scheme = new ColorScheme (); + var lbl = new Label (); + lbl.ColorScheme = scheme; + lbl.Redraw (lbl.Bounds); + } + + [Fact] + public void TestAllColors () + { + var colors = System.Enum.GetValues (typeof (Color)); + Attribute [] attrs = new Attribute [colors.Length]; + + int idx = 0; + foreach (Color bg in colors) { + attrs [idx] = new Attribute (bg, colors.Length - 1 - bg); + idx++; + } + Assert.Equal (16, attrs.Length); + Assert.Equal (new Attribute (Color.Black, Color.White), attrs [0]); + Assert.Equal (new Attribute (Color.Blue, Color.BrightYellow), attrs [1]); + Assert.Equal (new Attribute (Color.Green, Color.BrightMagenta), attrs [2]); + Assert.Equal (new Attribute (Color.Cyan, Color.BrightRed), attrs [3]); + Assert.Equal (new Attribute (Color.Red, Color.BrightCyan), attrs [4]); + Assert.Equal (new Attribute (Color.Magenta, Color.BrightGreen), attrs [5]); + Assert.Equal (new Attribute (Color.Brown, Color.BrightBlue), attrs [6]); + Assert.Equal (new Attribute (Color.Gray, Color.DarkGray), attrs [7]); + Assert.Equal (new Attribute (Color.DarkGray, Color.Gray), attrs [8]); + Assert.Equal (new Attribute (Color.BrightBlue, Color.Brown), attrs [9]); + Assert.Equal (new Attribute (Color.BrightGreen, Color.Magenta), attrs [10]); + Assert.Equal (new Attribute (Color.BrightCyan, Color.Red), attrs [11]); + Assert.Equal (new Attribute (Color.BrightRed, Color.Cyan), attrs [12]); + Assert.Equal (new Attribute (Color.BrightMagenta, Color.Green), attrs [13]); + Assert.Equal (new Attribute (Color.BrightYellow, Color.Blue), attrs [14]); + Assert.Equal (new Attribute (Color.White, Color.Black), attrs [^1]); + } } } \ No newline at end of file diff --git a/UnitTests/Drivers/ConsoleDriverTests.cs b/UnitTests/Drivers/ConsoleDriverTests.cs index ef45894b10..6444b8f1c0 100644 --- a/UnitTests/Drivers/ConsoleDriverTests.cs +++ b/UnitTests/Drivers/ConsoleDriverTests.cs @@ -319,7 +319,7 @@ public void EnableConsoleScrolling_Is_True_Top_Cannot_Be_Greater_Than_BufferHeig Application.Shutdown (); } - + [Fact, AutoInitShutdown] public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space () { @@ -441,7 +441,7 @@ public void MakePrintable_Does_Not_Convert_Ansi_Chars_To_Unicode (uint code) } private static object packetLock = new object (); - + /// /// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'. /// These are indicated with the wVirtualKeyCode of 231. When we see this code @@ -487,6 +487,7 @@ public void TestVKPacket (uint unicodeCharacter, bool shift, bool alt, bool cont if (iterations == 0) Application.Driver.SendKeys ((char)mappedConsoleKey, ConsoleKey.Packet, shift, alt, control); }; + lock (packetLock) { Application.Run (); Application.Shutdown (); diff --git a/UnitTests/Drivers/KeyTests.cs b/UnitTests/Drivers/KeyTests.cs index 3d4d8606e3..3116c86285 100644 --- a/UnitTests/Drivers/KeyTests.cs +++ b/UnitTests/Drivers/KeyTests.cs @@ -1,4 +1,5 @@ using System; +using Terminal.Gui; using Xunit; namespace Terminal.Gui.DriverTests { diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index 7cd2b1d069..e39a73a2c3 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -696,7 +696,7 @@ public void Mouse_Drag_On_Top_With_Superview_Null () ((FakeDriver)Application.Driver).SetBufferSize (40, 15); MessageBox.Query ("About", "Hello Word", "Ok"); - } else if (iterations == 1) TestHelpers.AssertDriverContentsWithFrameAre (@" + } else if (iterations == 1) TestHelpers.AssertDriverContentsWithFrameAre (@" File ┌ Window ──────────────────────────────┐ │ │ @@ -712,7 +712,7 @@ public void Mouse_Drag_On_Top_With_Superview_Null () │ │ └──────────────────────────────────────┘ CTRL-N New ", output); -else if (iterations == 2) { + else if (iterations == 2) { Assert.Null (Application.MouseGrabView); // Grab the mouse ReflectionTools.InvokePrivate ( @@ -815,8 +815,8 @@ public void Mouse_Drag_On_Top_With_Superview_Null () Assert.Null (Application.MouseGrabView); - } else if (iterations == 8) Application.RequestStop (); -else if (iterations == 9) Application.RequestStop (); + } else if (iterations == 8) Application.RequestStop (); + else if (iterations == 9) Application.RequestStop (); }; Application.Run (); @@ -956,7 +956,7 @@ public void Mouse_Drag_On_Top_With_Superview_Not_Null () Assert.Null (Application.MouseGrabView); - } else if (iterations == 8) Application.RequestStop (); + } else if (iterations == 8) Application.RequestStop (); }; Application.Run (); @@ -974,5 +974,62 @@ public void EnsureVisibleBounds_With_Border_Null_Not_Throws () exception = Record.Exception (() => ((FakeDriver)Application.Driver).SetBufferSize (10, 0)); Assert.Null (exception); } + + [Fact, AutoInitShutdown] + public void OnEnter_OnLeave_Triggered_On_Application_Begin_End () + { + var isEnter = false; + var isLeave = false; + var v = new View (); + v.Enter += (_) => isEnter = true; + v.Leave += (_) => isLeave = true; + var top = Application.Top; + top.Add (v); + + Assert.False (v.CanFocus); + var exception = Record.Exception (() => top.OnEnter (top)); + Assert.Null (exception); + exception = Record.Exception (() => top.OnLeave (top)); + Assert.Null (exception); + + v.CanFocus = true; + Application.Begin (top); + + Assert.True (isEnter); + Assert.False (isLeave); + + isEnter = false; + var d = new Dialog (); + var rs = Application.Begin (d); + + Assert.False (isEnter); + Assert.True (isLeave); + + isLeave = false; + Application.End (rs); + + Assert.True (isEnter); + Assert.False (isLeave); + } + + [Fact, AutoInitShutdown] + public void PositionCursor_SetCursorVisibility_To_Invisible_If_Focused_Is_Null () + { + var tf = new TextField ("test") { Width = 5 }; + var view = new View () { Width = 10, Height = 10 }; + view.Add (tf); + Application.Top.Add (view); + Application.Begin (Application.Top); + + Assert.True (tf.HasFocus); + Application.Driver.GetCursorVisibility (out CursorVisibility cursor); + Assert.Equal (CursorVisibility.Default, cursor); + + view.Enabled = false; + Assert.False (tf.HasFocus); + Application.Refresh (); + Application.Driver.GetCursorVisibility (out cursor); + Assert.Equal (CursorVisibility.Invisible, cursor); + } } } \ No newline at end of file diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index a884bdc5c8..2bd8964d87 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -69,6 +69,9 @@ public void Run_All_Scenarios () scenario.Init (Colors.Base); scenario.Setup (); scenario.Run (); + + scenario.Dispose(); + Application.Shutdown (); #if DEBUG_IDISPOSABLE foreach (var inst in Responder.Instances) { @@ -136,6 +139,8 @@ public void Run_Generic () // Using variable in the left side of Assert.Equal/NotEqual give error. Must be used literals values. //Assert.Equal (stackSize, iterations); + generic.Dispose(); + // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 68da4e1d00..bd8677ca0e 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -7,12 +7,12 @@ false - + - 1.0 - 1.0 - 1.0 - 1.0 + 2.0 + 2.0 + 2.0 + 2.0 TRACE @@ -21,8 +21,8 @@ TRACE;DEBUG_IDISPOSABLE - - + + diff --git a/UnitTests/Views/AutocompleteTests.cs b/UnitTests/Views/AutocompleteTests.cs index 51dd0076ce..dca1fd81ce 100644 --- a/UnitTests/Views/AutocompleteTests.cs +++ b/UnitTests/Views/AutocompleteTests.cs @@ -6,9 +6,16 @@ using System.Threading.Tasks; using Terminal.Gui; using Xunit; +using Xunit.Abstractions; namespace Terminal.Gui.ViewTests { public class AutocompleteTests { + readonly ITestOutputHelper output; + + public AutocompleteTests (ITestOutputHelper output) + { + this.output = output; + } [Fact] public void Test_GenerateSuggestions_Simple () @@ -151,5 +158,84 @@ public void KeyBindings_Command () Assert.Empty (tv.Autocomplete.Suggestions); Assert.Equal (3, tv.Autocomplete.AllSuggestions.Count); } + + [Fact, AutoInitShutdown] + public void CursorLeft_CursorRight_Mouse_Button_Pressed_Does_Not_Show_Popup () + { + var tv = new TextView () { + Width = 50, + Height = 5, + Text = "This a long line and against TextView." + }; + tv.Autocomplete.AllSuggestions = Regex.Matches (tv.Text.ToString (), "\\w+") + .Select (s => s.Value) + .Distinct ().ToList (); + var top = Application.Top; + top.Add (tv); + Application.Begin (top); + + + for (int i = 0; i < 7; i++) { + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This a long line and against TextView.", output); + } + + Assert.True (tv.MouseEvent (new MouseEvent () { + X = 6, + Y = 0, + Flags = MouseFlags.Button1Pressed + })); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This a long line and against TextView.", output); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.g, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This ag long line and against TextView. + against ", output); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This ag long line and against TextView. + against ", output); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This ag long line and against TextView. + against ", output); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This ag long line and against TextView.", output); + + for (int i = 0; i < 3; i++) { + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This ag long line and against TextView.", output); + } + + Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This a long line and against TextView.", output); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.n, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This an long line and against TextView. + and ", output); + + Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ()))); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +This an long line and against TextView.", output); + } } } \ No newline at end of file diff --git a/UnitTests/Views/ScrollViewTests.cs b/UnitTests/Views/ScrollViewTests.cs index eb14fe339e..f441e3c86f 100644 --- a/UnitTests/Views/ScrollViewTests.cs +++ b/UnitTests/Views/ScrollViewTests.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using NStack; using Xunit; using Xunit.Abstractions; @@ -280,5 +276,227 @@ public void ContentOffset_ContentSize_AutoHideScrollBars_ShowHorizontalScrollInd ◄░░░├─┤░► ", output); } + + [Fact, AutoInitShutdown] + public void Frame_And_Labels_Does_Not_Overspill_ScrollView () + { + var sv = new ScrollView { + X = 3, + Y = 3, + Width = 10, + Height = 10, + ContentSize = new Size (50, 50) + }; + for (int i = 0; i < 8; i++) { + sv.Add (new CustomButton ("█", $"Button {i}", 20, 3) { Y = i * 3 }); + } + Application.Top.Add (sv); + Application.Begin (Application.Top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + █████████▲ + ██████But┬ + █████████┴ + ┌────────░ + │ But░ + └────────░ + ┌────────░ + │ But░ + └────────▼ + ◄├┤░░░░░► ", output); + + sv.ContentOffset = new Point (5, 5); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + ─────────▲ + ─────────┬ + Button 2│ + ─────────┴ + ─────────░ + Button 3░ + ─────────░ + ─────────░ + Button 4▼ + ◄├─┤░░░░► ", output); + } + + private class CustomButton : FrameView { + private Label labelFill; + private Label labelText; + + public CustomButton (string fill, ustring text, int width, int height) + { + Width = width; + Height = height; + labelFill = new Label () { AutoSize = false, Width = Dim.Fill (), Height = Dim.Fill (), Visible = false }; + var fillText = new System.Text.StringBuilder (); + for (int i = 0; i < Bounds.Height; i++) { + if (i > 0) { + fillText.AppendLine (""); + } + for (int j = 0; j < Bounds.Width; j++) { + fillText.Append (fill); + } + } + labelFill.Text = fillText.ToString (); + labelText = new Label (text) { X = Pos.Center (), Y = Pos.Center () }; + Add (labelFill, labelText); + CanFocus = true; + } + + public override bool OnEnter (View view) + { + Border.BorderStyle = BorderStyle.None; + Border.DrawMarginFrame = false; + labelFill.Visible = true; + view = this; + return base.OnEnter (view); + } + + public override bool OnLeave (View view) + { + Border.BorderStyle = BorderStyle.Single; + Border.DrawMarginFrame = true; + labelFill.Visible = false; + if (view == null) + view = this; + return base.OnLeave (view); + } + } + + [Fact, AutoInitShutdown] + public void Clear_Window_Inside_ScrollView () + { + var topLabel = new Label ("At 15,0") { X = 15 }; + var sv = new ScrollView { + X = 3, + Y = 3, + Width = 10, + Height = 10, + ContentSize = new Size (23, 23), + KeepContentAlwaysInViewport = false + }; + var bottomLabel = new Label ("At 15,15") { X = 15, Y = 15 }; + Application.Top.Add (topLabel, sv, bottomLabel); + Application.Begin (Application.Top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" + At 15,0 + + + ▲ + ┬ + ┴ + ░ + ░ + ░ + ░ + ░ + ▼ + ◄├┤░░░░░► + + + At 15,15", output); + + var attributes = new Attribute [] { + Colors.TopLevel.Normal, + Colors.TopLevel.Focus, + Colors.Base.Normal + }; + + TestHelpers.AssertDriverColorsAre (@" +00000000000000000000000 +00000000000000000000000 +00000000000000000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00011111111110000000000 +00000000000000000000000 +00000000000000000000000 +00000000000000000000000", attributes); + + sv.Add (new Window ("1") { X = 3, Y = 3, Width = 20, Height = 20 }); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + At 15,0 + + + ▲ + ┬ + ┴ + ┌ 1 ──░ + │ ░ + │ ░ + │ ░ + │ ░ + │ ▼ + ◄├┤░░░░░► + + + At 15,15", output); + + TestHelpers.AssertDriverColorsAre (@" +00000000000000000000000 +00000000000000000000000 +00000000000000000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00000022222210000000000 +00011111111110000000000 +00000000000000000000000 +00000000000000000000000 +00000000000000000000000", attributes); + + sv.ContentOffset = new Point (20, 20); + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" + At 15,0 + + + │ ▲ + │ ░ + ──┘ ░ + ░ + ░ + ┬ + │ + ┴ + ▼ + ◄░░░░├─┤► + + + At 15,15", output); + + TestHelpers.AssertDriverColorsAre (@" +00000000000000000000000 +00000000000000000000000 +00000000000000000000000 +00022200000010000000000 +00022200000010000000000 +00022200000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00000000000010000000000 +00011111111110000000000 +00000000000000000000000 +00000000000000000000000 +00000000000000000000000", attributes); + } } } diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 860cfb68c1..1c0ddb34e6 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -1,9 +1,18 @@ -using System; +using NStack; +using System; +using System.Globalization; using System.Reflection; using Xunit; +using Xunit.Abstractions; namespace Terminal.Gui.ViewTests { public class TextFieldTests { + readonly ITestOutputHelper output; + + public TextFieldTests (ITestOutputHelper output) + { + this.output = output; + } // This class enables test functions annotated with the [InitShutdown] attribute // to have a function called before the test function is called and after. @@ -1177,6 +1186,21 @@ public void DeleteSelectedText_InsertText_DeleteCharLeft_DeleteCharRight_Cut () Assert.Equal ("-", newText); Assert.Equal ("-1", oldText); Assert.Equal ("-", tf.Text.ToString ()); + + // Delete word with accented char + tf.Text = "Les Misérables movie."; + Assert.True (tf.MouseEvent (new MouseEvent { + X = 7, + Y = 1, + Flags = MouseFlags.Button1DoubleClicked, + View = tf + })); + Assert.Equal ("Misérables ", tf.SelectedText); + Assert.Equal (11, tf.SelectedLength); + Assert.True (tf.ProcessKey (new KeyEvent (Key.Delete, new KeyModifiers ()))); + Assert.Equal ("Les movie.", newText); + Assert.Equal ("Les Misérables movie.", oldText); + Assert.Equal ("Les movie.", tf.Text.ToString ()); } [Fact] @@ -1323,5 +1347,98 @@ public void TestSetTextAndMoveCursorToEnd_WhenExistingSelection (string newText) Assert.Equal (0, tf.SelectedLength); Assert.Null (tf.SelectedText); } + + + [Fact] + public void WordBackward_WordForward_SelectedText_With_Accent () + { + string text = "Les Misérables movie."; + var tf = new TextField (text) { Width = 30 }; + + Assert.Equal (21, text.Length); + Assert.Equal (21, tf.Text.RuneCount); + Assert.Equal (21, tf.Text.ConsoleWidth); + + var runes = tf.Text.ToRuneList (); + Assert.Equal (21, runes.Count); + Assert.Equal (22, tf.Text.Length); + + for (int i = 0; i < runes.Count; i++) { + var cs = text [i]; + var cus = (char)runes [i]; + Assert.Equal (cs, cus); + } + + var idx = 15; + Assert.Equal ('m', text [idx]); + Assert.Equal ('m', (char)runes [idx]); + Assert.Equal ("m", ustring.Make (runes [idx])); + + Assert.True (tf.MouseEvent (new MouseEvent { + X = idx, + Y = 1, + Flags = MouseFlags.Button1DoubleClicked, + View = tf + })); + Assert.Equal ("movie.", tf.SelectedText); + + Assert.True (tf.MouseEvent (new MouseEvent { + X = idx + 1, + Y = 1, + Flags = MouseFlags.Button1DoubleClicked, + View = tf + })); + Assert.Equal ("movie.", tf.SelectedText); + } + + [Fact, AutoInitShutdown] + public void Words_With_Accents_Incorrect_Order_Will_Result_With_Wrong_Accent_Place () + { + var tf = new TextField ("Les Misérables") { Width = 30 }; + var top = Application.Top; + top.Add (tf); + Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" +Les Misérables", output); + + tf.Text = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables"; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +Les Misérables", output); + + // incorrect order will result with a wrong accent place + tf.Text = "Les Mis" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "erables"; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +Les Miśerables", output); + } + + [Fact, AutoInitShutdown] + public void Accented_Letter_With_Three_Combining_Unicode_Chars () + { + var tf = new TextField ("ắ") { Width = 3 }; + var top = Application.Top; + top.Add (tf); + Application.Begin (top); + + TestHelpers.AssertDriverContentsWithFrameAre (@" +ắ", output); + + tf.Text = "\u1eaf"; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +ắ", output); + + tf.Text = "\u0103\u0301"; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +ắ", output); + + tf.Text = "\u0061\u0306\u0301"; + Application.Refresh (); + TestHelpers.AssertDriverContentsWithFrameAre (@" +ắ", output); + } } } \ No newline at end of file diff --git a/UnitTests/Views/ViewTests.cs b/UnitTests/Views/ViewTests.cs index 3d93e7d6c4..396bf7bded 100644 --- a/UnitTests/Views/ViewTests.cs +++ b/UnitTests/Views/ViewTests.cs @@ -1,5 +1,7 @@ using NStack; using System; +using System.Collections.Generic; +using Terminal.Gui.Graphs; using Xunit; using Xunit.Abstractions; //using GraphViewTests = Terminal.Gui.Views.GraphViewTests; @@ -1602,8 +1604,8 @@ public void LabelChangeText_RendersCorrectly_Constructors (int choice) // Calling the Text constructor. lbl = new Label (text); } - lbl.ColorScheme = new ColorScheme (); - lbl.Redraw (lbl.Bounds); + Application.Top.Add (lbl); + Application.Top.Redraw (Application.Top.Bounds); // should have the initial text Assert.Equal ('t', driver.Contents [0, 0, 0]); @@ -4208,6 +4210,7 @@ public void Clear_Does_Not_Spillover_Its_Parent (bool label) v.CanFocus = true; Assert.False (v.HasFocus); v.SetFocus (); + Assert.True (v.HasFocus); Application.Refresh (); TestHelpers.AssertDriverColorsAre (@" 111111111111111111110", attributes); @@ -4489,5 +4492,32 @@ A text with some long width A text with some long width A text witith two lines. ", output); } + + + [Fact, AutoInitShutdown] + public void Test_Nested_Views_With_Height_Equal_To_One () + { + var v = new View () { Width = 11, Height = 3, ColorScheme = new ColorScheme () }; + + var top = new View () { Width = Dim.Fill (), Height = 1 }; + var bottom = new View () { Width = Dim.Fill (), Height = 1, Y = 2 }; + + top.Add (new Label ("111")); + v.Add (top); + v.Add (new LineView (Orientation.Horizontal) { Y = 1 }); + bottom.Add (new Label ("222")); + v.Add (bottom); + + v.LayoutSubviews (); + v.Redraw (v.Bounds); + + + string looksLike = +@" +111 +─────────── +222"; + TestHelpers.AssertDriverContentsAre (looksLike, output); + } } } From d53fe68347a0dc9ede25869c7df1225888662b81 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 02:54:51 +0000 Subject: [PATCH 31/36] Ensuring reset the EnableConsoleScrolling. --- Terminal.Gui/Core/Application.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 27dbc86162..fec0de8024 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -111,7 +111,7 @@ public static Toplevel MdiTop { /// public static View WantContinuousButtonPressedView { get; private set; } - private static bool? _heightAsBuffer; + private static bool? _enableConsoleScrolling; /// /// The current used in the terminal. @@ -133,12 +133,12 @@ public static Toplevel MdiTop { public static bool EnableConsoleScrolling { get { if (Driver == null) { - return _heightAsBuffer.HasValue && _heightAsBuffer.Value; + return _enableConsoleScrolling.HasValue && _enableConsoleScrolling.Value; } return Driver.EnableConsoleScrolling; } set { - _heightAsBuffer = value; + _enableConsoleScrolling = value; if (Driver == null) { return; } @@ -173,7 +173,7 @@ public static Key AlternateForwardKey { static void OnAlternateForwardKeyChanged (Key oldKey) { - foreach (var top in toplevels.ToArray()) { + foreach (var top in toplevels.ToArray ()) { top.OnAlternateForwardKeyChanged (oldKey); } } @@ -196,7 +196,7 @@ public static Key AlternateBackwardKey { static void OnAlternateBackwardKeyChanged (Key oldKey) { - foreach (var top in toplevels.ToArray()) { + foreach (var top in toplevels.ToArray ()) { top.OnAlternateBackwardKeyChanged (oldKey); } } @@ -227,7 +227,7 @@ public static Key QuitKey { static void OnQuitKeyChanged (Key oldKey) { // Duplicate the list so if it changes during enumeration we're safe - foreach (var top in toplevels.ToArray()) { + foreach (var top in toplevels.ToArray ()) { top.OnQuitKeyChanged (oldKey); } } @@ -1125,6 +1125,7 @@ static void ResetState () NotifyStopRunState = null; _initialized = false; mouseGrabView = null; + _enableConsoleScrolling = false; // Reset synchronization context to allow the user to run async/await, // as the main loop has been ended, the synchronization context from From a1b42f755e797f679216062b940f8a7e2c83728c Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 03:25:45 +0000 Subject: [PATCH 32/36] Changing from HeightAsBuffer to EnableConsoleScrolling. --- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 2 +- Terminal.Gui/Core/Application.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index da202f0f0e..f8370c41ea 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -1334,7 +1334,7 @@ void ProcessInput (NetEvents.InputResult inputEvent) void ChangeWin (Size size) { winChanging = true; - if (!HeightAsBuffer) { + if (!EnableConsoleScrolling) { largestBufferHeight = Math.Max (size.Height, 0); } else { largestBufferHeight = Math.Max (size.Height, largestBufferHeight); diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index fec0de8024..8d929682bc 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -449,7 +449,7 @@ internal static void InternalInit (Func topLevelFactory, ConsoleDriver MainLoop = new MainLoop (mainLoopDriver); try { - Driver.HeightAsBuffer = HeightAsBuffer; + Driver.EnableConsoleScrolling = EnableConsoleScrolling; Driver.Init (TerminalResized); } catch (InvalidOperationException ex) { // This is a case where the driver is unable to initialize the console. From 66d0753d6d67c2e4aba424bb5829518229e23ca6 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 03:26:56 +0000 Subject: [PATCH 33/36] Done requested changes. --- Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs | 42 +++++++++++----------- UnitTests/Core/EscSeqReqTests.cs | 20 +++++------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs index a5e305c6de..0f5f746d4a 100644 --- a/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs +++ b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs @@ -3,14 +3,14 @@ namespace Terminal.Gui { /// - /// Represents a state of escape sequence request. + /// Represents the state of an ANSI escape sequence request. /// /// /// This is needed because there are some escape sequence requests responses that are equal /// with some normal escape sequences and thus, will be only considered the responses to the /// requests that were registered with this object. /// - public class EscSeqReqStat { + public class EscSequenceRequestState { /// /// Gets the terminating. /// @@ -18,35 +18,35 @@ public class EscSeqReqStat { /// /// Gets the number of requests. /// - public int NumOfReq { get; } + public int NumRequests { get; } /// /// Gets information about unfinished requests. /// - public int Unfinished { get; set; } + public int NumOutstanding { get; set; } /// /// Creates a new state of escape sequence request. /// /// The terminating. /// The number of requests. - public EscSeqReqStat (string terminating, int numOfReq) + public EscSequenceRequestState (string terminating, int numOfReq) { Terminating = terminating; - NumOfReq = Unfinished = numOfReq; + NumRequests = NumOutstanding = numOfReq; } } /// - /// Manages a list of escape sequence requests. + /// Manages a list of . /// public class EscSeqReqProc { /// - /// Gets the list. + /// Gets the list. /// - public List EscSeqReqStats { get; } = new List (); + public List EscSeqReqStats { get; } = new List (); /// - /// Adds a new instance to the list. + /// Adds a new instance to the list. /// /// The terminating. /// The number of requests. @@ -55,17 +55,17 @@ public void Add (string terminating, int numOfReq = 1) lock (EscSeqReqStats) { var found = EscSeqReqStats.Find (x => x.Terminating == terminating); if (found == null) { - EscSeqReqStats.Add (new EscSeqReqStat (terminating, numOfReq)); - } else if (found != null && found.Unfinished < found.NumOfReq) { - found.Unfinished = Math.Min (found.Unfinished + numOfReq, found.NumOfReq); + EscSeqReqStats.Add (new EscSequenceRequestState (terminating, numOfReq)); + } else if (found != null && found.NumOutstanding < found.NumRequests) { + found.NumOutstanding = Math.Min (found.NumOutstanding + numOfReq, found.NumRequests); } } } /// - /// Removes a instance from the list. + /// Removes a instance from the list. /// - /// The terminating. + /// The terminating string. public void Remove (string terminating) { lock (EscSeqReqStats) { @@ -73,11 +73,11 @@ public void Remove (string terminating) if (found == null) { return; } - if (found != null && found.Unfinished == 0) { + if (found != null && found.NumOutstanding == 0) { EscSeqReqStats.Remove (found); - } else if (found != null && found.Unfinished > 0) { - found.Unfinished--; - if (found.Unfinished == 0) { + } else if (found != null && found.NumOutstanding > 0) { + found.NumOutstanding--; + if (found.NumOutstanding == 0) { EscSeqReqStats.Remove (found); } } @@ -85,7 +85,7 @@ public void Remove (string terminating) } /// - /// Indicates if a with the exist + /// Indicates if a with the exist /// in the list. /// /// @@ -97,7 +97,7 @@ public bool Requested (string terminating) if (found == null) { return false; } - if (found != null && found.Unfinished > 0) { + if (found != null && found.NumOutstanding > 0) { return true; } else { EscSeqReqStats.Remove (found); diff --git a/UnitTests/Core/EscSeqReqTests.cs b/UnitTests/Core/EscSeqReqTests.cs index 224e742e7d..33f955d493 100644 --- a/UnitTests/Core/EscSeqReqTests.cs +++ b/UnitTests/Core/EscSeqReqTests.cs @@ -22,27 +22,27 @@ public void Add_Tests () escSeqReq.Add ("t"); Assert.Single (escSeqReq.EscSeqReqStats); Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOfReq); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].Unfinished); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); escSeqReq.Add ("t", 2); Assert.Single (escSeqReq.EscSeqReqStats); Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOfReq); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].Unfinished); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); escSeqReq = new EscSeqReqProc (); escSeqReq.Add ("t", 2); Assert.Single (escSeqReq.EscSeqReqStats); Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOfReq); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].Unfinished); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding); escSeqReq.Add ("t", 3); Assert.Single (escSeqReq.EscSeqReqStats); Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOfReq); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].Unfinished); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOutstanding); } [Fact] @@ -57,8 +57,8 @@ public void Remove_Tests () escSeqReq.Remove ("t"); Assert.Single (escSeqReq.EscSeqReqStats); Assert.Equal ("t", escSeqReq.EscSeqReqStats [^1].Terminating); - Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumOfReq); - Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].Unfinished); + Assert.Equal (2, escSeqReq.EscSeqReqStats [^1].NumRequests); + Assert.Equal (1, escSeqReq.EscSeqReqStats [^1].NumOutstanding); escSeqReq.Remove ("t"); Assert.Empty (escSeqReq.EscSeqReqStats); From 9a73e708826a5b47abdb40f2e43fc4790211965d Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 12:13:59 +0000 Subject: [PATCH 34/36] Reformatting. --- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 3a81bf461e..a2f87af0e1 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1487,13 +1487,13 @@ public override void ResizeScreen () }; WinConsole.ForceRefreshCursorVisibility (); if (!EnableConsoleScrolling) { - // ANSI ESC "[xJ" Clears part of the screen. - // If n is 0 (or missing), clear from cursor to end of screen. - // If n is 1, clear from cursor to beginning of the screen. - // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). - // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer - // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer - Console.Out.Write ("\x1b[3J"); + // ANSI ESC "[xJ" Clears part of the screen. + // If n is 0 (or missing), clear from cursor to end of screen. + // If n is 1, clear from cursor to beginning of the screen. + // If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS). + // If n is 3, clear entire screen and delete all lines saved in the scrollback buffer + // DO NOT USE 3J - even with the alternate screen buffer, it clears the entire scrollback buffer + Console.Out.Write ("\x1b[3J"); } } From 09e69d5e694cad350ec2950ed27e811eb7d0e791 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 23:38:54 +0000 Subject: [PATCH 35/36] Rename to EscSeqReqStatus. --- Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs index 0f5f746d4a..eaa3084ad9 100644 --- a/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs +++ b/Terminal.Gui/Core/EscSeqUtils/EscSeqReq.cs @@ -10,7 +10,7 @@ namespace Terminal.Gui { /// with some normal escape sequences and thus, will be only considered the responses to the /// requests that were registered with this object. /// - public class EscSequenceRequestState { + public class EscSeqReqStatus { /// /// Gets the terminating. /// @@ -29,7 +29,7 @@ public class EscSequenceRequestState { /// /// The terminating. /// The number of requests. - public EscSequenceRequestState (string terminating, int numOfReq) + public EscSeqReqStatus (string terminating, int numOfReq) { Terminating = terminating; NumRequests = NumOutstanding = numOfReq; @@ -37,16 +37,16 @@ public EscSequenceRequestState (string terminating, int numOfReq) } /// - /// Manages a list of . + /// Manages a list of . /// public class EscSeqReqProc { /// - /// Gets the list. + /// Gets the list. /// - public List EscSeqReqStats { get; } = new List (); + public List EscSeqReqStats { get; } = new List (); /// - /// Adds a new instance to the list. + /// Adds a new instance to the list. /// /// The terminating. /// The number of requests. @@ -55,7 +55,7 @@ public void Add (string terminating, int numOfReq = 1) lock (EscSeqReqStats) { var found = EscSeqReqStats.Find (x => x.Terminating == terminating); if (found == null) { - EscSeqReqStats.Add (new EscSequenceRequestState (terminating, numOfReq)); + EscSeqReqStats.Add (new EscSeqReqStatus (terminating, numOfReq)); } else if (found != null && found.NumOutstanding < found.NumRequests) { found.NumOutstanding = Math.Min (found.NumOutstanding + numOfReq, found.NumRequests); } @@ -63,7 +63,7 @@ public void Add (string terminating, int numOfReq = 1) } /// - /// Removes a instance from the list. + /// Removes a instance from the list. /// /// The terminating string. public void Remove (string terminating) @@ -85,7 +85,7 @@ public void Remove (string terminating) } /// - /// Indicates if a with the exist + /// Indicates if a with the exist /// in the list. /// /// From 5c63d1d41208d2c77e3ff7a45b5b7a3b41352fa9 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 24 Feb 2023 23:48:21 +0000 Subject: [PATCH 36/36] Removing Console.Out.Flush (); --- Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs | 2 -- Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs | 5 ----- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 7 ------- 3 files changed, 14 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 418d11970a..7e5f4dcfd4 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -767,13 +767,11 @@ public override void Suspend () public override void StartReportingMouseMoves () { Console.Out.Write (EscSeqUtils.EnableMouseEvents); - Console.Out.Flush (); } public override void StopReportingMouseMoves () { Console.Out.Write (EscSeqUtils.DisableMouseEvents); - Console.Out.Flush (); } //int lastMouseInterval; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs index 4195344eb3..42612b3ae8 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs @@ -145,11 +145,6 @@ public static bool CheckWinChange () if (l == 1 || l != lines || c != cols) { lines = l; cols = c; - //if (l <= 0 || c <= 0) { - // Console.Out.Write ($"\x1b[8;50;{c}t"); - // Console.Out.Flush (); - // return false; - //} return true; } return false; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index f8370c41ea..da969d1361 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -754,11 +754,9 @@ public override void End () //Disable alternative screen buffer. Console.Out.Write ("\x1b[?1049l"); - Console.Out.Flush (); //Set cursor key to cursor. Console.Out.Write ("\x1b[?25h"); - Console.Out.Flush (); Console.Out.Close (); } @@ -784,11 +782,9 @@ public override void Init (Action terminalResized) //Enable alternative screen buffer. Console.Out.Write ("\x1b[?1049h"); - Console.Out.Flush (); //Set cursor key to application. Console.Out.Write ("\x1b[?25l"); - Console.Out.Flush (); Console.TreatControlCAsInput = true; @@ -981,7 +977,6 @@ public override void UpdateScreen () void SetVirtualCursorPosition (int col, int row) { Console.Out.Write ($"\x1b[{row + 1};{col + 1}H"); - Console.Out.Flush (); } System.Text.StringBuilder WriteAttributes (int attr) @@ -1115,13 +1110,11 @@ public override void UpdateCursor () public override void StartReportingMouseMoves () { Console.Out.Write (EscSeqUtils.EnableMouseEvents); - Console.Out.Flush (); } public override void StopReportingMouseMoves () { Console.Out.Write (EscSeqUtils.DisableMouseEvents); - Console.Out.Flush (); } public override void Suspend ()