Fixes #1020. NetDriver does not deal well with unicode characters yet.

This commit is contained in:
BDisp
2020-11-23 23:19:39 +00:00
parent cb3df2292d
commit 415cd11e20
8 changed files with 404 additions and 220 deletions

View File

@@ -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<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> 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 {
/// <remarks>
/// This implementation is used for NetDriver.
/// </remarks>
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<ConsoleKeyInfo?> keyResult = new Queue<ConsoleKeyInfo?> ();
MainLoop mainLoop;
ConsoleDriver consoleDriver;
bool winChanged;
int newTop;
CancellationTokenSource tokenSource = new CancellationTokenSource ();
/// <summary>
@@ -439,6 +492,8 @@ namespace Terminal.Gui {
/// </summary>
public Action<ConsoleKeyInfo> KeyPressed;
public Action<int> WinChanged;
/// <summary>
/// Initializes the class with the console driver.
/// </summary>
@@ -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);
}
}
}