From 415cd11e20dac6b8c9d380a0127a9b4def8ba463 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 23 Nov 2020 23:19:39 +0000 Subject: [PATCH] Fixes #1020. NetDriver does not deal well with unicode characters yet. --- Example/demo.cs | 4 +- .../CursesDriver/CursesDriver.cs | 2 + .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 1 + Terminal.Gui/ConsoleDrivers/NetDriver.cs | 195 +++++++---- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 331 +++++++++++------- Terminal.Gui/Core/Application.cs | 32 +- Terminal.Gui/Core/ConsoleDriver.cs | 19 + UICatalog/Scenarios/CharacterMap.cs | 40 ++- 8 files changed, 404 insertions(+), 220 deletions(-) diff --git a/Example/demo.cs b/Example/demo.cs index 1bcc64446..c10bb84ce 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -562,9 +562,11 @@ static class Demo { if (Debugger.IsAttached) CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US"); - //Application.UseSystemConsole = true; + Application.UseSystemConsole = true; Application.Init(); + Application.HeightSize = HeightSize.BufferHeight; + //ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler; var top = Application.Top; diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 3551b0699..e430f4da1 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -20,6 +20,7 @@ namespace Terminal.Gui { public override int Cols => Curses.Cols; public override int Rows => Curses.Lines; public override int Top => 0; + public override HeightSize HeightSize { get; set; } // Current row, and current col, tracked by Move/AddRune only int ccol, crow; @@ -70,6 +71,7 @@ namespace Terminal.Gui { public override void Refresh () { Curses.refresh (); if (Curses.CheckWinChange ()) { + Clip = new Rect (0, 0, Cols, Rows); TerminalResized?.Invoke (); } } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 7566d6928..424e1def0 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -20,6 +20,7 @@ namespace Terminal.Gui { public override int Cols => cols; public override int Rows => rows; public override int Top => 0; + public override HeightSize HeightSize { get; set; } // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag int [,,] contents; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index ab36cf957..59a9b1375 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -18,17 +18,12 @@ namespace Terminal.Gui { public override int Cols => cols; public override int Rows => rows; public override int Top => top; + public override HeightSize HeightSize { get; set; } // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag int [,,] contents; bool [] dirtyLine; - public NetDriver () - { - ResizeScreen (); - UpdateOffscreen (); - } - void UpdateOffscreen () { int cols = Cols; @@ -48,53 +43,39 @@ namespace Terminal.Gui { static bool sync = false; - bool needMove; // Current row, and current col, tracked by Move/AddCh only int ccol, crow; public override void Move (int col, int row) { ccol = col; crow = row; - - if (Clip.Contains (col, row)) { - if (cols == Console.WindowWidth && rows == Console.WindowHeight) { - Console.SetCursorPosition (col, row); - needMove = false; - } - } else { - if (cols == Console.WindowWidth && rows == Console.WindowHeight) { - if (Console.WindowHeight > 0) { - Console.SetCursorPosition (Clip.X, Clip.Y); - } - needMove = true; - } - } } public override void AddRune (Rune rune) { rune = MakePrintable (rune); if (Clip.Contains (ccol, crow)) { - if (needMove) { - if (cols == Console.WindowWidth && rows == Console.WindowHeight) { - Console.SetCursorPosition (ccol, crow); - } - needMove = false; - } contents [crow, ccol, 0] = (int)(uint)rune; contents [crow, ccol, 1] = currentAttribute; contents [crow, ccol, 2] = 1; dirtyLine [crow] = true; - } else - needMove = true; + } ccol++; + var runeWidth = Rune.ColumnWidth (rune); + if (runeWidth > 1) { + for (int i = 1; i < runeWidth; i++) { + contents [crow, ccol, 2] = 0; + ccol++; + } + } //if (ccol == Cols) { // ccol = 0; // if (crow + 1 < Rows) // crow++; //} - if (sync) + if (sync) { UpdateScreen (); + } } public override void AddStr (ustring str) @@ -122,10 +103,16 @@ namespace Terminal.Gui { return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) }; } + bool isWinPlatform; + public override void Init (Action terminalResized) { TerminalResized = terminalResized; Console.TreatControlCAsInput = true; + var p = Environment.OSVersion.Platform; + if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { + isWinPlatform = true; + } Colors.TopLevel = new ColorScheme (); Colors.Base = new ColorScheme (); @@ -164,14 +151,64 @@ namespace Terminal.Gui { Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red); Colors.Error.HotFocus = Colors.Error.HotNormal; Clear (); + ResizeScreen (); + UpdateOffscreen (); } void ResizeScreen () { - cols = Console.WindowWidth; - rows = Console.WindowHeight; + const int Min_WindowWidth = 14; + + switch (HeightSize) { + case HeightSize.WindowHeight: + if (Console.WindowHeight > 0) { + // Can raise an exception while is still resizing. + try { + // Not supported on Unix. + if (isWinPlatform) { + Console.CursorTop = 0; + Console.CursorLeft = 0; + Console.WindowTop = 0; + Console.WindowLeft = 0; + Console.SetBufferSize (Math.Max (Min_WindowWidth, Console.WindowWidth), + Console.WindowHeight); + } else { + //Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t"); + //Console.Out.Flush (); + Console.Out.Write ($"\x1b[0;0" + + $";{Console.WindowHeight};" + + $"{Math.Max (Min_WindowWidth, Console.WindowWidth)}w"); + } + } catch (System.IO.IOException) { + return; + } catch (ArgumentOutOfRangeException) { + return; + } + } + cols = Console.WindowWidth; + rows = Console.WindowHeight; + top = 0; + break; + case HeightSize.BufferHeight: + if (isWinPlatform && Console.WindowHeight > 0) { + if (isWinPlatform) { + // Can raise an exception while is still resizing. + try { + Console.WindowTop = Math.Max (Math.Min (top, Console.BufferHeight - Console.WindowHeight), 0); + } catch (Exception) { + return; + } + } + } else { + Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" + + $";{Console.BufferHeight}" + + $";{Math.Max (Min_WindowWidth, Console.BufferWidth)}w"); + } + cols = Console.BufferWidth; + rows = Console.BufferHeight; + break; + } Clip = new Rect (0, 0, Cols, Rows); - top = Console.WindowTop; } public override Attribute MakeAttribute (Color fore, Color back) @@ -196,29 +233,40 @@ namespace Terminal.Gui { public override void UpdateScreen () { - if (Rows == 0) { + if (winChanging || Console.WindowHeight == 0 + || (HeightSize == HeightSize.WindowHeight && Rows != Console.WindowHeight) + || (HeightSize == HeightSize.BufferHeight && Rows != Console.BufferHeight)) { return; } - int rows = Rows; + int top = Top; + int rows = Math.Min (Console.WindowHeight + top, Rows); int cols = Cols; for (int row = top; row < rows; row++) { - if (!dirtyLine [row]) + if (!dirtyLine [row]) { continue; + } dirtyLine [row] = false; + int [,,] damage = new int [0, 0, 0]; for (int col = 0; col < cols; col++) { - if (contents [row, col, 2] != 1) + if (contents [row, col, 2] != 1) { continue; + } if (Console.WindowHeight > 0) { - Console.SetCursorPosition (col, row); + // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth. + try { + Console.SetCursorPosition (col, row); + } catch (Exception) { + return; + } } for (; col < cols && contents [row, col, 2] == 1; col++) { var color = contents [row, col, 1]; - if (color != redrawColor) + if (color != redrawColor) { SetColor (color); - + } Console.Write ((char)contents [row, col, 0]); contents [row, col, 2] = 0; } @@ -230,12 +278,6 @@ namespace Terminal.Gui { public override void Refresh () { - if (Console.WindowWidth != Cols || Console.WindowHeight != Rows || Console.WindowTop != Top) { - ResizeScreen (); - UpdateOffscreen (); - TerminalResized.Invoke (); - } - UpdateScreen (); } @@ -243,10 +285,12 @@ namespace Terminal.Gui { { // Prevents the exception of size changing during resizing. try { - if (ccol > 0 && ccol < Console.WindowWidth && crow > 0 && crow < Console.WindowHeight) { + if (ccol >= 0 && ccol <= cols && crow >= 0 && crow <= rows) { Console.SetCursorPosition (ccol, crow); } - } catch (ArgumentOutOfRangeException) { } + } catch (System.IO.IOException) { + } catch (ArgumentOutOfRangeException) { + } } public override void StartReportingMouseMoves () @@ -377,10 +421,11 @@ namespace Terminal.Gui { keyModifiers.Alt = true; } + bool winChanging; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - (mainLoop.Driver as NetMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) { + (mainLoop.Driver as NetMainLoop).KeyPressed = (consoleKey) => { var map = MapKey (consoleKey); if (map == (Key)0xffffffff) { return; @@ -389,16 +434,23 @@ namespace Terminal.Gui { keyUpHandler (new KeyEvent (map, keyModifiers)); keyModifiers = null; }; + + (mainLoop.Driver as NetMainLoop).WinChanged = (e) => { + winChanging = true; + top = e; + ResizeScreen (); + UpdateOffscreen (); + winChanging = false; + TerminalResized.Invoke (); + }; } public override void SetColors (ConsoleColor foreground, ConsoleColor background) { - throw new NotImplementedException (); } public override void SetColors (short foregroundColorId, short backgroundColorId) { - throw new NotImplementedException (); } public override void CookMouse () @@ -424,14 +476,15 @@ namespace Terminal.Gui { /// /// This implementation is used for NetDriver. /// - public class NetMainLoop : IMainLoopDriver { + internal class NetMainLoop : IMainLoopDriver { ManualResetEventSlim keyReady = new ManualResetEventSlim (false); ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false); ManualResetEventSlim winChange = new ManualResetEventSlim (false); - ConsoleKeyInfo? keyResult = null; + Queue keyResult = new Queue (); MainLoop mainLoop; ConsoleDriver consoleDriver; bool winChanged; + int newTop; CancellationTokenSource tokenSource = new CancellationTokenSource (); /// @@ -439,6 +492,8 @@ namespace Terminal.Gui { /// public Action KeyPressed; + public Action WinChanged; + /// /// Initializes the class with the console driver. /// @@ -449,7 +504,7 @@ namespace Terminal.Gui { public NetMainLoop (ConsoleDriver consoleDriver = null) { if (consoleDriver == null) { - throw new ArgumentNullException ("console driver instance must be provided."); + throw new ArgumentNullException ("Console driver instance must be provided."); } this.consoleDriver = consoleDriver; } @@ -459,7 +514,9 @@ namespace Terminal.Gui { while (true) { waitForProbe.Wait (); waitForProbe.Reset (); - keyResult = Console.ReadKey (true); + if (keyResult.Count == 0) { + keyResult.Enqueue (Console.ReadKey (true)); + } keyReady.Set (); } } @@ -478,9 +535,19 @@ namespace Terminal.Gui { void WaitWinChange () { while (true) { - if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows - || Console.WindowTop != consoleDriver.Top) { // Top only working on Windows. - return; + switch (consoleDriver.HeightSize) { + case HeightSize.WindowHeight: + if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) { + return; + } + break; + case HeightSize.BufferHeight: + if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows + || Console.WindowTop != consoleDriver.Top) { + newTop = Console.WindowTop; + return; + } + break; } } } @@ -499,8 +566,6 @@ namespace Terminal.Gui { bool IMainLoopDriver.EventsPending (bool wait) { - long now = DateTime.UtcNow.Ticks; - waitForProbe.Set (); winChange.Set (); @@ -519,7 +584,7 @@ namespace Terminal.Gui { } if (!tokenSource.IsCancellationRequested) { - return keyResult.HasValue || CheckTimers (wait, out _) || winChanged; + return keyResult.Count > 0 || CheckTimers (wait, out _) || winChanged; } tokenSource.Dispose (); @@ -552,14 +617,12 @@ namespace Terminal.Gui { void IMainLoopDriver.MainIteration () { - if (keyResult.HasValue) { - var kr = keyResult; - keyResult = null; - KeyPressed?.Invoke (kr.Value); + if (keyResult.Count > 0) { + KeyPressed?.Invoke (keyResult.Dequeue ().Value); } if (winChanged) { winChanged = false; - consoleDriver.Refresh (); + WinChanged.Invoke (newTop); } } } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 714a6a76f..f02e1ed8e 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -207,7 +207,7 @@ namespace Terminal.Gui { public override string ToString () => $"({X},{Y})"; }; - internal struct WindowBufferSizeRecord { + public struct WindowBufferSizeRecord { public Coordinate size; public WindowBufferSizeRecord (short x, short y) @@ -355,6 +355,20 @@ namespace Terminal.Gui { } } + [StructLayout (LayoutKind.Sequential)] + public struct ConsoleKeyInfoEx { + public ConsoleKeyInfo consoleKeyInfo; + public bool CapsLock; + public bool NumLock; + + public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock) + { + this.consoleKeyInfo = consoleKeyInfo; + CapsLock = capslock; + NumLock = numlock; + } + } + [DllImport ("kernel32.dll", SetLastError = true)] static extern IntPtr GetStdHandle (int nStdHandle); @@ -431,14 +445,15 @@ namespace Terminal.Gui { return numberEventsRead == 0 ? null - : new [] {Marshal.PtrToStructure (pRecord)}; + : new [] { Marshal.PtrToStructure (pRecord) }; } catch (Exception) { return null; } finally { Marshal.FreeHGlobal (pRecord); } } -#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)] private static extern IntPtr GetConsoleWindow (); @@ -498,19 +513,17 @@ namespace Terminal.Gui { #endif } - internal class WindowsDriver : ConsoleDriver, IMainLoopDriver { + internal class WindowsDriver : ConsoleDriver { static bool sync = false; - ManualResetEventSlim eventReady = new ManualResetEventSlim (false); - ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false); - MainLoop mainLoop; WindowsConsole.CharInfo [] OutputBuffer; - int cols, rows; + int cols, rows, top; WindowsConsole winConsole; WindowsConsole.SmallRect damageRegion; public override int Cols => cols; public override int Rows => rows; - public override int Top => 0; + public override int Top => top; + public override HeightSize HeightSize { get; set; } public WindowsDriver () { @@ -527,8 +540,11 @@ namespace Terminal.Gui { ResizeScreen (); UpdateOffScreen (); + } - Task.Run ((Action)WindowsInputHandler); + public WindowsConsole WinConsole { + get => winConsole; + private set => winConsole = value; } private void SetupColorsAndBorders () @@ -566,98 +582,6 @@ namespace Terminal.Gui { Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed); } - [StructLayout (LayoutKind.Sequential)] - public struct ConsoleKeyInfoEx { - public ConsoleKeyInfo consoleKeyInfo; - public bool CapsLock; - public bool NumLock; - - public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock) - { - this.consoleKeyInfo = consoleKeyInfo; - CapsLock = capslock; - NumLock = numlock; - } - } - - // The records that we keep fetching - WindowsConsole.InputRecord [] result, records = new WindowsConsole.InputRecord [1]; - - void WindowsInputHandler () - { - while (true) { - waitForProbe.Wait (); - waitForProbe.Reset (); - - result = winConsole.ReadConsoleInput (); - - eventReady.Set (); - } - } - - void IMainLoopDriver.Setup (MainLoop mainLoop) - { - this.mainLoop = mainLoop; - } - - void IMainLoopDriver.Wakeup () - { - //tokenSource.Cancel (); - eventReady.Reset (); - eventReady.Set (); - } - - bool IMainLoopDriver.EventsPending (bool wait) - { - if (CheckTimers (wait, out var waitTimeout)) { - return true; - } - - result = null; - waitForProbe.Set (); - - try { - if (!tokenSource.IsCancellationRequested) { - eventReady.Wait (waitTimeout, tokenSource.Token); - } - } catch (OperationCanceledException) { - return true; - } finally { - eventReady.Reset (); - } - - if (!tokenSource.IsCancellationRequested) { - return result != null || CheckTimers (wait, out waitTimeout); - } - - tokenSource.Dispose (); - tokenSource = new CancellationTokenSource (); - return true; - } - - bool CheckTimers (bool wait, out int waitTimeout) - { - long now = DateTime.UtcNow.Ticks; - - if (mainLoop.timeouts.Count > 0) { - waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); - if (waitTimeout < 0) - return true; - } else { - waitTimeout = -1; - } - - if (!wait) - waitTimeout = 0; - - int ic; - lock (mainLoop.idleHandlers) { - ic = mainLoop.idleHandlers.Count; - } - - return ic > 0; - } - Action keyHandler; Action keyDownHandler; Action keyUpHandler; @@ -669,14 +593,12 @@ namespace Terminal.Gui { this.keyDownHandler = keyDownHandler; this.keyUpHandler = keyUpHandler; this.mouseHandler = mouseHandler; + + (mainLoop.Driver as WindowsMainLoop).ProcessInput = (e) => ProcessInput (e); } - void IMainLoopDriver.MainIteration () + void ProcessInput (WindowsConsole.InputRecord inputEvent) { - if (result == null) - return; - - var inputEvent = result [0]; switch (inputEvent.EventType) { case WindowsConsole.EventType.Key: var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent)); @@ -775,11 +697,7 @@ namespace Terminal.Gui { case WindowsConsole.EventType.Focus: break; - - default: - break; } - result = null; } WindowsConsole.ButtonState? LastMouseButtonPressed = null; @@ -871,23 +789,23 @@ namespace Terminal.Gui { Y = mouseEvent.MousePosition.Y }; //if (p == point) { - switch (LastMouseButtonPressed) { - case WindowsConsole.ButtonState.Button1Pressed: - mouseFlag = MouseFlags.Button1Clicked; - break; + switch (LastMouseButtonPressed) { + case WindowsConsole.ButtonState.Button1Pressed: + mouseFlag = MouseFlags.Button1Clicked; + break; - case WindowsConsole.ButtonState.Button2Pressed: - mouseFlag = MouseFlags.Button2Clicked; - break; + case WindowsConsole.ButtonState.Button2Pressed: + mouseFlag = MouseFlags.Button2Clicked; + break; - case WindowsConsole.ButtonState.RightmostButtonPressed: - mouseFlag = MouseFlags.Button3Clicked; - break; - } - point = new Point () { - X = mouseEvent.MousePosition.X, - Y = mouseEvent.MousePosition.Y - }; + case WindowsConsole.ButtonState.RightmostButtonPressed: + mouseFlag = MouseFlags.Button3Clicked; + break; + } + point = new Point () { + X = mouseEvent.MousePosition.X, + Y = mouseEvent.MousePosition.Y + }; //} else { // mouseFlag = 0; //} @@ -1009,7 +927,7 @@ namespace Terminal.Gui { KeyModifiers keyModifiers; - public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) + public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent) { var state = keyEvent.dwControlKeyState; @@ -1036,10 +954,10 @@ namespace Terminal.Gui { keyModifiers.Scrolllock = scrolllock; var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control); - return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock); + return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock); } - public Key MapKey (ConsoleKeyInfoEx keyInfoEx) + public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx) { var keyInfo = keyInfoEx.consoleKeyInfo; switch (keyInfo.Key) { @@ -1245,7 +1163,6 @@ namespace Terminal.Gui { } int currentAttribute; - CancellationTokenSource tokenSource = new CancellationTokenSource (); public override void SetAttribute (Attribute c) { @@ -1325,7 +1242,7 @@ namespace Terminal.Gui { winConsole.Cleanup (); } -#region Unused + #region Unused public override void SetColors (ConsoleColor foreground, ConsoleColor background) { } @@ -1353,8 +1270,156 @@ namespace Terminal.Gui { public override void CookMouse () { } -#endregion - + #endregion } + /// + /// Mainloop intended to be used with the , and can + /// only be used on Windows. + /// + /// + /// This implementation is used for WindowsDriver. + /// + internal class WindowsMainLoop : IMainLoopDriver { + ManualResetEventSlim eventReady = new ManualResetEventSlim (false); + ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false); + ManualResetEventSlim winChange = new ManualResetEventSlim (false); + MainLoop mainLoop; + ConsoleDriver consoleDriver; + WindowsConsole winConsole; + bool winChanged; + CancellationTokenSource tokenSource = new CancellationTokenSource (); + + // The records that we keep fetching + WindowsConsole.InputRecord [] result = new WindowsConsole.InputRecord [1]; + + /// + /// Invoked when a Key is pressed or released. + /// + public Action ProcessInput; + + public WindowsMainLoop (ConsoleDriver consoleDriver = null) + { + this.consoleDriver = consoleDriver; + winConsole = ((WindowsDriver)consoleDriver).WinConsole; + } + + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + this.mainLoop = mainLoop; + Task.Run ((Action)WindowsInputHandler); + Task.Run (CheckWinChange); + } + + void WindowsInputHandler () + { + while (true) { + waitForProbe.Wait (); + waitForProbe.Reset (); + + result = winConsole.ReadConsoleInput (); + + eventReady.Set (); + } + } + + void CheckWinChange () + { + while (true) { + winChange.Wait (); + winChange.Reset (); + WaitWinChange (); + winChanged = true; + eventReady.Set (); + } + } + + void WaitWinChange () + { + while (true) { + switch (consoleDriver.HeightSize) { + case HeightSize.WindowHeight: + if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows + || Console.WindowTop != consoleDriver.Top) { // Top only working on Windows. + return; + } + break; + case HeightSize.BufferHeight: + if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows) { + return; + } + break; + } + } + } + + void IMainLoopDriver.Wakeup () + { + //tokenSource.Cancel (); + eventReady.Reset (); + eventReady.Set (); + } + + bool IMainLoopDriver.EventsPending (bool wait) + { + if (CheckTimers (wait, out var waitTimeout)) { + return true; + } + + //result = null; + waitForProbe.Set (); + winChange.Set (); + + try { + if (!tokenSource.IsCancellationRequested) { + eventReady.Wait (waitTimeout, tokenSource.Token); + } + } catch (OperationCanceledException) { + return true; + } finally { + eventReady.Reset (); + } + + if (!tokenSource.IsCancellationRequested) { + return result != null || CheckTimers (wait, out waitTimeout); + } + + tokenSource.Dispose (); + tokenSource = new CancellationTokenSource (); + return true; + } + + bool CheckTimers (bool wait, out int waitTimeout) + { + long now = DateTime.UtcNow.Ticks; + + if (mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + return true; + } else { + waitTimeout = -1; + } + + if (!wait) + waitTimeout = 0; + + int ic; + lock (mainLoop.idleHandlers) { + ic = mainLoop.idleHandlers.Count; + } + + return ic > 0; + } + + void IMainLoopDriver.MainIteration () + { + if (result == null) + return; + + var inputEvent = result [0]; + result = null; + ProcessInput.Invoke (inputEvent); + } + } } diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 51c4dc941..d30bf1b94 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -59,7 +59,7 @@ namespace Terminal.Gui { /// The current in use. /// public static ConsoleDriver Driver; - + /// /// The object used for the application on startup () /// @@ -73,11 +73,32 @@ namespace Terminal.Gui { public static Toplevel Current { get; private set; } /// - /// TThe current object being redrawn. + /// The current object being redrawn. /// /// /// The current. public static View CurrentView { get; set; } + /// + /// The current used in the terminal. + /// + public static HeightSize HeightSize { + get { + if (Driver == null) { + throw new ArgumentNullException ("The driver must be initialized first."); + } + return Driver.HeightSize; + } + set { + if (Driver == null) { + throw new ArgumentNullException ("The driver must be initialized first."); + } + if (Driver.HeightSize != value) { + Driver.HeightSize = value; + Driver.Refresh (); + } + } + } + /// /// The driver for the application /// @@ -167,7 +188,7 @@ namespace Terminal.Gui { static void Init (Func topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null) { if (_initialized && driver == null) return; - + // Used only for start debugging on Unix. //#if DEBUG // while (!System.Diagnostics.Debugger.IsAttached) { @@ -193,9 +214,8 @@ namespace Terminal.Gui { Driver = new NetDriver (); mainLoopDriver = new NetMainLoop (Driver); } else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { - var windowsDriver = new WindowsDriver (); - mainLoopDriver = windowsDriver; - Driver = windowsDriver; + Driver = new WindowsDriver (); + mainLoopDriver = new WindowsMainLoop (Driver); } else { mainLoopDriver = new UnixMainLoop (); Driver = new CursesDriver (); diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index c0084e9ce..093db0d60 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -458,6 +458,20 @@ namespace Terminal.Gui { public static Dictionary ColorSchemes { get; } } + /// + /// The visible height should be used on the window. + /// + public enum HeightSize { + /// + /// Only window height will be visible not allowing scroll. + /// + WindowHeight, + /// + /// All buffer height will be visible allowing scroll. + /// + BufferHeight + } + ///// ///// Special characters that can be drawn with ///// @@ -546,6 +560,11 @@ namespace Terminal.Gui { /// public abstract int Top { get; } + /// + /// The current used in the terminal. + /// + public abstract HeightSize HeightSize { get; set; } + /// /// Initializes the driver /// diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 849978f26..ca60f4874 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -1,4 +1,7 @@ -using NStack; +#define DRAW_CONTENT +//#define BASE_DRAW_CONTENT + +using NStack; using System.Collections.Generic; using System.Linq; using System.Text; @@ -58,15 +61,19 @@ namespace UICatalog { CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal), }; - var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ()); - jumpList.X = Pos.X (label); - jumpList.Y = Pos.Bottom (label); - jumpList.Width = Dim.Fill (); + var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ()) { + X = Pos.X (label), + Y = Pos.Bottom (label), + Width = Dim.Fill (), + SelectedItem = 8 + }; jumpList.SelectedItemChanged += (args) => { - _charMap.Start = radioItems[args.SelectedItem].start; + _charMap.Start = radioItems [args.SelectedItem].start; }; Win.Add (jumpList); + + jumpList.Refresh (); } public override void Run () @@ -91,11 +98,14 @@ namespace UICatalog { } int _start = 0x2500; + public const int H_SPACE = 2; + public const int V_SPACE = 2; + public static int MaxCodePointVal => 0xE0FFF; // Row Header + space + (space + char + space) public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length; - public static int RowWidth => RowHeaderWidth + (" c".Length * 16); + public static int RowWidth => RowHeaderWidth + (H_SPACE * 16); public CharMap () { @@ -109,10 +119,12 @@ namespace UICatalog { ShowHorizontalScrollIndicator = false; } }; +#if DRAW_CONTENT DrawContent += CharMap_DrawContent; +#endif } -#if true + private void CharMap_DrawContent (Rect viewport) { //Rune ReplaceNonPrintables (Rune c) @@ -125,10 +137,10 @@ namespace UICatalog { //} for (int header = 0; header < 16; header++) { - Move (viewport.X + RowHeaderWidth + (header * 2), 0); + Move (viewport.X + RowHeaderWidth + (header * H_SPACE), 0); Driver.AddStr ($" {header:x} "); } - for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) { + for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y+= V_SPACE) { int val = (-viewport.Y + row) * 16; if (val < MaxCodePointVal) { var rowLabel = $"U+{val / 16:x4}x"; @@ -137,17 +149,17 @@ namespace UICatalog { var prevColWasWide = false; for (int col = 0; col < 16; col++) { var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 16 + col)); - Move (viewport.X + RowHeaderWidth + (col * 2) + (prevColWasWide ? 0 : 1), 0 + y + 1); + Move (viewport.X + RowHeaderWidth + (col * H_SPACE) + (prevColWasWide ? 0 : 1), y + 1); Driver.AddRune (rune); - //prevColWasWide = Rune.ColumnWidth(rune) > 1; + //prevColWasWide = Rune.ColumnWidth (rune) > 1; } } } } -#else +#if BASE_DRAW_CONTENT public override void OnDrawContent (Rect viewport) { - CharMap_DrawContent(this, viewport); + CharMap_DrawContent (viewport); base.OnDrawContent (viewport); } #endif