diff --git a/Example/Example.csproj b/Example/Example.csproj
index 7ef1a3473..a3d79ade2 100644
--- a/Example/Example.csproj
+++ b/Example/Example.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net5.0
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
index ee06284e9..3551b0699 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
@@ -17,9 +17,9 @@ namespace Terminal.Gui {
/// This is the Curses driver for the gui.cs/Terminal framework.
///
internal class CursesDriver : ConsoleDriver {
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public override int Cols => Curses.Cols;
public override int Rows => Curses.Lines;
+ public override int Top => 0;
// Current row, and current col, tracked by Move/AddRune only
int ccol, crow;
@@ -907,7 +907,5 @@ namespace Terminal.Gui {
killpg (0, signal);
return true;
}
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}
-
}
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
index 7fc3e76a3..7566d6928 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
@@ -15,15 +15,11 @@ namespace Terminal.Gui {
/// Implements a mock ConsoleDriver for unit testing
///
public class FakeDriver : ConsoleDriver {
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
int cols, rows;
- ///
- ///
- ///
public override int Cols => cols;
- ///
- ///
- ///
public override int Rows => rows;
+ public override int Top => 0;
// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
int [,,] contents;
@@ -49,9 +45,6 @@ namespace Terminal.Gui {
static bool sync = false;
- ///
- ///
- ///
public FakeDriver ()
{
cols = FakeConsole.WindowWidth;
@@ -62,11 +55,6 @@ namespace Terminal.Gui {
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;
@@ -84,10 +72,6 @@ namespace Terminal.Gui {
}
- ///
- ///
- ///
- ///
public override void AddRune (Rune rune)
{
rune = MakePrintable (rune);
@@ -113,19 +97,12 @@ namespace Terminal.Gui {
UpdateScreen ();
}
- ///
- ///
- ///
- ///
public override void AddStr (ustring str)
{
foreach (var rune in str)
AddRune (rune);
}
- ///
- ///
- ///
public override void End ()
{
FakeConsole.ResetColor ();
@@ -138,10 +115,6 @@ namespace Terminal.Gui {
return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
}
- ///
- ///
- ///
- ///
public override void Init (Action terminalResized)
{
Colors.TopLevel = new ColorScheme ();
@@ -185,12 +158,6 @@ namespace Terminal.Gui {
//MockConsole.Clear ();
}
- ///
- ///
- ///
- ///
- ///
- ///
public override Attribute MakeAttribute (Color fore, Color back)
{
return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
@@ -211,9 +178,6 @@ namespace Terminal.Gui {
}
}
- ///
- ///
- ///
public override void UpdateScreen ()
{
int rows = Rows;
@@ -233,9 +197,6 @@ namespace Terminal.Gui {
}
}
- ///
- ///
- ///
public override void Refresh ()
{
int rows = Rows;
@@ -267,40 +228,24 @@ namespace Terminal.Gui {
FakeConsole.CursorLeft = savedCol;
}
- ///
- ///
- ///
public override void UpdateCursor ()
{
//
}
- ///
- ///
- ///
public override void StartReportingMouseMoves ()
{
}
- ///
- ///
- ///
public override void StopReportingMouseMoves ()
{
}
- ///
- ///
- ///
public override void Suspend ()
{
}
int currentAttribute;
- ///
- ///
- ///
- ///
public override void SetAttribute (Attribute c)
{
currentAttribute = c.value;
@@ -417,18 +362,10 @@ namespace Terminal.Gui {
return keyMod != Key.Null ? keyMod | key : key;
}
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
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 FakeMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) {
var map = MapKey (consoleKey);
if (map == (Key)0xffffffff)
return;
@@ -452,38 +389,23 @@ namespace Terminal.Gui {
};
}
- ///
- ///
- ///
- ///
- ///
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 ()
{
}
- ///
- ///
- ///
public override void UncookMouse ()
{
}
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}
}
\ No newline at end of file
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
new file mode 100644
index 000000000..42c9ea1e0
--- /dev/null
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Threading;
+
+namespace Terminal.Gui {
+ ///
+ /// Mainloop intended to be used with the .NET System.Console API, and can
+ /// be used on Windows and Unix, it is cross platform but lacks things like
+ /// file descriptor monitoring.
+ ///
+ ///
+ /// This implementation is used for FakeDriver.
+ ///
+ public class FakeMainLoop : IMainLoopDriver {
+ AutoResetEvent keyReady = new AutoResetEvent (false);
+ AutoResetEvent waitForProbe = new AutoResetEvent (false);
+ ConsoleKeyInfo? keyResult = null;
+ MainLoop mainLoop;
+ Func consoleKeyReaderFn = null;
+
+ ///
+ /// Invoked when a Key is pressed.
+ ///
+ public Action KeyPressed;
+
+ ///
+ /// Initializes the class.
+ ///
+ ///
+ /// Passing a consoleKeyReaderfn is provided to support unit test scenarios.
+ ///
+ /// The method to be called to get a key from the console.
+ public FakeMainLoop (Func consoleKeyReaderFn = null)
+ {
+ if (consoleKeyReaderFn == null) {
+ throw new ArgumentNullException ("key reader function must be provided.");
+ }
+ this.consoleKeyReaderFn = consoleKeyReaderFn;
+ }
+
+ void WindowsKeyReader ()
+ {
+ while (true) {
+ waitForProbe.WaitOne ();
+ keyResult = consoleKeyReaderFn ();
+ keyReady.Set ();
+ }
+ }
+
+ void IMainLoopDriver.Setup (MainLoop mainLoop)
+ {
+ this.mainLoop = mainLoop;
+ Thread readThread = new Thread (WindowsKeyReader);
+ readThread.Start ();
+ }
+
+ void IMainLoopDriver.Wakeup ()
+ {
+ }
+
+ bool IMainLoopDriver.EventsPending (bool wait)
+ {
+ long now = DateTime.UtcNow.Ticks;
+
+ int waitTimeout;
+ 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;
+
+ keyResult = null;
+ waitForProbe.Set ();
+ keyReady.WaitOne (waitTimeout);
+ return keyResult.HasValue;
+ }
+
+ void IMainLoopDriver.MainIteration ()
+ {
+ if (keyResult.HasValue) {
+ KeyPressed?.Invoke (keyResult.Value);
+ keyResult = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
index 418385edb..ab36cf957 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
@@ -8,46 +8,46 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using NStack;
namespace Terminal.Gui {
internal class NetDriver : ConsoleDriver {
- int cols, rows;
+ int cols, rows, top;
public override int Cols => cols;
public override int Rows => rows;
+ public override int Top => top;
// 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;
int rows = Rows;
contents = new int [rows, cols, 3];
- for (int r = 0; r < rows; r++) {
+ dirtyLine = new bool [rows];
+ for (int row = 0; row < rows; row++) {
for (int c = 0; c < cols; c++) {
- contents [r, c, 0] = ' ';
- contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black);
- contents [r, c, 2] = 0;
+ contents [row, c, 0] = ' ';
+ contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
+ contents [row, c, 2] = 0;
+ dirtyLine [row] = true;
}
}
- dirtyLine = new bool [rows];
- for (int row = 0; row < rows; row++)
- dirtyLine [row] = true;
}
static bool sync = false;
- public NetDriver ()
- {
- cols = Console.WindowWidth;
- rows = Console.WindowHeight - 1;
- UpdateOffscreen ();
- }
-
bool needMove;
// Current row, and current col, tracked by Move/AddCh only
int ccol, crow;
@@ -57,15 +57,18 @@ namespace Terminal.Gui {
crow = row;
if (Clip.Contains (col, row)) {
- Console.CursorTop = row;
- Console.CursorLeft = col;
- needMove = false;
+ if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
+ Console.SetCursorPosition (col, row);
+ needMove = false;
+ }
} else {
- Console.CursorTop = Clip.Y;
- Console.CursorLeft = Clip.X;
- needMove = true;
+ 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)
@@ -73,8 +76,9 @@ namespace Terminal.Gui {
rune = MakePrintable (rune);
if (Clip.Contains (ccol, crow)) {
if (needMove) {
- //Console.CursorLeft = ccol;
- //Console.CursorTop = crow;
+ if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
+ Console.SetCursorPosition (ccol, crow);
+ }
needMove = false;
}
contents [crow, ccol, 0] = (int)(uint)rune;
@@ -102,7 +106,14 @@ namespace Terminal.Gui {
public override void End ()
{
Console.ResetColor ();
- Console.Clear ();
+ Clear ();
+ }
+
+ void Clear ()
+ {
+ if (Rows > 0) {
+ Console.Clear ();
+ }
}
static Attribute MakeColor (ConsoleColor f, ConsoleColor b)
@@ -111,15 +122,16 @@ namespace Terminal.Gui {
return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
}
-
public override void Init (Action terminalResized)
{
+ TerminalResized = terminalResized;
+ Console.TreatControlCAsInput = true;
+
Colors.TopLevel = new ColorScheme ();
Colors.Base = new ColorScheme ();
Colors.Dialog = new ColorScheme ();
Colors.Menu = new ColorScheme ();
Colors.Error = new ColorScheme ();
- Clip = new Rect (0, 0, Cols, Rows);
Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
@@ -151,7 +163,15 @@ namespace Terminal.Gui {
Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
Colors.Error.HotFocus = Colors.Error.HotNormal;
- Console.Clear ();
+ Clear ();
+ }
+
+ void ResizeScreen ()
+ {
+ cols = Console.WindowWidth;
+ rows = Console.WindowHeight;
+ Clip = new Rect (0, 0, Cols, Rows);
+ top = Console.WindowTop;
}
public override Attribute MakeAttribute (Color fore, Color back)
@@ -176,31 +196,14 @@ namespace Terminal.Gui {
public override void UpdateScreen ()
{
- int rows = Rows;
- int cols = Cols;
-
- Console.CursorTop = 0;
- Console.CursorLeft = 0;
- for (int row = 0; row < rows; row++) {
- dirtyLine [row] = false;
- for (int col = 0; col < cols; col++) {
- contents [row, col, 2] = 0;
- var color = contents [row, col, 1];
- if (color != redrawColor)
- SetColor (color);
- Console.Write ((char)contents [row, col, 0]);
- }
+ if (Rows == 0) {
+ return;
}
- }
- public override void Refresh ()
- {
int rows = Rows;
int cols = Cols;
- var savedRow = Console.CursorTop;
- var savedCol = Console.CursorLeft;
- for (int row = 0; row < rows; row++) {
+ for (int row = top; row < rows; row++) {
if (!dirtyLine [row])
continue;
dirtyLine [row] = false;
@@ -208,8 +211,9 @@ namespace Terminal.Gui {
if (contents [row, col, 2] != 1)
continue;
- Console.CursorTop = row;
- Console.CursorLeft = col;
+ if (Console.WindowHeight > 0) {
+ Console.SetCursorPosition (col, row);
+ }
for (; col < cols && contents [row, col, 2] == 1; col++) {
var color = contents [row, col, 1];
if (color != redrawColor)
@@ -220,13 +224,29 @@ namespace Terminal.Gui {
}
}
}
- Console.CursorTop = savedRow;
- Console.CursorLeft = savedCol;
+
+ UpdateCursor ();
+ }
+
+ public override void Refresh ()
+ {
+ if (Console.WindowWidth != Cols || Console.WindowHeight != Rows || Console.WindowTop != Top) {
+ ResizeScreen ();
+ UpdateOffscreen ();
+ TerminalResized.Invoke ();
+ }
+
+ UpdateScreen ();
}
public override void UpdateCursor ()
{
- //
+ // Prevents the exception of size changing during resizing.
+ try {
+ if (ccol > 0 && ccol < Console.WindowWidth && crow > 0 && crow < Console.WindowHeight) {
+ Console.SetCursorPosition (ccol, crow);
+ }
+ } catch (ArgumentOutOfRangeException) { }
}
public override void StartReportingMouseMoves ()
@@ -249,6 +269,7 @@ namespace Terminal.Gui {
Key MapKey (ConsoleKeyInfo keyInfo)
{
+ MapKeyModifiers (keyInfo);
switch (keyInfo.Key) {
case ConsoleKey.Escape:
return Key.Esc;
@@ -298,42 +319,75 @@ namespace Terminal.Gui {
var key = keyInfo.Key;
if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
var delta = key - ConsoleKey.A;
- if (keyInfo.Modifiers == ConsoleModifiers.Control)
- return (Key)((uint)Key.A + delta);
- if (keyInfo.Modifiers == ConsoleModifiers.Alt)
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta));
+ }
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
- if (keyInfo.Modifiers == ConsoleModifiers.Shift)
- return (Key)((uint)Key.A + delta);
- else
- return (Key)((uint)'a' + delta);
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
+ return (Key)((uint)Key.A + delta);
+ }
+ }
+ return (Key)((uint)keyInfo.KeyChar);
}
if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
var delta = key - ConsoleKey.D0;
- if (keyInfo.Modifiers == ConsoleModifiers.Alt)
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
- if (keyInfo.Modifiers == ConsoleModifiers.Shift)
- return (Key)((uint)keyInfo.KeyChar);
- return (Key)((uint)Key.D0 + delta);
+ }
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
+ return (Key)((uint)Key.D0 + delta);
+ }
+ }
+ return (Key)((uint)keyInfo.KeyChar);
}
- if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
+ if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
var delta = key - ConsoleKey.F1;
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ return (Key)((uint)Key.F1 + delta);
+ }
- return (Key)((int)Key.F1 + delta);
+ return (Key)((uint)Key.F1 + delta);
}
+ if (keyInfo.KeyChar != 0) {
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+
return (Key)(0xffffffff);
}
- KeyModifiers keyModifiers = new KeyModifiers ();
+ KeyModifiers keyModifiers;
+
+ void MapKeyModifiers (ConsoleKeyInfo keyInfo)
+ {
+ if (keyModifiers == null)
+ keyModifiers = new KeyModifiers ();
+
+ if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
+ keyModifiers.Shift = true;
+ if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
+ keyModifiers.Ctrl = true;
+ if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
+ keyModifiers.Alt = true;
+ }
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) {
var map = MapKey (consoleKey);
- if (map == (Key)0xffffffff)
+ if (map == (Key)0xffffffff) {
return;
+ }
keyHandler (new KeyEvent (map, keyModifiers));
keyUpHandler (new KeyEvent (map, keyModifiers));
+ keyModifiers = null;
};
}
@@ -368,14 +422,17 @@ namespace Terminal.Gui {
/// file descriptor monitoring.
///
///
- /// This implementation is used for both NetDriver and FakeDriver.
+ /// This implementation is used for NetDriver.
///
public class NetMainLoop : IMainLoopDriver {
- AutoResetEvent keyReady = new AutoResetEvent (false);
- AutoResetEvent waitForProbe = new AutoResetEvent (false);
+ ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
+ ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
+ ManualResetEventSlim winChange = new ManualResetEventSlim (false);
ConsoleKeyInfo? keyResult = null;
MainLoop mainLoop;
- Func consoleKeyReaderFn = null;
+ ConsoleDriver consoleDriver;
+ bool winChanged;
+ CancellationTokenSource tokenSource = new CancellationTokenSource ();
///
/// Invoked when a Key is pressed.
@@ -383,66 +440,126 @@ namespace Terminal.Gui {
public Action KeyPressed;
///
- /// Initializes the class.
+ /// Initializes the class with the console driver.
///
///
- /// Passing a consoleKeyReaderfn is provided to support unit test sceanrios.
+ /// Passing a consoleDriver is provided to capture windows resizing.
///
- /// The method to be called to get a key from the console.
- public NetMainLoop (Func consoleKeyReaderFn = null)
+ /// The console driver used by this Net main loop.
+ public NetMainLoop (ConsoleDriver consoleDriver = null)
{
- if (consoleKeyReaderFn == null) {
- throw new ArgumentNullException ("key reader function must be provided.");
+ if (consoleDriver == null) {
+ throw new ArgumentNullException ("console driver instance must be provided.");
}
- this.consoleKeyReaderFn = consoleKeyReaderFn;
+ this.consoleDriver = consoleDriver;
}
- void WindowsKeyReader ()
+ void KeyReader ()
{
while (true) {
- waitForProbe.WaitOne ();
- keyResult = consoleKeyReaderFn();
+ waitForProbe.Wait ();
+ waitForProbe.Reset ();
+ keyResult = Console.ReadKey (true);
keyReady.Set ();
}
}
+ void CheckWinChange ()
+ {
+ while (true) {
+ winChange.Wait ();
+ winChange.Reset ();
+ WaitWinChange ();
+ winChanged = true;
+ keyReady.Set ();
+ }
+ }
+
+ void WaitWinChange ()
+ {
+ while (true) {
+ if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows
+ || Console.WindowTop != consoleDriver.Top) { // Top only working on Windows.
+ return;
+ }
+ }
+ }
+
void IMainLoopDriver.Setup (MainLoop mainLoop)
{
this.mainLoop = mainLoop;
- Thread readThread = new Thread (WindowsKeyReader);
- readThread.Start ();
+ Task.Run (KeyReader);
+ Task.Run (CheckWinChange);
}
void IMainLoopDriver.Wakeup ()
{
+ keyReady.Set ();
}
bool IMainLoopDriver.EventsPending (bool wait)
{
long now = DateTime.UtcNow.Ticks;
- int waitTimeout;
+ waitForProbe.Set ();
+ winChange.Set ();
+
+ if (CheckTimers (wait, out var waitTimeout)) {
+ return true;
+ }
+
+ try {
+ if (!tokenSource.IsCancellationRequested) {
+ keyReady.Wait (waitTimeout, tokenSource.Token);
+ }
+ } catch (OperationCanceledException) {
+ return true;
+ } finally {
+ keyReady.Reset ();
+ }
+
+ if (!tokenSource.IsCancellationRequested) {
+ return keyResult.HasValue || CheckTimers (wait, out _) || winChanged;
+ }
+
+ 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
+ } else {
waitTimeout = -1;
+ }
if (!wait)
waitTimeout = 0;
- keyResult = null;
- waitForProbe.Set ();
- keyReady.WaitOne (waitTimeout);
- return keyResult.HasValue;
+ int ic;
+ lock (mainLoop.idleHandlers) {
+ ic = mainLoop.idleHandlers.Count;
+ }
+
+ return ic > 0;
}
void IMainLoopDriver.MainIteration ()
{
if (keyResult.HasValue) {
- KeyPressed?.Invoke (keyResult.Value);
+ var kr = keyResult;
keyResult = null;
+ KeyPressed?.Invoke (kr.Value);
+ }
+ if (winChanged) {
+ winChanged = false;
+ consoleDriver.Refresh ();
}
}
}
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index 48a132148..714a6a76f 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -510,6 +510,7 @@ namespace Terminal.Gui {
public override int Cols => cols;
public override int Rows => rows;
+ public override int Top => 0;
public WindowsDriver ()
{
diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs
index 35f6fc195..a3b2ce667 100644
--- a/Terminal.Gui/Core/Application.cs
+++ b/Terminal.Gui/Core/Application.cs
@@ -190,8 +190,8 @@ namespace Terminal.Gui {
if (Driver == null) {
var p = Environment.OSVersion.Platform;
if (UseSystemConsole) {
- mainLoopDriver = new NetMainLoop (() => Console.ReadKey (true));
Driver = new NetDriver ();
+ mainLoopDriver = new NetMainLoop (Driver);
} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
var windowsDriver = new WindowsDriver ();
mainLoopDriver = windowsDriver;
diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs
index 54511d642..c0084e9ce 100644
--- a/Terminal.Gui/Core/ConsoleDriver.cs
+++ b/Terminal.Gui/Core/ConsoleDriver.cs
@@ -541,6 +541,11 @@ namespace Terminal.Gui {
/// The current number of rows in the terminal.
///
public abstract int Rows { get; }
+ ///
+ /// The current top in the terminal.
+ ///
+ public abstract int Top { get; }
+
///
/// Initializes the driver
///
diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs
index add994704..3eb7ed4a4 100644
--- a/Terminal.Gui/Core/View.cs
+++ b/Terminal.Gui/Core/View.cs
@@ -1095,6 +1095,10 @@ namespace Terminal.Gui {
/// Row.
public void Move (int col, int row)
{
+ if (Driver.Rows == 0) {
+ return;
+ }
+
ViewToScreen (col, row, out var rcol, out var rrow);
Driver.Move (rcol, rrow);
}
diff --git a/UICatalog/Scenarios/Threading.cs b/UICatalog/Scenarios/Threading.cs
index 479c49e0a..68ebf92f9 100644
--- a/UICatalog/Scenarios/Threading.cs
+++ b/UICatalog/Scenarios/Threading.cs
@@ -127,6 +127,7 @@ namespace UICatalog {
await Task.Delay (3000);
LogJob ("Returning from task method");
await _itemsList.SetSourceAsync (items);
+ _itemsList.SetNeedsDisplay ();
}
private CancellationTokenSource cancellationTokenSource;
diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs
index 7a8234825..110ebcdac 100644
--- a/UnitTests/ApplicationTests.cs
+++ b/UnitTests/ApplicationTests.cs
@@ -28,7 +28,7 @@ namespace Terminal.Gui {
Assert.Null (Application.MainLoop);
Assert.Null (Application.Driver);
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
Assert.NotNull (Application.Current);
Assert.NotNull (Application.CurrentView);
Assert.NotNull (Application.Top);
@@ -66,7 +66,7 @@ namespace Terminal.Gui {
void Init ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey(true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
Assert.NotNull (Application.Driver);
Assert.NotNull (Application.MainLoop);
}
diff --git a/UnitTests/ConsoleDriverTests.cs b/UnitTests/ConsoleDriverTests.cs
index 05d94bab5..c241f9682 100644
--- a/UnitTests/ConsoleDriverTests.cs
+++ b/UnitTests/ConsoleDriverTests.cs
@@ -11,7 +11,7 @@ namespace Terminal.Gui {
public void Init_Inits ()
{
var driver = new FakeDriver ();
- Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true)));
driver.Init (() => { });
Assert.Equal (80, Console.BufferWidth);
@@ -27,7 +27,7 @@ namespace Terminal.Gui {
public void End_Cleans_Up ()
{
var driver = new FakeDriver ();
- Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true)));
driver.Init (() => { });
FakeConsole.ForegroundColor = ConsoleColor.Red;
@@ -50,7 +50,7 @@ namespace Terminal.Gui {
public void SetColors_Changes_Colors ()
{
var driver = new FakeDriver ();
- Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true)));
driver.Init (() => { });
Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor);
Assert.Equal (ConsoleColor.Black, Console.BackgroundColor);
diff --git a/UnitTests/DimTests.cs b/UnitTests/DimTests.cs
index a414e94d1..1b8f9b7fa 100644
--- a/UnitTests/DimTests.cs
+++ b/UnitTests/DimTests.cs
@@ -247,7 +247,7 @@ namespace Terminal.Gui {
[Fact]
public void Dim_Validation_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -279,7 +279,7 @@ namespace Terminal.Gui {
[Fact]
public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -300,7 +300,7 @@ namespace Terminal.Gui {
[Fact]
public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -333,7 +333,7 @@ namespace Terminal.Gui {
{
// Testing with the Button because it properly handles the Dim class.
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
diff --git a/UnitTests/MainLoopTests.cs b/UnitTests/MainLoopTests.cs
index 78a868600..2e9b85d40 100644
--- a/UnitTests/MainLoopTests.cs
+++ b/UnitTests/MainLoopTests.cs
@@ -18,7 +18,7 @@ namespace Terminal.Gui {
[Fact]
public void Constructor_Setups_Driver ()
{
- var ml = new MainLoop (new NetMainLoop(() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
Assert.NotNull (ml.Driver);
}
@@ -26,7 +26,7 @@ namespace Terminal.Gui {
[Fact]
public void AddIdle_Adds_And_Removes ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
Func fnTrue = () => { return true; };
Func fnFalse = () => { return false; };
@@ -60,7 +60,7 @@ namespace Terminal.Gui {
[Fact]
public void AddIdle_Function_GetsCalled_OnIteration ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn = () => {
@@ -76,7 +76,7 @@ namespace Terminal.Gui {
[Fact]
public void RemoveIdle_Function_NotCalled ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn = () => {
@@ -93,7 +93,7 @@ namespace Terminal.Gui {
[Fact]
public void AddThenRemoveIdle_Function_NotCalled ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn = () => {
@@ -111,7 +111,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTwice_Function_CalledTwice ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn = () => {
@@ -139,7 +139,7 @@ namespace Terminal.Gui {
[Fact]
public void False_Idle_Stops_It_Being_Called_Again ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn1 = () => {
@@ -172,7 +172,7 @@ namespace Terminal.Gui {
[Fact]
public void AddIdle_Twice_Returns_False_Called_Twice ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn1 = () => {
@@ -204,7 +204,7 @@ namespace Terminal.Gui {
[Fact]
public void Run_Runs_Idle_Stop_Stops_Idle ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var functionCalled = 0;
Func fn = () => {
@@ -226,7 +226,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTimer_Adds_Removes_NoFaults ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = 100;
var callbackCount = 0;
@@ -246,7 +246,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTimer_Run_Called ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = 100;
var callbackCount = 0;
@@ -274,7 +274,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTimer_Run_CalledAtApproximatelyRightTime ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = TimeSpan.FromMilliseconds (50);
var watch = new System.Diagnostics.Stopwatch ();
@@ -300,7 +300,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = TimeSpan.FromMilliseconds (50);
var watch = new System.Diagnostics.Stopwatch ();
@@ -328,7 +328,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTimer_Remove_NotCalled ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = TimeSpan.FromMilliseconds (50);
// Force stop if 10 iterations
@@ -357,7 +357,7 @@ namespace Terminal.Gui {
[Fact]
public void AddTimer_ReturnFalse_StopsBeingCalled ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = TimeSpan.FromMilliseconds (50);
// Force stop if 10 iterations
@@ -390,7 +390,7 @@ namespace Terminal.Gui {
[Fact]
public void Invoke_Adds_Idle ()
{
- var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var actionCalled = 0;
ml.Invoke (() => { actionCalled++; });
diff --git a/UnitTests/PosTests.cs b/UnitTests/PosTests.cs
index 5f51c998c..3c7a2793b 100644
--- a/UnitTests/PosTests.cs
+++ b/UnitTests/PosTests.cs
@@ -262,7 +262,7 @@ namespace Terminal.Gui {
// Setup Fake driver
(Window win, Button button) setup ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
Application.Iteration = () => {
Application.RequestStop ();
};
@@ -374,7 +374,7 @@ namespace Terminal.Gui {
[Fact]
public void Pos_Validation_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -406,7 +406,7 @@ namespace Terminal.Gui {
[Fact]
public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -428,7 +428,7 @@ namespace Terminal.Gui {
[Fact]
public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
diff --git a/UnitTests/ScenarioTests.cs b/UnitTests/ScenarioTests.cs
index e60a8448d..007a18143 100644
--- a/UnitTests/ScenarioTests.cs
+++ b/UnitTests/ScenarioTests.cs
@@ -55,7 +55,7 @@ namespace Terminal.Gui {
Application.RequestStop ();
}
};
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = 1000;
var abortCount = 0;
@@ -107,7 +107,7 @@ namespace Terminal.Gui {
Application.RequestStop ();
}
};
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var ms = 1000;
var abortCount = 0;
diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs
index 56435e806..dfb45df8b 100644
--- a/UnitTests/ViewTests.cs
+++ b/UnitTests/ViewTests.cs
@@ -544,7 +544,7 @@ namespace Terminal.Gui {
[Fact]
public void Initialized_Event_Comparing_With_Added_Event ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = new Toplevel () { Id = "0", };
@@ -643,7 +643,7 @@ namespace Terminal.Gui {
[Fact]
public void Initialized_Event_Will_Be_Invoked_When_Added_Dynamically ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = new Toplevel () { Id = "0", };
@@ -751,7 +751,7 @@ namespace Terminal.Gui {
[Fact]
public void CanFocus_Faced_With_Container_Before_Run ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -788,7 +788,7 @@ namespace Terminal.Gui {
[Fact]
public void CanFocus_Faced_With_Container_After_Run ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -831,7 +831,7 @@ namespace Terminal.Gui {
[Fact]
public void CanFocus_Container_ToFalse_Turns_All_Subviews_ToFalse_Too ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -866,7 +866,7 @@ namespace Terminal.Gui {
[Fact]
public void CanFocus_Container_Toggling_All_Subviews_To_Old_Value_When_Is_True ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
@@ -910,7 +910,7 @@ namespace Terminal.Gui {
{
// Non-regression test for #882 (NullReferenceException during keyboard navigation when Focused is null)
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
Application.Top.Ready += () => {
Assert.Null (Application.Top.Focused);
@@ -928,7 +928,7 @@ namespace Terminal.Gui {
[Fact]
public void Multi_Thread_Toplevels ()
{
- Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true)));
+ Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var t = Application.Top;
var w = new Window ();