diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 2e79252a05..ed35267b2f 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -2,7 +2,9 @@
# the repo. Unless a later match takes precedence,
# @global-owner1 and @global-owner2 will be requested for
# review when someone opens a pull request.
-* @migueldeicaza @tig
+* @tig
+
+/docs/ @tig @bdisp @tznind
# Order is important; the last matching pattern takes the most
# precedence. When someone opens a pull request that only
diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml
index 6c951c2453..39bc0906e4 100644
--- a/.github/workflows/dotnet-core.yml
+++ b/.github/workflows/dotnet-core.yml
@@ -29,6 +29,7 @@ jobs:
- name: Test
run: |
+ sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json
dotnet test --no-restore --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings
mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 34ef36b1c7..5149575773 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -50,6 +50,7 @@ jobs:
#- name: Test to generate Code Coverage Report
# run: |
+ # sed -i 's/"stopOnFail": false/"stopOnFail": true/g' UnitTests/xunit.runner.json
# dotnet test --verbosity normal --collect:"XPlat Code Coverage" --settings UnitTests/coverlet.runsettings
# mv -v UnitTests/TestResults/*/*.* UnitTests/TestResults/
diff --git a/.gitignore b/.gitignore
index 8376953074..8aab8590be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ docfx/api
docs/
UnitTests/TestResults
+TestResults
#git merge files
*.orig
diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs
index f18149b2a5..b571894a83 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application.cs
@@ -6,7 +6,6 @@
using System.Reflection;
using System.IO;
using System.Text.Json.Serialization;
-using static Terminal.Gui.ConfigurationManager;
namespace Terminal.Gui {
///
@@ -55,42 +54,7 @@ public static partial class Application {
// For Unit testing - ignores UseSystemConsole
internal static bool _forceFakeConsole;
-
- private static bool? _enableConsoleScrolling;
- ///
- /// 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 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.
- ///
- /// This API was previously named 'HeightAsBuffer` but was renamed to make its purpose more clear.
- ///
- [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
- public static bool EnableConsoleScrolling {
- get {
- if (Driver == null) {
- return _enableConsoleScrolling.HasValue && _enableConsoleScrolling.Value;
- }
- return Driver.EnableConsoleScrolling;
- }
- set {
- _enableConsoleScrolling = value;
- if (Driver == null) {
- return;
- }
- Driver.EnableConsoleScrolling = value;
- }
- }
-
+
private static List _cachedSupportedCultures;
///
@@ -164,7 +128,9 @@ private static List GetSupportedCultures ()
// calledViaRunT: If false (default) all state will be reset. If true the state will not be reset.
internal static void InternalInit (Func topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null, bool calledViaRunT = false)
{
- if (_initialized && driver == null) return;
+ if (_initialized && driver == null) {
+ return;
+ }
if (_initialized) {
throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown.");
@@ -224,7 +190,6 @@ internal static void InternalInit (Func topLevelFactory, ConsoleDriver
MainLoop = new MainLoop (mainLoopDriver);
try {
- Driver.EnableConsoleScrolling = EnableConsoleScrolling;
Driver.Init (OnTerminalResized);
} catch (InvalidOperationException ex) {
// This is a case where the driver is unable to initialize the console.
@@ -277,6 +242,7 @@ static void ResetState ()
// BUGBUG: OverlappedTop is not cleared here, but it should be?
+ MainLoop?.Stop();
MainLoop = null;
Driver?.End ();
Driver = null;
@@ -289,7 +255,6 @@ static void ResetState ()
NotifyStopRunState = null;
_initialized = false;
_mouseGrabView = null;
- _enableConsoleScrolling = false;
_lastMouseOwnerView = null;
// Reset synchronization context to allow the user to run async/await,
@@ -552,7 +517,8 @@ public static void Run (Toplevel view, Func errorHandler = null
///
public static void Refresh ()
{
- Driver.UpdateOffScreen ();
+ // TODO: Figure out how to remove this call to ClearContents. Refresh should just repaint damaged areas, not clear
+ Driver.ClearContents();
View last = null;
foreach (var v in _toplevels.Reverse ()) {
if (v.Visible) {
@@ -674,13 +640,14 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool
Iteration?.Invoke ();
EnsureModalOrVisibleAlwaysOnTop (state.Toplevel);
- if ((state.Toplevel != Current && Current?.Modal == true)
- || (state.Toplevel != Current && Current?.Modal == false)) {
+ if (state.Toplevel != Current) {
OverlappedTop?.OnDeactivate (state.Toplevel);
state.Toplevel = Current;
OverlappedTop?.OnActivate (state.Toplevel);
Top.SetSubViewNeedsDisplay ();
Refresh ();
+ } else if (Current.SuperView == null && Current?.Modal == true) {
+ Refresh ();
}
if (Driver.EnsureCursorVisibility ()) {
state.Toplevel.SetNeedsDisplay ();
@@ -690,42 +657,41 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool
}
firstIteration = false;
- if (state.Toplevel != Top
- && (!Top._needsDisplay.IsEmpty || Top._subViewNeedsDisplay || Top.LayoutNeeded)) {
- state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
+ if (state.Toplevel != Top &&
+ (Top.NeedsDisplay|| Top.SubViewNeedsDisplay || Top.LayoutNeeded)) {
+ state.Toplevel.SetNeedsDisplay (state.Toplevel.Frame);
+ Top.Clear ();
Top.Draw ();
foreach (var top in _toplevels.Reverse ()) {
if (top != Top && top != state.Toplevel) {
top.SetNeedsDisplay ();
top.SetSubViewNeedsDisplay ();
+ top.Clear ();
top.Draw ();
}
}
}
if (_toplevels.Count == 1 && state.Toplevel == Top
&& (Driver.Cols != state.Toplevel.Frame.Width || Driver.Rows != state.Toplevel.Frame.Height)
- && (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._subViewNeedsDisplay || state.Toplevel.LayoutNeeded)) {
-
- Driver.SetAttribute (Colors.TopLevel.Normal);
- state.Toplevel.Clear (new Rect (0, 0, Driver.Cols, Driver.Rows));
+ && (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded)) {
+ state.Toplevel.Clear ();
}
- if (!state.Toplevel._needsDisplay.IsEmpty || state.Toplevel._subViewNeedsDisplay || state.Toplevel.LayoutNeeded
- || OverlappedChildNeedsDisplay ()) {
+ if (state.Toplevel.NeedsDisplay ||
+ state.Toplevel.SubViewNeedsDisplay ||
+ state.Toplevel.LayoutNeeded ||
+ OverlappedChildNeedsDisplay ()) {
+ state.Toplevel.Clear ();
state.Toplevel.Draw ();
- //if (state.Toplevel.SuperView != null) {
- // state.Toplevel.SuperView?.OnRenderLineCanvas ();
- //} else {
- // state.Toplevel.OnRenderLineCanvas ();
- //}
state.Toplevel.PositionCursor ();
Driver.Refresh ();
} else {
Driver.UpdateCursor ();
}
- if (state.Toplevel != Top && !state.Toplevel.Modal
- && (!Top._needsDisplay.IsEmpty || Top._subViewNeedsDisplay || Top.LayoutNeeded)) {
+ if (state.Toplevel != Top &&
+ !state.Toplevel.Modal &&
+ (Top.NeedsDisplay|| Top.SubViewNeedsDisplay || Top.LayoutNeeded)) {
Top.Draw ();
}
}
@@ -735,7 +701,7 @@ public static void RunMainLoopIteration (ref RunState state, bool wait, ref bool
///
public static void DoEvents ()
{
- MainLoop.Driver.Wakeup ();
+ MainLoop.MainLoopDriver.Wakeup ();
}
///
@@ -1011,7 +977,6 @@ static void OnTerminalResized ()
{
var full = new Rect (0, 0, Driver.Cols, Driver.Rows);
TerminalResized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height });
- Driver.Clip = full;
foreach (var t in _toplevels) {
t.SetRelativeLayout (full);
t.LayoutSubviews ();
@@ -1073,7 +1038,7 @@ public static void GrabMouse (View view)
if (!OnGrabbingMouse (view)) {
OnGrabbedMouse (view);
_mouseGrabView = view;
- Driver.UncookMouse ();
+ //Driver.UncookMouse ();
}
}
@@ -1087,7 +1052,7 @@ public static void UngrabMouse ()
if (!OnUnGrabbingMouse (_mouseGrabView)) {
OnUnGrabbedMouse (_mouseGrabView);
_mouseGrabView = null;
- Driver.CookMouse ();
+ //Driver.CookMouse ();
}
}
@@ -1132,10 +1097,7 @@ static void OnUnGrabbedMouse (View view)
static void ProcessMouseEvent (MouseEvent me)
{
- bool OutsideBounds (Point p, Rect r)
- {
- return p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom;
- }
+ static bool OutsideBounds (Point p, Rect r) => p.X < 0 || p.X > r.Right || p.Y < 0 || p.Y > r.Bottom;
if (IsMouseDisabled) {
return;
diff --git a/Terminal.Gui/Clipboard/Clipboard.cs b/Terminal.Gui/Clipboard/Clipboard.cs
index 98c0ca1810..47de47ab60 100644
--- a/Terminal.Gui/Clipboard/Clipboard.cs
+++ b/Terminal.Gui/Clipboard/Clipboard.cs
@@ -1,31 +1,34 @@
using System.Text;
using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
-namespace Terminal.Gui {
- ///
- /// Provides cut, copy, and paste support for the OS clipboard.
- ///
- ///
- ///
- /// On Windows, the class uses the Windows Clipboard APIs via P/Invoke.
- ///
- ///
- /// On Linux, when not running under Windows Subsystem for Linux (WSL),
- /// the class uses the xclip command line tool. If xclip is not installed,
- /// the clipboard will not work.
- ///
- ///
- /// On Linux, when running under Windows Subsystem for Linux (WSL),
- /// the class launches Windows' powershell.exe via WSL interop and uses the
- /// "Set-Clipboard" and "Get-Clipboard" Powershell CmdLets.
- ///
- ///
- /// On the Mac, the class uses the MacO OS X pbcopy and pbpaste command line tools
- /// and the Mac clipboard APIs vai P/Invoke.
- ///
- ///
- public static class Clipboard {
- static string contents;
+namespace Terminal.Gui;
+
+///
+/// Provides cut, copy, and paste support for the OS clipboard.
+///
+///
+///
+/// On Windows, the class uses the Windows Clipboard APIs via P/Invoke.
+///
+///
+/// On Linux, when not running under Windows Subsystem for Linux (WSL),
+/// the class uses the xclip command line tool. If xclip is not installed,
+/// the clipboard will not work.
+///
+///
+/// On Linux, when running under Windows Subsystem for Linux (WSL),
+/// the class launches Windows' powershell.exe via WSL interop and uses the
+/// "Set-Clipboard" and "Get-Clipboard" Powershell CmdLets.
+///
+///
+/// On the Mac, the class uses the MacO OS X pbcopy and pbpaste command line tools
+/// and the Mac clipboard APIs vai P/Invoke.
+///
+///
+public static class Clipboard {
+ static string _contents = string.Empty;
///
/// Gets (copies from) or sets (pastes to) the contents of the OS clipboard.
@@ -39,12 +42,12 @@ public static string Contents {
// throw new InvalidOperationException ($"{Application.Driver.GetType ().Name}.GetClipboardData returned null instead of string.Empty");
clipData = string.Empty;
}
- contents = clipData;
+ _contents = clipData;
}
} catch (Exception) {
- contents = string.Empty;
+ _contents = string.Empty;
}
- return contents;
+ return _contents;
}
set {
try {
@@ -54,51 +57,120 @@ public static string Contents {
}
Application.Driver.Clipboard.SetClipboardData (value);
}
- contents = value;
+ _contents = value;
} catch (NotSupportedException e) {
throw e;
} catch (Exception) {
- contents = value;
+ _contents = value;
}
}
}
- ///
- /// Returns true if the environmental dependencies are in place to interact with the OS clipboard.
- ///
- ///
- ///
- public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; }
+ ///
+ /// Returns true if the environmental dependencies are in place to interact with the OS clipboard.
+ ///
+ ///
+ ///
+ public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; }
- ///
- /// Copies the contents of the OS clipboard to if possible.
- ///
- /// The contents of the OS clipboard if successful, if not.
- /// the OS clipboard was retrieved, otherwise.
- public static bool TryGetClipboardData (out string result)
- {
- if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) {
- if (contents != result) {
- contents = result;
- }
- return true;
+ ///
+ /// Copies the _contents of the OS clipboard to if possible.
+ ///
+ /// The _contents of the OS clipboard if successful, if not.
+ /// the OS clipboard was retrieved, otherwise.
+ public static bool TryGetClipboardData (out string result)
+ {
+ if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) {
+ if (_contents != result) {
+ _contents = result;
}
- result = string.Empty;
- return false;
+ return true;
}
+ result = string.Empty;
+ return false;
+ }
- ///
- /// Pastes the to the OS clipboard if possible.
- ///
- /// The text to paste to the OS clipboard.
- /// the OS clipboard was set, otherwise.
- public static bool TrySetClipboardData (string text)
- {
- if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) {
- contents = text;
- return true;
- }
- return false;
+ ///
+ /// Pastes the to the OS clipboard if possible.
+ ///
+ /// The text to paste to the OS clipboard.
+ /// the OS clipboard was set, otherwise.
+ public static bool TrySetClipboardData (string text)
+ {
+ if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) {
+ _contents = text;
+ return true;
}
+ return false;
}
}
+
+///
+/// Helper class for console drivers to invoke shell commands to interact with the clipboard.
+/// Used primarily by CursesDriver, but also used in Unit tests which is why it is in
+/// ConsoleDriver.cs.
+///
+internal static class ClipboardProcessRunner {
+ public static (int exitCode, string result) Bash (string commandLine, string inputText = "", bool waitForOutput = false)
+ {
+ var arguments = $"-c \"{commandLine}\"";
+ var (exitCode, result) = Process ("bash", arguments, inputText, waitForOutput);
+
+ return (exitCode, result.TrimEnd ());
+ }
+
+ public static (int exitCode, string result) Process (string cmd, string arguments, string input = null, bool waitForOutput = true)
+ {
+ var output = string.Empty;
+
+ using (Process process = new Process {
+ StartInfo = new ProcessStartInfo {
+ FileName = cmd,
+ Arguments = arguments,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ }
+ }) {
+ var eventHandled = new TaskCompletionSource ();
+ process.Start ();
+ if (!string.IsNullOrEmpty (input)) {
+ process.StandardInput.Write (input);
+ process.StandardInput.Close ();
+ }
+
+ if (!process.WaitForExit (5000)) {
+ var timeoutError = $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}.";
+ throw new TimeoutException (timeoutError);
+ }
+
+ if (waitForOutput && process.StandardOutput.Peek () != -1) {
+ output = process.StandardOutput.ReadToEnd ();
+ }
+
+ if (process.ExitCode > 0) {
+ output = $@"Process failed to run. Command line: {cmd} {arguments}.
+ Output: {output}
+ Error: {process.StandardError.ReadToEnd ()}";
+ }
+
+ return (process.ExitCode, output);
+ }
+ }
+
+ public static bool DoubleWaitForExit (this System.Diagnostics.Process process)
+ {
+ var result = process.WaitForExit (500);
+ if (result) {
+ process.WaitForExit ();
+ }
+ return result;
+ }
+
+ public static bool FileExists (this string value)
+ {
+ return !string.IsNullOrEmpty (value) && !value.Contains ("not found");
+ }
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Clipboard/ClipboardBase.cs b/Terminal.Gui/Clipboard/ClipboardBase.cs
index ee2e437d41..043aad10bd 100644
--- a/Terminal.Gui/Clipboard/ClipboardBase.cs
+++ b/Terminal.Gui/Clipboard/ClipboardBase.cs
@@ -22,6 +22,10 @@ public abstract class ClipboardBase : IClipboard {
public string GetClipboardData ()
{
try {
+ var result = GetClipboardDataImpl ();
+ if (result == null) {
+ return string.Empty;
+ }
return GetClipboardDataImpl ();
} catch (NotSupportedException ex) {
throw new NotSupportedException ("Failed to copy from the OS clipboard.", ex);
@@ -42,6 +46,10 @@ public string GetClipboardData ()
/// Thrown if it was not possible to paste to the OS clipboard.
public void SetClipboardData (string text)
{
+ if (text == null) {
+ throw new ArgumentNullException (nameof (text));
+ }
+
try {
SetClipboardDataImpl (text);
} catch (NotSupportedException ex) {
@@ -59,25 +67,21 @@ public void SetClipboardData (string text)
///
/// Copies the contents of the OS clipboard to if possible.
///
- /// The contents of the OS clipboard if successful, if not.
+ /// The contents of the OS clipboard if successful.
/// the OS clipboard was retrieved, otherwise.
public bool TryGetClipboardData (out string result)
{
+ result = string.Empty;
// Don't even try to read because environment is not set up.
if (!IsSupported) {
- result = null;
return false;
}
try {
result = GetClipboardDataImpl ();
- while (result == null) {
- result = GetClipboardDataImpl ();
- }
return true;
} catch (NotSupportedException ex) {
System.Diagnostics.Debug.WriteLine ($"TryGetClipboardData: {ex.Message}");
- result = null;
return false;
}
}
diff --git a/Terminal.Gui/Clipboard/IClipboard.cs b/Terminal.Gui/Clipboard/IClipboard.cs
index 9619d8125b..3b2b5f7be2 100644
--- a/Terminal.Gui/Clipboard/IClipboard.cs
+++ b/Terminal.Gui/Clipboard/IClipboard.cs
@@ -1,8 +1,4 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace Terminal.Gui {
///
diff --git a/Terminal.Gui/Configuration/AttributeJsonConverter.cs b/Terminal.Gui/Configuration/AttributeJsonConverter.cs
index 11ab89ccbe..390c70a0f6 100644
--- a/Terminal.Gui/Configuration/AttributeJsonConverter.cs
+++ b/Terminal.Gui/Configuration/AttributeJsonConverter.cs
@@ -32,10 +32,11 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J
Attribute attribute = new Attribute ();
Color foreground = (Color)(-1);
Color background = (Color)(-1);
- int valuePair = 0;
+ TrueColor? trueColorForeground = null;
+ TrueColor? trueColorBackground = null;
while (reader.Read ()) {
if (reader.TokenType == JsonTokenType.EndObject) {
- if (foreground == (Color)(-1) || background == (Color)(-1)) {
+ if (!attribute.TrueColorForeground.HasValue || !attribute.TrueColorBackground.HasValue) {
throw new JsonException ($"Both Foreground and Background colors must be provided.");
}
return attribute;
@@ -49,8 +50,6 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J
reader.Read ();
string color = $"\"{reader.GetString ()}\"";
- valuePair++;
-
switch (propertyName.ToLower ()) {
case "foreground":
foreground = JsonSerializer.Deserialize (color, options);
@@ -58,6 +57,12 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J
case "background":
background = JsonSerializer.Deserialize (color, options);
break;
+ case "truecolorforeground":
+ trueColorForeground = JsonSerializer.Deserialize (color, options);
+ break;
+ case "truecolorbackground":
+ trueColorBackground = JsonSerializer.Deserialize (color, options);
+ break;
//case "Bright":
// attribute.Bright = reader.GetBoolean ();
// break;
@@ -71,9 +76,11 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J
throw new JsonException ($"Unknown Attribute property {propertyName}.");
}
- if (valuePair == 2) {
+ if (foreground != (Color)(-1) && background != (Color)(-1)) {
attribute = new Attribute (foreground, background);
- valuePair = 0;
+ }
+ if (trueColorForeground.HasValue && trueColorBackground.HasValue) {
+ attribute = new Attribute (trueColorForeground, trueColorBackground);
}
}
throw new JsonException ();
@@ -82,10 +89,16 @@ public override Attribute Read (ref Utf8JsonReader reader, Type typeToConvert, J
public override void Write (Utf8JsonWriter writer, Attribute value, JsonSerializerOptions options)
{
writer.WriteStartObject ();
- writer.WritePropertyName ("Foreground");
+ writer.WritePropertyName (nameof(Attribute.Foreground));
ColorJsonConverter.Instance.Write (writer, value.Foreground, options);
- writer.WritePropertyName ("Background");
+ writer.WritePropertyName (nameof (Attribute.Background));
ColorJsonConverter.Instance.Write (writer, value.Background, options);
+ if (value.TrueColorForeground.HasValue && value.TrueColorBackground.HasValue) {
+ writer.WritePropertyName (nameof (Attribute.TrueColorForeground));
+ TrueColorJsonConverter.Instance.Write (writer, value.TrueColorForeground.Value, options);
+ writer.WritePropertyName (nameof (Attribute.TrueColorBackground));
+ TrueColorJsonConverter.Instance.Write (writer, value.TrueColorBackground.Value, options);
+ }
writer.WriteEndObject ();
}
}
diff --git a/Terminal.Gui/Configuration/ColorJsonConverter.cs b/Terminal.Gui/Configuration/ColorJsonConverter.cs
index 0a417ac423..7dfafe03d6 100644
--- a/Terminal.Gui/Configuration/ColorJsonConverter.cs
+++ b/Terminal.Gui/Configuration/ColorJsonConverter.cs
@@ -41,7 +41,7 @@ public override Color Read (ref Utf8JsonReader reader, Type typeToConvert, JsonS
var r = int.Parse (match.Groups [1].Value);
var g = int.Parse (match.Groups [2].Value);
var b = int.Parse (match.Groups [3].Value);
- return new TrueColor (r, g, b).ToConsoleColor ();
+ return TrueColor.ToConsoleColor(new TrueColor (r, g, b));
} else {
throw new JsonException ($"Invalid Color: '{colorString}'");
}
diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs
index 7a47fc70b8..e41e9233bd 100644
--- a/Terminal.Gui/Configuration/ConfigurationManager.cs
+++ b/Terminal.Gui/Configuration/ConfigurationManager.cs
@@ -1,4 +1,5 @@
-global using CM = Terminal.Gui.ConfigurationManager;
+global using static Terminal.Gui.ConfigurationManager;
+global using CM = Terminal.Gui.ConfigurationManager;
using System;
using System.Collections;
diff --git a/Terminal.Gui/Configuration/ThemeScope.cs b/Terminal.Gui/Configuration/ThemeScope.cs
index 924388392e..529bcc44a7 100644
--- a/Terminal.Gui/Configuration/ThemeScope.cs
+++ b/Terminal.Gui/Configuration/ThemeScope.cs
@@ -2,7 +2,7 @@
#nullable enable
-namespace Terminal.Gui;
+namespace Terminal.Gui;
///
/// The root object for a Theme. A Theme is a set of settings that are applied to the running
@@ -49,7 +49,7 @@ public class ThemeScope : Scope {
internal override bool Apply ()
{
var ret = base.Apply ();
- Application.Driver?.InitalizeColorSchemes ();
+ Application.Driver?.InitializeColorSchemes ();
return ret;
}
-}
\ No newline at end of file
+}
diff --git a/Terminal.Gui/Configuration/TrueColorJsonConverter.cs b/Terminal.Gui/Configuration/TrueColorJsonConverter.cs
new file mode 100644
index 0000000000..bb1bd741cb
--- /dev/null
+++ b/Terminal.Gui/Configuration/TrueColorJsonConverter.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui {
+ ///
+ /// for .
+ ///
+ internal class TrueColorJsonConverter : JsonConverter {
+ private static TrueColorJsonConverter instance;
+
+ ///
+ /// Singleton
+ ///
+ public static TrueColorJsonConverter Instance {
+ get {
+ if (instance == null) {
+ instance = new TrueColorJsonConverter ();
+ }
+
+ return instance;
+ }
+ }
+
+ public override TrueColor Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ // Check if the value is a string
+ if (reader.TokenType == JsonTokenType.String) {
+ // Get the color string
+ var colorString = reader.GetString ();
+
+ if (!TrueColor.TryParse (colorString, out TrueColor? trueColor)) {
+ throw new JsonException ($"Invalid TrueColor: '{colorString}'");
+ }
+
+ return trueColor.Value;
+ } else {
+ throw new JsonException ($"Unexpected token when parsing TrueColor: {reader.TokenType}");
+ }
+ }
+
+ public override void Write (Utf8JsonWriter writer, TrueColor value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue (value.ToString ());
+ }
+ }
+}
diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
index 7cc5a7fcb8..69c5a609d9 100644
--- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
@@ -1,1135 +1,587 @@
-//
+//
// ConsoleDriver.cs: Base class for Terminal.Gui ConsoleDriver implementations.
//
using System.Text;
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Diagnostics;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text.Json.Serialization;
-using System.Threading.Tasks;
-using Terminal.Gui;
-using static Terminal.Gui.ConfigurationManager;
-
-namespace Terminal.Gui {
+using static Terminal.Gui.ColorScheme;
+
+namespace Terminal.Gui;
+
+
+///
+/// Base class for Terminal.Gui ConsoleDriver implementations.
+///
+///
+/// There are currently four implementations:
+/// - (for Unix and Mac)
+/// -
+/// - that uses the .NET Console API
+/// - for unit testing.
+///
+public abstract class ConsoleDriver {
///
- /// Colors that can be used to set the foreground and background colors in console applications.
+ /// Prepare the driver and set the key and mouse events handlers.
///
- ///
- /// The value indicates either no-color has been set or the color is invalid.
- ///
- [JsonConverter (typeof (ColorJsonConverter))]
- public enum Color {
- ///
- /// The black color.
- ///
- Black,
- ///
- /// The blue color.
- ///
- Blue,
- ///
- /// The green color.
- ///
- Green,
- ///
- /// The cyan color.
- ///
- Cyan,
- ///
- /// The red color.
- ///
- Red,
- ///
- /// The magenta color.
- ///
- Magenta,
- ///
- /// The brown color.
- ///
- Brown,
- ///
- /// The gray color.
- ///
- Gray,
- ///
- /// The dark gray color.
- ///
- DarkGray,
- ///
- /// The bright bBlue color.
- ///
- BrightBlue,
- ///
- /// The bright green color.
- ///
- BrightGreen,
- ///
- /// The bright cyan color.
- ///
- BrightCyan,
- ///
- /// The bright red color.
- ///
- BrightRed,
- ///
- /// The bright magenta color.
- ///
- BrightMagenta,
- ///
- /// The bright yellow color.
- ///
- BrightYellow,
- ///
- /// The White color.
- ///
- White
- }
+ /// The main loop.
+ /// The handler for ProcessKey
+ /// The handler for key down events
+ /// The handler for key up events
+ /// The handler for mouse events
+ public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler);
///
- /// Indicates the RGB for true colors.
+ /// The handler fired when the terminal is resized.
///
- 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;
- }
-
- ///
- /// Converts true color to console color.
- ///
- ///
- 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);
- }
- }
+ protected Action TerminalResized;
///
- /// Attributes are used as elements that contain both a foreground and a background or platform specific features.
+ /// The number of columns visible in the terminal.
///
- ///
- /// 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.
- ///
- [JsonConverter (typeof (AttributeJsonConverter))]
- public struct Attribute {
- ///
- /// 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.
- ///
- [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
- public int Value { get; }
+ public virtual int Cols { get; internal set; }
- ///
- /// The foreground color.
- ///
- [JsonConverter (typeof (ColorJsonConverter))]
- public Color Foreground { get; }
-
- ///
- /// The background color.
- ///
- [JsonConverter (typeof (ColorJsonConverter))]
- public Color Background { get; }
+ ///
+ /// The number of rows visible in the terminal.
+ ///
+ public virtual int Rows { get; internal set; }
- ///
- /// Initializes a new instance of the struct with only the value passed to
- /// and trying to get the colors if defined.
- ///
- /// Value.
- 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;
- Background = background;
- }
+ ///
+ /// The leftmost column in the terminal.
+ ///
+ public virtual int Left { get; internal set; } = 0;
- ///
- /// Initializes a new instance of the struct.
- ///
- /// Value.
- /// Foreground
- /// Background
- public Attribute (int value, Color foreground, Color background)
- {
- Value = value;
- Foreground = foreground;
- Background = background;
- Initialized = true;
- }
+ ///
+ /// The topmost row in the terminal.
+ ///
+ public virtual int Top { get; internal set; } = 0;
- ///
- /// Initializes a new instance of the struct.
- ///
- /// Foreground
- /// Background
- public Attribute (Color foreground = new Color (), Color background = new Color ())
- {
- var make = Make (foreground, background);
- Initialized = make.Initialized;
- Value = make.Value;
- Foreground = foreground;
- Background = background;
- }
+ ///
+ /// Get the operating system clipboard.
+ ///
+ public IClipboard Clipboard { get; internal set; }
- ///
- /// Initializes a new instance of the struct
- /// with the same colors for the foreground and background.
- ///
- /// The color.
- public Attribute (Color color) : this (color, color) { }
+ ///
+ /// The contents of the application output. The driver outputs this buffer to the terminal when
+ /// is called.
+ ///
+ /// The format of the array is rows, columns, and 3 values on the last column: Rune, Attribute and Dirty Flag
+ ///
+ ///
+ //public int [,,] Contents { get; internal set; }
- ///
- /// Implicit conversion from an to the underlying, driver-specific, Int32 representation
- /// of the color.
- ///
- /// The driver-specific color value stored in the attribute.
- /// The attribute to convert
- 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;
- }
+ /////
+ ///// The contents of the application output. The driver outputs this buffer to the terminal when
+ ///// is called.
+ /////
+ ///// The format of the array is rows, columns. The first index is the row, the second index is the column.
+ /////
+ /////
+ public Cell [,] Contents { get; internal set; }
- ///
- /// Implicitly convert an driver-specific color value into an
- ///
- /// An attribute with the specified driver-specific color value.
- /// value
- public static implicit operator Attribute (int v) => new Attribute (v);
+ ///
+ /// Initializes the driver
+ ///
+ /// Method to invoke when the terminal is resized.
+ public abstract void Init (Action terminalResized);
- ///
- /// Creates an from the specified foreground and background colors.
- ///
- ///
- /// 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) {
- // Create the attribute, but show it's not been initialized
- return new Attribute (-1, foreground, background) {
- Initialized = false
- };
- }
- return Application.Driver.MakeAttribute (foreground, background);
- }
+ ///
+ /// Gets the column last set by . and
+ /// are used by and to determine where to add content.
+ ///
+ public int Col { get; internal set; }
- ///
- /// Gets the current from the driver.
- ///
- /// The current attribute.
- public static Attribute Get ()
- {
- if (Application.Driver == null)
- throw new InvalidOperationException ("The Application has not been initialized");
- return Application.Driver.GetAttribute ();
- }
+ ///
+ /// Gets the row last set by . and
+ /// are used by and to determine where to add content.
+ ///
+ public int Row { get; internal set; }
- ///
- /// 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.
- ///
- [JsonIgnore]
- public bool Initialized { get; internal set; }
+ ///
+ /// Updates and to the specified column and row in .
+ /// Used by and to determine where to add content.
+ ///
+ ///
+ ///
+ /// This does not move the cursor on the screen, it only updates the internal state of the driver.
+ ///
+ ///
+ /// If or are negative or beyond and ,
+ /// the method still sets those properties.
+ ///
+ ///
+ /// Column to move to.
+ /// Row to move to.
+ public virtual void Move (int col, int row)
+ {
+ Col = col;
+ Row = row;
+ }
- ///
- /// Returns if the Attribute is valid (both foreground and background have valid color values).
- ///
- ///
- [JsonIgnore]
- public bool HasValidColors { get => (int)Foreground > -1 && (int)Background > -1; }
+ ///
+ /// Tests if the specified rune is supported by the driver.
+ ///
+ ///
+ /// if the rune can be properly presented; if the driver
+ /// does not support displaying this rune.
+ public virtual bool IsRuneSupported (Rune rune)
+ {
+ return Rune.IsValid (rune.Value);
}
///
- /// Defines the color s for common visible elements in a .
- /// Containers such as and use to determine
- /// the colors used by sub-views.
+ /// Adds the specified rune to the display at the current cursor position.
///
///
- /// See also: .
+ ///
+ /// When the method returns, will be incremented by the number of columns required,
+ /// even if the new column value is outside of the or screen dimensions defined by .
+ ///
+ ///
+ /// If requires more than one column, and plus the number of columns needed
+ /// exceeds the or screen dimensions, the default Unicode replacement character (U+FFFD) will be added instead.
+ ///
///
- [JsonConverter (typeof (ColorSchemeJsonConverter))]
- public class ColorScheme : IEquatable {
- 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);
-
- ///
- /// Used by and to track which ColorScheme
- /// is being accessed.
- ///
- internal string schemeBeingSet = "";
-
- ///
- /// Creates a new instance.
- ///
- public ColorScheme () { }
-
- ///
- /// Creates a new instance, initialized with the values from .
- ///
- /// The scheme to initialize the new instance with.
- public ColorScheme (ColorScheme scheme) : base ()
- {
- if (scheme != null) {
- _normal = scheme.Normal;
- _focus = scheme.Focus;
- _hotNormal = scheme.HotNormal;
- _disabled = scheme.Disabled;
- _hotFocus = scheme.HotFocus;
- }
- }
-
- ///
- /// Creates a new instance, initialized with the values from .
- ///
- /// The attribute to initialize the new instance with.
- public ColorScheme (Attribute attribute)
- {
- _normal = attribute;
- _focus = attribute;
- _hotNormal = attribute;
- _disabled = attribute;
- _hotFocus = attribute;
- }
-
- ///
- /// The foreground and background color for text when the view is not focused, hot, or disabled.
- ///
- public Attribute Normal {
- get { return _normal; }
- set {
- if (!value.HasValidColors) {
- return;
+ /// Rune to add.
+ public void AddRune (Rune rune)
+ {
+ int runeWidth = -1;
+ var validLocation = IsValidLocation (Col, Row);
+ if (validLocation) {
+ rune = rune.MakePrintable ();
+ runeWidth = rune.GetColumns ();
+ if (runeWidth == 0 && rune.IsCombiningMark () && Col > 0) {
+ // This is a combining character, and we are not at the beginning of the line.
+ // TODO: Remove hard-coded [0] once combining pairs is supported
+
+ // Convert Runes to string and concatenate
+ string combined = Contents [Row, Col - 1].Runes [0].ToString () + rune.ToString ();
+
+ // Normalize to Form C (Canonical Composition)
+ string normalized = combined.Normalize (NormalizationForm.FormC);
+
+ Contents [Row, Col - 1].Runes = new List { (Rune)normalized [0] }; ;
+ Contents [Row, Col - 1].Attribute = CurrentAttribute;
+ Contents [Row, Col - 1].IsDirty = true;
+
+ //Col--;
+ } else {
+ Contents [Row, Col].Attribute = CurrentAttribute;
+ Contents [Row, Col].IsDirty = true;
+
+ if (Col > 0) {
+ // Check if cell to left has a wide glyph
+ if (Contents [Row, Col - 1].Runes [0].GetColumns () > 1) {
+ // Invalidate cell to left
+ Contents [Row, Col - 1].Runes = new List { Rune.ReplacementChar };
+ Contents [Row, Col - 1].IsDirty = true;
+ }
}
- _normal = value;
- }
- }
- ///
- /// The foreground and background color for text when the view has the focus.
- ///
- public Attribute Focus {
- get { return _focus; }
- set {
- if (!value.HasValidColors) {
- return;
- }
- _focus = value;
- }
- }
- ///
- /// The foreground and background color for text when the view is highlighted (hot).
- ///
- public Attribute HotNormal {
- get { return _hotNormal; }
- set {
- if (!value.HasValidColors) {
- return;
+ if (runeWidth < 1) {
+ Contents [Row, Col].Runes = new List { Rune.ReplacementChar };
+
+ } else if (runeWidth == 1) {
+ Contents [Row, Col].Runes = new List { rune };
+ if (Col < Clip.Right - 1) {
+ Contents [Row, Col + 1].IsDirty = true;
+ }
+ } else if (runeWidth == 2) {
+ if (Col == Clip.Right - 1) {
+ // We're at the right edge of the clip, so we can't display a wide character.
+ // TODO: Figure out if it is better to show a replacement character or ' '
+ Contents [Row, Col].Runes = new List { Rune.ReplacementChar };
+ } else {
+ Contents [Row, Col].Runes = new List { rune };
+ if (Col < Clip.Right - 1) {
+ // Invalidate cell to right so that it doesn't get drawn
+ // TODO: Figure out if it is better to show a replacement character or ' '
+ Contents [Row, Col + 1].Runes = new List { Rune.ReplacementChar };
+ Contents [Row, Col + 1].IsDirty = true;
+ }
+ }
+ } else {
+ // This is a non-spacing character, so we don't need to do anything
+ Contents [Row, Col].Runes = new List { (Rune)' ' };
+ Contents [Row, Col].IsDirty = false;
}
- _hotNormal = value;
+ _dirtyLines [Row] = true;
}
}
- ///
- /// The foreground and background color for text when the view is highlighted (hot) and has focus.
- ///
- public Attribute HotFocus {
- get { return _hotFocus; }
- set {
- if (!value.HasValidColors) {
- return;
- }
- _hotFocus = value;
- }
+ if (runeWidth is < 0 or > 0) {
+ Col++;
}
- ///
- /// 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;
- }
- }
+ if (runeWidth > 1) {
+ Debug.Assert (runeWidth <= 2);
+ if (validLocation && Col < Clip.Right) {
+ // This is a double-width character, and we are not at the end of the line.
+ // Col now points to the second column of the character. Ensure it doesn't
+ // Get rendered.
+ Contents [Row, Col].IsDirty = false;
+ Contents [Row, Col].Attribute = CurrentAttribute;
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// true if the two objects are equal
- public override bool Equals (object obj)
- {
- return Equals (obj as ColorScheme);
- }
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// true if the two objects are equal
- public bool Equals (ColorScheme other)
- {
- return other != null &&
- EqualityComparer.Default.Equals (_normal, other._normal) &&
- EqualityComparer.Default.Equals (_focus, other._focus) &&
- EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) &&
- EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) &&
- EqualityComparer.Default.Equals (_disabled, other._disabled);
- }
-
- ///
- /// Returns a hashcode for this instance.
- ///
- /// hashcode for this instance
- public override int GetHashCode ()
- {
- int hashCode = -1242460230;
- hashCode = hashCode * -1521134295 + _normal.GetHashCode ();
- hashCode = hashCode * -1521134295 + _focus.GetHashCode ();
- hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode ();
- hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode ();
- hashCode = hashCode * -1521134295 + _disabled.GetHashCode ();
- return hashCode;
- }
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- ///
- /// true if the two objects are equivalent
- public static bool operator == (ColorScheme left, ColorScheme right)
- {
- return EqualityComparer.Default.Equals (left, right);
- }
-
- ///
- /// Compares two objects for inequality.
- ///
- ///
- ///
- /// true if the two objects are not equivalent
- public static bool operator != (ColorScheme left, ColorScheme right)
- {
- 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);
+ // TODO: Determine if we should wipe this out (for now now)
+ //Contents [Row, Col].Runes [0] = (Rune)' ';
}
+ Col++;
}
}
///
- /// The default s for the application.
+ /// Adds the specified to the display at the current cursor position. This method
+ /// is a convenience method that calls with the constructor.
+ ///
+ /// Character to add.
+ public void AddRune (char c) => AddRune (new Rune (c));
+
+ ///
+ /// Adds the to the display at the cursor position.
///
///
- /// This property can be set in a Theme to change the default for the application.
+ ///
+ /// When the method returns, will be incremented by the number of columns required,
+ /// unless the new column value is outside of the or screen dimensions defined by .
+ ///
+ ///
+ /// If requires more columns than are available, the output will be clipped.
+ ///
///
- public static class Colors {
- private class SchemeNameComparerIgnoreCase : IEqualityComparer {
- public bool Equals (string x, string y)
- {
- if (x != null && y != null) {
- return string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase);
- }
- 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.
- return typeof (Colors).GetProperties ()
- .Where (p => p.PropertyType == typeof (ColorScheme))
- .Select (p => new KeyValuePair (p.Name, new ColorScheme ()))
- .ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ());
- }
-
- ///
- /// The application Toplevel color scheme, for the default Toplevel views.
- ///
- ///
- ///
- /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"];
- ///
- ///
- public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); }
-
- ///
- /// The base color scheme, for the default Toplevel views.
- ///
- ///
- ///
- /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"];
- ///
- ///
- public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); }
-
- ///
- /// The dialog color scheme, for standard popup dialog boxes
- ///
- ///
- ///
- /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"];
- ///
- ///
- public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); }
-
- ///
- /// The menu bar color
- ///
- ///
- ///
- /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"];
- ///
- ///
- public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); }
-
- ///
- /// The color scheme for showing errors.
- ///
- ///
- ///
- /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"];
- ///
- ///
- public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); }
-
- static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null)
- {
- return ColorSchemes [schemeBeingSet];
- }
-
- static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null)
- {
- ColorSchemes [schemeBeingSet] = colorScheme;
- colorScheme.schemeBeingSet = schemeBeingSet;
+ /// String.
+ public void AddStr (string str)
+ {
+ foreach (var rune in str.EnumerateRunes ()) {
+ AddRune (rune);
}
-
- ///
- /// Provides the defined s.
- ///
- [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
- [JsonConverter (typeof (DictionaryJsonConverter))]
- public static Dictionary ColorSchemes { get; private set; }
}
+ Rect _clip;
+
///
- /// Cursors Visibility that are displayed
+ /// Tests whether the specified coordinate are valid for drawing.
///
- //
- // Hexa value are set as 0xAABBCCDD where :
- //
- // AA stand for the TERMINFO DECSUSR parameter value to be used under Linux & MacOS
- // BB stand for the NCurses curs_set parameter value to be used under Linux & MacOS
- // CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows
- // DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows
- //
- public enum CursorVisibility {
- ///
- /// Cursor caret has default
- ///
- /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking.
- Default = 0x00010119,
-
- ///
- /// Cursor caret is hidden
- ///
- Invisible = 0x03000019,
-
- ///
- /// Cursor caret is normally shown as a blinking underline bar _
- ///
- Underline = 0x03010119,
-
- ///
- /// Cursor caret is normally shown as a underline bar _
- ///
- /// Under Windows, this is equivalent to
- UnderlineFix = 0x04010119,
-
- ///
- /// Cursor caret is displayed a blinking vertical bar |
- ///
- /// Works under Xterm-like terminal otherwise this is equivalent to
- Vertical = 0x05010119,
-
- ///
- /// Cursor caret is displayed a blinking vertical bar |
- ///
- /// Works under Xterm-like terminal otherwise this is equivalent to
- VerticalFix = 0x06010119,
-
- ///
- /// Cursor caret is displayed as a blinking block ▉
- ///
- Box = 0x01020164,
+ /// The column.
+ /// The row.
+ /// if the coordinate is outside of the
+ /// screen bounds or outside of . otherwise.
+ public bool IsValidLocation (int col, int row) =>
+ col >= 0 && row >= 0 &&
+ col < Cols && row < Rows &&
+ Clip.Contains (col, row);
- ///
- /// Cursor caret is displayed a block ▉
- ///
- /// Works under Xterm-like terminal otherwise this is equivalent to
- BoxFix = 0x02020164,
+ ///
+ /// Gets or sets the clip rectangle that and are
+ /// subject to.
+ ///
+ /// The rectangle describing the bounds of .
+ public Rect Clip {
+ get => _clip;
+ set => _clip = value;
}
///
- /// 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.
+ /// Updates the screen to reflect all the changes that have been done to the display buffer
///
- public abstract class ConsoleDriver {
-
- ///
- /// The handler fired when the terminal is resized.
- ///
- protected Action TerminalResized;
-
- ///
- /// The current number of columns in the terminal.
- ///
- public abstract int Cols { get; }
+ public abstract void Refresh ();
- ///
- /// The current number of rows in the terminal.
- ///
- public abstract int Rows { get; }
-
- ///
- /// The current left in the terminal.
- ///
- public abstract int Left { get; }
-
- ///
- /// The current top in the terminal.
- ///
- public abstract int Top { get; }
+ ///
+ /// Sets the position of the terminal cursor to and .
+ ///
+ public abstract void UpdateCursor ();
- ///
- /// Get the operation system clipboard.
- ///
- public abstract IClipboard Clipboard { get; }
+ ///
+ /// Gets the terminal cursor visibility.
+ ///
+ /// The current
+ /// upon success
+ public abstract bool GetCursorVisibility (out CursorVisibility visibility);
- ///
- ///
- /// 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 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.
- ///
- ///
- ///
- /// NOTE: This functionaliy is currently broken on Windows Terminal.
- ///
- public abstract bool EnableConsoleScrolling { get; set; }
+ ///
+ /// Sets the terminal cursor visibility.
+ ///
+ /// The wished
+ /// upon success
+ public abstract bool SetCursorVisibility (CursorVisibility visibility);
- ///
- /// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
- ///
- public virtual int [,,] Contents { get; }
+ ///
+ /// Determines if the terminal cursor should be visible or not and sets it accordingly.
+ ///
+ /// upon success
+ public abstract bool EnsureCursorVisibility ();
- ///
- /// Initializes the driver
- ///
- /// Method to invoke when the terminal is resized.
- public abstract void Init (Action terminalResized);
- ///
- /// Moves the cursor to the specified column and row.
- ///
- /// Column to move the cursor to.
- /// Row to move the cursor to.
- public abstract void Move (int col, int row);
+ // As performance is a concern, we keep track of the dirty lines and only refresh those.
+ // This is in addition to the dirty flag on each cell.
+ internal bool [] _dirtyLines;
- ///
- /// Tests if the specified rune is supported by the driver.
- ///
- ///
- /// if the rune can be properly presented; if the driver
- /// does not support displaying this rune.
- public virtual bool IsRuneSupported (Rune rune)
- {
- if (rune.Value > RuneExtensions.MaxUnicodeCodePoint) {
- return false;
- }
- return true;
+ ///
+ /// Clears the of the driver.
+ ///
+ public void ClearContents ()
+ {
+ // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual)
+ Contents = new Cell [Rows, Cols];
+ Clip = new Rect (0, 0, Cols, Rows);
+ _dirtyLines = new bool [Rows];
+
+ lock (Contents) {
+ // Can raise an exception while is still resizing.
+ try {
+ for (var row = 0; row < Rows; row++) {
+ for (var c = 0; c < Cols; c++) {
+ Contents [row, c] = new Cell () {
+ Runes = new List { (Rune)' ' },
+ Attribute = new Attribute (Color.White, Color.Black),
+ IsDirty = true
+ };
+ _dirtyLines [row] = true;
+ }
+ }
+ } catch (IndexOutOfRangeException) { }
}
+ }
- ///
- /// Adds the specified rune to the display at the current cursor position.
- ///
- /// Rune to add.
- public abstract void AddRune (Rune rune);
-
- ///
- /// Ensures that the column and line are in a valid range from the size of the driver.
- ///
- /// The column.
- /// The row.
- /// The clip.
- /// trueif it's a valid range,falseotherwise.
- public bool IsValidContent (int col, int row, Rect clip) =>
- col >= 0 && row >= 0 && col < Cols && row < Rows && clip.Contains (col, row);
-
- ///
- /// Adds the to the display at the cursor position.
- ///
- /// String.
- public abstract void AddStr (string str);
-
- ///
- /// Prepare the driver and set the key and mouse events handlers.
- ///
- /// The main loop.
- /// The handler for ProcessKey
- /// The handler for key down events
- /// The handler for key up events
- /// The handler for mouse events
- public abstract void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler);
-
- ///
- /// Updates the screen to reflect all the changes that have been done to the display buffer
- ///
- public abstract void Refresh ();
-
- ///
- /// Updates the location of the cursor position
- ///
- public abstract void UpdateCursor ();
-
- ///
- /// Retreive the cursor caret visibility
- ///
- /// The current
- /// true upon success
- public abstract bool GetCursorVisibility (out CursorVisibility visibility);
-
- ///
- /// Change the cursor caret visibility
- ///
- /// The wished
- /// true upon success
- public abstract bool SetCursorVisibility (CursorVisibility visibility);
+ ///
+ /// Redraws the physical screen with the contents that have been queued up via any of the printing commands.
+ ///
+ public abstract void UpdateScreen ();
- ///
- /// Ensure the cursor visibility
- ///
- /// true upon success
- public abstract bool EnsureCursorVisibility ();
+ #region Color Handling
- ///
- /// Ends the execution of the console driver.
- ///
- public abstract void End ();
- ///
- /// Resizes the clip area when the screen is resized.
- ///
- public abstract void ResizeScreen ();
+ ///
+ /// Gets whether the supports TrueColor output.
+ ///
+ public virtual bool SupportsTrueColor { get => false; }
- ///
- /// Reset and recreate the contents and the driver buffer.
- ///
- public abstract void UpdateOffScreen ();
+ private bool _useTrueColor = true;
- ///
- /// Redraws the physical screen with the contents that have been queued up via any of the printing commands.
- ///
- public abstract void UpdateScreen ();
+ // TODO: Make this a ConfiguationManager setting on Application
+ ///
+ /// Gets or sets whether the should use TrueColor output.
+ ///
+ ///
+ /// Can only be enabled if is true, indicating
+ /// that the supports it.
+ ///
+ public bool UseTrueColor {
+ get => _useTrueColor && SupportsTrueColor;
+ set => this._useTrueColor = (value && SupportsTrueColor);
+ }
- ///
- /// 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.");
+ Attribute _currentAttribute;
- currentAttribute = value;
+ ///
+ /// The that will be used for the next or call.
+ ///
+ public Attribute CurrentAttribute {
+ get => _currentAttribute;
+ set {
+ if (value is { Initialized: false, HasValidColors: true } && 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.");
- ///
- /// Selects the specified attribute as the attribute to use for future calls to AddRune and AddString.
- ///
- ///
- /// Implementations should call base.SetAttribute(c).
- ///
- /// C.
- public virtual void SetAttribute (Attribute c)
- {
- CurrentAttribute = c;
- }
-
- ///
- /// Set Colors from limit sets of colors. Not implemented by any driver: See Issue #2300.
- ///
- /// Foreground.
- /// Background.
- public abstract void SetColors (ConsoleColor foreground, ConsoleColor background);
-
- // Advanced uses - set colors to any pre-set pairs, you would need to init_color
- // 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. Not implemented by any driver: See Issue #2300.
- ///
- /// Foreground color identifier.
- /// Background color identifier.
- public abstract void SetColors (short foregroundColorId, short backgroundColorId);
-
- ///
- /// Gets the foreground and background colors based on the value.
- ///
- /// The value.
- /// The foreground.
- /// The background.
- ///
- public abstract bool GetColors (int value, out Color foreground, out Color background);
-
- ///
- /// Allows sending keys without typing on a keyboard.
- ///
- /// The character key.
- /// The key.
- /// If shift key is sending.
- /// If alt key is sending.
- /// If control key is sending.
- public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control);
-
- ///
- /// Set the handler when the terminal is resized.
- ///
- ///
- public void SetTerminalResized (Action terminalResized)
- {
- TerminalResized = terminalResized;
+ _currentAttribute = value;
}
+ }
- ///
- /// Fills the specified rectangle with the specified rune.
- ///
- ///
- ///
- public virtual void FillRect (Rect rect, Rune rune = default)
- {
- for (var r = rect.Y; r < rect.Y + rect.Height; r++) {
- for (var c = rect.X; c < rect.X + rect.Width; c++) {
- Application.Driver.Move (c, r);
- Application.Driver.AddRune ((Rune)(rune == default ? ' ' : rune.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 Attribute SetAttribute (Attribute c)
+ {
+ var prevAttribute = CurrentAttribute;
+ CurrentAttribute = c;
+ return prevAttribute;
+ }
- ///
- /// Enables diagnostic functions
- ///
- [Flags]
- public enum DiagnosticFlags : uint {
- ///
- /// All diagnostics off
- ///
- Off = 0b_0000_0000,
- ///
- /// When enabled, will draw a
- /// ruler in the frame for any side with a padding value greater than 0.
- ///
- FrameRuler = 0b_0000_0001,
- ///
- /// When enabled, will draw a
- /// 'L', 'R', 'T', and 'B' when clearing 's instead of ' '.
- ///
- FramePadding = 0b_0000_0010,
- }
+ ///
+ /// Make the attribute for the foreground and background colors.
+ ///
+ /// Foreground.
+ /// Background.
+ ///
+ public virtual Attribute MakeAttribute (Color fore, Color back)
+ {
+ return MakeColor (fore, back);
+ }
- ///
- /// Set flags to enable/disable diagnostics.
- ///
- public static DiagnosticFlags Diagnostics { get; set; }
+ ///
+ /// Gets the foreground and background colors based on a platform-dependent color value.
+ ///
+ /// The platform-dependent color value.
+ /// The foreground.
+ /// The background.
+ internal abstract void GetColors (int value, out Color foreground, out Color background);
- ///
- /// Suspend the application, typically needs to save the state, suspend the app and upon return, reset the console driver.
- ///
- public abstract void Suspend ();
+ ///
+ /// Gets the current .
+ ///
+ /// The current attribute.
+ public Attribute GetAttribute () => CurrentAttribute;
- Rect clip;
+ ///
+ /// Makes an .
+ ///
+ /// The foreground color.
+ /// The background color.
+ /// The attribute for the foreground and background colors.
+ public abstract Attribute MakeColor (Color foreground, Color background);
- ///
- /// Controls the current clipping region that AddRune/AddStr is subject to.
- ///
- /// The clip.
- public Rect Clip {
- get => clip;
- set => this.clip = value;
+ ///
+ /// Ensures all s in are correctly
+ /// initialized by the driver.
+ ///
+ public void InitializeColorSchemes ()
+ {
+ // Ensure all Attributes are initialized by the driver
+ foreach (var s in Colors.ColorSchemes) {
+ s.Value.Initialize ();
}
+ }
- ///
- /// Start of mouse moves.
- ///
- public abstract void StartReportingMouseMoves ();
-
- ///
- /// Stop reporting mouses moves.
- ///
- public abstract void StopReportingMouseMoves ();
+ #endregion
+ ///
+ /// Enables diagnostic functions
+ ///
+ [Flags]
+ public enum DiagnosticFlags : uint {
///
- /// 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.
+ /// All diagnostics off
///
- public abstract void UncookMouse ();
-
+ Off = 0b_0000_0000,
///
- /// Enables the cooked event processing from the mouse driver. Not implemented by any driver: See Issue #2300.
+ /// When enabled, will draw a
+ /// ruler in the frame for any side with a padding value greater than 0.
///
- public abstract void CookMouse ();
-
- private Attribute currentAttribute;
-
+ FrameRuler = 0b_0000_0001,
///
- /// Make the attribute for the foreground and background colors.
+ /// When enabled, will draw a
+ /// 'L', 'R', 'T', and 'B' when clearing 's instead of ' '.
///
- /// Foreground.
- /// Background.
- ///
- public abstract Attribute MakeAttribute (Color fore, Color back);
-
- ///
- /// Gets the current .
- ///
- /// The current attribute.
- public Attribute GetAttribute () => CurrentAttribute;
+ FramePadding = 0b_0000_0010,
+ }
- ///
- /// Make the for the .
- ///
- /// The foreground color.
- /// The background color.
- /// The attribute for the foreground and background colors.
- public abstract Attribute MakeColor (Color foreground, Color background);
+ ///
+ /// Set flags to enable/disable diagnostics.
+ ///
+ public static DiagnosticFlags Diagnostics { get; set; }
- ///
- /// Ensures all s in are correctly
- /// initialized by the driver.
- ///
- ///
- /// This method was previsouly named CreateColors. It was reanmed to InitalizeColorSchemes when
- /// was enabled.
- ///
- /// Flag indicating if colors are supported (not used).
- public void InitalizeColorSchemes (bool supportsColors = true)
- {
- // Ensure all Attributes are initialized by the driver
- foreach (var s in Colors.ColorSchemes) {
- s.Value.Initialize ();
- }
+ ///
+ /// Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.
+ ///
+ /// This is only implemented in .
+ public abstract void Suspend ();
- if (!supportsColors) {
- return;
+ ///
+ /// Simulates a key press.
+ ///
+ /// The key character.
+ /// The key.
+ /// If simulates the Shift key being pressed.
+ /// If simulates the Alt key being pressed.
+ /// If simulates the Ctrl key being pressed.
+ public abstract void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool ctrl);
+
+ // TODO: Move FillRect to ./Drawing
+ ///
+ /// Fills the specified rectangle with the specified rune.
+ ///
+ ///
+ ///
+ public void FillRect (Rect rect, Rune rune = default)
+ {
+ for (var r = rect.Y; r < rect.Y + rect.Height; r++) {
+ for (var c = rect.X; c < rect.X + rect.Width; c++) {
+ Application.Driver.Move (c, r);
+ Application.Driver.AddRune (rune == default ? new Rune (' ') : rune);
}
-
- }
-
- internal void SetAttribute (object attribute)
- {
- throw new NotImplementedException ();
}
}
///
- /// Helper class for console drivers to invoke shell commands to interact with the clipboard.
- /// Used primarily by CursesDriver, but also used in Unit tests which is why it is in
- /// ConsoleDriver.cs.
+ /// Fills the specified rectangle with the specified . This method
+ /// is a convenience method that calls .
///
- internal static class ClipboardProcessRunner {
- public static (int exitCode, string result) Bash (string commandLine, string inputText = "", bool waitForOutput = false)
- {
- var arguments = $"-c \"{commandLine}\"";
- var (exitCode, result) = Process ("bash", arguments, inputText, waitForOutput);
+ ///
+ ///
+ public void FillRect (Rect rect, char c) => FillRect (rect, new Rune (c));
- return (exitCode, result.TrimEnd ());
- }
+ ///
+ /// Ends the execution of the console driver.
+ ///
+ public abstract void End ();
+
+ ///
+ /// Returns the name of the driver and relevant library version information.
+ ///
+ ///
+ public virtual string GetVersionInfo () => GetType ().Name;
+}
- public static (int exitCode, string result) Process (string cmd, string arguments, string input = null, bool waitForOutput = true)
- {
- var output = string.Empty;
-
- using (Process process = new Process {
- StartInfo = new ProcessStartInfo {
- FileName = cmd,
- Arguments = arguments,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
- UseShellExecute = false,
- CreateNoWindow = true,
- }
- }) {
- var eventHandled = new TaskCompletionSource ();
- process.Start ();
- if (!string.IsNullOrEmpty (input)) {
- process.StandardInput.Write (input);
- process.StandardInput.Close ();
- }
- if (!process.WaitForExit (5000)) {
- var timeoutError = $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}.";
- throw new TimeoutException (timeoutError);
- }
+///
+/// Terminal Cursor Visibility settings.
+///
+///
+/// Hex value are set as 0xAABBCCDD where :
+///
+/// AA stand for the TERMINFO DECSUSR parameter value to be used under Linux and MacOS
+/// BB stand for the NCurses curs_set parameter value to be used under Linux and MacOS
+/// CC stand for the CONSOLE_CURSOR_INFO.bVisible parameter value to be used under Windows
+/// DD stand for the CONSOLE_CURSOR_INFO.dwSize parameter value to be used under Windows
+///
+public enum CursorVisibility {
+ ///
+ /// Cursor caret has default
+ ///
+ /// Works under Xterm-like terminal otherwise this is equivalent to . This default directly depends of the XTerm user configuration settings so it could be Block, I-Beam, Underline with possible blinking.
+ Default = 0x00010119,
- if (waitForOutput && process.StandardOutput.Peek () != -1) {
- output = process.StandardOutput.ReadToEnd ();
- }
+ ///
+ /// Cursor caret is hidden
+ ///
+ Invisible = 0x03000019,
- if (process.ExitCode > 0) {
- output = $@"Process failed to run. Command line: {cmd} {arguments}.
- Output: {output}
- Error: {process.StandardError.ReadToEnd ()}";
- }
+ ///
+ /// Cursor caret is normally shown as a blinking underline bar _
+ ///
+ Underline = 0x03010119,
- return (process.ExitCode, output);
- }
- }
+ ///
+ /// Cursor caret is normally shown as a underline bar _
+ ///
+ /// Under Windows, this is equivalent to
+ UnderlineFix = 0x04010119,
- public static bool DoubleWaitForExit (this System.Diagnostics.Process process)
- {
- var result = process.WaitForExit (500);
- if (result) {
- process.WaitForExit ();
- }
- return result;
- }
+ ///
+ /// Cursor caret is displayed a blinking vertical bar |
+ ///
+ /// Works under Xterm-like terminal otherwise this is equivalent to
+ Vertical = 0x05010119,
- public static bool FileExists (this string value)
- {
- return !string.IsNullOrEmpty (value) && !value.Contains ("not found");
- }
- }
+ ///
+ /// Cursor caret is displayed a blinking vertical bar |
+ ///
+ /// Works under Xterm-like terminal otherwise this is equivalent to
+ VerticalFix = 0x06010119,
+
+ ///
+ /// Cursor caret is displayed as a blinking block ▉
+ ///
+ Box = 0x01020164,
+
+ ///
+ /// Cursor caret is displayed a block ▉
+ ///
+ /// Works under Xterm-like terminal otherwise this is equivalent to
+ BoxFix = 0x02020164,
}
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs
new file mode 100644
index 0000000000..ddf5811851
--- /dev/null
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/ClipboardImpl.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Runtime.InteropServices;
+using Unix.Terminal;
+
+namespace Terminal.Gui;
+
+///
+/// A clipboard implementation for Linux.
+/// This implementation uses the xclip command to access the clipboard.
+///
+///
+/// If xclip is not installed, this implementation will not work.
+///
+class CursesClipboard : ClipboardBase {
+ public CursesClipboard ()
+ {
+ IsSupported = CheckSupport ();
+ }
+
+ string _xclipPath = string.Empty;
+ public override bool IsSupported { get; }
+
+ 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 ()) {
+ _xclipPath = result;
+ return true;
+ }
+ } catch (Exception) {
+ // Permissions issue.
+ }
+#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception.
+ return false;
+ }
+
+ protected override string GetClipboardDataImpl ()
+ {
+ var tempFileName = System.IO.Path.GetTempFileName ();
+ var xclipargs = "-selection clipboard -o";
+
+ try {
+ var (exitCode, result) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false);
+ if (exitCode == 0) {
+ if (Application.Driver is CursesDriver) {
+ Curses.raw ();
+ Curses.noecho ();
+ }
+ return System.IO.File.ReadAllText (tempFileName);
+ }
+ } catch (Exception e) {
+ throw new NotSupportedException ($"\"{_xclipPath} {xclipargs}\" failed.", e);
+ } finally {
+ System.IO.File.Delete (tempFileName);
+ }
+ return string.Empty;
+ }
+
+ protected override void SetClipboardDataImpl (string text)
+ {
+ var xclipargs = "-selection clipboard -i";
+ try {
+ var (exitCode, _) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs}", text, waitForOutput: false);
+ if (exitCode == 0 && Application.Driver is CursesDriver) {
+ Curses.raw ();
+ Curses.noecho ();
+ }
+ } catch (Exception e) {
+ throw new NotSupportedException ($"\"{_xclipPath} {xclipargs} < {text}\" failed", e);
+ }
+ }
+}
+///
+/// A clipboard implementation for MacOSX.
+/// This implementation uses the Mac clipboard API (via P/Invoke) to copy/paste.
+/// The existance of the Mac pbcopy and pbpaste commands
+/// is used to determine if copy/paste is supported.
+///
+class MacOSXClipboard : ClipboardBase {
+ IntPtr _nsString = objc_getClass ("NSString");
+ IntPtr _nsPasteboard = objc_getClass ("NSPasteboard");
+ IntPtr _utfTextType;
+ IntPtr _generalPasteboard;
+ IntPtr _initWithUtf8Register = sel_registerName ("initWithUTF8String:");
+ IntPtr _allocRegister = sel_registerName ("alloc");
+ IntPtr _setStringRegister = sel_registerName ("setString:forType:");
+ IntPtr _stringForTypeRegister = sel_registerName ("stringForType:");
+ IntPtr _utf8Register = sel_registerName ("UTF8String");
+ IntPtr _nsStringPboardType;
+ IntPtr _generalPasteboardRegister = sel_registerName ("generalPasteboard");
+ IntPtr _clearContentsRegister = sel_registerName ("clearContents");
+
+ public MacOSXClipboard ()
+ {
+ _utfTextType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "public.utf8-plain-text");
+ _nsStringPboardType = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, "NSStringPboardType");
+ _generalPasteboard = objc_msgSend (_nsPasteboard, _generalPasteboardRegister);
+ IsSupported = CheckSupport ();
+ }
+
+ public override bool IsSupported { get; }
+
+ bool CheckSupport ()
+ {
+ var (exitCode, result) = ClipboardProcessRunner.Bash ("which pbcopy", waitForOutput: true);
+ if (exitCode != 0 || !result.FileExists ()) {
+ return false;
+ }
+ (exitCode, result) = ClipboardProcessRunner.Bash ("which pbpaste", waitForOutput: true);
+ return exitCode == 0 && result.FileExists ();
+ }
+
+ protected override string GetClipboardDataImpl ()
+ {
+ var ptr = objc_msgSend (_generalPasteboard, _stringForTypeRegister, _nsStringPboardType);
+ var charArray = objc_msgSend (ptr, _utf8Register);
+ return Marshal.PtrToStringAnsi (charArray);
+ }
+
+ protected override void SetClipboardDataImpl (string text)
+ {
+ IntPtr str = default;
+ try {
+ str = objc_msgSend (objc_msgSend (_nsString, _allocRegister), _initWithUtf8Register, text);
+ objc_msgSend (_generalPasteboard, _clearContentsRegister);
+ objc_msgSend (_generalPasteboard, _setStringRegister, str, _utfTextType);
+ } finally {
+ if (str != default) {
+ objc_msgSend (str, sel_registerName ("release"));
+ }
+ }
+ }
+
+ [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
+ static extern IntPtr objc_getClass (string className);
+
+ [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
+ static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector);
+
+ [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
+ static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, string arg1);
+
+ [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
+ static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1);
+
+ [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
+ static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);
+
+ [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
+ static extern IntPtr sel_registerName (string selectorName);
+}
+
+///
+/// A clipboard implementation for Linux, when running under WSL.
+/// This implementation uses the Windows clipboard to store the data, and uses Windows'
+/// powershell.exe (launched via WSL interop services) to set/get the Windows
+/// clipboard.
+///
+class WSLClipboard : ClipboardBase {
+ public WSLClipboard ()
+ {
+ }
+
+ public override bool IsSupported {
+ get {
+ return CheckSupport ();
+ }
+ }
+
+ private static string _powershellPath = string.Empty;
+
+ bool CheckSupport ()
+ {
+ if (string.IsNullOrEmpty (_powershellPath)) {
+ // Specify pwsh.exe (not pwsh) to ensure we get the Windows version (invoked via WSL)
+ var (exitCode, result) = ClipboardProcessRunner.Bash ("which pwsh.exe", waitForOutput: true);
+ if (exitCode > 0) {
+ (exitCode, result) = ClipboardProcessRunner.Bash ("which powershell.exe", waitForOutput: true);
+ }
+
+ if (exitCode == 0) {
+ _powershellPath = result;
+ }
+ }
+ return !string.IsNullOrEmpty (_powershellPath);
+ }
+
+ protected override string GetClipboardDataImpl ()
+ {
+ if (!IsSupported) {
+ return string.Empty;
+ }
+
+ var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, "-noprofile -command \"Get-Clipboard\"");
+ if (exitCode == 0) {
+ if (Application.Driver is CursesDriver) {
+ Curses.raw ();
+ Curses.noecho ();
+ }
+
+ if (output.EndsWith ("\r\n")) {
+ output = output.Substring (0, output.Length - 2);
+ }
+ return output;
+ }
+ return string.Empty;
+ }
+
+ protected override void SetClipboardDataImpl (string text)
+ {
+ if (!IsSupported) {
+ return;
+ }
+
+ var (exitCode, output) = ClipboardProcessRunner.Process (_powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\"");
+ if (exitCode == 0) {
+ if (Application.Driver is CursesDriver) {
+ Curses.raw ();
+ Curses.noecho ();
+ }
+ }
+ }
+}
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
index eaba76d7e4..54fda896f1 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
@@ -3,1183 +3,840 @@
//
using System;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
using System.Runtime.InteropServices;
-using System.Threading.Tasks;
using System.Text;
using Unix.Terminal;
-namespace Terminal.Gui {
+namespace Terminal.Gui;
- ///
- /// This is the Curses driver for the gui.cs/Terminal framework.
- ///
- internal class CursesDriver : ConsoleDriver {
- public override int Cols => Curses.Cols;
- public override int Rows => Curses.Lines;
- public override int Left => 0;
- public override int Top => 0;
- public override bool EnableConsoleScrolling { get; set; }
- public override IClipboard Clipboard { get => clipboard; }
-
- CursorVisibility? initialCursorVisibility = null;
- CursorVisibility? currentCursorVisibility = null;
- IClipboard clipboard;
- int [,,] contents;
-
- public override int [,,] Contents => contents;
-
- // Current row, and current col, tracked by Move/AddRune only
- int ccol, crow;
- bool needMove;
- public override void Move (int col, int row)
- {
- ccol = col;
- crow = row;
+///
+/// This is the Curses driver for the gui.cs/Terminal framework.
+///
+internal class CursesDriver : ConsoleDriver {
+ public override int Cols => Curses.Cols;
+ public override int Rows => Curses.Lines;
- if (Clip.Contains (col, row)) {
- Curses.move (row, col);
- needMove = false;
- } else {
- Curses.move (Clip.Y, Clip.X);
- needMove = true;
- }
- }
-
- static bool sync = false;
+ CursorVisibility? _initialCursorVisibility = null;
+ CursorVisibility? _currentCursorVisibility = null;
- public override bool IsRuneSupported (Rune rune)
- {
- // See Issue #2615 - CursesDriver is broken with non-BMP characters
- return base.IsRuneSupported (rune) && rune.IsBmp;
- }
+ public override string GetVersionInfo () => $"{Curses.curses_version()}";
- public override void AddRune (Rune rune)
- {
- if (!IsRuneSupported (rune)) {
- rune = Rune.ReplacementChar;
- }
+ public override void Move (int col, int row)
+ {
+ base.Move (col, row);
- rune = rune.MakePrintable ();
- var runeWidth = rune.GetColumns ();
- var validClip = IsValidContent (ccol, crow, Clip);
+ if (IsValidLocation (col, row)) {
+ Curses.move (row, col);
+ } else {
+ // Not a valid location (outside screen or clip region)
+ // Move within the clip region, then AddRune will actually move to Col, Row
+ Curses.move (Clip.Y, Clip.X);
+ }
+ }
- if (validClip) {
- if (needMove) {
- Curses.move (crow, ccol);
- needMove = false;
- }
- if (runeWidth == 0 && ccol > 0) {
- var r = contents [crow, ccol - 1, 0];
- var s = new string (new char [] { (char)r, (char)rune.Value });
- string sn;
- if (!s.IsNormalized ()) {
- sn = s.Normalize ();
- } else {
- sn = s;
- }
- 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, 2] = 1;
+ public override bool IsRuneSupported (Rune rune)
+ {
+ // See Issue #2615 - CursesDriver is broken with non-BMP characters
+ return base.IsRuneSupported (rune) && rune.IsBmp;
+ }
- } else {
- if (runeWidth < 2 && ccol > 0
- && ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
-
- var curAtttib = CurrentAttribute;
- Curses.attrset (contents [crow, ccol - 1, 1]);
- Curses.mvaddch (crow, ccol - 1, (int)(uint)' ');
- contents [crow, ccol - 1, 0] = (int)(uint)' ';
- Curses.move (crow, ccol);
- Curses.attrset (curAtttib);
-
- } else if (runeWidth < 2 && ccol <= Clip.Right - 1
- && ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
-
- var curAtttib = CurrentAttribute;
- Curses.attrset (contents [crow, ccol + 1, 1]);
- Curses.mvaddch (crow, ccol + 1, (int)(uint)' ');
- contents [crow, ccol + 1, 0] = (int)(uint)' ';
- Curses.move (crow, ccol);
- Curses.attrset (curAtttib);
+ public override void Refresh ()
+ {
+ UpdateScreen ();
+ UpdateCursor ();
+ }
- }
- if (runeWidth > 1 && ccol == Clip.Right - 1) {
- Curses.addch ((int)(uint)' ');
- contents [crow, ccol, 0] = (int)(uint)' ';
- } else {
- Curses.addch ((int)(uint)rune.Value);
- contents [crow, ccol, 0] = (int)(uint)rune.Value;
- }
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 1;
- }
- } else {
- needMove = true;
- }
+ private void ProcessWinChange ()
+ {
+ if (Curses.CheckWinChange ()) {
+ ClearContents ();
+ TerminalResized?.Invoke ();
+ }
+ }
- if (runeWidth < 0 || runeWidth > 0) {
- ccol++;
- }
+ #region Color Handling
- if (runeWidth > 1) {
- if (validClip && ccol < Clip.Right) {
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 0;
- }
- ccol++;
- }
+ ///
+ /// Creates an Attribute from the provided curses-based foreground and background color numbers
+ ///
+ /// Contains the curses color number for the foreground (color, plus any attributes)
+ /// Contains the curses color number for the background (color, plus any attributes)
+ ///
+ static Attribute MakeColor (short foreground, short background)
+ {
+ var v = (short)((int)foreground | background << 4);
+ // TODO: for TrueColor - Use InitExtendedPair
+ Curses.InitColorPair (v, foreground, background);
+ return new Attribute (
+ value: Curses.ColorPair (v),
+ foreground: CursesColorNumberToColor (foreground),
+ background: CursesColorNumberToColor (background));
+ }
- if (sync) {
- UpdateScreen ();
- }
- }
+ ///
+ /// In the CursesDriver, colors are encoded as an int.
+ /// The foreground color is stored in the most significant 4 bits,
+ /// and the background color is stored in the least significant 4 bits.
+ /// The Terminal.GUi Color values are converted to curses color encoding before being encoded.
+ ///
+ public override Attribute MakeColor (Color fore, Color back)
+ {
+ return MakeColor (ColorToCursesColorNumber (fore), ColorToCursesColorNumber (back));
+ }
- public override void AddStr (string str)
- {
- // TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
- foreach (var rune in str.EnumerateRunes ())
- AddRune (rune);
- }
+ static short ColorToCursesColorNumber (Color color)
+ {
+ switch (color) {
+ case Color.Black:
+ return Curses.COLOR_BLACK;
+ case Color.Blue:
+ return Curses.COLOR_BLUE;
+ case Color.Green:
+ return Curses.COLOR_GREEN;
+ case Color.Cyan:
+ return Curses.COLOR_CYAN;
+ case Color.Red:
+ return Curses.COLOR_RED;
+ case Color.Magenta:
+ return Curses.COLOR_MAGENTA;
+ case Color.Brown:
+ return Curses.COLOR_YELLOW;
+ case Color.Gray:
+ return Curses.COLOR_WHITE;
+ case Color.DarkGray:
+ return Curses.COLOR_GRAY;
+ case Color.BrightBlue:
+ return Curses.COLOR_BLUE | Curses.COLOR_GRAY;
+ case Color.BrightGreen:
+ return Curses.COLOR_GREEN | Curses.COLOR_GRAY;
+ case Color.BrightCyan:
+ return Curses.COLOR_CYAN | Curses.COLOR_GRAY;
+ case Color.BrightRed:
+ return Curses.COLOR_RED | Curses.COLOR_GRAY;
+ case Color.BrightMagenta:
+ return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY;
+ case Color.BrightYellow:
+ return Curses.COLOR_YELLOW | Curses.COLOR_GRAY;
+ case Color.White:
+ return Curses.COLOR_WHITE | Curses.COLOR_GRAY;
+ }
+ throw new ArgumentException ("Invalid color code");
+ }
- public override void Refresh ()
- {
- Curses.raw ();
- Curses.noecho ();
- Curses.refresh ();
- ProcessWinChange ();
- }
+ static Color CursesColorNumberToColor (short color)
+ {
+ switch (color) {
+ case Curses.COLOR_BLACK:
+ return Color.Black;
+ case Curses.COLOR_BLUE:
+ return Color.Blue;
+ case Curses.COLOR_GREEN:
+ return Color.Green;
+ case Curses.COLOR_CYAN:
+ return Color.Cyan;
+ case Curses.COLOR_RED:
+ return Color.Red;
+ case Curses.COLOR_MAGENTA:
+ return Color.Magenta;
+ case Curses.COLOR_YELLOW:
+ return Color.Brown;
+ case Curses.COLOR_WHITE:
+ return Color.Gray;
+ case Curses.COLOR_GRAY:
+ return Color.DarkGray;
+ case Curses.COLOR_BLUE | Curses.COLOR_GRAY:
+ return Color.BrightBlue;
+ case Curses.COLOR_GREEN | Curses.COLOR_GRAY:
+ return Color.BrightGreen;
+ case Curses.COLOR_CYAN | Curses.COLOR_GRAY:
+ return Color.BrightCyan;
+ case Curses.COLOR_RED | Curses.COLOR_GRAY:
+ return Color.BrightRed;
+ case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY:
+ return Color.BrightMagenta;
+ case Curses.COLOR_YELLOW | Curses.COLOR_GRAY:
+ return Color.BrightYellow;
+ case Curses.COLOR_WHITE | Curses.COLOR_GRAY:
+ return Color.White;
+ }
+ throw new ArgumentException ("Invalid curses color code");
+ }
- private void ProcessWinChange ()
- {
- if (Curses.CheckWinChange ()) {
- ResizeScreen ();
- UpdateOffScreen ();
- TerminalResized?.Invoke ();
- }
- }
+ ///
+ /// In the CursesDriver, colors are encoded as an int.
+ /// The foreground color is stored in the most significant 4 bits,
+ /// and the background color is stored in the least significant 4 bits.
+ /// The Terminal.GUI Color values are converted to curses color encoding before being encoded.
+ ///
+ internal override void GetColors (int value, out Color foreground, out Color background)
+ {
+ // Assume a 4-bit encoded value for both foreground and background colors.
+ foreground = CursesColorNumberToColor ((short)((value >> 4) & 0xF));
+ background = CursesColorNumberToColor ((short)(value & 0xF));
+ }
- public override void UpdateCursor () => Refresh ();
+ #endregion
- public override void End ()
- {
- StopReportingMouseMoves ();
- SetCursorVisibility (CursorVisibility.Default);
+ public override void UpdateCursor ()
+ {
+ EnsureCursorVisibility ();
- Curses.endwin ();
+ if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) {
+ Curses.move (Row, Col);
}
+ }
- public override void UpdateScreen () => window.redrawwin ();
+ public override void End ()
+ {
+ StopReportingMouseMoves ();
+ SetCursorVisibility (CursorVisibility.Default);
- public override void SetAttribute (Attribute c)
- {
- base.SetAttribute (c);
- Curses.attrset (CurrentAttribute);
- }
+ // throws away any typeahead that has been typed by
+ // the user and has not yet been read by the program.
+ Curses.flushinp ();
- public Curses.Window window;
+ Curses.endwin ();
+ }
- //static short last_color_pair = 16;
+ public override void UpdateScreen ()
+ {
+ for (int row = 0; row < Rows; row++) {
+ if (!_dirtyLines [row]) {
+ continue;
+ }
+ _dirtyLines [row] = false;
- ///
- /// Creates a curses color from the provided foreground and background colors
- ///
- /// Contains the curses attributes for the foreground (color, plus any attributes)
- /// Contains the curses attributes for the background (color, plus any attributes)
- ///
- public static Attribute MakeColor (short foreground, short background)
- {
- var v = (short)((int)foreground | background << 4);
- //Curses.InitColorPair (++last_color_pair, foreground, background);
- Curses.InitColorPair (v, foreground, background);
- return new Attribute (
- //value: Curses.ColorPair (last_color_pair),
- value: Curses.ColorPair (v),
- //foreground: (Color)foreground,
- foreground: MapCursesColor (foreground),
- //background: (Color)background);
- background: MapCursesColor (background));
- }
+ for (int col = 0; col < Cols; col++) {
+ if (Contents [row, col].IsDirty == false) {
+ continue;
+ }
+ Curses.attrset (Contents [row, col].Attribute.GetValueOrDefault ().Value);
+
+ var rune = Contents [row, col].Runes [0];
+ if (rune.IsBmp) {
+ // BUGBUG: CursesDriver doesn't render CharMap correctly for wide chars (and other Unicode) - Curses is doing something funky with glyphs that report GetColums() of 1 yet are rendered wide. E.g. 0x2064 (invisible times) is reported as 1 column but is rendered as 2. WindowsDriver & NetDriver correctly render this as 1 column, overlapping the next cell.
+ if (rune.GetColumns () < 2) {
+ Curses.mvaddch (row, col, rune.Value);
+ } else /*if (col + 1 < Cols)*/ {
+ Curses.mvaddwstr (row, col, rune.ToString ());
+ }
- public override Attribute MakeColor (Color fore, Color back)
- {
- return MakeColor ((short)MapColor (fore), (short)MapColor (back));
+ } else {
+ Curses.mvaddwstr (row, col, rune.ToString());
+ if (rune.GetColumns () > 1 && col + 1 < Cols) {
+ // TODO: This is a hack to deal with non-BMP and wide characters.
+ //col++;
+ Curses.mvaddch (row, ++col, '*');
+ }
+ }
+ }
}
- int [,] colorPairs = new int [16, 16];
+ Curses.move (Row, Col);
+ _window.wrefresh ();
+ }
- 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];
- if ((v & 0x10000) == 0) {
- b &= 0x7;
- bool bold = (f & 0x8) != 0;
- f &= 0x7;
-
- v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0);
- colorPairs [(int)foreground, (int)background] = v | 0x1000;
- }
- SetAttribute (v & 0xffff);
+ public Curses.Window _window;
+
+ static Key MapCursesKey (int cursesKey)
+ {
+ switch (cursesKey) {
+ case Curses.KeyF1: return Key.F1;
+ case Curses.KeyF2: return Key.F2;
+ case Curses.KeyF3: return Key.F3;
+ case Curses.KeyF4: return Key.F4;
+ case Curses.KeyF5: return Key.F5;
+ case Curses.KeyF6: return Key.F6;
+ case Curses.KeyF7: return Key.F7;
+ case Curses.KeyF8: return Key.F8;
+ case Curses.KeyF9: return Key.F9;
+ case Curses.KeyF10: return Key.F10;
+ case Curses.KeyF11: return Key.F11;
+ case Curses.KeyF12: return Key.F12;
+ case Curses.KeyUp: return Key.CursorUp;
+ case Curses.KeyDown: return Key.CursorDown;
+ case Curses.KeyLeft: return Key.CursorLeft;
+ case Curses.KeyRight: return Key.CursorRight;
+ case Curses.KeyHome: return Key.Home;
+ case Curses.KeyEnd: return Key.End;
+ case Curses.KeyNPage: return Key.PageDown;
+ case Curses.KeyPPage: return Key.PageUp;
+ case Curses.KeyDeleteChar: return Key.DeleteChar;
+ case Curses.KeyInsertChar: return Key.InsertChar;
+ case Curses.KeyTab: return Key.Tab;
+ case Curses.KeyBackTab: return Key.BackTab;
+ case Curses.KeyBackspace: return Key.Backspace;
+ case Curses.ShiftKeyUp: return Key.CursorUp | Key.ShiftMask;
+ case Curses.ShiftKeyDown: return Key.CursorDown | Key.ShiftMask;
+ case Curses.ShiftKeyLeft: return Key.CursorLeft | Key.ShiftMask;
+ case Curses.ShiftKeyRight: return Key.CursorRight | Key.ShiftMask;
+ case Curses.ShiftKeyHome: return Key.Home | Key.ShiftMask;
+ case Curses.ShiftKeyEnd: return Key.End | Key.ShiftMask;
+ case Curses.ShiftKeyNPage: return Key.PageDown | Key.ShiftMask;
+ case Curses.ShiftKeyPPage: return Key.PageUp | Key.ShiftMask;
+ case Curses.AltKeyUp: return Key.CursorUp | Key.AltMask;
+ case Curses.AltKeyDown: return Key.CursorDown | Key.AltMask;
+ case Curses.AltKeyLeft: return Key.CursorLeft | Key.AltMask;
+ case Curses.AltKeyRight: return Key.CursorRight | Key.AltMask;
+ case Curses.AltKeyHome: return Key.Home | Key.AltMask;
+ case Curses.AltKeyEnd: return Key.End | Key.AltMask;
+ case Curses.AltKeyNPage: return Key.PageDown | Key.AltMask;
+ case Curses.AltKeyPPage: return Key.PageUp | Key.AltMask;
+ case Curses.CtrlKeyUp: return Key.CursorUp | Key.CtrlMask;
+ case Curses.CtrlKeyDown: return Key.CursorDown | Key.CtrlMask;
+ case Curses.CtrlKeyLeft: return Key.CursorLeft | Key.CtrlMask;
+ case Curses.CtrlKeyRight: return Key.CursorRight | Key.CtrlMask;
+ case Curses.CtrlKeyHome: return Key.Home | Key.CtrlMask;
+ case Curses.CtrlKeyEnd: return Key.End | Key.CtrlMask;
+ case Curses.CtrlKeyNPage: return Key.PageDown | Key.CtrlMask;
+ case Curses.CtrlKeyPPage: return Key.PageUp | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyUp: return Key.CursorUp | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyDown: return Key.CursorDown | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyRight: return Key.CursorRight | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyHome: return Key.Home | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask;
+ case Curses.ShiftAltKeyUp: return Key.CursorUp | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyDown: return Key.CursorDown | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyRight: return Key.CursorRight | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyNPage: return Key.PageDown | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyPPage: return Key.PageUp | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyHome: return Key.Home | Key.ShiftMask | Key.AltMask;
+ case Curses.ShiftAltKeyEnd: return Key.End | Key.ShiftMask | Key.AltMask;
+ case Curses.AltCtrlKeyNPage: return Key.PageDown | Key.AltMask | Key.CtrlMask;
+ case Curses.AltCtrlKeyPPage: return Key.PageUp | Key.AltMask | Key.CtrlMask;
+ case Curses.AltCtrlKeyHome: return Key.Home | Key.AltMask | Key.CtrlMask;
+ case Curses.AltCtrlKeyEnd: return Key.End | Key.AltMask | Key.CtrlMask;
+ default: return Key.Unknown;
}
+ }
- 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);
- rawPairs [key] = v;
- }
- SetAttribute (v);
- }
+ KeyModifiers _keyModifiers;
- static Key MapCursesKey (int cursesKey)
- {
- switch (cursesKey) {
- case Curses.KeyF1: return Key.F1;
- case Curses.KeyF2: return Key.F2;
- case Curses.KeyF3: return Key.F3;
- case Curses.KeyF4: return Key.F4;
- case Curses.KeyF5: return Key.F5;
- case Curses.KeyF6: return Key.F6;
- case Curses.KeyF7: return Key.F7;
- case Curses.KeyF8: return Key.F8;
- case Curses.KeyF9: return Key.F9;
- case Curses.KeyF10: return Key.F10;
- case Curses.KeyF11: return Key.F11;
- case Curses.KeyF12: return Key.F12;
- case Curses.KeyUp: return Key.CursorUp;
- case Curses.KeyDown: return Key.CursorDown;
- case Curses.KeyLeft: return Key.CursorLeft;
- case Curses.KeyRight: return Key.CursorRight;
- case Curses.KeyHome: return Key.Home;
- case Curses.KeyEnd: return Key.End;
- case Curses.KeyNPage: return Key.PageDown;
- case Curses.KeyPPage: return Key.PageUp;
- case Curses.KeyDeleteChar: return Key.DeleteChar;
- case Curses.KeyInsertChar: return Key.InsertChar;
- case Curses.KeyTab: return Key.Tab;
- case Curses.KeyBackTab: return Key.BackTab;
- case Curses.KeyBackspace: return Key.Backspace;
- case Curses.ShiftKeyUp: return Key.CursorUp | Key.ShiftMask;
- case Curses.ShiftKeyDown: return Key.CursorDown | Key.ShiftMask;
- case Curses.ShiftKeyLeft: return Key.CursorLeft | Key.ShiftMask;
- case Curses.ShiftKeyRight: return Key.CursorRight | Key.ShiftMask;
- case Curses.ShiftKeyHome: return Key.Home | Key.ShiftMask;
- case Curses.ShiftKeyEnd: return Key.End | Key.ShiftMask;
- case Curses.ShiftKeyNPage: return Key.PageDown | Key.ShiftMask;
- case Curses.ShiftKeyPPage: return Key.PageUp | Key.ShiftMask;
- case Curses.AltKeyUp: return Key.CursorUp | Key.AltMask;
- case Curses.AltKeyDown: return Key.CursorDown | Key.AltMask;
- case Curses.AltKeyLeft: return Key.CursorLeft | Key.AltMask;
- case Curses.AltKeyRight: return Key.CursorRight | Key.AltMask;
- case Curses.AltKeyHome: return Key.Home | Key.AltMask;
- case Curses.AltKeyEnd: return Key.End | Key.AltMask;
- case Curses.AltKeyNPage: return Key.PageDown | Key.AltMask;
- case Curses.AltKeyPPage: return Key.PageUp | Key.AltMask;
- case Curses.CtrlKeyUp: return Key.CursorUp | Key.CtrlMask;
- case Curses.CtrlKeyDown: return Key.CursorDown | Key.CtrlMask;
- case Curses.CtrlKeyLeft: return Key.CursorLeft | Key.CtrlMask;
- case Curses.CtrlKeyRight: return Key.CursorRight | Key.CtrlMask;
- case Curses.CtrlKeyHome: return Key.Home | Key.CtrlMask;
- case Curses.CtrlKeyEnd: return Key.End | Key.CtrlMask;
- case Curses.CtrlKeyNPage: return Key.PageDown | Key.CtrlMask;
- case Curses.CtrlKeyPPage: return Key.PageUp | Key.CtrlMask;
- case Curses.ShiftCtrlKeyUp: return Key.CursorUp | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyDown: return Key.CursorDown | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyRight: return Key.CursorRight | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyHome: return Key.Home | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask;
- case Curses.ShiftAltKeyUp: return Key.CursorUp | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyDown: return Key.CursorDown | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyRight: return Key.CursorRight | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyNPage: return Key.PageDown | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyPPage: return Key.PageUp | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyHome: return Key.Home | Key.ShiftMask | Key.AltMask;
- case Curses.ShiftAltKeyEnd: return Key.End | Key.ShiftMask | Key.AltMask;
- case Curses.AltCtrlKeyNPage: return Key.PageDown | Key.AltMask | Key.CtrlMask;
- case Curses.AltCtrlKeyPPage: return Key.PageUp | Key.AltMask | Key.CtrlMask;
- case Curses.AltCtrlKeyHome: return Key.Home | Key.AltMask | Key.CtrlMask;
- case Curses.AltCtrlKeyEnd: return Key.End | Key.AltMask | Key.CtrlMask;
- default: return Key.Unknown;
- }
+ KeyModifiers MapKeyModifiers (Key key)
+ {
+ if (_keyModifiers == null) {
+ _keyModifiers = new KeyModifiers ();
}
- KeyModifiers keyModifiers;
-
- KeyModifiers MapKeyModifiers (Key key)
- {
- if (keyModifiers == null)
- keyModifiers = new KeyModifiers ();
+ if (!_keyModifiers.Shift && (key & Key.ShiftMask) != 0) {
+ _keyModifiers.Shift = true;
+ }
+ if (!_keyModifiers.Alt && (key & Key.AltMask) != 0) {
+ _keyModifiers.Alt = true;
+ }
+ if (!_keyModifiers.Ctrl && (key & Key.CtrlMask) != 0) {
+ _keyModifiers.Ctrl = true;
+ }
- if (!keyModifiers.Shift && (key & Key.ShiftMask) != 0)
- keyModifiers.Shift = true;
- if (!keyModifiers.Alt && (key & Key.AltMask) != 0)
- keyModifiers.Alt = true;
- if (!keyModifiers.Ctrl && (key & Key.CtrlMask) != 0)
- keyModifiers.Ctrl = true;
+ return _keyModifiers;
+ }
- return keyModifiers;
+ void ProcessInput ()
+ {
+ int wch;
+ var code = Curses.get_wch (out wch);
+ //System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}");
+ if (code == Curses.ERR) {
+ return;
}
- void ProcessInput ()
- {
- int wch;
- var code = Curses.get_wch (out wch);
- //System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}");
- if (code == Curses.ERR)
- return;
-
- keyModifiers = new KeyModifiers ();
- Key k = Key.Null;
+ _keyModifiers = new KeyModifiers ();
+ Key k = Key.Null;
- if (code == Curses.KEY_CODE_YES) {
- if (wch == Curses.KeyResize) {
- ProcessWinChange ();
- }
- if (wch == Curses.KeyMouse) {
- int wch2 = wch;
+ if (code == Curses.KEY_CODE_YES) {
+ while (code == Curses.KEY_CODE_YES && wch == Curses.KeyResize) {
+ ProcessWinChange ();
+ code = Curses.get_wch (out wch);
+ }
+ if (wch == 0) {
+ return;
+ }
+ if (wch == Curses.KeyMouse) {
+ int wch2 = wch;
- while (wch2 == Curses.KeyMouse) {
- KeyEvent key = null;
- ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] {
+ 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);
- if (wch >= 277 && wch <= 288) { // Shift+(F1 - F12)
- wch -= 12;
- k = Key.ShiftMask | MapCursesKey (wch);
- } else if (wch >= 289 && wch <= 300) { // Ctrl+(F1 - F12)
- wch -= 24;
- k = Key.CtrlMask | MapCursesKey (wch);
- } else if (wch >= 301 && wch <= 312) { // Ctrl+Shift+(F1 - F12)
- wch -= 36;
- k = Key.CtrlMask | Key.ShiftMask | MapCursesKey (wch);
- } else if (wch >= 313 && wch <= 324) { // Alt+(F1 - F12)
- wch -= 48;
- k = Key.AltMask | MapCursesKey (wch);
- } else if (wch >= 325 && wch <= 327) { // Shift+Alt+(F1 - F3)
- wch -= 60;
- k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch);
+ code = 0;
+ HandleEscSeqResponse (ref code, ref k, ref wch2, ref key, ref cki);
}
- keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
- keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
- keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
return;
}
+ k = MapCursesKey (wch);
+ if (wch >= 277 && wch <= 288) {
+ // Shift+(F1 - F12)
+ wch -= 12;
+ k = Key.ShiftMask | MapCursesKey (wch);
+ } else if (wch >= 289 && wch <= 300) {
+ // Ctrl+(F1 - F12)
+ wch -= 24;
+ k = Key.CtrlMask | MapCursesKey (wch);
+ } else if (wch >= 301 && wch <= 312) {
+ // Ctrl+Shift+(F1 - F12)
+ wch -= 36;
+ k = Key.CtrlMask | Key.ShiftMask | MapCursesKey (wch);
+ } else if (wch >= 313 && wch <= 324) {
+ // Alt+(F1 - F12)
+ wch -= 48;
+ k = Key.AltMask | MapCursesKey (wch);
+ } else if (wch >= 325 && wch <= 327) {
+ // Shift+Alt+(F1 - F3)
+ wch -= 60;
+ k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch);
+ }
+ _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ _keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ return;
+ }
+
+ // Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter as well as Alt-Fkey
+ if (wch == 27) {
+ Curses.timeout (10);
+
+ code = Curses.get_wch (out int wch2);
- // Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter as well as Alt-Fkey
- if (wch == 27) {
- Curses.timeout (10);
-
- code = Curses.get_wch (out int wch2);
-
- if (code == Curses.KEY_CODE_YES) {
- k = Key.AltMask | MapCursesKey (wch);
- }
- if (code == 0) {
- KeyEvent key = null;
-
- // The ESC-number handling, debatable.
- // Simulates the AltMask itself by pressing Alt + Space.
- if (wch2 == (int)Key.Space) {
- k = Key.AltMask;
- } else if (wch2 - (int)Key.Space >= (uint)Key.A && wch2 - (int)Key.Space <= (uint)Key.Z) {
- k = (Key)((uint)Key.AltMask + (wch2 - (int)Key.Space));
- } else if (wch2 >= (uint)Key.A - 64 && wch2 <= (uint)Key.Z - 64) {
- 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 == Curses.KeyCSI) {
- ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] {
+ if (code == Curses.KEY_CODE_YES) {
+ k = Key.AltMask | MapCursesKey (wch);
+ }
+ if (code == 0) {
+ KeyEvent key = null;
+
+ // The ESC-number handling, debatable.
+ // Simulates the AltMask itself by pressing Alt + Space.
+ if (wch2 == (int)Key.Space) {
+ k = Key.AltMask;
+ } else if (wch2 - (int)Key.Space >= (uint)Key.A && wch2 - (int)Key.Space <= (uint)Key.Z) {
+ k = (Key)((uint)Key.AltMask + (wch2 - (int)Key.Space));
+ } else if (wch2 >= (uint)Key.A - 64 && wch2 <= (uint)Key.Z - 64) {
+ 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 == 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;
+ HandleEscSeqResponse (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) {
+ _keyModifiers.Ctrl = true;
+ }
+ if (wch2 == 0) {
+ k = Key.CtrlMask | Key.AltMask | Key.Space;
+ } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
+ _keyModifiers.Shift = true;
+ _keyModifiers.Alt = true;
+ } else if (wch2 < 256) {
+ k = (Key)wch2;
+ _keyModifiers.Alt = true;
} else {
- // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa.
- if (((Key)wch2 & Key.CtrlMask) != 0) {
- keyModifiers.Ctrl = true;
- }
- if (wch2 == 0) {
- k = Key.CtrlMask | Key.AltMask | Key.Space;
- } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
- keyModifiers.Shift = true;
- keyModifiers.Alt = true;
- } else if (wch2 < 256) {
- k = (Key)wch2;
- keyModifiers.Alt = true;
- } else {
- k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
- }
+ k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2);
}
- key = new KeyEvent (k, MapKeyModifiers (k));
- keyDownHandler (key);
- keyHandler (key);
- } else {
- k = Key.Esc;
- keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
}
- } else if (wch == Curses.KeyTab) {
- k = MapCursesKey (wch);
- keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
- keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ key = new KeyEvent (k, MapKeyModifiers (k));
+ _keyDownHandler (key);
+ _keyHandler (key);
} else {
- // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
- k = (Key)wch;
- if (wch == 0) {
- k = Key.CtrlMask | Key.Space;
- } else if (wch >= (uint)Key.A - 64 && wch <= (uint)Key.Z - 64) {
- if ((Key)(wch + 64) != Key.J) {
- k = Key.CtrlMask | (Key)(wch + 64);
- }
- } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
- keyModifiers.Shift = true;
+ k = Key.Esc;
+ _keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ }
+ } else if (wch == Curses.KeyTab) {
+ k = MapCursesKey (wch);
+ _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ _keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ } else {
+ // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
+ k = (Key)wch;
+ if (wch == 0) {
+ k = Key.CtrlMask | Key.Space;
+ } else if (wch >= (uint)Key.A - 64 && wch <= (uint)Key.Z - 64) {
+ if ((Key)(wch + 64) != Key.J) {
+ k = Key.CtrlMask | (Key)(wch + 64);
}
- keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
- keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
- keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
- }
- // Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above
- // will not impact KeyUp.
- // This is causing ESC firing even if another keystroke was handled.
- //if (wch == Curses.KeyTab) {
- // keyUpHandler (new KeyEvent (MapCursesKey (wch), keyModifiers));
- //} else {
- // keyUpHandler (new KeyEvent ((Key)wch, keyModifiers));
- //}
- }
+ } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) {
+ _keyModifiers.Shift = true;
+ }
+ _keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ _keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ _keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
+ }
+ // Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above
+ // will not impact KeyUp.
+ // This is causing ESC firing even if another keystroke was handled.
+ //if (wch == Curses.KeyTab) {
+ // keyUpHandler (new KeyEvent (MapCursesKey (wch), keyModifiers));
+ //} else {
+ // keyUpHandler (new KeyEvent ((Key)wch, keyModifiers));
+ //}
+ }
- 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);
+ void HandleEscSeqResponse (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 {
- cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki);
+ 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);
}
}
+ }
+
+ MouseFlags _lastMouseFlags;
- void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
+ void ProcessMouseEvent (MouseFlags mouseFlag, Point pos)
+ {
+ bool WasButtonReleased (MouseFlags flag)
{
- var me = new MouseEvent () {
- Flags = mouseFlag,
- X = pos.X,
- Y = pos.Y
- };
- mouseHandler (me);
+ return flag.HasFlag (MouseFlags.Button1Released) ||
+ flag.HasFlag (MouseFlags.Button2Released) ||
+ flag.HasFlag (MouseFlags.Button3Released) ||
+ flag.HasFlag (MouseFlags.Button4Released);
}
- void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
+ bool IsButtonNotPressed (MouseFlags flag)
{
- ProcessMouseEvent (mouseFlag, pos);
+ return !flag.HasFlag (MouseFlags.Button1Pressed) &&
+ !flag.HasFlag (MouseFlags.Button2Pressed) &&
+ !flag.HasFlag (MouseFlags.Button3Pressed) &&
+ !flag.HasFlag (MouseFlags.Button4Pressed);
}
- Action keyHandler;
- Action keyDownHandler;
- Action keyUpHandler;
- Action mouseHandler;
-
- public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
+ bool IsButtonClickedOrDoubleClicked (MouseFlags flag)
{
- // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
- Curses.timeout (0);
- this.keyHandler = keyHandler;
- this.keyDownHandler = keyDownHandler;
- this.keyUpHandler = keyUpHandler;
- this.mouseHandler = mouseHandler;
-
- var mLoop = mainLoop.Driver as UnixMainLoop;
-
- mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
- ProcessInput ();
- return true;
- });
-
- mLoop.WinChanged += () => {
- ProcessWinChange ();
- };
+ return flag.HasFlag (MouseFlags.Button1Clicked) ||
+ flag.HasFlag (MouseFlags.Button2Clicked) ||
+ flag.HasFlag (MouseFlags.Button3Clicked) ||
+ flag.HasFlag (MouseFlags.Button4Clicked) ||
+ flag.HasFlag (MouseFlags.Button1DoubleClicked) ||
+ flag.HasFlag (MouseFlags.Button2DoubleClicked) ||
+ flag.HasFlag (MouseFlags.Button3DoubleClicked) ||
+ flag.HasFlag (MouseFlags.Button4DoubleClicked);
}
- public override void Init (Action terminalResized)
- {
- if (window != null)
- return;
+ if ((WasButtonReleased (mouseFlag) && IsButtonNotPressed (_lastMouseFlags)) ||
+ (IsButtonClickedOrDoubleClicked (mouseFlag) && _lastMouseFlags == 0)) {
+ return;
+ }
- try {
- window = Curses.initscr ();
- Curses.set_escdelay (10);
- } catch (Exception e) {
- throw new Exception ($"Curses failed to initialize, the exception is: {e.Message}");
- }
+ _lastMouseFlags = mouseFlag;
- // Ensures that all procedures are performed at some previous closing.
- Curses.doupdate ();
+ var me = new MouseEvent () {
+ Flags = mouseFlag,
+ X = pos.X,
+ Y = pos.Y
+ };
+ _mouseHandler (me);
+ }
- //
- // We are setting Invisible as default so we could ignore XTerm DECSUSR setting
- //
- switch (Curses.curs_set (0)) {
- case 0:
- currentCursorVisibility = initialCursorVisibility = CursorVisibility.Invisible;
- break;
- case 1:
- currentCursorVisibility = initialCursorVisibility = CursorVisibility.Underline;
- Curses.curs_set (1);
- break;
+ void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
+ {
+ ProcessMouseEvent (mouseFlag, pos);
+ }
- case 2:
- currentCursorVisibility = initialCursorVisibility = CursorVisibility.Box;
- Curses.curs_set (2);
- break;
+ Action _keyHandler;
+ Action _keyDownHandler;
+ Action _keyUpHandler;
+ Action _mouseHandler;
- default:
- currentCursorVisibility = initialCursorVisibility = null;
- break;
- }
+ public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
+ {
+ // Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
+ Curses.timeout (0);
+ this._keyHandler = keyHandler;
+ this._keyDownHandler = keyDownHandler;
+ this._keyUpHandler = keyUpHandler;
+ this._mouseHandler = mouseHandler;
- if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
- clipboard = new MacOSXClipboard ();
- } else {
- if (Is_WSL_Platform ()) {
- clipboard = new WSLClipboard ();
- } else {
- clipboard = new CursesClipboard ();
- }
- }
+ var mLoop = mainLoop.MainLoopDriver as UnixMainLoop;
- Curses.raw ();
- Curses.noecho ();
+ mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
+ ProcessInput ();
+ return true;
+ });
- Curses.Window.Standard.keypad (true);
- TerminalResized = terminalResized;
- StartReportingMouseMoves ();
+ mLoop.WinChanged += ProcessInput;
+ }
- CurrentAttribute = MakeColor (Color.White, Color.Black);
+ public override void Init (Action terminalResized)
+ {
+ if (_window != null) {
+ return;
+ }
- if (Curses.HasColors) {
- Curses.StartColor ();
- Curses.UseDefaultColors ();
+ try {
+ _window = Curses.initscr ();
+ Curses.set_escdelay (10);
+ } catch (Exception e) {
+ throw new Exception ($"Curses failed to initialize, the exception is: {e.Message}");
+ }
- InitalizeColorSchemes ();
- } else {
- 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;
- Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW;
- Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
- Colors.Base.Normal = Curses.A_NORMAL;
- Colors.Base.Focus = Curses.A_REVERSE;
- Colors.Base.HotNormal = Curses.A_BOLD;
- Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE;
- Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
- Colors.Menu.Normal = Curses.A_REVERSE;
- Colors.Menu.Focus = Curses.A_NORMAL;
- Colors.Menu.HotNormal = Curses.A_BOLD;
- Colors.Menu.HotFocus = Curses.A_NORMAL;
- Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
- Colors.Dialog.Normal = Curses.A_REVERSE;
- Colors.Dialog.Focus = Curses.A_NORMAL;
- Colors.Dialog.HotNormal = Curses.A_BOLD;
- Colors.Dialog.HotFocus = Curses.A_NORMAL;
- Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
- Colors.Error.Normal = Curses.A_BOLD;
- Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE;
- Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE;
- Colors.Error.HotFocus = Curses.A_REVERSE;
- Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
- }
+ // Ensures that all procedures are performed at some previous closing.
+ Curses.doupdate ();
- ResizeScreen ();
- UpdateOffScreen ();
+ //
+ // We are setting Invisible as default so we could ignore XTerm DECSUSR setting
+ //
+ switch (Curses.curs_set (0)) {
+ case 0:
+ _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Invisible;
+ break;
- }
+ case 1:
+ _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Underline;
+ Curses.curs_set (1);
+ break;
- public override void ResizeScreen ()
- {
- Clip = new Rect (0, 0, Cols, Rows);
- Curses.refresh ();
- }
+ case 2:
+ _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box;
+ Curses.curs_set (2);
+ break;
- public override void UpdateOffScreen ()
- {
- contents = new int [Rows, Cols, 3];
- for (int row = 0; row < Rows; row++) {
- for (int col = 0; col < Cols; col++) {
- //Curses.move (row, col);
- //Curses.attrset (Colors.TopLevel.Normal);
- //Curses.addch ((int)(uint)' ');
- contents [row, col, 0] = ' ';
- contents [row, col, 1] = Colors.TopLevel.Normal;
- contents [row, col, 2] = 0;
- }
- }
+ default:
+ _currentCursorVisibility = _initialCursorVisibility = null;
+ break;
}
- public static bool Is_WSL_Platform ()
- {
- // xclip does not work on WSL, so we need to use the Windows clipboard vis Powershell
- //if (new CursesClipboard ().IsSupported) {
- // // If xclip is installed on Linux under WSL, this will return true.
- // return false;
- //}
- var (exitCode, result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true);
- if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) {
- return true;
+ if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
+ Clipboard = new MacOSXClipboard ();
+ } else {
+ if (Is_WSL_Platform ()) {
+ Clipboard = new WSLClipboard ();
+ } else {
+ Clipboard = new CursesClipboard ();
}
- return false;
}
- static int MapColor (Color color)
- {
- switch (color) {
- case Color.Black:
- return Curses.COLOR_BLACK;
- case Color.Blue:
- return Curses.COLOR_BLUE;
- case Color.Green:
- return Curses.COLOR_GREEN;
- case Color.Cyan:
- return Curses.COLOR_CYAN;
- case Color.Red:
- return Curses.COLOR_RED;
- case Color.Magenta:
- return Curses.COLOR_MAGENTA;
- case Color.Brown:
- return Curses.COLOR_YELLOW;
- case Color.Gray:
- return Curses.COLOR_WHITE;
- case Color.DarkGray:
- //return Curses.COLOR_BLACK | Curses.A_BOLD;
- return Curses.COLOR_GRAY;
- case Color.BrightBlue:
- return Curses.COLOR_BLUE | Curses.A_BOLD | Curses.COLOR_GRAY;
- case Color.BrightGreen:
- return Curses.COLOR_GREEN | Curses.A_BOLD | Curses.COLOR_GRAY;
- case Color.BrightCyan:
- return Curses.COLOR_CYAN | Curses.A_BOLD | Curses.COLOR_GRAY;
- case Color.BrightRed:
- return Curses.COLOR_RED | Curses.A_BOLD | Curses.COLOR_GRAY;
- case Color.BrightMagenta:
- return Curses.COLOR_MAGENTA | Curses.A_BOLD | Curses.COLOR_GRAY;
- case Color.BrightYellow:
- return Curses.COLOR_YELLOW | Curses.A_BOLD | Curses.COLOR_GRAY;
- case Color.White:
- return Curses.COLOR_WHITE | Curses.A_BOLD | Curses.COLOR_GRAY;
- }
- throw new ArgumentException ("Invalid color code");
+ if (!Curses.HasColors) {
+ throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does.");
}
- static Color MapCursesColor (int color)
- {
- switch (color) {
- case Curses.COLOR_BLACK:
- return Color.Black;
- case Curses.COLOR_BLUE:
- return Color.Blue;
- case Curses.COLOR_GREEN:
- return Color.Green;
- case Curses.COLOR_CYAN:
- return Color.Cyan;
- case Curses.COLOR_RED:
- return Color.Red;
- case Curses.COLOR_MAGENTA:
- return Color.Magenta;
- case Curses.COLOR_YELLOW:
- return Color.Brown;
- case Curses.COLOR_WHITE:
- return Color.Gray;
- case Curses.COLOR_GRAY:
- return Color.DarkGray;
- case Curses.COLOR_BLUE | Curses.COLOR_GRAY:
- return Color.BrightBlue;
- case Curses.COLOR_GREEN | Curses.COLOR_GRAY:
- return Color.BrightGreen;
- case Curses.COLOR_CYAN | Curses.COLOR_GRAY:
- return Color.BrightCyan;
- case Curses.COLOR_RED | Curses.COLOR_GRAY:
- return Color.BrightRed;
- case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY:
- return Color.BrightMagenta;
- case Curses.COLOR_YELLOW | Curses.COLOR_GRAY:
- return Color.BrightYellow;
- case Curses.COLOR_WHITE | Curses.COLOR_GRAY:
- return Color.White;
- }
- throw new ArgumentException ("Invalid curses color code");
- }
+ Curses.raw ();
+ Curses.noecho ();
- public override Attribute MakeAttribute (Color fore, Color back)
- {
- var f = MapColor (fore);
- //return MakeColor ((short)(f & 0xffff), (short)MapColor (back)) | ((f & Curses.A_BOLD) != 0 ? Curses.A_BOLD : 0);
- return MakeColor ((short)(f & 0xffff), (short)MapColor (back));
- }
+ Curses.Window.Standard.keypad (true);
- public override void Suspend ()
- {
- StopReportingMouseMoves ();
- Platform.Suspend ();
- Curses.Window.Standard.redrawwin ();
- Curses.refresh ();
- StartReportingMouseMoves ();
- }
+ TerminalResized = terminalResized;
- public override void StartReportingMouseMoves ()
- {
- Console.Out.Write (EscSeqUtils.EnableMouseEvents);
- }
+ Curses.StartColor ();
+ Curses.UseDefaultColors ();
+ CurrentAttribute = MakeColor (Color.White, Color.Black);
+ InitializeColorSchemes ();
- public override void StopReportingMouseMoves ()
- {
- Console.Out.Write (EscSeqUtils.DisableMouseEvents);
- }
+ Curses.CheckWinChange ();
+ ClearContents ();
+ Curses.refresh ();
- //int lastMouseInterval;
- //bool mouseGrabbed;
+ StartReportingMouseMoves ();
+ }
- public override void UncookMouse ()
- {
- //if (mouseGrabbed)
- // return;
- //lastMouseInterval = Curses.mouseinterval (0);
- //mouseGrabbed = true;
+ public static bool Is_WSL_Platform ()
+ {
+ // xclip does not work on WSL, so we need to use the Windows clipboard vis Powershell
+ //if (new CursesClipboard ().IsSupported) {
+ // // If xclip is installed on Linux under WSL, this will return true.
+ // return false;
+ //}
+ var (exitCode, result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true);
+ if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL")) {
+ return true;
}
+ return false;
+ }
- public override void CookMouse ()
- {
- //mouseGrabbed = false;
- //Curses.mouseinterval (lastMouseInterval);
- }
+ public override void Suspend ()
+ {
+ StopReportingMouseMoves ();
+ Platform.Suspend ();
+ Curses.Window.Standard.redrawwin ();
+ Curses.refresh ();
+ StartReportingMouseMoves ();
+ }
- ///
- public override bool GetCursorVisibility (out CursorVisibility visibility)
- {
- visibility = CursorVisibility.Invisible;
+ public void StartReportingMouseMoves ()
+ {
+ Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
+ }
- if (!currentCursorVisibility.HasValue)
- return false;
+ public void StopReportingMouseMoves ()
+ {
+ Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
+ }
- visibility = currentCursorVisibility.Value;
+ ///
+ public override bool GetCursorVisibility (out CursorVisibility visibility)
+ {
+ visibility = CursorVisibility.Invisible;
- return true;
- }
+ if (!_currentCursorVisibility.HasValue)
+ return false;
- ///
- public override bool SetCursorVisibility (CursorVisibility visibility)
- {
- if (initialCursorVisibility.HasValue == false)
- return false;
+ visibility = _currentCursorVisibility.Value;
- Curses.curs_set (((int)visibility >> 16) & 0x000000FF);
+ return true;
+ }
- if (visibility != CursorVisibility.Invisible) {
- Console.Out.Write ("\x1b[{0} q", ((int)visibility >> 24) & 0xFF);
- }
+ ///
+ public override bool SetCursorVisibility (CursorVisibility visibility)
+ {
+ if (_initialCursorVisibility.HasValue == false) {
+ return false;
+ }
- currentCursorVisibility = visibility;
+ Curses.curs_set (((int)visibility >> 16) & 0x000000FF);
- return true;
+ if (visibility != CursorVisibility.Invisible) {
+ Console.Out.Write (EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF)));
}
- ///
- public override bool EnsureCursorVisibility ()
- {
- return false;
- }
+ _currentCursorVisibility = visibility;
- public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control)
- {
- Key key;
+ return true;
+ }
- if (consoleKey == ConsoleKey.Packet) {
- ConsoleModifiers mod = new ConsoleModifiers ();
- if (shift) {
- mod |= ConsoleModifiers.Shift;
- }
- if (alt) {
- mod |= ConsoleModifiers.Alt;
- }
- if (control) {
- mod |= ConsoleModifiers.Control;
- }
- var kchar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyChar, mod, out uint ckey, out _);
- key = ConsoleKeyMapping.MapConsoleKeyToKey ((ConsoleKey)ckey, out bool mappable);
- if (mappable) {
- key = (Key)kchar;
- }
- } else {
- key = (Key)keyChar;
- }
+ ///
+ public override bool EnsureCursorVisibility ()
+ {
+ return false;
+ }
- KeyModifiers km = new KeyModifiers ();
+ public override void SendKeys (char keyChar, ConsoleKey consoleKey, bool shift, bool alt, bool control)
+ {
+ Key key;
+
+ if (consoleKey == ConsoleKey.Packet) {
+ ConsoleModifiers mod = new ConsoleModifiers ();
if (shift) {
- if (keyChar == 0) {
- key |= Key.ShiftMask;
- }
- km.Shift = shift;
+ mod |= ConsoleModifiers.Shift;
}
if (alt) {
- key |= Key.AltMask;
- km.Alt = alt;
+ mod |= ConsoleModifiers.Alt;
}
if (control) {
- key |= Key.CtrlMask;
- km.Ctrl = control;
+ mod |= ConsoleModifiers.Control;
}
- keyDownHandler (new KeyEvent (key, km));
- keyHandler (new KeyEvent (key, km));
- keyUpHandler (new KeyEvent (key, km));
- }
-
- public override bool GetColors (int value, out Color foreground, out Color background)
- {
- bool hasColor = false;
- foreground = default;
- background = default;
- int back = -1;
- IEnumerable values = Enum.GetValues (typeof (ConsoleColor))
- .OfType ()
- .Select (s => (int)s);
- if (values.Contains ((value >> 12) & 0xffff)) {
- hasColor = true;
- back = (value >> 12) & 0xffff;
- background = MapCursesColor (back);
+ var kchar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyChar, mod, out uint ckey, out _);
+ key = ConsoleKeyMapping.MapConsoleKeyToKey ((ConsoleKey)ckey, out bool mappable);
+ if (mappable) {
+ key = (Key)kchar;
}
- if (values.Contains ((value - (back << 12)) >> 8)) {
- hasColor = true;
- foreground = MapCursesColor ((value - (back << 12)) >> 8);
- }
- return hasColor;
+ } else {
+ key = (Key)keyChar;
}
- }
- internal static class Platform {
- [DllImport ("libc")]
- static extern int uname (IntPtr buf);
-
- [DllImport ("libc")]
- static extern int killpg (int pgrp, int pid);
-
- static int suspendSignal;
-
- static int GetSuspendSignal ()
- {
- if (suspendSignal != 0)
- return suspendSignal;
-
- IntPtr buf = Marshal.AllocHGlobal (8192);
- if (uname (buf) != 0) {
- Marshal.FreeHGlobal (buf);
- suspendSignal = -1;
- return suspendSignal;
- }
- try {
- switch (Marshal.PtrToStringAnsi (buf)) {
- case "Darwin":
- case "DragonFly":
- case "FreeBSD":
- case "NetBSD":
- case "OpenBSD":
- suspendSignal = 18;
- break;
- case "Linux":
- // TODO: should fetch the machine name and
- // if it is MIPS return 24
- suspendSignal = 20;
- break;
- case "Solaris":
- suspendSignal = 24;
- break;
- default:
- suspendSignal = -1;
- break;
- }
- return suspendSignal;
- } finally {
- Marshal.FreeHGlobal (buf);
+ KeyModifiers km = new KeyModifiers ();
+ if (shift) {
+ if (keyChar == 0) {
+ key |= Key.ShiftMask;
}
+ km.Shift = shift;
}
-
- ///
- /// Suspends the process by sending SIGTSTP to itself
- ///
- /// The suspend.
- static public bool Suspend ()
- {
- int signal = GetSuspendSignal ();
- if (signal == -1)
- return false;
- killpg (0, signal);
- return true;
+ if (alt) {
+ key |= Key.AltMask;
+ km.Alt = alt;
}
- }
-
- ///
- /// A clipboard implementation for Linux.
- /// This implementation uses the xclip command to access the clipboard.
- ///
- ///
- /// If xclip is not installed, this implementation will not work.
- ///
- class CursesClipboard : ClipboardBase {
- public CursesClipboard ()
- {
- IsSupported = CheckSupport ();
+ if (control) {
+ key |= Key.CtrlMask;
+ km.Ctrl = control;
}
+ _keyDownHandler (new KeyEvent (key, km));
+ _keyHandler (new KeyEvent (key, km));
+ _keyUpHandler (new KeyEvent (key, km));
+ }
- string xclipPath = string.Empty;
- public override bool IsSupported { get; }
-
- 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 ()) {
- xclipPath = result;
- return true;
- }
- } catch (Exception) {
- // Permissions issue.
- }
-#pragma warning restore RCS1075 // Avoid empty catch clause that catches System.Exception.
- return false;
- }
- protected override string GetClipboardDataImpl ()
- {
- var tempFileName = System.IO.Path.GetTempFileName ();
- var xclipargs = "-selection clipboard -o";
-
- try {
- var (exitCode, result) = ClipboardProcessRunner.Bash ($"{xclipPath} {xclipargs} > {tempFileName}", waitForOutput: false);
- if (exitCode == 0) {
- if (Application.Driver is CursesDriver) {
- Curses.raw ();
- Curses.noecho ();
- }
- return System.IO.File.ReadAllText (tempFileName);
- }
- } catch (Exception e) {
- throw new NotSupportedException ($"\"{xclipPath} {xclipargs}\" failed.", e);
- } finally {
- System.IO.File.Delete (tempFileName);
- }
- return string.Empty;
- }
+}
- protected override void SetClipboardDataImpl (string text)
- {
- var xclipargs = "-selection clipboard -i";
- try {
- var (exitCode, _) = ClipboardProcessRunner.Bash ($"{xclipPath} {xclipargs}", text, waitForOutput: false);
- if (exitCode == 0 && Application.Driver is CursesDriver) {
- Curses.raw ();
- Curses.noecho ();
- }
- } catch (Exception e) {
- throw new NotSupportedException ($"\"{xclipPath} {xclipargs} < {text}\" failed", e);
- }
- }
- }
+internal static class Platform {
+ [DllImport ("libc")]
+ static extern int uname (IntPtr buf);
- ///
- /// A clipboard implementation for MacOSX.
- /// This implementation uses the Mac clipboard API (via P/Invoke) to copy/paste.
- /// The existance of the Mac pbcopy and pbpaste commands
- /// is used to determine if copy/paste is supported.
- ///
- class MacOSXClipboard : ClipboardBase {
- IntPtr nsString = objc_getClass ("NSString");
- IntPtr nsPasteboard = objc_getClass ("NSPasteboard");
- IntPtr utfTextType;
- IntPtr generalPasteboard;
- IntPtr initWithUtf8Register = sel_registerName ("initWithUTF8String:");
- IntPtr allocRegister = sel_registerName ("alloc");
- IntPtr setStringRegister = sel_registerName ("setString:forType:");
- IntPtr stringForTypeRegister = sel_registerName ("stringForType:");
- IntPtr utf8Register = sel_registerName ("UTF8String");
- IntPtr nsStringPboardType;
- IntPtr generalPasteboardRegister = sel_registerName ("generalPasteboard");
- IntPtr clearContentsRegister = sel_registerName ("clearContents");
-
- public MacOSXClipboard ()
- {
- utfTextType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "public.utf8-plain-text");
- nsStringPboardType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "NSStringPboardType");
- generalPasteboard = objc_msgSend (nsPasteboard, generalPasteboardRegister);
- IsSupported = CheckSupport ();
- }
+ [DllImport ("libc")]
+ static extern int killpg (int pgrp, int pid);
- public override bool IsSupported { get; }
+ static int _suspendSignal;
- bool CheckSupport ()
- {
- var (exitCode, result) = ClipboardProcessRunner.Bash ("which pbcopy", waitForOutput: true);
- if (exitCode != 0 || !result.FileExists ()) {
- return false;
- }
- (exitCode, result) = ClipboardProcessRunner.Bash ("which pbpaste", waitForOutput: true);
- return exitCode == 0 && result.FileExists ();
+ static int GetSuspendSignal ()
+ {
+ if (_suspendSignal != 0) {
+ return _suspendSignal;
}
- protected override string GetClipboardDataImpl ()
- {
- var ptr = objc_msgSend (generalPasteboard, stringForTypeRegister, nsStringPboardType);
- var charArray = objc_msgSend (ptr, utf8Register);
- return Marshal.PtrToStringAnsi (charArray);
+ IntPtr buf = Marshal.AllocHGlobal (8192);
+ if (uname (buf) != 0) {
+ Marshal.FreeHGlobal (buf);
+ _suspendSignal = -1;
+ return _suspendSignal;
}
-
- protected override void SetClipboardDataImpl (string text)
- {
- IntPtr str = default;
- try {
- str = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, text);
- objc_msgSend (generalPasteboard, clearContentsRegister);
- objc_msgSend (generalPasteboard, setStringRegister, str, utfTextType);
- } finally {
- if (str != default) {
- objc_msgSend (str, sel_registerName ("release"));
- }
+ try {
+ switch (Marshal.PtrToStringAnsi (buf)) {
+ case "Darwin":
+ case "DragonFly":
+ case "FreeBSD":
+ case "NetBSD":
+ case "OpenBSD":
+ _suspendSignal = 18;
+ break;
+ case "Linux":
+ // TODO: should fetch the machine name and
+ // if it is MIPS return 24
+ _suspendSignal = 20;
+ break;
+ case "Solaris":
+ _suspendSignal = 24;
+ break;
+ default:
+ _suspendSignal = -1;
+ break;
}
+ return _suspendSignal;
+ } finally {
+ Marshal.FreeHGlobal (buf);
}
-
- [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
- static extern IntPtr objc_getClass (string className);
-
- [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
- static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector);
-
- [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
- static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, string arg1);
-
- [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
- static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1);
-
- [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
- static extern IntPtr objc_msgSend (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);
-
- [DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
- static extern IntPtr sel_registerName (string selectorName);
}
///
- /// A clipboard implementation for Linux, when running under WSL.
- /// This implementation uses the Windows clipboard to store the data, and uses Windows'
- /// powershell.exe (launched via WSL interop services) to set/get the Windows
- /// clipboard.
+ /// Suspends the process by sending SIGTSTP to itself
///
- class WSLClipboard : ClipboardBase {
- bool isSupported = false;
- public WSLClipboard ()
- {
- isSupported = CheckSupport ();
- }
-
- public override bool IsSupported {
- get {
- return isSupported = CheckSupport ();
- }
- }
-
- private static string powershellPath = string.Empty;
-
- bool CheckSupport ()
- {
- if (string.IsNullOrEmpty (powershellPath)) {
- // Specify pwsh.exe (not pwsh) to ensure we get the Windows version (invoked via WSL)
- var (exitCode, result) = ClipboardProcessRunner.Bash ("which pwsh.exe", waitForOutput: true);
- if (exitCode > 0) {
- (exitCode, result) = ClipboardProcessRunner.Bash ("which powershell.exe", waitForOutput: true);
- }
-
- if (exitCode == 0) {
- powershellPath = result;
- }
- }
- return !string.IsNullOrEmpty (powershellPath);
- }
-
- protected override string GetClipboardDataImpl ()
- {
- if (!IsSupported) {
- return string.Empty;
- }
-
- var (exitCode, output) = ClipboardProcessRunner.Process (powershellPath, "-noprofile -command \"Get-Clipboard\"");
- if (exitCode == 0) {
- if (Application.Driver is CursesDriver) {
- Curses.raw ();
- Curses.noecho ();
- }
-
- if (output.EndsWith ("\r\n")) {
- output = output.Substring (0, output.Length - 2);
- }
- return output;
- }
- return string.Empty;
- }
-
- protected override void SetClipboardDataImpl (string text)
- {
- if (!IsSupported) {
- return;
- }
-
- var (exitCode, output) = ClipboardProcessRunner.Process (powershellPath, $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\"");
- if (exitCode == 0) {
- if (Application.Driver is CursesDriver) {
- Curses.raw ();
- Curses.noecho ();
- }
- }
+ /// The suspend.
+ static public bool Suspend ()
+ {
+ int signal = GetSuspendSignal ();
+ if (signal == -1) {
+ return false;
}
+ killpg (0, signal);
+ return true;
}
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
index 7448e5340e..48d8fbcb22 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs
@@ -97,8 +97,8 @@ void IMainLoopDriver.Setup (MainLoop mainLoop)
{
this.mainLoop = mainLoop;
pipe (wakeupPipes);
- AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
- read (wakeupPipes [0], ignore, readHandle);
+ AddWatch (wakeupPipes [1], Condition.PollIn, ml => {
+ read (wakeupPipes [1], ignore, readHandle);
return true;
});
}
@@ -212,5 +212,9 @@ void IMainLoopDriver.Iteration ()
}
}
}
+ public void TearDown ()
+ {
+ //throw new NotImplementedException ();
+ }
}
}
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs
index 67dbe236fd..1edb532756 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnmanagedLibrary.cs
@@ -91,7 +91,7 @@ static UnmanagedLibrary ()
const int RTLD_LAZY = 1;
const int RTLD_GLOBAL = 8;
- readonly string libraryPath;
+ public readonly string LibraryPath;
readonly IntPtr handle;
public IntPtr NativeLibraryHandle => handle;
@@ -104,13 +104,15 @@ static UnmanagedLibrary ()
public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath)
{
if (isFullPath) {
- this.libraryPath = FirstValidLibraryPath (libraryPathAlternatives);
- this.handle = PlatformSpecificLoadLibrary (this.libraryPath);
+ this.LibraryPath = FirstValidLibraryPath (libraryPathAlternatives);
+ this.handle = PlatformSpecificLoadLibrary (this.LibraryPath);
} else {
foreach (var lib in libraryPathAlternatives) {
this.handle = PlatformSpecificLoadLibrary (lib);
- if (this.handle != IntPtr.Zero)
+ if (this.handle != IntPtr.Zero) {
+ this.LibraryPath = lib;
break;
+ }
}
}
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
index 8a0ed3da78..da2cf3ccf5 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/binding.cs
@@ -71,6 +71,7 @@ public struct MouseEvent {
//static bool use_naked_driver;
static UnmanagedLibrary curses_library;
+
static NativeMethods methods;
[DllImport ("libc")]
@@ -97,6 +98,13 @@ static void FindNCurses ()
cols_ptr = get_ptr ("COLS");
}
+ static public string curses_version ()
+ {
+ var v = methods.curses_version ();
+ return $"{Marshal.PtrToStringAnsi (v)}, {curses_library.LibraryPath}";
+
+ }
+
static public Window initscr ()
{
setlocale (LC_ALL, "");
@@ -116,6 +124,9 @@ static public Window initscr ()
"or DYLD_LIBRARY_PATH directory or run /sbin/ldconfig");
Environment.Exit (1);
}
+
+ //Console.Error.WriteLine ($"using curses {Curses.curses_version ()}");
+
return main_window;
}
@@ -141,7 +152,10 @@ public static bool CheckWinChange ()
console_sharp_get_dims (out l, out c);
- if (l == 1 || l != lines || c != cols) {
+ if (l < 1) {
+ l = 1;
+ }
+ if (l != lines || c != cols) {
lines = l;
cols = c;
return true;
@@ -250,7 +264,35 @@ static public int IsAlt (int key)
public static int StartColor () => methods.start_color ();
public static bool HasColors => methods.has_colors ();
+ ///
+ /// The init_pair routine changes the definition of a color-pair.It takes
+ /// three arguments: the number of the color-pair to be changed, the fore-
+ /// ground color number, and the background color number.For portable ap-
+ /// plications:
+ ///
+ /// o The first argument must be a legal color pair value.If default
+ /// colors are used (see use_default_colors(3x)) the upper limit is ad-
+ /// justed to allow for extra pairs which use a default color in fore-
+ /// ground and/or background.
+ ///
+ /// o The second and third arguments must be legal color values.
+ ///
+ /// If the color-pair was previously initialized, the screen is refreshed
+ /// and all occurrences of that color-pair are changed to the new defini-
+ /// tion.
+ ///
+ /// As an extension, ncurses allows you to set color pair 0 via the as-
+ /// sume_default_colors (3x) routine, or to specify the use of default col-
+ /// ors (color number -1) if you first invoke the use_default_colors (3x)
+ /// routine.
+ ///
+ ///
+ ///
+ ///
+ ///
public static int InitColorPair (short pair, short foreground, short background) => methods.init_pair (pair, foreground, background);
+ // TODO: Upgrade to ncurses 6.1 and use the extended version
+ //public static int InitExtendedPair (int pair, int foreground, int background) => methods.init_extended_pair (pair, foreground, background);
public static int UseDefaultColors () => methods.use_default_colors ();
public static int ColorPairs => methods.COLOR_PAIRS ();
@@ -294,10 +336,12 @@ static public int IsAlt (int key)
static public int move (int line, int col) => methods.move (line, col);
static public int curs_set (int visibility) => methods.curs_set (visibility);
//static public int addch (int ch) => methods.addch (ch);
+ static public int echochar (int ch) => methods.echochar (ch);
static public int addwstr (string s) => methods.addwstr (s);
static public int mvaddwstr (int y, int x, string s) => methods.mvaddwstr (y, x, s);
static public int wmove (IntPtr win, int line, int col) => methods.wmove (win, line, col);
static public int waddch (IntPtr win, int ch) => methods.waddch (win, ch);
+ //static public int wechochar (IntPtr win, int ch) => methods.wechochar (win, ch);
static public int attron (int attrs) => methods.attron (attrs);
static public int attroff (int attrs) => methods.attroff (attrs);
static public int attrset (int attrs) => methods.attrset (attrs);
@@ -369,6 +413,7 @@ internal class Delegates {
public delegate int move (int line, int col);
public delegate int curs_set (int visibility);
public delegate int addch (int ch);
+ public delegate int echochar (int ch);
public delegate int mvaddch (int y, int x, int ch);
public delegate int addwstr ([MarshalAs (UnmanagedType.LPWStr)] string s);
public delegate int mvaddwstr (int y, int x, [MarshalAs (UnmanagedType.LPWStr)] string s);
@@ -402,6 +447,7 @@ internal class Delegates {
public delegate int savetty ();
public delegate int resetty ();
public delegate int set_escdelay (int size);
+ public delegate IntPtr curses_version ();
}
internal class NativeMethods {
@@ -443,11 +489,13 @@ internal class NativeMethods {
public readonly Delegates.move move;
public readonly Delegates.curs_set curs_set;
public readonly Delegates.addch addch;
+ public readonly Delegates.echochar echochar;
public readonly Delegates.mvaddch mvaddch;
public readonly Delegates.addwstr addwstr;
public readonly Delegates.mvaddwstr mvaddwstr;
public readonly Delegates.wmove wmove;
public readonly Delegates.waddch waddch;
+ //public readonly Delegates.wechochar wechochar;
public readonly Delegates.attron attron;
public readonly Delegates.attroff attroff;
public readonly Delegates.attrset attrset;
@@ -476,6 +524,7 @@ internal class NativeMethods {
public readonly Delegates.savetty savetty;
public readonly Delegates.resetty resetty;
public readonly Delegates.set_escdelay set_escdelay;
+ public readonly Delegates.curses_version curses_version;
public UnmanagedLibrary UnmanagedLibrary;
public NativeMethods (UnmanagedLibrary lib)
@@ -519,6 +568,7 @@ public NativeMethods (UnmanagedLibrary lib)
move = lib.GetNativeMethodDelegate ("move");
curs_set = lib.GetNativeMethodDelegate ("curs_set");
addch = lib.GetNativeMethodDelegate ("addch");
+ echochar = lib.GetNativeMethodDelegate ("echochar");
mvaddch = lib.GetNativeMethodDelegate ("mvaddch");
addwstr = lib.GetNativeMethodDelegate ("addwstr");
mvaddwstr = lib.GetNativeMethodDelegate ("mvaddwstr");
@@ -552,6 +602,7 @@ public NativeMethods (UnmanagedLibrary lib)
savetty = lib.GetNativeMethodDelegate ("savetty");
resetty = lib.GetNativeMethodDelegate ("resetty");
set_escdelay = lib.GetNativeMethodDelegate ("set_escdelay");
+ curses_version = lib.GetNativeMethodDelegate ("curses_version");
}
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs
index 8a46149d50..0158a32512 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs
@@ -149,7 +149,12 @@ public int addch (char ch)
{
return Curses.waddch (Handle, ch);
}
-
+
+ //public int echochar (char ch)
+ //{
+ // return Curses.wechochar (Handle, ch);
+ //}
+
public int refresh ()
{
return Curses.wrefresh (Handle);
diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs
new file mode 100644
index 0000000000..bce7ba8c8e
--- /dev/null
+++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqReq.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+
+namespace Terminal.Gui {
+ ///
+ /// Represents the status of an ANSI escape sequence request made to the terminal using .
+ ///
+ ///
+ ///
+ public class EscSeqReqStatus {
+ ///
+ /// Gets the Escape Sequence Termintor (e.g. ESC[8t ... t is the terminator).
+ ///
+ public string Terminator { get; }
+ ///
+ /// Gets the number of requests.
+ ///
+ public int NumRequests { get; }
+ ///
+ /// Gets the number of unfinished requests.
+ ///
+ public int NumOutstanding { get; set; }
+
+ ///
+ /// Creates a new state of escape sequence request.
+ ///
+ /// The terminator.
+ /// The number of requests.
+ public EscSeqReqStatus (string terminator, int numReq)
+ {
+ Terminator = terminator;
+ NumRequests = NumOutstanding = numReq;
+ }
+ }
+
+ // TODO: This class is a singleton. It should use the singleton pattern.
+ ///
+ /// Manages ANSI Escape Sequence requests and responses. The list of contains the status of the request.
+ /// Each request is identified by the terminator (e.g. ESC[8t ... t is the terminator).
+ ///
+ public class EscSeqRequests {
+ ///
+ /// Gets the list.
+ ///
+ public List Statuses { get; } = new List ();
+
+ ///
+ /// Adds a new request for the ANSI Escape Sequence defined by . Adds a
+ /// instance to list.
+ ///
+ /// The terminator.
+ /// The number of requests.
+ public void Add (string terminator, int numReq = 1)
+ {
+ lock (Statuses) {
+ var found = Statuses.Find (x => x.Terminator == terminator);
+ if (found == null) {
+ Statuses.Add (new EscSeqReqStatus (terminator, numReq));
+ } else if (found != null && found.NumOutstanding < found.NumRequests) {
+ found.NumOutstanding = Math.Min (found.NumOutstanding + numReq, found.NumRequests);
+ }
+ }
+ }
+
+ ///
+ /// Removes a request defined by . If a matching is found
+ /// and the number of outstanding
+ /// requests is greater than 0, the number of outstanding requests is decremented. If the number of outstanding requests
+ /// is 0, the is removed from .
+ ///
+ /// The terminating string.
+ public void Remove (string terminator)
+ {
+ lock (Statuses) {
+ var found = Statuses.Find (x => x.Terminator == terminator);
+ if (found == null) {
+ return;
+ }
+ if (found != null && found.NumOutstanding == 0) {
+ Statuses.Remove (found);
+ } else if (found != null && found.NumOutstanding > 0) {
+ found.NumOutstanding--;
+ if (found.NumOutstanding == 0) {
+ Statuses.Remove (found);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Indicates if a with the exists
+ /// in the list.
+ ///
+ ///
+ /// if exist, otherwise.
+ public bool HasResponse (string terminator)
+ {
+ lock (Statuses) {
+ var found = Statuses.Find (x => x.Terminator == terminator);
+ if (found == null) {
+ return false;
+ }
+ if (found != null && found.NumOutstanding > 0) {
+ return true;
+ } else {
+ // BUGBUG: Why does an API that returns a bool remove the entry from the list?
+ // NetDriver and Unit tests never exercise this line of code. Maybe Curses does?
+ Statuses.Remove (found);
+ }
+ return false;
+ }
+ }
+ }
+}
diff --git a/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
new file mode 100644
index 0000000000..3d71b1a21f
--- /dev/null
+++ b/Terminal.Gui/ConsoleDrivers/EscSeqUtils/EscSeqUtils.cs
@@ -0,0 +1,1165 @@
+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 sequences.
+///
+///
+/// Useful resources:
+/// * https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
+/// * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+/// * https://vt100.net/
+///
+public static class EscSeqUtils {
+ ///
+ /// Escape key code (ASCII 27/0x1B).
+ ///
+ public static readonly char KeyEsc = (char)Key.Esc;
+
+ ///
+ /// ESC [ - The CSI (Control Sequence Introducer).
+ ///
+ public static readonly string CSI = $"{KeyEsc}[";
+
+ ///
+ /// ESC [ ? 1003 h - Enable mouse event tracking.
+ ///
+ public static readonly string CSI_EnableAnyEventMouse = CSI + "?1003h";
+
+ ///
+ /// ESC [ ? 1006 h - Enable SGR (Select Graphic Rendition).
+ ///
+ public static readonly string CSI_EnableSgrExtModeMouse = CSI + "?1006h";
+
+ ///
+ /// ESC [ ? 1015 h - Enable URXVT (Unicode Extended Virtual Terminal).
+ ///
+ public static readonly string CSI_EnableUrxvtExtModeMouse = CSI + "?1015h";
+
+ ///
+ /// ESC [ ? 1003 l - Disable any mouse event tracking.
+ ///
+ public static readonly string CSI_DisableAnyEventMouse = CSI + "?1003l";
+
+ ///
+ /// ESC [ ? 1006 l - Disable SGR (Select Graphic Rendition).
+ ///
+ public static readonly string CSI_DisableSgrExtModeMouse = CSI + "?1006l";
+
+ ///
+ /// ESC [ ? 1015 l - Disable URXVT (Unicode Extended Virtual Terminal).
+ ///
+ public static readonly string CSI_DisableUrxvtExtModeMouse = CSI + "?1015l";
+
+ ///
+ /// Control sequence for enabling mouse events.
+ ///
+ public static string CSI_EnableMouseEvents { get; set; } =
+ CSI_EnableAnyEventMouse + CSI_EnableUrxvtExtModeMouse + CSI_EnableSgrExtModeMouse;
+
+ ///
+ /// Control sequence for disabling mouse events.
+ ///
+ public static string CSI_DisableMouseEvents { get; set; } =
+ CSI_DisableAnyEventMouse + CSI_DisableUrxvtExtModeMouse + CSI_DisableSgrExtModeMouse;
+
+ ///
+ /// ESC [ ? 1047 h - Activate xterm alternative buffer (no backscroll)
+ ///
+ public static readonly string CSI_ActivateAltBufferNoBackscroll = CSI + "?1047h";
+
+ ///
+ /// ESC [ ? 1047 l - Restore xterm working buffer (with backscroll)
+ ///
+ public static readonly string CSI_RestoreAltBufferWithBackscroll = CSI + "?1047l";
+
+ ///
+ /// ESC [ ? 1049 h - Save cursor position and activate xterm alternative buffer (no backscroll)
+ ///
+ public static readonly string CSI_SaveCursorAndActivateAltBufferNoBackscroll = CSI + "?1049h";
+
+ ///
+ /// ESC [ ? 1049 l - Restore cursor position and restore xterm working buffer (with backscroll)
+ ///
+ public static readonly string CSI_RestoreCursorAndActivateAltBufferWithBackscroll = CSI + "?1049l";
+
+ ///
+ /// Options for ANSI ESC "[xJ" - Clears part of the screen.
+ ///
+ public enum ClearScreenOptions {
+ ///
+ /// If n is 0 (or missing), clear from cursor to end of screen.
+ ///
+ CursorToEndOfScreen = 0,
+ ///
+ /// If n is 1, clear from cursor to beginning of the screen.
+ ///
+ CursorToBeginningOfScreen = 1,
+ ///
+ /// If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
+ ///
+ EntireScreen = 2,
+ ///
+ /// If n is 3, clear entire screen and delete all lines saved in the scrollback buffer
+ ///
+ EntireScreenAndScrollbackBuffer = 3
+ }
+
+ ///
+ /// ESC [ x J - Clears part of the screen. See .
+ ///
+ ///
+ ///
+ public static string CSI_ClearScreen (ClearScreenOptions option) => $"{CSI}{(int)option}J";
+
+ #region Cursor
+ //ESC [ M - RI Reverse Index – Performs the reverse operation of \n, moves cursor up one line, maintains horizontal position, scrolls buffer if necessary*
+
+ ///
+ /// ESC [ 7 - Save Cursor Position in Memory**
+ ///
+ public static readonly string CSI_SaveCursorPosition = CSI + "7";
+
+ ///
+ /// ESC [ 8 - DECSR Restore Cursor Position from Memory**
+ ///
+ public static readonly string CSI_RestoreCursorPosition = CSI + "8";
+
+ ///
+ /// ESC [ 8 ; height ; width t - Set Terminal Window Size
+ /// https://terminalguide.namepad.de/seq/csi_st-8/
+ ///
+ public static string CSI_SetTerminalWindowSize (int height, int width) => $"{CSI}8;{height};{width}t";
+
+ //ESC [ < n > A - CUU - Cursor Up Cursor up by < n >
+ //ESC [ < n > B - CUD - Cursor Down Cursor down by < n >
+ //ESC [ < n > C - CUF - Cursor Forward Cursor forward (Right) by < n >
+ //ESC [ < n > D - CUB - Cursor Backward Cursor backward (Left) by < n >
+ //ESC [ < n > E - CNL - Cursor Next Line - Cursor down < n > lines from current position
+ //ESC [ < n > F - CPL - Cursor Previous Line Cursor up < n > lines from current position
+ //ESC [ < n > G - CHA - Cursor Horizontal Absolute Cursor moves to < n > th position horizontally in the current line
+ //ESC [ < n > d - VPA - Vertical Line Position Absolute Cursor moves to the < n > th position vertically in the current column
+
+ ///
+ /// ESC [ y ; x H - CUP Cursor Position - Cursor moves to x ; y coordinate within the viewport, where x is the column of the y line
+ ///
+ ///
+ ///
+ ///
+ public static string CSI_SetCursorPosition (int x, int y) => $"{CSI}{y};{x}H";
+
+
+ //ESC [ ; f - HVP Horizontal Vertical Position* Cursor moves to; coordinate within the viewport, where is the column of the line
+ //ESC [ s - ANSISYSSC Save Cursor – Ansi.sys emulation **With no parameters, performs a save cursor operation like DECSC
+ //ESC [ u - ANSISYSRC Restore Cursor – Ansi.sys emulation **With no parameters, performs a restore cursor operation like DECRC
+ //ESC [ ? 12 h - ATT160 Text Cursor Enable Blinking Start the cursor blinking
+ //ESC [ ? 12 l - ATT160 Text Cursor Disable Blinking Stop blinking the cursor
+ ///
+ /// ESC [ ? 25 h - DECTCEM Text Cursor Enable Mode Show Show the cursor
+ ///
+ public static readonly string CSI_ShowCursor = CSI + "?25h";
+
+ ///
+ /// ESC [ ? 25 l - DECTCEM Text Cursor Enable Mode Hide Hide the cursor
+ ///
+ public static readonly string CSI_HideCursor = CSI + "?25l";
+ //ESC [ ? 12 h - ATT160 Text Cursor Enable Blinking Start the cursor blinking
+ //ESC [ ? 12 l - ATT160 Text Cursor Disable Blinking Stop blinking the cursor
+ //ESC [ ? 25 h - DECTCEM Text Cursor Enable Mode Show Show the cursor
+ //ESC [ ? 25 l - DECTCEM Text Cursor Enable Mode Hide Hide the cursor
+
+ ///
+ /// Styles for ANSI ESC "[x q" - Set Cursor Style
+ ///
+ public enum DECSCUSR_Style {
+ ///
+ /// DECSCUSR - User Shape - Default cursor shape configured by the user
+ ///
+ UserShape = 0,
+ ///
+ /// DECSCUSR - Blinking Block - Blinking block cursor shape
+ ///
+ BlinkingBlock = 1,
+ ///
+ /// DECSCUSR - Steady Block - Steady block cursor shape
+ ///
+ SteadyBlock = 2,
+ ///
+ /// DECSCUSR - Blinking Underline - Blinking underline cursor shape
+ ///
+ BlinkingUnderline = 3,
+ ///
+ /// DECSCUSR - Steady Underline - Steady underline cursor shape
+ ///
+ SteadyUnderline = 4,
+ ///
+ /// DECSCUSR - Blinking Bar - Blinking bar cursor shape
+ ///
+ BlinkingBar = 5,
+ ///
+ /// DECSCUSR - Steady Bar - Steady bar cursor shape
+ ///
+ SteadyBar = 6
+ }
+
+ ///
+ /// ESC [ n SP q - Select Cursor Style (DECSCUSR)
+ /// https://terminalguide.namepad.de/seq/csi_sq_t_space/
+ ///
+ ///
+ ///
+ public static string CSI_SetCursorStyle (DECSCUSR_Style style) => $"{CSI}{(int)style} q";
+
+ #endregion
+
+ #region Colors
+ ///
+ /// ESC [ (n) m - SGR - Set Graphics Rendition - Set the format of the screen and text as specified by (n)
+ /// This command is special in that the (n) position can accept between 0 and 16 parameters separated by semicolons.
+ /// When no parameters are specified, it is treated the same as a single 0 parameter.
+ /// https://terminalguide.namepad.de/seq/csi_sm/
+ ///
+ public static string CSI_SetGraphicsRendition (params int [] parameters) => $"{CSI}{string.Join (";", parameters)}m";
+
+ ///
+ /// ESC[38;5;{id}m - Set foreground color (256 colors)
+ ///
+ public static string CSI_SetForegroundColor (int id) => $"{CSI}38;5;{id}m";
+
+ ///
+ /// ESC[48;5;{id}m - Set background color (256 colors)
+ ///
+ public static string CSI_SetBackgroundColor (int id) => $"{CSI}48;5;{id}m";
+
+ ///
+ /// ESC[38;2;{r};{g};{b}m Set foreground color as RGB.
+ ///
+ public static string CSI_SetForegroundColorRGB (int r, int g, int b) => $"{CSI}38;2;{r};{g};{b}m";
+
+ ///
+ /// ESC[48;2;{r};{g};{b}m Set background color as RGB.
+ ///
+ public static string CSI_SetBackgroundColorRGB (int r, int g, int b) => $"{CSI}48;2;{r};{g};{b}m";
+
+ #endregion
+
+ #region Requests
+ ///
+ /// ESC [ ? 6 n - Request Cursor Position Report (?) (DECXCPR)
+ /// https://terminalguide.namepad.de/seq/csi_sn__p-6/
+ ///
+ public static readonly string CSI_RequestCursorPositionReport = CSI + "?6n";
+
+ ///
+ /// The terminal reply to . ESC [ ? (y) ; (x) R
+ ///
+ public const string CSI_RequestCursorPositionReport_Terminator = "R";
+
+ ///
+ /// ESC [ 0 c - Send Device Attributes (Primary DA)
+ ///
+ /// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Application-Program-Command-functions
+ /// https://www.xfree86.org/current/ctlseqs.html
+ /// Windows Terminal v1.17 and below emits “\x1b[?1;0c”, indicating "VT101 with No Options".
+ /// Windows Terminal v1.18+ emits: \x1b[?61;6;7;22;23;24;28;32;42c"
+ /// See https://github.com/microsoft/terminal/pull/14906
+ ///
+ /// 61 - The device conforms to level 1 of the character cell display architecture
+ /// (See https://github.com/microsoft/terminal/issues/15693#issuecomment-1633304497)
+ /// 6 = Selective erase
+ /// 7 = Soft fonts
+ /// 22 = Color text
+ /// 23 = Greek character sets
+ /// 24 = Turkish character sets
+ /// 28 = Rectangular area operations
+ /// 32 = Text macros
+ /// 42 = ISO Latin-2 character set
+ ///
+ ///
+ public static readonly string CSI_SendDeviceAttributes = CSI + "0c";
+
+ ///
+ /// ESC [ > 0 c - Send Device Attributes (Secondary DA)
+ /// Windows Terminal v1.18+ emits: "\x1b[>0;10;1c" (vt100, firmware version 1.0, vt220)
+ ///
+ public static readonly string CSI_SendDeviceAttributes2 = CSI + ">0c";
+
+ ///
+ /// The terminator indicating a reply to or
+ ///
+ ///
+ public const string CSI_ReportDeviceAttributes_Terminator = "c";
+
+ ///
+ /// CSI 1 8 t | yes | yes | yes | report window size in chars
+ /// https://terminalguide.namepad.de/seq/csi_st-18/
+ ///
+ public static readonly string CSI_ReportTerminalSizeInChars = CSI + "18t";
+
+ ///
+ /// The terminator indicating a reply to : ESC [ 8 ; height ; width t
+ ///
+ public const string CSI_ReportTerminalSizeInChars_Terminator = "t";
+
+ ///
+ /// The value of the response to indicating value 1 and 2 are the terminal size in chars.
+ ///
+ public const string CSI_ReportTerminalSizeInChars_ResponseValue = "8";
+ #endregion
+
+ ///
+ /// Ensures a console key is mapped to one that works correctly with ANSI escape sequences.
+ ///
+ /// The .
+ /// The modified.
+ public static ConsoleKeyInfo MapConsoleKeyInfo (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 > 0 && n <= KeyEsc:
+ 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: // DEL
+ 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 an ANSI escape sequence.
+ ///
+ /// 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 terminator returned by the method.
+ /// Indicates if the escape sequence is a mouse event.
+ /// 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 (EscSeqRequests escSeqRequests, ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod, out string c1Control, out string code, out string [] values, out string terminator, out bool isMouse, out List buttonState, out Point pos, out bool isResponse, Action continuousButtonPressedHandler)
+ {
+ char [] kChars = GetKeyCharArray (cki);
+ (c1Control, code, values, terminator) = GetEscapeResult (kChars);
+ isMouse = false;
+ buttonState = new List () { 0 };
+ pos = default;
+ isResponse = false;
+ switch (c1Control) {
+ case "ESC":
+ if (values == null && string.IsNullOrEmpty (terminator)) {
+ 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 (terminator [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);
+ isMouse = true;
+ return;
+ } else if (escSeqRequests != null && escSeqRequests.HasResponse (terminator)) {
+ isResponse = true;
+ escSeqRequests.Remove (terminator);
+ return;
+ }
+ key = GetConsoleKey (terminator [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] != KeyEsc) {
+ 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;
+ }
+
+ // TODO: Move this out of here and into ConsoleDriver or somewhere else.
+ ///
+ /// 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/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
index 75a58f9c32..69b2032765 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs
@@ -5,2013 +5,2007 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
+using Rune = System.Text.Rune;
-namespace Terminal.Gui {
+namespace Terminal.Gui;
#pragma warning disable RCS1138 // Add summary to documentation comment.
+///
+///
+///
+public static class FakeConsole {
+#pragma warning restore RCS1138 // Add summary to documentation comment.
+
+ //
+ // Summary:
+ // Gets or sets the width of the console window.
+ //
+ // Returns:
+ // 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.IO.IOException:
+ // Error reading or writing information.
+#pragma warning disable RCS1138 // Add summary to documentation comment.
+
+ ///
+ /// Specifies the initial console width.
+ ///
+ public const int WIDTH = 80;
+
+ ///
+ /// Specifies the initial console height.
+ ///
+ public const int HEIGHT = 25;
+
///
///
///
- public static class FakeConsole {
-#pragma warning restore RCS1138 // Add summary to documentation comment.
+ public static int WindowWidth { get; set; } = WIDTH;
+ //
+ // Summary:
+ // Gets a value that indicates whether output has been redirected from the standard
+ // output stream.
+ //
+ // Returns:
+ // 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.
+ //
+ // Returns:
+ // true if error output is redirected; otherwise, false.
+ ///
+ ///
+ ///
+ public static bool IsErrorRedirected { get; }
+ //
+ // Summary:
+ // Gets the standard input stream.
+ //
+ // Returns:
+ // A System.IO.TextReader that represents the standard input stream.
+ ///
+ ///
+ ///
+ public static TextReader In { get; }
+ //
+ // Summary:
+ // Gets the standard output stream.
+ //
+ // Returns:
+ // A System.IO.TextWriter that represents the standard output stream.
+ ///
+ ///
+ ///
+ public static TextWriter Out { get; }
+ //
+ // Summary:
+ // Gets the standard error output stream.
+ //
+ // Returns:
+ // 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.
+ //
+ // Returns:
+ // The encoding used to read console input.
+ //
+ // Exceptions:
+ // 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.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.
+ //
+ // Returns:
+ // The encoding used to write console output.
+ //
+ // Exceptions:
+ // 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.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.
+ //
+ // Returns:
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+
+ static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black;
- //
- // Summary:
- // Gets or sets the width of the console window.
- //
- // Returns:
- // 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.IO.IOException:
- // Error reading or writing information.
-#pragma warning disable RCS1138 // Add summary to documentation comment.
+ ///
+ ///
+ ///
+ public static ConsoleColor BackgroundColor { get; set; } = _defaultBackgroundColor;
+
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // 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.IO.IOException:
+ // An I/O error occurred.
+
+ static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray;
- ///
- /// Specifies the initial console width.
- ///
- public const int WIDTH = 80;
-
- ///
- /// Specifies the initial console height.
- ///
- public const int HEIGHT = 25;
-
- ///
- ///
- ///
- public static int WindowWidth { get; set; } = WIDTH;
- //
- // Summary:
- // Gets a value that indicates whether output has been redirected from the standard
- // output stream.
- //
- // Returns:
- // 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.
- //
- // Returns:
- // true if error output is redirected; otherwise, false.
- ///
- ///
- ///
- public static bool IsErrorRedirected { get; }
- //
- // Summary:
- // Gets the standard input stream.
- //
- // Returns:
- // A System.IO.TextReader that represents the standard input stream.
- ///
- ///
- ///
- public static TextReader In { get; }
- //
- // Summary:
- // Gets the standard output stream.
- //
- // Returns:
- // A System.IO.TextWriter that represents the standard output stream.
- ///
- ///
- ///
- public static TextWriter Out { get; }
- //
- // Summary:
- // Gets the standard error output stream.
- //
- // Returns:
- // 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.
- //
- // Returns:
- // The encoding used to read console input.
- //
- // Exceptions:
- // 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.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.
- //
- // Returns:
- // The encoding used to write console output.
- //
- // Exceptions:
- // 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.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.
- //
- // Returns:
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // T:System.IO.IOException:
- // An I/O error occurred.
-
- static ConsoleColor _defaultBackgroundColor = ConsoleColor.Black;
-
- ///
- ///
- ///
- public static ConsoleColor BackgroundColor { get; set; } = _defaultBackgroundColor;
-
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // 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.IO.IOException:
- // An I/O error occurred.
-
- static ConsoleColor _defaultForegroundColor = ConsoleColor.Gray;
-
- ///
- ///
- ///
- public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor;
- //
- // Summary:
- // Gets or sets the height of the buffer area.
- //
- // Returns:
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // 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.
- //
- // Returns:
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // 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.
- //
- // Returns:
- // 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.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.
- //
- // Returns:
- // 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.
- ///
- ///
- ///
- public static bool TreatControlCAsInput { get; set; }
- //
- // Summary:
- // 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.
- ///
- ///
- ///
- public static int LargestWindowWidth { get; }
- //
- // Summary:
- // 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.
- ///
- ///
- ///
- public static int LargestWindowHeight { get; }
- //
- // Summary:
- // 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.
- //
- // 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.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.
- //
- // Returns:
- // 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.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.
- //
- // Returns:
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // 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.
- //
- // Returns:
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // 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.
- //
- // 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.
- //
- // Exceptions:
- // 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.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.
- //
- // Returns:
- // 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.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.
- //
- // Returns:
- // 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.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.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.
- //
- // Returns:
- // true if a key press is available; otherwise, false.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //
- // 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.
- //
- // Returns:
- // 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.
- //
- // Returns:
- // 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.
- //
- // Returns:
- // true if input is redirected; otherwise, false.
- ///
- ///
- ///
- public static bool IsInputRedirected { get; }
-
- //
- // Summary:
- // 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.
- ///
- ///
- ///
- public static void Beep ()
- {
- throw new NotImplementedException ();
- }
- //
- // Summary:
- // 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.
- //
- // 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.Security.HostProtectionException:
- // This method was executed on a server, such as SQL Server, that does not permit
- // access to the console.
- ///
- ///
- ///
- public static void Beep (int frequency, int duration)
- {
- throw new NotImplementedException ();
- }
- //
- // Summary:
- // Clears the console buffer and corresponding console window of display information.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
-
- static char [,] _buffer = new char [WindowWidth, WindowHeight];
-
- ///
- ///
- ///
- public static void Clear ()
- {
- _buffer = new char [BufferWidth, BufferHeight];
- SetCursorPosition (0, 0);
- }
+ ///
+ ///
+ ///
+ public static ConsoleColor ForegroundColor { get; set; } = _defaultForegroundColor;
+ //
+ // Summary:
+ // Gets or sets the height of the buffer area.
+ //
+ // Returns:
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // 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.
+ //
+ // Returns:
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // 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.
+ //
+ // Returns:
+ // 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.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.
+ //
+ // Returns:
+ // 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.
+ ///
+ ///
+ ///
+ public static bool TreatControlCAsInput { get; set; }
+ //
+ // Summary:
+ // 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.
+ ///
+ ///
+ ///
+ public static int LargestWindowWidth { get; }
+ //
+ // Summary:
+ // 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.
+ ///
+ ///
+ ///
+ public static int LargestWindowHeight { get; }
+ //
+ // Summary:
+ // 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.
+ //
+ // 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.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.
+ //
+ // Returns:
+ // 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.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.
+ //
+ // Returns:
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // 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.
+ //
+ // Returns:
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // 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.
+ //
+ // 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.
+ //
+ // Exceptions:
+ // 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.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.
+ //
+ // Returns:
+ // 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.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.
+ //
+ // Returns:
+ // 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.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.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.
+ //
+ // Returns:
+ // true if a key press is available; otherwise, false.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //
+ // 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.
+ //
+ // Returns:
+ // 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.
+ //
+ // Returns:
+ // 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.
+ //
+ // Returns:
+ // true if input is redirected; otherwise, false.
+ ///
+ ///
+ ///
+ public static bool IsInputRedirected { get; }
+
+ //
+ // Summary:
+ // 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.
+ ///
+ ///
+ ///
+ public static void Beep ()
+ {
+ throw new NotImplementedException ();
+ }
+ //
+ // Summary:
+ // 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.
+ //
+ // 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.Security.HostProtectionException:
+ // This method was executed on a server, such as SQL Server, that does not permit
+ // access to the console.
+ ///
+ ///
+ ///
+ public static void Beep (int frequency, int duration)
+ {
+ throw new NotImplementedException ();
+ }
+ //
+ // Summary:
+ // Clears the console buffer and corresponding console window of display information.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
- //
- // Summary:
- // Copies a specified source area of the screen buffer to a specified destination
- // area.
- //
- // Parameters:
- // sourceLeft:
- // The leftmost column of the source area.
- //
- // sourceTop:
- // The topmost row of the source area.
- //
- // sourceWidth:
- // The number of columns in the source area.
- //
- // sourceHeight:
- // The number of rows in the source area.
- //
- // targetLeft:
- // The leftmost column 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop)
- {
- throw new NotImplementedException ();
- }
+ static char [,] _buffer = new char [WindowWidth, WindowHeight];
- //
- // Summary:
- // Copies a specified source area of the screen buffer to a specified destination
- // area.
- //
- // Parameters:
- // sourceLeft:
- // The leftmost column of the source area.
- //
- // sourceTop:
- // The topmost row of the source area.
- //
- // sourceWidth:
- // The number of columns in the source area.
- //
- // sourceHeight:
- // The number of rows in the source area.
- //
- // targetLeft:
- // The leftmost column of the destination area.
- //
- // targetTop:
- // The topmost row of the destination area.
- //
- // sourceChar:
- // The character 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.
- //
- // 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.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.IO.IOException:
- // An I/O error occurred.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
- {
- throw new NotImplementedException ();
- }
+ ///
+ ///
+ ///
+ public static void Clear ()
+ {
+ _buffer = new char [BufferWidth, BufferHeight];
+ SetCursorPosition (0, 0);
+ }
- //
- // Summary:
- // Acquires the standard error stream.
- //
- // Returns:
- // The standard error stream.
- ///
- ///
- ///
- public static Stream OpenStandardError ()
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Copies a specified source area of the screen buffer to a specified destination
+ // area.
+ //
+ // Parameters:
+ // sourceLeft:
+ // The leftmost column of the source area.
+ //
+ // sourceTop:
+ // The topmost row of the source area.
+ //
+ // sourceWidth:
+ // The number of columns in the source area.
+ //
+ // sourceHeight:
+ // The number of rows in the source area.
+ //
+ // targetLeft:
+ // The leftmost column 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Acquires the standard error stream, which is set to a specified buffer size.
- //
- // Parameters:
- // bufferSize:
- // The internal stream buffer size.
- //
- // Returns:
- // The standard error stream.
- //
- // Exceptions:
- // T:System.ArgumentOutOfRangeException:
- // bufferSize is less than or equal to zero.
- ///
- ///
- ///
- public static Stream OpenStandardError (int bufferSize)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Copies a specified source area of the screen buffer to a specified destination
+ // area.
+ //
+ // Parameters:
+ // sourceLeft:
+ // The leftmost column of the source area.
+ //
+ // sourceTop:
+ // The topmost row of the source area.
+ //
+ // sourceWidth:
+ // The number of columns in the source area.
+ //
+ // sourceHeight:
+ // The number of rows in the source area.
+ //
+ // targetLeft:
+ // The leftmost column of the destination area.
+ //
+ // targetTop:
+ // The topmost row of the destination area.
+ //
+ // sourceChar:
+ // The character 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.
+ //
+ // 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.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.IO.IOException:
+ // An I/O error occurred.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ public static void MoveBufferArea (int sourceLeft, int sourceTop, int sourceWidth, int sourceHeight, int targetLeft, int targetTop, char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Acquires the standard input stream, which is set to a specified buffer size.
- //
- // Parameters:
- // bufferSize:
- // The internal stream buffer size.
- //
- // Returns:
- // The standard input stream.
- //
- // Exceptions:
- // T:System.ArgumentOutOfRangeException:
- // bufferSize is less than or equal to zero.
- ///
- ///
- ///
- public static Stream OpenStandardInput (int bufferSize)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Acquires the standard error stream.
+ //
+ // Returns:
+ // The standard error stream.
+ ///
+ ///
+ ///
+ public static Stream OpenStandardError ()
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Acquires the standard input stream.
- //
- // Returns:
- // The standard input stream.
- ///
- ///
- ///
- public static Stream OpenStandardInput ()
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Acquires the standard error stream, which is set to a specified buffer size.
+ //
+ // Parameters:
+ // bufferSize:
+ // The internal stream buffer size.
+ //
+ // Returns:
+ // The standard error stream.
+ //
+ // Exceptions:
+ // T:System.ArgumentOutOfRangeException:
+ // bufferSize is less than or equal to zero.
+ ///
+ ///
+ ///
+ public static Stream OpenStandardError (int bufferSize)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Acquires the standard output stream, which is set to a specified buffer size.
- //
- // Parameters:
- // bufferSize:
- // The internal stream buffer size.
- //
- // Returns:
- // The standard output stream.
- //
- // Exceptions:
- // T:System.ArgumentOutOfRangeException:
- // bufferSize is less than or equal to zero.
- ///
- ///
- ///
- public static Stream OpenStandardOutput (int bufferSize)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Acquires the standard input stream, which is set to a specified buffer size.
+ //
+ // Parameters:
+ // bufferSize:
+ // The internal stream buffer size.
+ //
+ // Returns:
+ // The standard input stream.
+ //
+ // Exceptions:
+ // T:System.ArgumentOutOfRangeException:
+ // bufferSize is less than or equal to zero.
+ ///
+ ///
+ ///
+ public static Stream OpenStandardInput (int bufferSize)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Acquires the standard output stream.
- //
- // Returns:
- // The standard output stream.
- ///
- ///
- ///
- public static Stream OpenStandardOutput ()
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Acquires the standard input stream.
+ //
+ // Returns:
+ // The standard input stream.
+ ///
+ ///
+ ///
+ public static Stream OpenStandardInput ()
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- public static int Read ()
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Acquires the standard output stream, which is set to a specified buffer size.
+ //
+ // Parameters:
+ // bufferSize:
+ // The internal stream buffer size.
+ //
+ // Returns:
+ // The standard output stream.
+ //
+ // Exceptions:
+ // T:System.ArgumentOutOfRangeException:
+ // bufferSize is less than or equal to zero.
+ ///
+ ///
+ ///
+ public static Stream OpenStandardOutput (int bufferSize)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // 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.
- //
- // Exceptions:
- // T:System.InvalidOperationException:
- // The System.Console.In property is redirected from some stream other than the
- // console.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static ConsoleKeyInfo ReadKey (bool intercept)
- {
- if (MockKeyPresses.Count > 0) {
- return MockKeyPresses.Pop ();
- } else {
- return new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', false, false, false);
- }
- }
+ //
+ // Summary:
+ // Acquires the standard output stream.
+ //
+ // Returns:
+ // The standard output stream.
+ ///
+ ///
+ ///
+ public static Stream OpenStandardOutput ()
+ {
+ throw new NotImplementedException ();
+ }
- ///
- /// A stack of keypresses to return when ReadKey is called.
- ///
- public static Stack MockKeyPresses = new Stack ();
-
- ///
- /// Helper to push a onto .
- ///
- ///
- public static void PushMockKeyPress (Key key)
- {
- MockKeyPresses.Push (new ConsoleKeyInfo (
- (char)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask),
- ConsoleKeyMapping.GetConsoleKeyFromKey (key),
- key.HasFlag (Key.ShiftMask),
- key.HasFlag (Key.AltMask),
- key.HasFlag (Key.CtrlMask)));
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ public static int Read ()
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.InvalidOperationException:
- // The System.Console.In property is redirected from some stream other than the
- // console.
- ///
- ///
- ///
- public static ConsoleKeyInfo ReadKey ()
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // 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.
+ //
+ // Exceptions:
+ // T:System.InvalidOperationException:
+ // The System.Console.In property is redirected from some stream other than the
+ // console.
+ //[SecuritySafeCritical]
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // 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.ArgumentOutOfRangeException:
- // The number of characters in the next line of characters is greater than System.Int32.MaxValue.
- ///
- ///
- ///
- public static string ReadLine ()
- {
- throw new NotImplementedException ();
- }
+ ///
+ /// A stack of keypresses to return when ReadKey is called.
+ ///
+ public static Stack MockKeyPresses = new Stack ();
- //
- // Summary:
- // 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.IO.IOException:
- // An I/O error occurred.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static void ResetColor ()
- {
- BackgroundColor = _defaultBackgroundColor;
- ForegroundColor = _defaultForegroundColor;
- }
+ ///
+ /// Helper to push a onto .
+ ///
+ ///
+ public static void PushMockKeyPress (Key key)
+ {
+ MockKeyPresses.Push (new ConsoleKeyInfo (
+ (char)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask),
+ ConsoleKeyMapping.GetConsoleKeyFromKey (key),
+ key.HasFlag (Key.ShiftMask),
+ key.HasFlag (Key.AltMask),
+ key.HasFlag (Key.CtrlMask)));
+ }
- //
- // Summary:
- // 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.
- //
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static void SetBufferSize (int width, int height)
- {
- BufferWidth = width;
- BufferHeight = height;
- _buffer = new char [BufferWidth, BufferHeight];
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.InvalidOperationException:
+ // The System.Console.In property is redirected from some stream other than the
+ // console.
+ ///
+ ///
+ ///
+ public static ConsoleKeyInfo ReadKey ()
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Sets the position of the cursor.
- //
- // Parameters:
- // 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.
- //
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static void SetCursorPosition (int left, int top)
- {
- CursorLeft = left;
- CursorTop = top;
- WindowLeft = Math.Max (Math.Min (left, BufferWidth - WindowWidth), 0);
- WindowTop = Math.Max (Math.Min (top, BufferHeight - WindowHeight), 0);
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // 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.ArgumentOutOfRangeException:
+ // The number of characters in the next line of characters is greater than System.Int32.MaxValue.
+ ///
+ ///
+ ///
+ public static string ReadLine ()
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Sets the System.Console.Error property to the specified System.IO.TextWriter
- // object.
- //
- // Parameters:
- // newError:
- // A stream that is the new standard error output.
- //
- // Exceptions:
- // T:System.ArgumentNullException:
- // newError is null.
- //
- // T:System.Security.SecurityException:
- // The caller does not have the required permission.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static void SetError (TextWriter newError)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.IO.IOException:
+ // An I/O error occurred.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ public static void ResetColor ()
+ {
+ BackgroundColor = _defaultBackgroundColor;
+ ForegroundColor = _defaultForegroundColor;
+ }
- //
- // Summary:
- // Sets the System.Console.In property to the specified System.IO.TextReader object.
- //
- // Parameters:
- // newIn:
- // A stream that is the new standard input.
- //
- // Exceptions:
- // T:System.ArgumentNullException:
- // newIn is null.
- //
- // T:System.Security.SecurityException:
- // The caller does not have the required permission.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- public static void SetIn (TextReader newIn)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ public static void SetBufferSize (int width, int height)
+ {
+ BufferWidth = width;
+ BufferHeight = height;
+ _buffer = new char [BufferWidth, BufferHeight];
+ }
- //
- // Summary:
- // Sets the System.Console.Out property to the specified System.IO.TextWriter object.
- //
- // Parameters:
- // newOut:
- // A stream that is the new standard output.
- //
- // Exceptions:
- // T:System.ArgumentNullException:
- // newOut is null.
- //
- // T:System.Security.SecurityException:
- // The caller does not have the required permission.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- ///
- public static void SetOut (TextWriter newOut)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Sets the position of the cursor.
+ //
+ // Parameters:
+ // 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.
+ //
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ public static void SetCursorPosition (int left, int top)
+ {
+ CursorLeft = left;
+ CursorTop = top;
+ WindowLeft = Math.Max (Math.Min (left, BufferWidth - WindowWidth), 0);
+ WindowTop = Math.Max (Math.Min (top, BufferHeight - WindowHeight), 0);
+ }
- //
- // Summary:
- // 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.
- //
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- ///
- ///
- public static void SetWindowPosition (int left, int top)
- {
- WindowLeft = left;
- WindowTop = top;
- }
+ //
+ // Summary:
+ // Sets the System.Console.Error property to the specified System.IO.TextWriter
+ // object.
+ //
+ // Parameters:
+ // newError:
+ // A stream that is the new standard error output.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // newError is null.
+ //
+ // T:System.Security.SecurityException:
+ // The caller does not have the required permission.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ public static void SetError (TextWriter newError)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Sets the height and width of the console window to the specified values.
- //
- // Parameters:
- // width:
- // The width of the console window measured in columns.
- //
- // 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.Security.SecurityException:
- // The user does not have permission to perform this action.
- //
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[SecuritySafeCritical]
- ///
- ///
- ///
- ///
- ///
- public static void SetWindowSize (int width, int height)
- {
- WindowWidth = width;
- WindowHeight = height;
- }
+ //
+ // Summary:
+ // Sets the System.Console.In property to the specified System.IO.TextReader object.
+ //
+ // Parameters:
+ // newIn:
+ // A stream that is the new standard input.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // newIn is null.
+ //
+ // T:System.Security.SecurityException:
+ // The caller does not have the required permission.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ public static void SetIn (TextReader newIn)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified string value to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (string value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Sets the System.Console.Out property to the specified System.IO.TextWriter object.
+ //
+ // Parameters:
+ // newOut:
+ // A stream that is the new standard output.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // newOut is null.
+ //
+ // T:System.Security.SecurityException:
+ // The caller does not have the required permission.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ ///
+ public static void SetOut (TextWriter newOut)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the text representation of the specified object to the standard output
- // stream.
- //
- // Parameters:
- // value:
- // The value to write, or null.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (object value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void SetWindowPosition (int left, int top)
+ {
+ WindowLeft = left;
+ WindowTop = top;
+ }
- //
- // Summary:
- // Writes the text representation of the specified 64-bit unsigned integer value
- // to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[CLSCompliant (false)]
- ///
- ///
- ///
- ///
- public static void Write (ulong value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Sets the height and width of the console window to the specified values.
+ //
+ // Parameters:
+ // width:
+ // The width of the console window measured in columns.
+ //
+ // 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.Security.SecurityException:
+ // The user does not have permission to perform this action.
+ //
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[SecuritySafeCritical]
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void SetWindowSize (int width, int height)
+ {
+ WindowWidth = width;
+ WindowHeight = height;
+ }
- //
- // Summary:
- // Writes the text representation of the specified 64-bit signed integer value to
- // the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (long value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the specified string value to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (string value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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).
- //
- // arg0:
- // The first 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.ArgumentNullException:
- // format is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- ///
- public static void Write (string format, object arg0, object arg1)
- {
+ //
+ // Summary:
+ // Writes the text representation of the specified object to the standard output
+ // stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write, or null.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (object value)
+ {
+ if (value is Rune rune) {
+ Write ((char)rune.Value);
+ } else {
throw new NotImplementedException ();
}
+ }
- //
- // Summary:
- // Writes the text representation of the specified 32-bit signed integer value to
- // the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (int value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified 64-bit unsigned integer value
+ // to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[CLSCompliant (false)]
+ ///
+ ///
+ ///
+ ///
+ public static void Write (ulong value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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).
- //
- // arg0:
- // An object to write using format.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //
- // T:System.ArgumentNullException:
- // format is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- public static void Write (string format, object arg0)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified 64-bit signed integer value to
+ // the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (long value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the text representation of the specified 32-bit unsigned integer value
- // to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[CLSCompliant (false)]
- ///
- ///
- ///
- ///
- public static void Write (uint value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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).
+ //
+ // arg0:
+ // The first 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.ArgumentNullException:
+ // format is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Write (string format, object arg0, object arg1)
+ {
+ throw new NotImplementedException ();
+ }
- //[CLSCompliant (false)]
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static void Write (string format, object arg0, object arg1, object arg2, object arg3)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified 32-bit signed integer value to
+ // the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (int value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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).
- //
- // arg:
- // An array of objects to write using format.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //
- // T:System.ArgumentNullException:
- // format or arg is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- public static void Write (string format, params object [] arg)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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).
+ //
+ // arg0:
+ // An object to write using format.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //
+ // T:System.ArgumentNullException:
+ // format is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Write (string format, object arg0)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the text representation of the specified Boolean value to the standard
- // output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (bool value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified 32-bit unsigned integer value
+ // to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[CLSCompliant (false)]
+ ///
+ ///
+ ///
+ ///
+ public static void Write (uint value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified Unicode character value to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (char value)
- {
- _buffer [CursorLeft, CursorTop] = value;
- }
+ //[CLSCompliant (false)]
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Write (string format, object arg0, object arg1, object arg2, object arg3)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified array of Unicode characters to the standard output stream.
- //
- // Parameters:
- // buffer:
- // A Unicode character array.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (char [] buffer)
- {
- _buffer [CursorLeft, CursorTop] = (char)0;
- foreach (var ch in buffer) {
- _buffer [CursorLeft, CursorTop] += ch;
- }
- }
+ //
+ // Summary:
+ // 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).
+ //
+ // arg:
+ // An array of objects to write using format.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //
+ // T:System.ArgumentNullException:
+ // format or arg is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Write (string format, params object [] arg)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified subarray of Unicode characters to the standard output stream.
- //
- // Parameters:
- // buffer:
- // An array of Unicode characters.
- //
- // index:
- // The starting position in buffer.
- //
- // count:
- // The number of characters to write.
- //
- // Exceptions:
- // T:System.ArgumentNullException:
- // buffer is null.
- //
- // 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.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- ///
- ///
- public static void Write (char [] buffer, int index, int count)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified Boolean value to the standard
+ // output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (bool value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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).
- //
- // arg0:
- // The first object to write using format.
- //
- // arg1:
- // The second 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.ArgumentNullException:
- // format is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static void Write (string format, object arg0, object arg1, object arg2)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the specified Unicode character value to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (char value)
+ {
+ _buffer [CursorLeft, CursorTop] = value;
+ }
- //
- // Summary:
- // Writes the text representation of the specified System.Decimal value to the standard
- // output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (decimal value)
- {
- throw new NotImplementedException ();
+ //
+ // Summary:
+ // Writes the specified array of Unicode characters to the standard output stream.
+ //
+ // Parameters:
+ // buffer:
+ // A Unicode character array.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (char [] buffer)
+ {
+ _buffer [CursorLeft, CursorTop] = (char)0;
+ foreach (var ch in buffer) {
+ _buffer [CursorLeft, CursorTop] += ch;
}
+ }
- //
- // Summary:
- // Writes the text representation of the specified single-precision floating-point
- // value to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (float value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the specified subarray of Unicode characters to the standard output stream.
+ //
+ // Parameters:
+ // buffer:
+ // An array of Unicode characters.
+ //
+ // index:
+ // The starting position in buffer.
+ //
+ // count:
+ // The number of characters to write.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // buffer is null.
+ //
+ // 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.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Write (char [] buffer, int index, int count)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the text representation of the specified double-precision floating-point
- // value to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void Write (double value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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).
+ //
+ // arg0:
+ // The first object to write using format.
+ //
+ // arg1:
+ // The second 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.ArgumentNullException:
+ // format is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Write (string format, object arg0, object arg1, object arg2)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the current line terminator to the standard output stream.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- public static void WriteLine ()
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified System.Decimal value to the standard
+ // output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (decimal value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (float value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified single-precision floating-point
+ // value to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (float value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (int value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the text representation of the specified double-precision floating-point
+ // value to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void Write (double value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[CLSCompliant (false)]
- ///
- ///
- ///
- ///
- public static void WriteLine (uint value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the current line terminator to the standard output stream.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ public static void WriteLine ()
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (long value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (float value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //[CLSCompliant (false)]
- ///
- ///
- ///
- ///
- public static void WriteLine (ulong value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (int value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (object value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[CLSCompliant (false)]
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (uint value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified string value, followed by the current line terminator, to
- // the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (string value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (long value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // 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.
- //
- // Parameters:
- // format:
- // A composite format string (see Remarks).
- //
- // arg0:
- // An object to write using format.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //
- // T:System.ArgumentNullException:
- // format is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- public static void WriteLine (string format, object arg0)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //[CLSCompliant (false)]
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (ulong value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // 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.
- //
- // Parameters:
- // format:
- // A composite format string (see Remarks).
- //
- // arg0:
- // The first object to write using format.
- //
- // arg1:
- // The second 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.ArgumentNullException:
- // format is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static void WriteLine (string format, object arg0, object arg1, object arg2)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (object value)
+ {
+ throw new NotImplementedException ();
+ }
- //[CLSCompliant (false)]
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the specified string value, followed by the current line terminator, to
+ // the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (string value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // 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.
- //
- // Parameters:
- // format:
- // A composite format string (see Remarks).
- //
- // arg:
- // An array of objects to write using format.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- //
- // T:System.ArgumentNullException:
- // format or arg is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- public static void WriteLine (string format, params object [] arg)
- {
- throw new NotImplementedException ();
- }
+ //
+ // 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.
+ //
+ // Parameters:
+ // format:
+ // A composite format string (see Remarks).
+ //
+ // arg0:
+ // An object to write using format.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //
+ // T:System.ArgumentNullException:
+ // format is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (string format, object arg0)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // index:
- // The starting position in buffer.
- //
- // count:
- // The number of characters to write.
- //
- // Exceptions:
- // T:System.ArgumentNullException:
- // buffer is null.
- //
- // 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.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- ///
- ///
- public static void WriteLine (char [] buffer, int index, int count)
- {
- throw new NotImplementedException ();
- }
+ //
+ // 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.
+ //
+ // Parameters:
+ // format:
+ // A composite format string (see Remarks).
+ //
+ // arg0:
+ // The first object to write using format.
+ //
+ // arg1:
+ // The second 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.ArgumentNullException:
+ // format is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (string format, object arg0, object arg1, object arg2)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (decimal value)
- {
- throw new NotImplementedException ();
- }
+ //[CLSCompliant (false)]
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (string format, object arg0, object arg1, object arg2, object arg3)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified array of Unicode characters, followed by the current line
- // terminator, to the standard output stream.
- //
- // Parameters:
- // buffer:
- // A Unicode character array.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (char [] buffer)
- {
- throw new NotImplementedException ();
- }
+ //
+ // 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.
+ //
+ // Parameters:
+ // format:
+ // A composite format string (see Remarks).
+ //
+ // arg:
+ // An array of objects to write using format.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ //
+ // T:System.ArgumentNullException:
+ // format or arg is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (string format, params object [] arg)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // Writes the specified Unicode character, followed by the current line terminator,
- // value to the standard output stream.
- //
- // Parameters:
- // value:
- // The value to write.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (char value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // index:
+ // The starting position in buffer.
+ //
+ // count:
+ // The number of characters to write.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // buffer is null.
+ //
+ // 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.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (char [] buffer, int index, int count)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (bool value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (decimal value)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // 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.
- //
- // Parameters:
- // format:
- // A composite format string (see Remarks).
- //
- // arg0:
- // The first 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.ArgumentNullException:
- // format is null.
- //
- // T:System.FormatException:
- // The format specification in format is invalid.
- ///
- ///
- ///
- ///
- ///
- ///
- public static void WriteLine (string format, object arg0, object arg1)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the specified array of Unicode characters, followed by the current line
+ // terminator, to the standard output stream.
+ //
+ // Parameters:
+ // buffer:
+ // A Unicode character array.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (char [] buffer)
+ {
+ throw new NotImplementedException ();
+ }
- //
- // Summary:
- // 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.
- //
- // Exceptions:
- // T:System.IO.IOException:
- // An I/O error occurred.
- ///
- ///
- ///
- ///
- public static void WriteLine (double value)
- {
- throw new NotImplementedException ();
- }
+ //
+ // Summary:
+ // Writes the specified Unicode character, followed by the current line terminator,
+ // value to the standard output stream.
+ //
+ // Parameters:
+ // value:
+ // The value to write.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (char value)
+ {
+ throw new NotImplementedException ();
+ }
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (bool value)
+ {
+ throw new NotImplementedException ();
+ }
+
+ //
+ // 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.
+ //
+ // Parameters:
+ // format:
+ // A composite format string (see Remarks).
+ //
+ // arg0:
+ // The first 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.ArgumentNullException:
+ // format is null.
+ //
+ // T:System.FormatException:
+ // The format specification in format is invalid.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (string format, object arg0, object arg1)
+ {
+ throw new NotImplementedException ();
}
-}
+
+ //
+ // Summary:
+ // 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.
+ //
+ // Exceptions:
+ // T:System.IO.IOException:
+ // An I/O error occurred.
+ ///
+ ///
+ ///
+ ///
+ public static void WriteLine (double value)
+ {
+ throw new NotImplementedException ();
+ }
+
+}
\ No newline at end of file
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
index 2f7df9534d..5e1ae851f5 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
@@ -2,6 +2,7 @@
// FakeDriver.cs: A fake ConsoleDriver for unit tests.
//
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -11,717 +12,564 @@
// Alias Console to MockConsole so we don't accidentally use Console
using Console = Terminal.Gui.FakeConsole;
-
-namespace Terminal.Gui {
- ///
- /// Implements a mock ConsoleDriver for unit testing
- ///
- public class FakeDriver : ConsoleDriver {
+using Unix.Terminal;
+using static Terminal.Gui.WindowsConsole;
+using System.Drawing;
+
+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
- public class Behaviors {
+ public class Behaviors {
- public bool UseFakeClipboard { get; internal set; }
- public bool FakeClipboardAlwaysThrowsNotSupportedException { get; internal set; }
- public bool FakeClipboardIsSupportedAlwaysFalse { get; internal set; }
+ public bool UseFakeClipboard { get; internal set; }
+ public bool FakeClipboardAlwaysThrowsNotSupportedException { get; internal set; }
+ public bool FakeClipboardIsSupportedAlwaysFalse { get; internal set; }
- public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsNotSupportedException = false, bool fakeClipboardIsSupportedAlwaysTrue = false)
- {
- UseFakeClipboard = useFakeClipboard;
- FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException;
- FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue;
+ public Behaviors (bool useFakeClipboard = false, bool fakeClipboardAlwaysThrowsNotSupportedException = false, bool fakeClipboardIsSupportedAlwaysTrue = false)
+ {
+ UseFakeClipboard = useFakeClipboard;
+ FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException;
+ FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue;
- // double check usage is correct
- Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false);
- Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false);
- }
+ // double check usage is correct
+ Debug.Assert (useFakeClipboard == false && fakeClipboardAlwaysThrowsNotSupportedException == false);
+ Debug.Assert (useFakeClipboard == false && fakeClipboardIsSupportedAlwaysTrue == false);
}
+ }
- public static FakeDriver.Behaviors FakeBehaviors = new Behaviors ();
-
- int cols, rows, left, top;
- public override int Cols => cols;
- public override int Rows => rows;
- // 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 EnableConsoleScrolling { get; set; }
- private IClipboard clipboard = null;
- public override IClipboard Clipboard => clipboard;
-
- // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
- int [,,] contents;
- bool [] dirtyLine;
-
- ///
- /// Assists with testing, the format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
- ///
- public override int [,,] Contents => contents;
-
- //void UpdateOffscreen ()
- //{
- // int cols = Cols;
- // int rows = Rows;
-
- // contents = new int [rows, cols, 3];
- // for (int r = 0; r < rows; r++) {
- // 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;
- // }
- // }
- // dirtyLine = new bool [rows];
- // for (int row = 0; row < rows; row++)
- // dirtyLine [row] = true;
- //}
-
- static bool sync = false;
-
- public FakeDriver ()
- {
- if (FakeBehaviors.UseFakeClipboard) {
- clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse);
+ public static FakeDriver.Behaviors FakeBehaviors = new Behaviors ();
+
+ public FakeDriver ()
+ {
+ if (FakeBehaviors.UseFakeClipboard) {
+ Clipboard = new FakeClipboard (FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException, FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse);
+ } else {
+ if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
+ Clipboard = new WindowsClipboard ();
+ } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
+ Clipboard = new MacOSXClipboard ();
} else {
- if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
- clipboard = new WindowsClipboard ();
- } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
- clipboard = new MacOSXClipboard ();
+ if (CursesDriver.Is_WSL_Platform ()) {
+ Clipboard = new WSLClipboard ();
} else {
- if (CursesDriver.Is_WSL_Platform ()) {
- clipboard = new WSLClipboard ();
- } else {
- clipboard = new CursesClipboard ();
- }
+ Clipboard = new CursesClipboard ();
}
}
}
+ }
- 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;
+ public override void End ()
+ {
+ FakeConsole.ResetColor ();
+ FakeConsole.Clear ();
+ }
+
+ public override void Init (Action terminalResized)
+ {
+ FakeConsole.MockKeyPresses.Clear ();
+
+ TerminalResized = terminalResized;
+
+ Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;
+ Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT;
+ FakeConsole.Clear ();
+ ResizeScreen ();
+ // Call InitializeColorSchemes before UpdateOffScreen as it references Colors
+ CurrentAttribute = MakeColor (Color.White, Color.Black);
+ InitializeColorSchemes ();
+ ClearContents ();
+ }
- if (Clip.Contains (col, row)) {
- FakeConsole.CursorTop = row;
- FakeConsole.CursorLeft = col;
- needMove = false;
- } else {
- FakeConsole.CursorTop = Clip.Y;
- FakeConsole.CursorLeft = Clip.X;
- needMove = true;
- }
- }
- public override void AddRune (Rune rune)
- {
- rune = rune.MakePrintable ();
- var runeWidth = rune.GetColumns ();
- var validClip = IsValidContent (ccol, crow, Clip);
-
- if (validClip) {
- if (needMove) {
- //MockConsole.CursorLeft = ccol;
- //MockConsole.CursorTop = crow;
- needMove = false;
- }
- if (runeWidth == 0 && ccol > 0) {
- var r = contents [crow, ccol - 1, 0];
- var s = new string (new char [] { (char)r, (char)rune.Value });
- string sn;
- if (!s.IsNormalized ()) {
- sn = s.Normalize ();
- } else {
- sn = s;
+ public override void UpdateScreen ()
+ {
+ var savedRow = FakeConsole.CursorTop;
+ var savedCol = FakeConsole.CursorLeft;
+ var savedCursorVisible = FakeConsole.CursorVisible;
+
+ var top = 0;
+ var left = 0;
+ var rows = Rows;
+ var cols = Cols;
+ System.Text.StringBuilder output = new System.Text.StringBuilder ();
+ Attribute redrawAttr = new Attribute ();
+ var lastCol = -1;
+
+ for (var row = top; row < rows; row++) {
+ if (!_dirtyLines [row]) {
+ continue;
+ }
+
+ FakeConsole.CursorTop = row;
+ FakeConsole.CursorLeft = 0;
+
+ _dirtyLines [row] = false;
+ output.Clear ();
+ for (var col = left; col < cols; col++) {
+ lastCol = -1;
+ var outputWidth = 0;
+ for (; col < cols; col++) {
+ if (!Contents [row, col].IsDirty) {
+ if (output.Length > 0) {
+ WriteToConsole (output, ref lastCol, row, ref outputWidth);
+ } else if (lastCol == -1) {
+ lastCol = col;
+ }
+ if (lastCol + 1 < cols)
+ lastCol++;
+ continue;
}
- var c = sn [0];
- contents [crow, ccol - 1, 0] = c;
- contents [crow, ccol - 1, 1] = CurrentAttribute;
- contents [crow, ccol - 1, 2] = 1;
- } else {
- if (runeWidth < 2 && ccol > 0
- && ((Rune)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
-
- contents [crow, ccol - 1, 0] = (int)(uint)' ';
-
- } else if (runeWidth < 2 && ccol <= Clip.Right - 1
- && ((Rune)contents [crow, ccol, 0]).GetColumns () > 1) {
-
- contents [crow, ccol + 1, 0] = (int)(uint)' ';
- contents [crow, ccol + 1, 2] = 1;
+ if (lastCol == -1) {
+ lastCol = col;
+ }
+ Attribute attr = Contents [row, col].Attribute.Value;
+ // Performance: Only send the escape sequence if the attribute has changed.
+ if (attr != redrawAttr) {
+ redrawAttr = attr;
+ FakeConsole.ForegroundColor = (ConsoleColor)attr.Foreground;
+ FakeConsole.BackgroundColor = (ConsoleColor)attr.Background;
}
- if (runeWidth > 1 && ccol == Clip.Right - 1) {
- contents [crow, ccol, 0] = (int)(uint)' ';
- } else {
- contents [crow, ccol, 0] = (int)(uint)rune.Value;
+ outputWidth++;
+ var rune = (Rune)Contents [row, col].Runes [0];
+ output.Append (rune.ToString ());
+ if (rune.IsSurrogatePair () && rune.GetColumns () < 2) {
+ WriteToConsole (output, ref lastCol, row, ref outputWidth);
+ FakeConsole.CursorLeft--;
}
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 1;
-
- dirtyLine [crow] = true;
+ Contents [row, col].IsDirty = false;
}
- } else {
- needMove = true;
- }
-
- if (runeWidth < 0 || runeWidth > 0) {
- ccol++;
}
+ if (output.Length > 0) {
+ FakeConsole.CursorTop = row;
+ FakeConsole.CursorLeft = lastCol;
- if (runeWidth > 1) {
- if (validClip && ccol < Clip.Right) {
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 0;
+ foreach (var c in output.ToString ()) {
+ FakeConsole.Write (c);
}
- ccol++;
- }
-
- //if (ccol == Cols) {
- // ccol = 0;
- // if (crow + 1 < Rows)
- // crow++;
- //}
- if (sync) {
- UpdateScreen ();
}
}
+ FakeConsole.CursorTop = 0;
+ FakeConsole.CursorLeft = 0;
- public override void AddStr (string str)
- {
- foreach (var rune in str.EnumerateRunes ())
- AddRune (rune);
- }
+ //SetCursorVisibility (savedVisibitity);
- public override void End ()
+ void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
{
- FakeConsole.ResetColor ();
- FakeConsole.Clear ();
+ FakeConsole.CursorTop = row;
+ FakeConsole.CursorLeft = lastCol;
+ foreach (var c in output.ToString ()) {
+ FakeConsole.Write (c);
+ }
+
+ output.Clear ();
+ lastCol += outputWidth;
+ outputWidth = 0;
}
- public override Attribute MakeColor (Color foreground, Color background)
- {
- return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background);
- }
+ FakeConsole.CursorTop = savedRow;
+ FakeConsole.CursorLeft = savedCol;
+ FakeConsole.CursorVisible = savedCursorVisible;
+ }
- 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
- );
- }
+ public override void Refresh ()
+ {
+ UpdateScreen ();
+ UpdateCursor ();
+ }
- public override void Init (Action terminalResized)
- {
- FakeConsole.MockKeyPresses.Clear ();
-
- TerminalResized = terminalResized;
-
- cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;
- 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 ();
- }
+ #region Color Handling
- public override Attribute MakeAttribute (Color fore, Color back)
- {
- return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
- }
+ // Cache the list of ConsoleColor values.
+ private static readonly HashSet ConsoleColorValues = new HashSet (
+ Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c)
+ );
- int redrawColor = -1;
- void SetColor (int color)
- {
- redrawColor = color;
- IEnumerable values = Enum.GetValues (typeof (ConsoleColor))
- .OfType ()
- .Select (s => (int)s);
- if (values.Contains (color & 0xffff)) {
- FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff);
- }
- if (values.Contains ((color >> 16) & 0xffff)) {
- FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff);
- }
+ void SetColor (int color)
+ {
+ if (ConsoleColorValues.Contains (color & 0xffff)) {
+ FakeConsole.BackgroundColor = (ConsoleColor)(color & 0xffff);
}
+ if (ConsoleColorValues.Contains ((color >> 16) & 0xffff)) {
+ FakeConsole.ForegroundColor = (ConsoleColor)((color >> 16) & 0xffff);
+ }
+ }
- public override void UpdateScreen ()
- {
- int top = Top;
- int left = Left;
- int rows = Math.Min (FakeConsole.WindowHeight + top, Rows);
- int cols = Cols;
-
- var savedRow = FakeConsole.CursorTop;
- var savedCol = FakeConsole.CursorLeft;
- var savedCursorVisible = FakeConsole.CursorVisible;
- for (int row = top; row < rows; row++) {
- if (!dirtyLine [row])
- continue;
- dirtyLine [row] = false;
- for (int col = left; col < cols; col++) {
- FakeConsole.CursorTop = row;
- FakeConsole.CursorLeft = col;
- for (; col < cols; col++) {
- if (contents [row, col, 2] == 0) {
- FakeConsole.CursorLeft++;
- continue;
- }
-
- var color = contents [row, col, 1];
- if (color != redrawColor)
- SetColor (color);
+ ///
+ /// In the FakeDriver, colors are encoded as an int; same as NetDriver
+ /// Extracts the foreground and background colors from the encoded value.
+ /// Assumes a 4-bit encoded value for both foreground and background colors.
+ ///
+ internal override void GetColors (int value, out Color foreground, out Color background)
+ {
+ // Assume a 4-bit encoded value for both foreground and background colors.
+ foreground = (Color)((value >> 16) & 0xF);
+ background = (Color)(value & 0xF);
+ }
- Rune rune = (Rune)contents [row, col, 0];
- if (rune.DecodeSurrogatePair (out char [] spair)) {
- FakeConsole.Write (spair);
- } else {
- FakeConsole.Write ((char)rune.Value);
- }
- contents [row, col, 2] = 0;
- }
- }
- }
- FakeConsole.CursorTop = savedRow;
- FakeConsole.CursorLeft = savedCol;
- FakeConsole.CursorVisible = savedCursorVisible;
- }
+ ///
+ /// In the FakeDriver, colors are encoded as an int; same as NetDriver
+ /// However, the foreground color is stored in the most significant 16 bits,
+ /// and the background color is stored in the least significant 16 bits.
+ ///
+ public override Attribute MakeColor (Color foreground, Color background)
+ {
+ // Encode the colors into the int value.
+ return new Attribute (
+ value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff),
+ foreground: foreground,
+ background: background
+ );
+ }
- public override void Refresh ()
- {
- UpdateScreen ();
- UpdateCursor ();
- }
+ #endregion
- public override void SetAttribute (Attribute c)
- {
- base.SetAttribute (c);
+ public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+ {
+ if (consoleKeyInfo.Key != ConsoleKey.Packet) {
+ return consoleKeyInfo;
}
- public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
- {
- if (consoleKeyInfo.Key != ConsoleKey.Packet) {
- return consoleKeyInfo;
- }
+ var mod = consoleKeyInfo.Modifiers;
+ var shift = (mod & ConsoleModifiers.Shift) != 0;
+ var alt = (mod & ConsoleModifiers.Alt) != 0;
+ var control = (mod & ConsoleModifiers.Control) != 0;
- var mod = consoleKeyInfo.Modifiers;
- var shift = (mod & ConsoleModifiers.Shift) != 0;
- var alt = (mod & ConsoleModifiers.Alt) != 0;
- var control = (mod & ConsoleModifiers.Control) != 0;
+ var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
- var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
+ return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
+ }
- return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
+ Key MapKey (ConsoleKeyInfo keyInfo)
+ {
+ switch (keyInfo.Key) {
+ case ConsoleKey.Escape:
+ return MapKeyModifiers (keyInfo, Key.Esc);
+ case ConsoleKey.Tab:
+ return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
+ case ConsoleKey.Clear:
+ return MapKeyModifiers (keyInfo, Key.Clear);
+ case ConsoleKey.Home:
+ return MapKeyModifiers (keyInfo, Key.Home);
+ case ConsoleKey.End:
+ return MapKeyModifiers (keyInfo, Key.End);
+ case ConsoleKey.LeftArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorLeft);
+ case ConsoleKey.RightArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorRight);
+ case ConsoleKey.UpArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorUp);
+ case ConsoleKey.DownArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorDown);
+ case ConsoleKey.PageUp:
+ return MapKeyModifiers (keyInfo, Key.PageUp);
+ case ConsoleKey.PageDown:
+ return MapKeyModifiers (keyInfo, Key.PageDown);
+ case ConsoleKey.Enter:
+ return MapKeyModifiers (keyInfo, Key.Enter);
+ case ConsoleKey.Spacebar:
+ return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
+ case ConsoleKey.Backspace:
+ return MapKeyModifiers (keyInfo, Key.Backspace);
+ case ConsoleKey.Delete:
+ return MapKeyModifiers (keyInfo, Key.DeleteChar);
+ case ConsoleKey.Insert:
+ return MapKeyModifiers (keyInfo, Key.InsertChar);
+ case ConsoleKey.PrintScreen:
+ return MapKeyModifiers (keyInfo, Key.PrintScreen);
+
+ case ConsoleKey.Oem1:
+ case ConsoleKey.Oem2:
+ case ConsoleKey.Oem3:
+ case ConsoleKey.Oem4:
+ case ConsoleKey.Oem5:
+ case ConsoleKey.Oem6:
+ case ConsoleKey.Oem7:
+ case ConsoleKey.Oem8:
+ case ConsoleKey.Oem102:
+ case ConsoleKey.OemPeriod:
+ case ConsoleKey.OemComma:
+ case ConsoleKey.OemPlus:
+ case ConsoleKey.OemMinus:
+ if (keyInfo.KeyChar == 0) {
+ return Key.Unknown;
+ }
+
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+
+ 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.CtrlMask) | ((uint)Key.A + delta));
+ }
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
+ }
+ if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0) {
+ return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta));
+ } else {
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+ }
+ return (Key)((uint)keyInfo.KeyChar);
}
-
- Key MapKey (ConsoleKeyInfo keyInfo)
- {
- switch (keyInfo.Key) {
- case ConsoleKey.Escape:
- return MapKeyModifiers (keyInfo, Key.Esc);
- case ConsoleKey.Tab:
- return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
- case ConsoleKey.Clear:
- return MapKeyModifiers (keyInfo, Key.Clear);
- case ConsoleKey.Home:
- return MapKeyModifiers (keyInfo, Key.Home);
- case ConsoleKey.End:
- return MapKeyModifiers (keyInfo, Key.End);
- case ConsoleKey.LeftArrow:
- return MapKeyModifiers (keyInfo, Key.CursorLeft);
- case ConsoleKey.RightArrow:
- return MapKeyModifiers (keyInfo, Key.CursorRight);
- case ConsoleKey.UpArrow:
- return MapKeyModifiers (keyInfo, Key.CursorUp);
- case ConsoleKey.DownArrow:
- return MapKeyModifiers (keyInfo, Key.CursorDown);
- case ConsoleKey.PageUp:
- return MapKeyModifiers (keyInfo, Key.PageUp);
- case ConsoleKey.PageDown:
- return MapKeyModifiers (keyInfo, Key.PageDown);
- case ConsoleKey.Enter:
- return MapKeyModifiers (keyInfo, Key.Enter);
- case ConsoleKey.Spacebar:
- return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
- case ConsoleKey.Backspace:
- return MapKeyModifiers (keyInfo, Key.Backspace);
- case ConsoleKey.Delete:
- return MapKeyModifiers (keyInfo, Key.DeleteChar);
- case ConsoleKey.Insert:
- return MapKeyModifiers (keyInfo, Key.InsertChar);
- case ConsoleKey.PrintScreen:
- return MapKeyModifiers (keyInfo, Key.PrintScreen);
-
- case ConsoleKey.Oem1:
- case ConsoleKey.Oem2:
- case ConsoleKey.Oem3:
- case ConsoleKey.Oem4:
- case ConsoleKey.Oem5:
- case ConsoleKey.Oem6:
- case ConsoleKey.Oem7:
- case ConsoleKey.Oem8:
- case ConsoleKey.Oem102:
- case ConsoleKey.OemPeriod:
- case ConsoleKey.OemComma:
- case ConsoleKey.OemPlus:
- case ConsoleKey.OemMinus:
- if (keyInfo.KeyChar == 0)
- return Key.Unknown;
-
- return (Key)((uint)keyInfo.KeyChar);
+ if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
+ var delta = key - ConsoleKey.D0;
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
}
-
- 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.CtrlMask) | ((uint)Key.A + delta));
- }
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
- return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
- }
- if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
- }
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0) {
- return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta));
- } else {
- return (Key)((uint)keyInfo.KeyChar);
- }
- }
- return (Key)((uint)keyInfo.KeyChar);
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
}
- if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
- var delta = key - ConsoleKey.D0;
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
- return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
- }
- if (keyInfo.Modifiers == ConsoleModifiers.Control) {
- return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
- }
- if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+ if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
}
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
- }
- }
- return (Key)((uint)keyInfo.KeyChar);
}
- if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
- var delta = key - ConsoleKey.F1;
- if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
- }
-
- return (Key)((uint)Key.F1 + delta);
- }
- if (keyInfo.KeyChar != 0) {
- return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+ if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
+ var delta = key - ConsoleKey.F1;
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
}
- return (Key)(0xffffffff);
+ return (Key)((uint)Key.F1 + delta);
}
-
- KeyModifiers keyModifiers;
-
- private 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;
+ if (keyInfo.KeyChar != 0) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
}
- Action keyDownHandler;
- Action keyHandler;
- Action keyUpHandler;
- private CursorVisibility savedCursorVisibility;
+ return (Key)(0xffffffff);
+ }
- public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
- {
- this.keyDownHandler = keyDownHandler;
- this.keyHandler = keyHandler;
- this.keyUpHandler = keyUpHandler;
+ KeyModifiers keyModifiers;
- // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
- (mainLoop.Driver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey);
+ private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+ {
+ Key keyMod = new Key ();
+ if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
+ keyMod = Key.ShiftMask;
}
-
- void ProcessInput (ConsoleKeyInfo consoleKey)
- {
- if (consoleKey.Key == ConsoleKey.Packet) {
- consoleKey = FromVKPacketToKConsoleKeyInfo (consoleKey);
- }
- keyModifiers = new KeyModifiers ();
- if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Shift)) {
- keyModifiers.Shift = true;
- }
- if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Alt)) {
- keyModifiers.Alt = true;
- }
- if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Control)) {
- keyModifiers.Ctrl = true;
- }
- var map = MapKey (consoleKey);
- if (map == (Key)0xffffffff) {
- if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- keyDownHandler (new KeyEvent (map, keyModifiers));
- keyUpHandler (new KeyEvent (map, keyModifiers));
- }
- return;
- }
-
- keyDownHandler (new KeyEvent (map, keyModifiers));
- keyHandler (new KeyEvent (map, keyModifiers));
- keyUpHandler (new KeyEvent (map, keyModifiers));
+ if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
+ keyMod |= Key.CtrlMask;
+ }
+ if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
+ keyMod |= Key.AltMask;
}
- ///
- public override bool GetCursorVisibility (out CursorVisibility visibility)
- {
- visibility = FakeConsole.CursorVisible
- ? CursorVisibility.Default
- : CursorVisibility.Invisible;
+ return keyMod != Key.Null ? keyMod | key : key;
+ }
- return FakeConsole.CursorVisible;
- }
+ Action _keyDownHandler;
+ Action _keyHandler;
+ Action _keyUpHandler;
+ private CursorVisibility _savedCursorVisibility;
- ///
- public override bool SetCursorVisibility (CursorVisibility visibility)
- {
- savedCursorVisibility = visibility;
- return FakeConsole.CursorVisible = visibility == CursorVisibility.Default;
- }
+ public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
+ {
+ _keyDownHandler = keyDownHandler;
+ _keyHandler = keyHandler;
+ _keyUpHandler = keyUpHandler;
- ///
- public override bool EnsureCursorVisibility ()
- {
- if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) {
- GetCursorVisibility (out CursorVisibility cursorVisibility);
- savedCursorVisibility = cursorVisibility;
- SetCursorVisibility (CursorVisibility.Invisible);
- return false;
- }
+ // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
+ (mainLoop.MainLoopDriver as FakeMainLoop).KeyPressed += (consoleKey) => ProcessInput (consoleKey);
+ }
- SetCursorVisibility (savedCursorVisibility);
- return FakeConsole.CursorVisible;
+ void ProcessInput (ConsoleKeyInfo consoleKey)
+ {
+ if (consoleKey.Key == ConsoleKey.Packet) {
+ consoleKey = FromVKPacketToKConsoleKeyInfo (consoleKey);
}
-
- public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
- {
- ProcessInput (new ConsoleKeyInfo (keyChar, key, shift, alt, control));
+ keyModifiers = new KeyModifiers ();
+ if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Shift)) {
+ keyModifiers.Shift = true;
}
-
- public void SetBufferSize (int width, int height)
- {
- FakeConsole.SetBufferSize (width, height);
- cols = width;
- rows = height;
- if (!EnableConsoleScrolling) {
- SetWindowSize (width, height);
- }
- ProcessResize ();
+ if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Alt)) {
+ keyModifiers.Alt = true;
}
-
- public void SetWindowSize (int width, int height)
- {
- FakeConsole.SetWindowSize (width, height);
- if (!EnableConsoleScrolling) {
- if (width != cols || height != rows) {
- SetBufferSize (width, height);
- cols = width;
- rows = height;
- }
- }
- ProcessResize ();
+ if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Control)) {
+ keyModifiers.Ctrl = true;
}
-
- public void SetWindowPosition (int left, int top)
- {
- 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) {
- this.left = 0;
- this.top = 0;
+ var map = MapKey (consoleKey);
+ if (map == (Key)0xffffffff) {
+ if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ _keyDownHandler (new KeyEvent (map, keyModifiers));
+ _keyUpHandler (new KeyEvent (map, keyModifiers));
}
- FakeConsole.SetWindowPosition (this.left, this.top);
+ return;
}
- void ProcessResize ()
- {
- ResizeScreen ();
- UpdateOffScreen ();
- TerminalResized?.Invoke ();
- }
+ _keyDownHandler (new KeyEvent (map, keyModifiers));
+ _keyHandler (new KeyEvent (map, keyModifiers));
+ _keyUpHandler (new KeyEvent (map, keyModifiers));
+ }
- public override void ResizeScreen ()
- {
- if (!EnableConsoleScrolling) {
- if (FakeConsole.WindowHeight > 0) {
- // Can raise an exception while is still resizing.
- try {
-#pragma warning disable CA1416
- FakeConsole.CursorTop = 0;
- FakeConsole.CursorLeft = 0;
- FakeConsole.WindowTop = 0;
- FakeConsole.WindowLeft = 0;
-#pragma warning restore CA1416
- } catch (System.IO.IOException) {
- return;
- } catch (ArgumentOutOfRangeException) {
- return;
- }
- }
- } else {
- try {
-#pragma warning disable CA1416
- FakeConsole.WindowLeft = Math.Max (Math.Min (left, Cols - FakeConsole.WindowWidth), 0);
- FakeConsole.WindowTop = Math.Max (Math.Min (top, Rows - FakeConsole.WindowHeight), 0);
-#pragma warning restore CA1416
- } catch (Exception) {
- return;
- }
- }
+ ///
+ public override bool GetCursorVisibility (out CursorVisibility visibility)
+ {
+ visibility = FakeConsole.CursorVisible
+ ? CursorVisibility.Default
+ : CursorVisibility.Invisible;
- Clip = new Rect (0, 0, Cols, Rows);
- }
+ return FakeConsole.CursorVisible;
+ }
- public override void UpdateOffScreen ()
- {
- contents = new int [Rows, Cols, 3];
- dirtyLine = new bool [Rows];
+ ///
+ public override bool SetCursorVisibility (CursorVisibility visibility)
+ {
+ _savedCursorVisibility = visibility;
+ return FakeConsole.CursorVisible = visibility == CursorVisibility.Default;
+ }
- // Can raise an exception while is still resizing.
- try {
- for (int row = 0; row < rows; row++) {
- for (int c = 0; c < cols; c++) {
- contents [row, c, 0] = ' ';
- contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
- contents [row, c, 2] = 0;
- dirtyLine [row] = true;
- }
- }
- } catch (IndexOutOfRangeException) { }
+ ///
+ public override bool EnsureCursorVisibility ()
+ {
+ if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) {
+ GetCursorVisibility (out CursorVisibility cursorVisibility);
+ _savedCursorVisibility = cursorVisibility;
+ SetCursorVisibility (CursorVisibility.Invisible);
+ return false;
}
- public override bool GetColors (int value, out Color foreground, out Color background)
- {
- bool hasColor = false;
- foreground = default;
- background = default;
- IEnumerable values = Enum.GetValues (typeof (ConsoleColor))
- .OfType ()
- .Select (s => (int)s);
- if (values.Contains (value & 0xffff)) {
- hasColor = true;
- background = (Color)(ConsoleColor)(value & 0xffff);
- }
- if (values.Contains ((value >> 16) & 0xffff)) {
- hasColor = true;
- foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
- }
- return hasColor;
- }
+ SetCursorVisibility (_savedCursorVisibility);
+ return FakeConsole.CursorVisible;
+ }
- #region Unused
- public override void UpdateCursor ()
- {
- if (!EnsureCursorVisibility ())
- return;
+ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
+ {
+ ProcessInput (new ConsoleKeyInfo (keyChar, key, shift, alt, control));
+ }
- // Prevents the exception of size changing during resizing.
- try {
- if (ccol >= 0 && ccol < FakeConsole.BufferWidth && crow >= 0 && crow < FakeConsole.BufferHeight) {
- FakeConsole.SetCursorPosition (ccol, crow);
- }
- } catch (System.IO.IOException) {
- } catch (ArgumentOutOfRangeException) {
- }
- }
+ public void SetBufferSize (int width, int height)
+ {
+ FakeConsole.SetBufferSize (width, height);
+ Cols = width;
+ Rows = height;
+ SetWindowSize (width, height);
+ ProcessResize ();
+ }
- public override void StartReportingMouseMoves ()
- {
+ public void SetWindowSize (int width, int height)
+ {
+ FakeConsole.SetWindowSize (width, height);
+ if (width != Cols || height != Rows) {
+ SetBufferSize (width, height);
+ Cols = width;
+ Rows = height;
}
+ ProcessResize ();
+ }
- public override void StopReportingMouseMoves ()
- {
+ public void SetWindowPosition (int left, int top)
+ {
+ if (Left > 0 || Top > 0) {
+ Left = 0;
+ Top = 0;
}
+ FakeConsole.SetWindowPosition (Left, Top);
+ }
- public override void Suspend ()
- {
- }
+ void ProcessResize ()
+ {
+ ResizeScreen ();
+ ClearContents ();
+ TerminalResized?.Invoke ();
+ }
- public override void SetColors (ConsoleColor foreground, ConsoleColor background)
- {
+ public virtual void ResizeScreen ()
+ {
+ if (FakeConsole.WindowHeight > 0) {
+ // Can raise an exception while is still resizing.
+ try {
+ FakeConsole.CursorTop = 0;
+ FakeConsole.CursorLeft = 0;
+ FakeConsole.WindowTop = 0;
+ FakeConsole.WindowLeft = 0;
+ } catch (System.IO.IOException) {
+ return;
+ } catch (ArgumentOutOfRangeException) {
+ return;
+ }
}
- public override void SetColors (short foregroundColorId, short backgroundColorId)
- {
- throw new NotImplementedException ();
- }
+ Clip = new Rect (0, 0, Cols, Rows);
+ }
- public override void CookMouse ()
- {
+ public override void UpdateCursor ()
+ {
+ if (!EnsureCursorVisibility ()) {
+ return;
}
- public override void UncookMouse ()
- {
+ // Prevents the exception of size changing during resizing.
+ try {
+ // BUGBUG: Why is this using BufferWidth/Height and now Cols/Rows?
+ if (Col >= 0 && Col < FakeConsole.BufferWidth && Row >= 0 && Row < FakeConsole.BufferHeight) {
+ FakeConsole.SetCursorPosition (Col, Row);
+ }
+ } catch (System.IO.IOException) {
+ } catch (ArgumentOutOfRangeException) {
}
+ }
- #endregion
+ #region Not Implemented
+ public override void Suspend ()
+ {
+ throw new NotImplementedException ();
+ }
+ #endregion
- public class FakeClipboard : ClipboardBase {
- public Exception FakeException = null;
+ public class FakeClipboard : ClipboardBase {
+ public Exception FakeException = null;
- string contents = string.Empty;
+ string _contents = string.Empty;
- bool isSupportedAlwaysFalse = false;
+ bool _isSupportedAlwaysFalse = false;
- public override bool IsSupported => !isSupportedAlwaysFalse;
+ public override bool IsSupported => !_isSupportedAlwaysFalse;
- public FakeClipboard (bool fakeClipboardThrowsNotSupportedException = false, bool isSupportedAlwaysFalse = false)
- {
- this.isSupportedAlwaysFalse = isSupportedAlwaysFalse;
- if (fakeClipboardThrowsNotSupportedException) {
- FakeException = new NotSupportedException ("Fake clipboard exception");
- }
+ public FakeClipboard (bool fakeClipboardThrowsNotSupportedException = false, bool isSupportedAlwaysFalse = false)
+ {
+ _isSupportedAlwaysFalse = isSupportedAlwaysFalse;
+ if (fakeClipboardThrowsNotSupportedException) {
+ FakeException = new NotSupportedException ("Fake clipboard exception");
}
+ }
- protected override string GetClipboardDataImpl ()
- {
- if (FakeException != null) {
- throw FakeException;
- }
- return contents;
+ protected override string GetClipboardDataImpl ()
+ {
+ if (FakeException != null) {
+ throw FakeException;
}
+ return _contents;
+ }
- protected override void SetClipboardDataImpl (string text)
- {
- if (FakeException != null) {
- throw FakeException;
- }
- contents = text;
+ protected override void SetClipboardDataImpl (string text)
+ {
+ if (text == null) {
+ throw new ArgumentNullException (nameof (text));
+ }
+ if (FakeException != null) {
+ throw FakeException;
}
+ _contents = text;
}
+ }
#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
index 06a08a0698..69b9686603 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs
@@ -1,98 +1,37 @@
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 = () => ;
+namespace Terminal.Gui;
- ///
- /// Invoked when a Key is pressed.
- ///
- public Action KeyPressed;
+internal class FakeMainLoop : IMainLoopDriver {
- ///
- /// Creates an instance of the FakeMainLoop. is not used.
- ///
- ///
- public FakeMainLoop (ConsoleDriver consoleDriver = null)
- {
- // consoleDriver is not needed/used in FakeConsole
- }
-
- void MockKeyReader ()
- {
- while (true) {
- waitForProbe.WaitOne ();
- keyResult = FakeConsole.ReadKey (true);
- keyReady.Set ();
- }
- }
-
- void IMainLoopDriver.Setup (MainLoop mainLoop)
- {
- this.mainLoop = mainLoop;
- Thread readThread = new Thread (MockKeyReader);
- readThread.Start ();
- }
-
- void IMainLoopDriver.Wakeup ()
- {
- }
-
- bool IMainLoopDriver.EventsPending (bool wait)
- {
- keyResult = null;
- waitForProbe.Set ();
-
- if (CheckTimers (wait, out var waitTimeout)) {
- return true;
- }
+ public Action KeyPressed;
- keyReady.WaitOne (waitTimeout);
- return keyResult.HasValue;
- }
-
- 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;
- }
+ public FakeMainLoop (ConsoleDriver consoleDriver = null)
+ {
+ // No implementation needed for FakeMainLoop
+ }
- if (!wait)
- waitTimeout = 0;
+ public void Setup (MainLoop mainLoop)
+ {
+ // No implementation needed for FakeMainLoop
+ }
- int ic;
- lock (mainLoop.idleHandlers) {
- ic = mainLoop.idleHandlers.Count;
- }
+ public void Wakeup ()
+ {
+ // No implementation needed for FakeMainLoop
+ }
- return ic > 0;
- }
+ public bool EventsPending (bool wait)
+ {
+ // Always return true for FakeMainLoop
+ return true;
+ }
- void IMainLoopDriver.Iteration ()
- {
- if (keyResult.HasValue) {
- KeyPressed?.Invoke (keyResult.Value);
- keyResult = null;
- }
+ public void Iteration ()
+ {
+ if (FakeConsole.MockKeyPresses.Count > 0) {
+ KeyPressed?.Invoke (FakeConsole.MockKeyPresses.Pop ());
}
}
-}
\ No newline at end of file
+}
+
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
index 6d9fa4930a..2253765275 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
@@ -13,1638 +13,1361 @@
using System.Threading.Tasks;
using System.Text;
-namespace Terminal.Gui {
- internal class NetWinVTConsole {
- IntPtr InputHandle, OutputHandle, ErrorHandle;
- uint originalInputConsoleMode, originalOutputConsoleMode, originalErrorConsoleMode;
+namespace Terminal.Gui;
+internal class NetWinVTConsole {
+ IntPtr _inputHandle, _outputHandle, _errorHandle;
+ uint _originalInputConsoleMode, _originalOutputConsoleMode, _originalErrorConsoleMode;
- public NetWinVTConsole ()
- {
- InputHandle = GetStdHandle (STD_INPUT_HANDLE);
- if (!GetConsoleMode (InputHandle, out uint mode)) {
- throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}.");
- }
- originalInputConsoleMode = mode;
- if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) {
- mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
- if (!SetConsoleMode (InputHandle, mode)) {
- throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}.");
- }
- }
-
- OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
- if (!GetConsoleMode (OutputHandle, out mode)) {
- throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}.");
- }
- originalOutputConsoleMode = mode;
- if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
- mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
- if (!SetConsoleMode (OutputHandle, mode)) {
- throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}.");
- }
+ public NetWinVTConsole ()
+ {
+ _inputHandle = GetStdHandle (STD_INPUT_HANDLE);
+ if (!GetConsoleMode (_inputHandle, out uint mode)) {
+ throw new ApplicationException ($"Failed to get input console mode, error code: {GetLastError ()}.");
+ }
+ _originalInputConsoleMode = mode;
+ if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) < ENABLE_VIRTUAL_TERMINAL_INPUT) {
+ mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+ if (!SetConsoleMode (_inputHandle, mode)) {
+ throw new ApplicationException ($"Failed to set input console mode, error code: {GetLastError ()}.");
}
+ }
- ErrorHandle = GetStdHandle (STD_ERROR_HANDLE);
- if (!GetConsoleMode (ErrorHandle, out mode)) {
- throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
- }
- originalErrorConsoleMode = mode;
- if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
- mode |= DISABLE_NEWLINE_AUTO_RETURN;
- if (!SetConsoleMode (ErrorHandle, mode)) {
- throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
- }
+ _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
+ if (!GetConsoleMode (_outputHandle, out mode)) {
+ throw new ApplicationException ($"Failed to get output console mode, error code: {GetLastError ()}.");
+ }
+ _originalOutputConsoleMode = mode;
+ if ((mode & (ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
+ if (!SetConsoleMode (_outputHandle, mode)) {
+ throw new ApplicationException ($"Failed to set output console mode, error code: {GetLastError ()}.");
}
}
- public void Cleanup ()
- {
- if (!SetConsoleMode (InputHandle, originalInputConsoleMode)) {
- throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}.");
- }
- if (!SetConsoleMode (OutputHandle, originalOutputConsoleMode)) {
- throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}.");
- }
- if (!SetConsoleMode (ErrorHandle, originalErrorConsoleMode)) {
- throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}.");
+ _errorHandle = GetStdHandle (STD_ERROR_HANDLE);
+ if (!GetConsoleMode (_errorHandle, out mode)) {
+ throw new ApplicationException ($"Failed to get error console mode, error code: {GetLastError ()}.");
+ }
+ _originalErrorConsoleMode = mode;
+ if ((mode & (DISABLE_NEWLINE_AUTO_RETURN)) < DISABLE_NEWLINE_AUTO_RETURN) {
+ mode |= DISABLE_NEWLINE_AUTO_RETURN;
+ if (!SetConsoleMode (_errorHandle, mode)) {
+ throw new ApplicationException ($"Failed to set error console mode, error code: {GetLastError ()}.");
}
}
+ }
- const int STD_INPUT_HANDLE = -10;
- const int STD_OUTPUT_HANDLE = -11;
- const int STD_ERROR_HANDLE = -12;
-
- // Input modes.
- const uint ENABLE_PROCESSED_INPUT = 1;
- const uint ENABLE_LINE_INPUT = 2;
- const uint ENABLE_ECHO_INPUT = 4;
- const uint ENABLE_WINDOW_INPUT = 8;
- const uint ENABLE_MOUSE_INPUT = 16;
- const uint ENABLE_INSERT_MODE = 32;
- const uint ENABLE_QUICK_EDIT_MODE = 64;
- const uint ENABLE_EXTENDED_FLAGS = 128;
- const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
-
- // Output modes.
- const uint ENABLE_PROCESSED_OUTPUT = 1;
- const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2;
- const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
- const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
- const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
-
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern IntPtr GetStdHandle (int nStdHandle);
-
- [DllImport ("kernel32.dll")]
- static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
-
- [DllImport ("kernel32.dll")]
- static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
-
- [DllImport ("kernel32.dll")]
- static extern uint GetLastError ();
+ public void Cleanup ()
+ {
+ if (!SetConsoleMode (_inputHandle, _originalInputConsoleMode)) {
+ throw new ApplicationException ($"Failed to restore input console mode, error code: {GetLastError ()}.");
+ }
+ if (!SetConsoleMode (_outputHandle, _originalOutputConsoleMode)) {
+ throw new ApplicationException ($"Failed to restore output console mode, error code: {GetLastError ()}.");
+ }
+ if (!SetConsoleMode (_errorHandle, _originalErrorConsoleMode)) {
+ throw new ApplicationException ($"Failed to restore error console mode, error code: {GetLastError ()}.");
+ }
}
- internal class NetEvents {
- ManualResetEventSlim inputReady = new ManualResetEventSlim (false);
- ManualResetEventSlim waitForStart = new ManualResetEventSlim (false);
- ManualResetEventSlim winChange = new ManualResetEventSlim (false);
- Queue inputResultQueue = new Queue ();
- ConsoleDriver consoleDriver;
- volatile ConsoleKeyInfo [] cki = null;
- static volatile bool isEscSeq;
- int lastWindowHeight;
- bool stopTasks;
+ const int STD_INPUT_HANDLE = -10;
+ const int STD_OUTPUT_HANDLE = -11;
+ const int STD_ERROR_HANDLE = -12;
+
+ // Input modes.
+ const uint ENABLE_PROCESSED_INPUT = 1;
+ const uint ENABLE_LINE_INPUT = 2;
+ const uint ENABLE_ECHO_INPUT = 4;
+ const uint ENABLE_WINDOW_INPUT = 8;
+ const uint ENABLE_MOUSE_INPUT = 16;
+ const uint ENABLE_INSERT_MODE = 32;
+ const uint ENABLE_QUICK_EDIT_MODE = 64;
+ const uint ENABLE_EXTENDED_FLAGS = 128;
+ const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512;
+
+ // Output modes.
+ const uint ENABLE_PROCESSED_OUTPUT = 1;
+ const uint ENABLE_WRAP_AT_EOL_OUTPUT = 2;
+ const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
+ const uint DISABLE_NEWLINE_AUTO_RETURN = 8;
+ const uint ENABLE_LVB_GRID_WORLDWIDE = 10;
+
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern IntPtr GetStdHandle (int nStdHandle);
+
+ [DllImport ("kernel32.dll")]
+ static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
+
+ [DllImport ("kernel32.dll")]
+ static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
+
+ [DllImport ("kernel32.dll")]
+ static extern uint GetLastError ();
+}
+
+internal class NetEvents {
+ ManualResetEventSlim _inputReady = new ManualResetEventSlim (false);
+ ManualResetEventSlim _waitForStart = new ManualResetEventSlim (false);
+ ManualResetEventSlim _winChange = new ManualResetEventSlim (false);
+ Queue _inputResultQueue = new Queue ();
+ ConsoleDriver _consoleDriver;
+ volatile ConsoleKeyInfo [] _cki = null;
+ volatile static bool _isEscSeq;
+ bool _stopTasks;
#if PROCESS_REQUEST
- bool neededProcessRequest;
+ bool _neededProcessRequest;
#endif
- public bool IsTerminalWithOptions { get; set; }
- public EscSeqReqProc EscSeqReqProc { get; } = new EscSeqReqProc ();
+ public EscSeqRequests EscSeqRequests { get; } = new EscSeqRequests ();
- public NetEvents (ConsoleDriver consoleDriver)
- {
- if (consoleDriver == null) {
- throw new ArgumentNullException ("Console driver instance must be provided.");
- }
- this.consoleDriver = consoleDriver;
- Task.Run (ProcessInputResultQueue);
- Task.Run (CheckWinChange);
- }
+ public NetEvents (ConsoleDriver consoleDriver)
+ {
+ _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
+ Task.Run (ProcessInputResultQueue);
+ Task.Run (CheckWindowSizeChange);
+ }
- internal void StopTasks ()
- {
- stopTasks = true;
- }
+ internal void StopTasks ()
+ {
+ _stopTasks = true;
+ }
- public InputResult? ReadConsoleInput ()
- {
- while (true) {
- if (stopTasks) {
- return null;
- }
- waitForStart.Set ();
- winChange.Set ();
+ public InputResult? ReadConsoleInput ()
+ {
+ while (true) {
+ if (_stopTasks) {
+ return null;
+ }
+ _waitForStart.Set ();
+ _winChange.Set ();
- if (inputResultQueue.Count == 0) {
- inputReady.Wait ();
- inputReady.Reset ();
- }
+ if (_inputResultQueue.Count == 0) {
+ _inputReady.Wait ();
+ _inputReady.Reset ();
+ }
#if PROCESS_REQUEST
- neededProcessRequest = false;
+ _neededProcessRequest = false;
#endif
- if (inputResultQueue.Count > 0) {
- return inputResultQueue.Dequeue ();
- }
- }
- }
-
- void ProcessInputResultQueue ()
- {
- while (true) {
- waitForStart.Wait ();
- waitForStart.Reset ();
-
- if (inputResultQueue.Count == 0) {
- GetConsoleKey ();
- }
-
- inputReady.Set ();
+ if (_inputResultQueue.Count > 0) {
+ return _inputResultQueue.Dequeue ();
}
}
+ }
- 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;
+ void ProcessInputResultQueue ()
+ {
+ while (true) {
+ _waitForStart.Wait ();
+ _waitForStart.Reset ();
+
+ if (_inputResultQueue.Count == 0) {
+ 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) continue;
+ ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
+ _cki = null;
+ _isEscSeq = false;
+ break;
+ } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && _isEscSeq && _cki != null) {
+ if (_cki != null) {
+ ProcessRequestResponse (ref newConsoleKeyInfo, ref key, _cki, ref mod);
+ _cki = null;
+ }
+ break;
+ } else {
+ _inputResultQueue.Enqueue (new InputResult {
+ EventType = EventType.Key,
+ ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
+ });
+ _isEscSeq = false;
break;
}
- } else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq && cki != null) {
- DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod);
- cki = null;
- break;
- } else {
- GetConsoleInputType (consoleKeyInfo);
- isEscSeq = false;
- break;
}
}
- }
- void CheckWinChange ()
- {
- while (true) {
- if (stopTasks) {
- return;
- }
- winChange.Wait ();
- winChange.Reset ();
- WaitWinChange ();
- inputReady.Set ();
- }
+ _inputReady.Set ();
}
+ }
- void WaitWinChange ()
+ void CheckWindowSizeChange ()
+ {
+ void RequestWindowSize ()
{
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 (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;
- }
- if (IsWinChanged (
- Math.Max (Console.WindowHeight, 0),
- Math.Max (Console.WindowWidth, 0),
- buffHeight,
- buffWidth)) {
+ // Wait for a while then check if screen has changed sizes
+ Task.Delay (500).Wait ();
- return;
- }
- break;
- case true:
- //Request the size of the text area in characters.
- EscSeqReqProc.Add ("t");
- Console.Out.Write ("\x1b[18t");
- break;
+ if (_stopTasks) {
+ return;
}
- }
- }
-
- 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;
+ 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 {
- if (winWidth != consoleDriver.Cols || winHeight != lastWindowHeight
- || buffWidth != consoleDriver.Cols || buffHeight != consoleDriver.Rows) {
+ if (EnqueueWindowSizeEvent (
+ Math.Max (Console.WindowHeight, 0),
+ Math.Max (Console.WindowWidth, 0),
+ buffHeight,
+ buffWidth)) {
- lastWindowHeight = Math.Max (winHeight, 0);
- GetWindowSizeEvent (new Size (winWidth, lastWindowHeight));
- return true;
+ return;
}
}
- return false;
}
- void GetWindowSizeEvent (Size size)
- {
- WindowSizeEvent windowSizeEvent = new WindowSizeEvent () {
- Size = size
- };
-
- inputResultQueue.Enqueue (new InputResult () {
- EventType = EventType.WindowSize,
- WindowSizeEvent = windowSizeEvent
- });
- }
-
- void GetConsoleInputType (ConsoleKeyInfo consoleKeyInfo)
- {
- InputResult inputResult = new InputResult {
- EventType = EventType.Key
- };
- MouseEvent mouseEvent = new MouseEvent ();
- ConsoleKeyInfo newConsoleKeyInfo = EscSeqUtils.GetConsoleInputKey (consoleKeyInfo);
- if (inputResult.EventType == EventType.Key) {
- inputResult.ConsoleKeyInfo = newConsoleKeyInfo;
- } else {
- inputResult.MouseEvent = mouseEvent;
- }
-
- inputResultQueue.Enqueue (inputResult);
- }
-
- void DecodeEscSeq (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod)
- {
- 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);
-
- if (isKeyMouse) {
- foreach (var mf in mouseFlags) {
- GetMouseEvent (MapMouseFlags (mf), pos);
- }
+ while (true) {
+ if (_stopTasks) {
return;
- } else if (isReq) {
- GetRequestEvent (c1Control, code, values, terminating);
- return;
- }
- InputResult inputResult = new InputResult {
- EventType = EventType.Key,
- ConsoleKeyInfo = newConsoleKeyInfo
- };
-
- inputResultQueue.Enqueue (inputResult);
- }
-
- void ProcessContinuousButtonPressed (MouseFlags mouseFlag, Point pos)
- {
- GetMouseEvent (MapMouseFlags (mouseFlag), pos);
- }
-
- MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
- {
- 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 MouseFlags.Button1Released:
- mbs |= MouseButtonState.Button1Released;
- break;
- case MouseFlags.Button1Clicked:
- mbs |= MouseButtonState.Button1Clicked;
- break;
- case MouseFlags.Button1DoubleClicked:
- mbs |= MouseButtonState.Button1DoubleClicked;
- break;
- case MouseFlags.Button1TripleClicked:
- mbs |= MouseButtonState.Button1TripleClicked;
- break;
- case MouseFlags.Button2Pressed:
- mbs |= MouseButtonState.Button2Pressed;
- break;
- case MouseFlags.Button2Released:
- mbs |= MouseButtonState.Button2Released;
- break;
- case MouseFlags.Button2Clicked:
- mbs |= MouseButtonState.Button2Clicked;
- break;
- case MouseFlags.Button2DoubleClicked:
- mbs |= MouseButtonState.Button2DoubleClicked;
- break;
- case MouseFlags.Button2TripleClicked:
- mbs |= MouseButtonState.Button2TripleClicked;
- break;
- case MouseFlags.Button3Pressed:
- mbs |= MouseButtonState.Button3Pressed;
- break;
- case MouseFlags.Button3Released:
- mbs |= MouseButtonState.Button3Released;
- break;
- case MouseFlags.Button3Clicked:
- mbs |= MouseButtonState.Button3Clicked;
- break;
- case MouseFlags.Button3DoubleClicked:
- mbs |= MouseButtonState.Button3DoubleClicked;
- break;
- case MouseFlags.Button3TripleClicked:
- mbs |= MouseButtonState.Button3TripleClicked;
- break;
- case MouseFlags.WheeledUp:
- mbs |= MouseButtonState.ButtonWheeledUp;
- break;
- case MouseFlags.WheeledDown:
- mbs |= MouseButtonState.ButtonWheeledDown;
- break;
- case MouseFlags.WheeledLeft:
- mbs |= MouseButtonState.ButtonWheeledLeft;
- break;
- case MouseFlags.WheeledRight:
- mbs |= MouseButtonState.ButtonWheeledRight;
- break;
- case MouseFlags.Button4Pressed:
- mbs |= MouseButtonState.Button4Pressed;
- break;
- case MouseFlags.Button4Released:
- mbs |= MouseButtonState.Button4Released;
- break;
- case MouseFlags.Button4Clicked:
- mbs |= MouseButtonState.Button4Clicked;
- break;
- case MouseFlags.Button4DoubleClicked:
- mbs |= MouseButtonState.Button4DoubleClicked;
- break;
- case MouseFlags.Button4TripleClicked:
- mbs |= MouseButtonState.Button4TripleClicked;
- break;
- 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;
- }
- }
}
- return mbs;
+ _winChange.Wait ();
+ _winChange.Reset ();
+ RequestWindowSize ();
+ _inputReady.Set ();
}
+ }
- Point lastCursorPosition;
+ ///
+ /// Enqueue a window size event if the window size has changed.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth)
+ {
+ if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows) return false;
+ var w = Math.Max (winWidth, 0);
+ var h = Math.Max (winHeight, 0);
+ _inputResultQueue.Enqueue (new InputResult () {
+ EventType = EventType.WindowSize,
+ WindowSizeEvent = new WindowSizeEvent () {
+ Size = new Size (w, h)
+ }
+ });
+ return true;
+ }
- 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 {
- 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);
- }
+ // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event)
+ void ProcessRequestResponse (ref ConsoleKeyInfo newConsoleKeyInfo, ref ConsoleKey key, ConsoleKeyInfo [] cki, ref ConsoleModifiers mod)
+ {
+ // isMouse is true if it's CSI<, false otherwise
+ EscSeqUtils.DecodeEscSeq (EscSeqRequests, ref newConsoleKeyInfo, ref key, cki, ref mod,
+ out var c1Control, out var code, out var values, out var terminating,
+ out var isMouse, out var mouseFlags,
+ out var pos, out var isReq,
+ (f, p) => HandleMouseEvent (MapMouseFlags (f), p));
+
+ if (isMouse) {
+ foreach (var mf in mouseFlags) {
+ HandleMouseEvent (MapMouseFlags (mf), pos);
+ }
+ return;
+ } else if (isReq) {
+ HandleRequestResponseEvent (c1Control, code, values, terminating);
+ return;
+ }
+ HandleKeyboardEvent (newConsoleKeyInfo);
+ }
- if (c1Control == "CSI" && values.Length == 2
- && values [0] == "1" && values [1] == "0") {
- // Reports CSI?1;0c ("VT101 with No Options")
- IsTerminalWithOptions = false;
- } else {
- IsTerminalWithOptions = true;
- }
- 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));
+ MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
+ {
+ 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 MouseFlags.Button1Released:
+ mbs |= MouseButtonState.Button1Released;
+ break;
+ case MouseFlags.Button1Clicked:
+ mbs |= MouseButtonState.Button1Clicked;
+ break;
+ case MouseFlags.Button1DoubleClicked:
+ mbs |= MouseButtonState.Button1DoubleClicked;
+ break;
+ case MouseFlags.Button1TripleClicked:
+ mbs |= MouseButtonState.Button1TripleClicked;
+ break;
+ case MouseFlags.Button2Pressed:
+ mbs |= MouseButtonState.Button2Pressed;
+ break;
+ case MouseFlags.Button2Released:
+ mbs |= MouseButtonState.Button2Released;
+ break;
+ case MouseFlags.Button2Clicked:
+ mbs |= MouseButtonState.Button2Clicked;
+ break;
+ case MouseFlags.Button2DoubleClicked:
+ mbs |= MouseButtonState.Button2DoubleClicked;
+ break;
+ case MouseFlags.Button2TripleClicked:
+ mbs |= MouseButtonState.Button2TripleClicked;
break;
- default:
- SetRequestedEvent (c1Control, code, values, terminating);
+ case MouseFlags.Button3Pressed:
+ mbs |= MouseButtonState.Button3Pressed;
+ break;
+ case MouseFlags.Button3Released:
+ mbs |= MouseButtonState.Button3Released;
+ break;
+ case MouseFlags.Button3Clicked:
+ mbs |= MouseButtonState.Button3Clicked;
+ break;
+ case MouseFlags.Button3DoubleClicked:
+ mbs |= MouseButtonState.Button3DoubleClicked;
+ break;
+ case MouseFlags.Button3TripleClicked:
+ mbs |= MouseButtonState.Button3TripleClicked;
+ break;
+ case MouseFlags.WheeledUp:
+ mbs |= MouseButtonState.ButtonWheeledUp;
+ break;
+ case MouseFlags.WheeledDown:
+ mbs |= MouseButtonState.ButtonWheeledDown;
+ break;
+ case MouseFlags.WheeledLeft:
+ mbs |= MouseButtonState.ButtonWheeledLeft;
+ break;
+ case MouseFlags.WheeledRight:
+ mbs |= MouseButtonState.ButtonWheeledRight;
+ break;
+ case MouseFlags.Button4Pressed:
+ mbs |= MouseButtonState.Button4Pressed;
+ break;
+ case MouseFlags.Button4Released:
+ mbs |= MouseButtonState.Button4Released;
+ break;
+ case MouseFlags.Button4Clicked:
+ mbs |= MouseButtonState.Button4Clicked;
+ break;
+ case MouseFlags.Button4DoubleClicked:
+ mbs |= MouseButtonState.Button4DoubleClicked;
+ break;
+ case MouseFlags.Button4TripleClicked:
+ mbs |= MouseButtonState.Button4TripleClicked;
+ break;
+ 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;
}
- break;
- default:
- SetRequestedEvent (c1Control, code, values, terminating);
- break;
}
-
- inputReady.Set ();
}
+ return mbs;
+ }
- void SetRequestedEvent (string c1Control, string code, string [] values, string terminating)
- {
- EventType eventType = EventType.RequestResponse;
- var requestRespEv = new RequestResponseEvent () {
- ResultTuple = (c1Control, code, values, terminating)
- };
- inputResultQueue.Enqueue (new InputResult () {
- EventType = eventType,
- RequestResponseEvent = requestRespEv
- });
- }
+ Point _lastCursorPosition;
- void GetMouseEvent (MouseButtonState buttonState, Point pos)
- {
- MouseEvent mouseEvent = new MouseEvent () {
- Position = pos,
- ButtonState = buttonState,
+ void HandleRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
+ {
+ switch (terminating) {
+ // BUGBUG: I can't find where we send a request for cursor position (ESC[?6n), so I'm not sure if this is needed.
+ case EscSeqUtils.CSI_RequestCursorPositionReport_Terminator:
+ Point point = new Point {
+ X = int.Parse (values [1]) - 1,
+ Y = int.Parse (values [0]) - 1
};
-
- inputResultQueue.Enqueue (new InputResult () {
- EventType = EventType.Mouse,
- MouseEvent = mouseEvent
- });
-
- inputReady.Set ();
- }
-
- public enum EventType {
- Key = 1,
- Mouse = 2,
- WindowSize = 3,
- WindowPosition = 4,
- RequestResponse = 5
- }
-
- [Flags]
- public enum MouseButtonState {
- Button1Pressed = 0x1,
- Button1Released = 0x2,
- Button1Clicked = 0x4,
- Button1DoubleClicked = 0x8,
- Button1TripleClicked = 0x10,
- Button2Pressed = 0x20,
- Button2Released = 0x40,
- Button2Clicked = 0x80,
- Button2DoubleClicked = 0x100,
- Button2TripleClicked = 0x200,
- Button3Pressed = 0x400,
- Button3Released = 0x800,
- Button3Clicked = 0x1000,
- Button3DoubleClicked = 0x2000,
- Button3TripleClicked = 0x4000,
- ButtonWheeledUp = 0x8000,
- ButtonWheeledDown = 0x10000,
- ButtonWheeledLeft = 0x20000,
- ButtonWheeledRight = 0x40000,
- Button4Pressed = 0x80000,
- Button4Released = 0x100000,
- Button4Clicked = 0x200000,
- Button4DoubleClicked = 0x400000,
- Button4TripleClicked = 0x800000,
- ButtonShift = 0x1000000,
- ButtonCtrl = 0x2000000,
- ButtonAlt = 0x4000000,
- ReportMousePosition = 0x8000000,
- AllEvents = -1
- }
-
- public struct MouseEvent {
- public Point Position;
- public MouseButtonState ButtonState;
- }
-
- public struct WindowSizeEvent {
- public Size Size;
- }
-
- public struct WindowPositionEvent {
- public int Top;
- public int Left;
- 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;
- }
- }
-
- internal class NetDriver : ConsoleDriver {
- const int COLOR_BLACK = 30;
- const int COLOR_RED = 31;
- const int COLOR_GREEN = 32;
- const int COLOR_YELLOW = 33;
- const int COLOR_BLUE = 34;
- const int COLOR_MAGENTA = 35;
- const int COLOR_CYAN = 36;
- const int COLOR_WHITE = 37;
- const int COLOR_BRIGHT_BLACK = 90;
- const int COLOR_BRIGHT_RED = 91;
- const int COLOR_BRIGHT_GREEN = 92;
- const int COLOR_BRIGHT_YELLOW = 93;
- const int COLOR_BRIGHT_BLUE = 94;
- const int COLOR_BRIGHT_MAGENTA = 95;
- const int COLOR_BRIGHT_CYAN = 96;
- const int COLOR_BRIGHT_WHITE = 97;
-
- int cols, rows, left, top;
-
- public override int Cols => cols;
- public override int Rows => rows;
- public override int Left => left;
- public override int Top => top;
- public override bool EnableConsoleScrolling { get; set; }
- public NetWinVTConsole NetWinConsole { get; }
- public bool IsWinPlatform { get; }
- public override IClipboard Clipboard { get; }
- public override int [,,] Contents => contents;
-
- int largestBufferHeight;
-
- public NetDriver ()
- {
- var p = Environment.OSVersion.Platform;
- if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
- IsWinPlatform = true;
- NetWinConsole = new NetWinVTConsole ();
- }
- if (IsWinPlatform) {
- Clipboard = new WindowsClipboard ();
- } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
- Clipboard = new MacOSXClipboard ();
+ if (_lastCursorPosition.Y != point.Y) {
+ _lastCursorPosition = point;
+ var eventType = EventType.WindowPosition;
+ var winPositionEv = new WindowPositionEvent () {
+ CursorPosition = point
+ };
+ _inputResultQueue.Enqueue (new InputResult () {
+ EventType = eventType,
+ WindowPositionEvent = winPositionEv
+ });
} else {
- if (CursesDriver.Is_WSL_Platform ()) {
- Clipboard = new WSLClipboard ();
- } else {
- Clipboard = new CursesClipboard ();
- }
- }
- }
-
- // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
- int [,,] contents;
- bool [] dirtyLine;
-
- static bool sync = false;
-
- // 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;
- }
-
- public override void AddRune (Rune rune)
- {
- if (contents.Length != Rows * Cols * 3) {
return;
}
- rune = rune.MakePrintable ();
- var runeWidth = rune.GetColumns ();
- var validClip = IsValidContent (ccol, crow, Clip);
-
- if (validClip) {
- if (runeWidth == 0 && ccol > 0) {
- var r = contents [crow, ccol - 1, 0];
- var s = new string (new char [] { (char)r, (char)rune.Value });
- string sn;
- if (!s.IsNormalized ()) {
- sn = s.Normalize ();
- } else {
- sn = s;
- }
- var c = sn [0];
- contents [crow, ccol - 1, 0] = c;
- contents [crow, ccol - 1, 1] = CurrentAttribute;
- contents [crow, ccol - 1, 2] = 1;
-
- } else {
- if (runeWidth < 2 && ccol > 0
- && ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
-
- contents [crow, ccol - 1, 0] = (int)(uint)' ';
-
- } else if (runeWidth < 2 && ccol <= Clip.Right - 1
- && ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
-
- contents [crow, ccol + 1, 0] = (int)(uint)' ';
- contents [crow, ccol + 1, 2] = 1;
-
- }
- if (runeWidth > 1 && ccol == Clip.Right - 1) {
- contents [crow, ccol, 0] = (int)(uint)' ';
- } else {
- contents [crow, ccol, 0] = (int)(uint)rune.Value;
- }
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 1;
-
- }
- dirtyLine [crow] = true;
- }
+ break;
- if (runeWidth < 0 || runeWidth > 0) {
- ccol++;
- }
-
- if (runeWidth > 1) {
- if (validClip && ccol < Clip.Right) {
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 0;
- }
- ccol++;
- }
-
- if (sync) {
- UpdateScreen ();
+ case EscSeqUtils.CSI_ReportTerminalSizeInChars_Terminator:
+ switch (values [0]) {
+ case EscSeqUtils.CSI_ReportTerminalSizeInChars_ResponseValue:
+ EnqueueWindowSizeEvent (
+ 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:
+ EnqueueRequestResponseEvent (c1Control, code, values, terminating);
+ break;
}
+ break;
+ default:
+ EnqueueRequestResponseEvent (c1Control, code, values, terminating);
+ break;
}
- public override void AddStr (string str)
- {
- foreach (var rune in str.EnumerateRunes ())
- AddRune (rune);
- }
+ _inputReady.Set ();
+ }
- public override void End ()
- {
- mainLoop.netEvents.StopTasks ();
+ void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
+ {
+ EventType eventType = EventType.RequestResponse;
+ var requestRespEv = new RequestResponseEvent () {
+ ResultTuple = (c1Control, code, values, terminating)
+ };
+ _inputResultQueue.Enqueue (new InputResult () {
+ EventType = eventType,
+ RequestResponseEvent = requestRespEv
+ });
+ }
- if (IsWinPlatform) {
- NetWinConsole.Cleanup ();
- }
+ void HandleMouseEvent (MouseButtonState buttonState, Point pos)
+ {
+ MouseEvent mouseEvent = new MouseEvent () {
+ Position = pos,
+ ButtonState = buttonState,
+ };
- StopReportingMouseMoves ();
- Console.ResetColor ();
+ _inputResultQueue.Enqueue (new InputResult () {
+ EventType = EventType.Mouse,
+ MouseEvent = mouseEvent
+ });
- //Disable alternative screen buffer.
- Console.Out.Write ("\x1b[?1049l");
+ _inputReady.Set ();
+ }
- //Set cursor key to cursor.
- Console.Out.Write ("\x1b[?25h");
+ public enum EventType {
+ Key = 1,
+ Mouse = 2,
+ WindowSize = 3,
+ WindowPosition = 4,
+ RequestResponse = 5
+ }
- Console.Out.Close ();
- }
+ [Flags]
+ public enum MouseButtonState {
+ Button1Pressed = 0x1,
+ Button1Released = 0x2,
+ Button1Clicked = 0x4,
+ Button1DoubleClicked = 0x8,
+ Button1TripleClicked = 0x10,
+ Button2Pressed = 0x20,
+ Button2Released = 0x40,
+ Button2Clicked = 0x80,
+ Button2DoubleClicked = 0x100,
+ Button2TripleClicked = 0x200,
+ Button3Pressed = 0x400,
+ Button3Released = 0x800,
+ Button3Clicked = 0x1000,
+ Button3DoubleClicked = 0x2000,
+ Button3TripleClicked = 0x4000,
+ ButtonWheeledUp = 0x8000,
+ ButtonWheeledDown = 0x10000,
+ ButtonWheeledLeft = 0x20000,
+ ButtonWheeledRight = 0x40000,
+ Button4Pressed = 0x80000,
+ Button4Released = 0x100000,
+ Button4Clicked = 0x200000,
+ Button4DoubleClicked = 0x400000,
+ Button4TripleClicked = 0x800000,
+ ButtonShift = 0x1000000,
+ ButtonCtrl = 0x2000000,
+ ButtonAlt = 0x4000000,
+ ReportMousePosition = 0x8000000,
+ AllEvents = -1
+ }
- public override Attribute MakeColor (Color foreground, Color background)
- {
- return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background);
- }
+ public struct MouseEvent {
+ public Point Position;
+ public MouseButtonState ButtonState;
+ }
- 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
- );
- }
+ public struct WindowSizeEvent {
+ public Size Size;
+ }
- public override void Init (Action terminalResized)
- {
- TerminalResized = terminalResized;
+ public struct WindowPositionEvent {
+ public int Top;
+ public int Left;
+ public Point CursorPosition;
+ }
- //Enable alternative screen buffer.
- Console.Out.Write ("\x1b[?1049h");
+ public struct RequestResponseEvent {
+ public (string c1Control, string code, string [] values, string terminating) ResultTuple;
+ }
- //Set cursor key to application.
- Console.Out.Write ("\x1b[?25l");
+ public struct InputResult {
+ public EventType EventType;
+ public ConsoleKeyInfo ConsoleKeyInfo;
+ public MouseEvent MouseEvent;
+ public WindowSizeEvent WindowSizeEvent;
+ public WindowPositionEvent WindowPositionEvent;
+ public RequestResponseEvent RequestResponseEvent;
+ }
- Console.TreatControlCAsInput = true;
+ void HandleKeyboardEvent (ConsoleKeyInfo cki)
+ {
+ InputResult inputResult = new InputResult {
+ EventType = EventType.Key,
+ ConsoleKeyInfo = cki
+ };
- if (EnableConsoleScrolling) {
- largestBufferHeight = Console.BufferHeight;
- } else {
- largestBufferHeight = Console.WindowHeight;
- }
+ _inputResultQueue.Enqueue (inputResult);
+ }
+}
+
+internal class NetDriver : ConsoleDriver {
+ const int COLOR_BLACK = 30;
+ const int COLOR_RED = 31;
+ const int COLOR_GREEN = 32;
+ const int COLOR_YELLOW = 33;
+ const int COLOR_BLUE = 34;
+ const int COLOR_MAGENTA = 35;
+ const int COLOR_CYAN = 36;
+ const int COLOR_WHITE = 37;
+ const int COLOR_BRIGHT_BLACK = 90;
+ const int COLOR_BRIGHT_RED = 91;
+ const int COLOR_BRIGHT_GREEN = 92;
+ const int COLOR_BRIGHT_YELLOW = 93;
+ const int COLOR_BRIGHT_BLUE = 94;
+ const int COLOR_BRIGHT_MAGENTA = 95;
+ const int COLOR_BRIGHT_CYAN = 96;
+ const int COLOR_BRIGHT_WHITE = 97;
+
+ public NetWinVTConsole NetWinConsole { get; private set; }
+ public bool IsWinPlatform { get; private set; }
+
+ int _largestBufferHeight;
+
+ public NetDriver ()
+ {
+ }
- cols = Console.WindowWidth;
- rows = largestBufferHeight;
+ public override void End ()
+ {
+ _mainLoop._netEvents.StopTasks ();
- CurrentAttribute = MakeColor (Color.White, Color.Black);
- InitalizeColorSchemes ();
+ if (IsWinPlatform) {
+ NetWinConsole.Cleanup ();
+ }
- CurrentAttribute = MakeColor (Color.White, Color.Black);
- InitalizeColorSchemes ();
+ StopReportingMouseMoves ();
+ Console.ResetColor ();
- ResizeScreen ();
- UpdateOffScreen ();
+ //Disable alternative screen buffer.
+ Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndActivateAltBufferWithBackscroll);
- StartReportingMouseMoves ();
- }
+ //Set cursor key to cursor.
+ Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
- public override void ResizeScreen ()
- {
- if (!EnableConsoleScrolling) {
- if (Console.WindowHeight > 0) {
- // 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;
- Console.WindowTop = 0;
- Console.WindowLeft = 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 ();
- }
- } else {
- Console.Out.Write ($"\x1b[8;{Rows};{Cols}t");
- }
- }
- } else {
- 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;
- if (Console.WindowHeight > Rows) {
- Console.SetWindowSize (Cols, Rows);
- }
- Console.SetBufferSize (Cols, Rows);
-#pragma warning restore CA1416
- } catch (System.IO.IOException) {
- setClip ();
- } catch (ArgumentOutOfRangeException) {
- setClip ();
- }
- }
- } else {
- Console.Out.Write ($"\x1b[30;{Rows};{Cols}t");
- }
- }
- setClip ();
+ Console.Out.Close ();
+ }
- void setClip ()
- {
- Clip = new Rect (0, 0, Cols, Rows);
+ public override void Init (Action terminalResized)
+ {
+ var p = Environment.OSVersion.Platform;
+ if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
+ IsWinPlatform = true;
+ try {
+ NetWinConsole = new NetWinVTConsole ();
+ } catch (ApplicationException) {
+ // Likely running as a unit test, or in a non-interactive session.
}
}
-
- public override void UpdateOffScreen ()
- {
- contents = new int [Rows, Cols, 3];
- dirtyLine = new bool [Rows];
-
- lock (contents) {
- // Can raise an exception while is still resizing.
- try {
- for (int row = 0; row < rows; row++) {
- for (int c = 0; c < cols; c++) {
- contents [row, c, 0] = ' ';
- contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
- contents [row, c, 2] = 0;
- dirtyLine [row] = true;
- }
- }
- } catch (IndexOutOfRangeException) { }
+ if (IsWinPlatform) {
+ Clipboard = new WindowsClipboard ();
+ } else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
+ Clipboard = new MacOSXClipboard ();
+ } else {
+ if (CursesDriver.Is_WSL_Platform ()) {
+ Clipboard = new WSLClipboard ();
+ } else {
+ Clipboard = new CursesClipboard ();
}
}
- public override Attribute MakeAttribute (Color fore, Color back)
- {
- return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
- }
+ TerminalResized = terminalResized;
- public override void Refresh ()
- {
- UpdateScreen ();
- UpdateCursor ();
- }
-
- public override void UpdateScreen ()
- {
- if (winChanging || Console.WindowHeight < 1 || contents.Length != Rows * Cols * 3
- || (!EnableConsoleScrolling && Rows != Console.WindowHeight)
- || (EnableConsoleScrolling && Rows != largestBufferHeight)) {
- return;
- }
-
- int top = 0;
- int left = 0;
- int rows = Rows;
- int cols = Cols;
- System.Text.StringBuilder output = new System.Text.StringBuilder ();
- int redrawAttr = -1;
- var lastCol = -1;
-
- GetCursorVisibility (out CursorVisibility savedVisibitity);
- SetCursorVisibility (CursorVisibility.Invisible);
-
- for (int row = top; row < rows; row++) {
- if (Console.WindowHeight < 1) {
- return;
- }
- if (!dirtyLine [row]) {
- continue;
- }
- if (!SetCursorPosition (0, row)) {
- return;
- }
- dirtyLine [row] = false;
- output.Clear ();
- for (int col = left; col < cols; col++) {
- lastCol = -1;
- var outputWidth = 0;
- for (; col < cols; col++) {
- if (contents [row, col, 2] == 0) {
- if (output.Length > 0) {
- SetCursorPosition (lastCol, row);
- Console.Write (output);
- output.Clear ();
- lastCol += outputWidth;
- outputWidth = 0;
- } else if (lastCol == -1) {
- lastCol = col;
- }
- if (lastCol + 1 < cols)
- lastCol++;
- continue;
- }
+ if (NetWinConsole != null) {
+ //Enable alternative screen buffer.
+ Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
- if (lastCol == -1)
- lastCol = col;
+ //Set cursor key to application.
+ Console.Out.Write (EscSeqUtils.CSI_HideCursor);
- var attr = contents [row, col, 1];
- if (attr != redrawAttr) {
- redrawAttr = attr;
- output.Append (WriteAttributes (attr));
- }
- outputWidth++;
- var rune = (Rune)contents [row, col, 0];
- char [] spair;
- if (rune.DecodeSurrogatePair (out spair)) {
- output.Append (spair);
- } else {
- output.Append ((char)rune.Value);
- }
- contents [row, col, 2] = 0;
- }
- }
- if (output.Length > 0) {
- SetCursorPosition (lastCol, row);
- Console.Write (output);
- }
- }
- SetCursorPosition (0, 0);
- SetCursorVisibility (savedVisibitity);
- }
+ Console.TreatControlCAsInput = true;
- void SetVirtualCursorPosition (int col, int row)
- {
- Console.Out.Write ($"\x1b[{row + 1};{col + 1}H");
+ Cols = Console.WindowWidth;
+ Rows = Console.WindowHeight;
+ } else {
+ // Simluate
+ Cols = 80;
+ Rows = 25;
+ _largestBufferHeight = Rows;
}
- System.Text.StringBuilder WriteAttributes (int attr)
- {
- const string CSI = "\x1b[";
- int bg = 0;
- int fg = 0;
- System.Text.StringBuilder sb = new System.Text.StringBuilder ();
-
- IEnumerable values = Enum.GetValues (typeof (ConsoleColor))
- .OfType ()
- .Select (s => (int)s);
- if (values.Contains (attr & 0xffff)) {
- bg = MapColors ((ConsoleColor)(attr & 0xffff), false);
- }
- if (values.Contains ((attr >> 16) & 0xffff)) {
- fg = MapColors ((ConsoleColor)((attr >> 16) & 0xffff));
- }
- sb.Append ($"{CSI}{bg};{fg}m");
+ ResizeScreen ();
+ ClearContents ();
+ CurrentAttribute = MakeColor (Color.White, Color.Black);
+ InitializeColorSchemes ();
- return sb;
- }
+ StartReportingMouseMoves ();
+ }
- int MapColors (ConsoleColor color, bool isForeground = true)
- {
- switch (color) {
- case ConsoleColor.Black:
- return isForeground ? COLOR_BLACK : COLOR_BLACK + 10;
- case ConsoleColor.DarkBlue:
- return isForeground ? COLOR_BLUE : COLOR_BLUE + 10;
- case ConsoleColor.DarkGreen:
- return isForeground ? COLOR_GREEN : COLOR_GREEN + 10;
- case ConsoleColor.DarkCyan:
- return isForeground ? COLOR_CYAN : COLOR_CYAN + 10;
- case ConsoleColor.DarkRed:
- return isForeground ? COLOR_RED : COLOR_RED + 10;
- case ConsoleColor.DarkMagenta:
- return isForeground ? COLOR_MAGENTA : COLOR_MAGENTA + 10;
- case ConsoleColor.DarkYellow:
- return isForeground ? COLOR_YELLOW : COLOR_YELLOW + 10;
- case ConsoleColor.Gray:
- return isForeground ? COLOR_WHITE : COLOR_WHITE + 10;
- case ConsoleColor.DarkGray:
- return isForeground ? COLOR_BRIGHT_BLACK : COLOR_BRIGHT_BLACK + 10;
- case ConsoleColor.Blue:
- return isForeground ? COLOR_BRIGHT_BLUE : COLOR_BRIGHT_BLUE + 10;
- case ConsoleColor.Green:
- return isForeground ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_GREEN + 10;
- case ConsoleColor.Cyan:
- return isForeground ? COLOR_BRIGHT_CYAN : COLOR_BRIGHT_CYAN + 10;
- case ConsoleColor.Red:
- return isForeground ? COLOR_BRIGHT_RED : COLOR_BRIGHT_RED + 10;
- case ConsoleColor.Magenta:
- return isForeground ? COLOR_BRIGHT_MAGENTA : COLOR_BRIGHT_MAGENTA + 10;
- case ConsoleColor.Yellow:
- return isForeground ? COLOR_BRIGHT_YELLOW : COLOR_BRIGHT_YELLOW + 10;
- case ConsoleColor.White:
- return isForeground ? COLOR_BRIGHT_WHITE : COLOR_BRIGHT_WHITE + 10;
- }
- return 0;
+ public virtual void ResizeScreen ()
+ {
+ if (NetWinConsole == null) {
+ return;
}
- bool SetCursorPosition (int col, int row)
- {
+ if (Console.WindowHeight > 0) {
+ // Not supported on Unix.
if (IsWinPlatform) {
- // Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
+ // Can raise an exception while is still resizing.
try {
- Console.SetCursorPosition (col, row);
- return true;
- } catch (Exception) {
- return false;
+#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);
+#pragma warning restore CA1416
+ } catch (System.IO.IOException) {
+ Clip = new Rect (0, 0, Cols, Rows);
+ } catch (ArgumentOutOfRangeException) {
+ Clip = new Rect (0, 0, Cols, Rows);
}
} else {
- SetVirtualCursorPosition (col, row);
- return true;
+ Console.Out.Write (EscSeqUtils.CSI_SetTerminalWindowSize (Rows, Cols));
}
}
- 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) {
+ Clip = new Rect (0, 0, Cols, Rows);
+ }
- } catch (System.ArgumentOutOfRangeException) { }
- }
- }
- top = Console.WindowTop;
- left = Console.WindowLeft;
- }
+ public override void Refresh ()
+ {
+ UpdateScreen ();
+ UpdateCursor ();
+ }
- 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;
+ public override void UpdateScreen ()
+ {
+ if (_winSizeChanging || Console.WindowHeight < 1 || Contents.Length != Rows * Cols || Rows != Console.WindowHeight) {
+ return;
}
- private CursorVisibility? savedCursorVisibility;
+ var top = 0;
+ var left = 0;
+ var rows = Rows;
+ var cols = Cols;
+ System.Text.StringBuilder output = new System.Text.StringBuilder ();
+ Attribute redrawAttr = new Attribute ();
+ var lastCol = -1;
- public override void UpdateCursor ()
- {
- EnsureCursorVisibility ();
- //Debug.WriteLine ($"Before - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
+ //GetCursorVisibility (out CursorVisibility savedVisibitity);
+ //SetCursorVisibility (CursorVisibility.Invisible);
- if (ccol >= 0 && ccol < Cols && crow >= 0 && crow < Rows) {
- SetCursorPosition (ccol, crow);
- SetWindowPosition (0, crow);
+ for (var row = top; row < rows; row++) {
+ if (Console.WindowHeight < 1) {
+ return;
}
- //Debug.WriteLine ($"WindowTop: {Console.WindowTop};WindowLeft: {Console.WindowLeft}");
- //Debug.WriteLine ($"After - CursorTop: {Console.CursorTop};CursorLeft: {Console.CursorLeft}");
- }
+ if (!_dirtyLines [row]) {
+ continue;
+ }
+ if (!SetCursorPosition (0, row)) {
+ return;
+ }
+ _dirtyLines [row] = false;
+ output.Clear ();
+ for (var col = left; col < cols; col++) {
+ lastCol = -1;
+ var outputWidth = 0;
+ for (; col < cols; col++) {
+ if (!Contents [row, col].IsDirty) {
+ if (output.Length > 0) {
+ WriteToConsole (output, ref lastCol, row, ref outputWidth);
+ } else if (lastCol == -1) {
+ lastCol = col;
+ }
+ if (lastCol + 1 < cols)
+ lastCol++;
+ continue;
+ }
- public override void StartReportingMouseMoves ()
- {
- Console.Out.Write (EscSeqUtils.EnableMouseEvents);
- }
+ if (lastCol == -1) {
+ lastCol = col;
+ }
- public override void StopReportingMouseMoves ()
- {
- Console.Out.Write (EscSeqUtils.DisableMouseEvents);
+ Attribute attr = Contents [row, col].Attribute.Value;
+ // Performance: Only send the escape sequence if the attribute has changed.
+ if (attr != redrawAttr) {
+ redrawAttr = attr;
+ output.Append (EscSeqUtils.CSI_SetGraphicsRendition (
+ MapColors ((ConsoleColor)attr.Background, false), MapColors ((ConsoleColor)attr.Foreground, true)));
+ }
+ outputWidth++;
+ var rune = (Rune)Contents [row, col].Runes [0];
+ output.Append (rune.ToString ());
+ if (rune.IsSurrogatePair () && rune.GetColumns () < 2) {
+ WriteToConsole (output, ref lastCol, row, ref outputWidth);
+ Console.CursorLeft--;
+ }
+ Contents [row, col].IsDirty = false;
+ }
+ }
+ if (output.Length > 0) {
+ SetCursorPosition (lastCol, row);
+ Console.Write (output);
+ }
}
+ SetCursorPosition (0, 0);
- public override void Suspend ()
- {
- }
+ //SetCursorVisibility (savedVisibitity);
- public override void SetAttribute (Attribute c)
+ void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
{
- base.SetAttribute (c);
+ SetCursorPosition (lastCol, row);
+ Console.Write (output);
+ output.Clear ();
+ lastCol += outputWidth;
+ outputWidth = 0;
}
+ }
- public ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
- {
- if (consoleKeyInfo.Key != ConsoleKey.Packet) {
- return consoleKeyInfo;
- }
+ #region Color Handling
+
+ // Cache the list of ConsoleColor values.
+ private static readonly HashSet ConsoleColorValues = new HashSet (
+ Enum.GetValues (typeof (ConsoleColor)).OfType ().Select (c => (int)c)
+ );
+
+ // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console.
+ private static Dictionary colorMap = new Dictionary {
+ { ConsoleColor.Black, COLOR_BLACK },
+ { ConsoleColor.DarkBlue, COLOR_BLUE },
+ { ConsoleColor.DarkGreen, COLOR_GREEN },
+ { ConsoleColor.DarkCyan, COLOR_CYAN },
+ { ConsoleColor.DarkRed, COLOR_RED },
+ { ConsoleColor.DarkMagenta, COLOR_MAGENTA },
+ { ConsoleColor.DarkYellow, COLOR_YELLOW },
+ { ConsoleColor.Gray, COLOR_WHITE },
+ { ConsoleColor.DarkGray, COLOR_BRIGHT_BLACK },
+ { ConsoleColor.Blue, COLOR_BRIGHT_BLUE },
+ { ConsoleColor.Green, COLOR_BRIGHT_GREEN },
+ { ConsoleColor.Cyan, COLOR_BRIGHT_CYAN },
+ { ConsoleColor.Red, COLOR_BRIGHT_RED },
+ { ConsoleColor.Magenta, COLOR_BRIGHT_MAGENTA },
+ { ConsoleColor.Yellow, COLOR_BRIGHT_YELLOW },
+ { ConsoleColor.White, COLOR_BRIGHT_WHITE }
+ };
+
+ // Map a ConsoleColor to a platform dependent value.
+ int MapColors (ConsoleColor color, bool isForeground = true)
+ {
+ return colorMap.TryGetValue (color, out var colorValue) ? colorValue + (isForeground ? 0 : 10) : 0;
+ }
+
+ ///
+ /// In the NetDriver, colors are encoded as an int.
+ /// Extracts the foreground and background colors from the encoded value.
+ /// Assumes a 4-bit encoded value for both foreground and background colors.
+ ///
+ internal override void GetColors (int value, out Color foreground, out Color background)
+ {
+ // Assume a 4-bit encoded value for both foreground and background colors.
+ foreground = (Color)((value >> 16) & 0xF);
+ background = (Color)(value & 0xF);
+ }
- var mod = consoleKeyInfo.Modifiers;
- var shift = (mod & ConsoleModifiers.Shift) != 0;
- var alt = (mod & ConsoleModifiers.Alt) != 0;
- var control = (mod & ConsoleModifiers.Control) != 0;
+ ///
+ /// In the NetDriver, colors are encoded as an int.
+ /// However, the foreground color is stored in the most significant 16 bits,
+ /// and the background color is stored in the least significant 16 bits.
+ ///
+ public override Attribute MakeColor (Color foreground, Color background)
+ {
+ // Encode the colors into the int value.
+ return new Attribute (
+ value: ((((int)foreground) & 0xffff) << 16) | (((int)background) & 0xffff),
+ foreground: foreground,
+ background: background
+ );
+ }
- var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
+ #endregion
- return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
+ #region Cursor Handling
+ bool SetCursorPosition (int col, int 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;
}
+ // BUGBUG: This breaks -usc on WSL; not sure why. But commenting out fixes.
+ //} else {
+ // // TODO: Explain why + 1 is needed (and why we do this for non-Windows).
+ // Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1));
+ // return true;
+ //}
+ }
- Key MapKey (ConsoleKeyInfo keyInfo)
- {
- MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
- switch (keyInfo.Key) {
- case ConsoleKey.Escape:
- return MapKeyModifiers (keyInfo, Key.Esc);
- case ConsoleKey.Tab:
- return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
- case ConsoleKey.Home:
- return MapKeyModifiers (keyInfo, Key.Home);
- case ConsoleKey.End:
- return MapKeyModifiers (keyInfo, Key.End);
- case ConsoleKey.LeftArrow:
- return MapKeyModifiers (keyInfo, Key.CursorLeft);
- case ConsoleKey.RightArrow:
- return MapKeyModifiers (keyInfo, Key.CursorRight);
- case ConsoleKey.UpArrow:
- return MapKeyModifiers (keyInfo, Key.CursorUp);
- case ConsoleKey.DownArrow:
- return MapKeyModifiers (keyInfo, Key.CursorDown);
- case ConsoleKey.PageUp:
- return MapKeyModifiers (keyInfo, Key.PageUp);
- case ConsoleKey.PageDown:
- return MapKeyModifiers (keyInfo, Key.PageDown);
- case ConsoleKey.Enter:
- return MapKeyModifiers (keyInfo, Key.Enter);
- case ConsoleKey.Spacebar:
- return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
- case ConsoleKey.Backspace:
- return MapKeyModifiers (keyInfo, Key.Backspace);
- case ConsoleKey.Delete:
- return MapKeyModifiers (keyInfo, Key.DeleteChar);
- case ConsoleKey.Insert:
- return MapKeyModifiers (keyInfo, Key.InsertChar);
-
- case ConsoleKey.Oem1:
- case ConsoleKey.Oem2:
- case ConsoleKey.Oem3:
- case ConsoleKey.Oem4:
- case ConsoleKey.Oem5:
- case ConsoleKey.Oem6:
- case ConsoleKey.Oem7:
- case ConsoleKey.Oem8:
- case ConsoleKey.Oem102:
- case ConsoleKey.OemPeriod:
- case ConsoleKey.OemComma:
- case ConsoleKey.OemPlus:
- case ConsoleKey.OemMinus:
- return (Key)((uint)keyInfo.KeyChar);
- }
-
- 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.CtrlMask) | ((uint)Key.A + delta));
- }
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
- return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
- }
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
- return MapKeyModifiers (keyInfo, (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) {
- return (Key)(((uint)Key.AltMask) | ((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 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
- }
- }
- return (Key)((uint)keyInfo.KeyChar);
- }
- if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
- var delta = key - ConsoleKey.F1;
- if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
- }
+ CursorVisibility? _cachedCursorVisibility;
- return (Key)((uint)Key.F1 + delta);
- }
- if (keyInfo.KeyChar != 0) {
- return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
- }
+ public override void UpdateCursor ()
+ {
+ EnsureCursorVisibility ();
- return (Key)(0xffffffff);
+ if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows) {
+ SetCursorPosition (Col, Row);
+ SetWindowPosition (0, Row);
}
+ }
- KeyModifiers keyModifiers;
+ public override bool GetCursorVisibility (out CursorVisibility visibility)
+ {
+ visibility = _cachedCursorVisibility ?? CursorVisibility.Default;
+ return visibility == CursorVisibility.Default;
+ }
- Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
- {
- if (keyModifiers == null) {
- keyModifiers = new KeyModifiers ();
- }
- Key keyMod = new Key ();
- if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
- keyMod = Key.ShiftMask;
- keyModifiers.Shift = true;
- }
- if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
- keyMod |= Key.CtrlMask;
- keyModifiers.Ctrl = true;
- }
- if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
- keyMod |= Key.AltMask;
- keyModifiers.Alt = true;
- }
+ public override bool SetCursorVisibility (CursorVisibility visibility)
+ {
+ _cachedCursorVisibility = visibility;
+ var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default;
+ //Console.Out.Write (isVisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
+ return isVisible;
+ }
- return keyMod != Key.Null ? keyMod | key : key;
+ public override bool EnsureCursorVisibility ()
+ {
+ if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows)) {
+ GetCursorVisibility (out CursorVisibility cursorVisibility);
+ _cachedCursorVisibility = cursorVisibility;
+ SetCursorVisibility (CursorVisibility.Invisible);
+ return false;
}
- Action keyHandler;
- Action keyDownHandler;
- Action keyUpHandler;
- Action mouseHandler;
- NetMainLoop mainLoop;
-
- public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
- {
- this.keyHandler = keyHandler;
- this.keyDownHandler = keyDownHandler;
- this.keyUpHandler = keyUpHandler;
- this.mouseHandler = mouseHandler;
-
- var mLoop = this.mainLoop = mainLoop.Driver as NetMainLoop;
+ SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default);
+ return _cachedCursorVisibility == CursorVisibility.Default;
+ }
+ #endregion
- // 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);
+ #region Size and Position Handling
- // Check if terminal supports requests
- this.mainLoop.netEvents.EscSeqReqProc.Add ("c");
- Console.Out.Write ("\x1b[0c");
- }
+ void SetWindowPosition (int col, int row)
+ {
+ Top = Console.WindowTop;
+ Left = Console.WindowLeft;
+ }
- void ProcessInput (NetEvents.InputResult inputEvent)
- {
- switch (inputEvent.EventType) {
- case NetEvents.EventType.Key:
- ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
- if (consoleKeyInfo.Key == ConsoleKey.Packet) {
- consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
- }
- keyModifiers = new KeyModifiers ();
- var map = MapKey (consoleKeyInfo);
- if (map == (Key)0xffffffff) {
- return;
- }
- if (map == Key.Null) {
- keyDownHandler (new KeyEvent (map, keyModifiers));
- keyUpHandler (new KeyEvent (map, keyModifiers));
- } else {
- keyDownHandler (new KeyEvent (map, keyModifiers));
- keyHandler (new KeyEvent (map, keyModifiers));
- keyUpHandler (new KeyEvent (map, keyModifiers));
- }
- break;
- case NetEvents.EventType.Mouse:
- mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
- break;
- case NetEvents.EventType.WindowSize:
- ChangeWin (inputEvent.WindowSizeEvent.Size);
- break;
- case NetEvents.EventType.RequestResponse:
- Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
- break;
+ 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;
+ }
+ #endregion
- volatile bool winChanging;
- void ChangeWin (Size size)
- {
- winChanging = true;
- if (!EnableConsoleScrolling) {
- largestBufferHeight = Math.Max (size.Height, 0);
- } else {
- largestBufferHeight = Math.Max (size.Height, largestBufferHeight);
- }
- top = 0;
- left = 0;
- cols = size.Width;
- rows = largestBufferHeight;
- ResizeScreen ();
- UpdateOffScreen ();
- winChanging = false;
- TerminalResized?.Invoke ();
+ public void StartReportingMouseMoves ()
+ {
+ Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
+ }
+
+ public void StopReportingMouseMoves ()
+ {
+ Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
+ }
+
+ ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
+ {
+ if (consoleKeyInfo.Key != ConsoleKey.Packet) {
+ return consoleKeyInfo;
}
- MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
- {
- //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
+ var mod = consoleKeyInfo.Modifiers;
+ var shift = (mod & ConsoleModifiers.Shift) != 0;
+ var alt = (mod & ConsoleModifiers.Alt) != 0;
+ var control = (mod & ConsoleModifiers.Control) != 0;
- MouseFlags mouseFlag = 0;
+ var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (consoleKeyInfo.KeyChar, consoleKeyInfo.Modifiers, out uint virtualKey, out _);
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
- mouseFlag |= MouseFlags.Button1Pressed;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
- mouseFlag |= MouseFlags.Button1Released;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
- mouseFlag |= MouseFlags.Button1Clicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
- mouseFlag |= MouseFlags.Button1DoubleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
- mouseFlag |= MouseFlags.Button1TripleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
- mouseFlag |= MouseFlags.Button2Pressed;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
- mouseFlag |= MouseFlags.Button2Released;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
- mouseFlag |= MouseFlags.Button2Clicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
- mouseFlag |= MouseFlags.Button2DoubleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
- mouseFlag |= MouseFlags.Button2TripleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
- mouseFlag |= MouseFlags.Button3Pressed;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
- mouseFlag |= MouseFlags.Button3Released;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
- mouseFlag |= MouseFlags.Button3Clicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
- mouseFlag |= MouseFlags.Button3DoubleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
- mouseFlag |= MouseFlags.Button3TripleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
- mouseFlag |= MouseFlags.WheeledUp;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
- mouseFlag |= MouseFlags.WheeledDown;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
- mouseFlag |= MouseFlags.WheeledLeft;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
- mouseFlag |= MouseFlags.WheeledRight;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
- mouseFlag |= MouseFlags.Button4Pressed;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
- mouseFlag |= MouseFlags.Button4Released;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
- mouseFlag |= MouseFlags.Button4Clicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
- mouseFlag |= MouseFlags.Button4DoubleClicked;
- }
- if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
- mouseFlag |= MouseFlags.Button4TripleClicked;
+ return new ConsoleKeyInfo ((char)keyChar, (ConsoleKey)virtualKey, shift, alt, control);
+ }
+
+ Key MapKey (ConsoleKeyInfo keyInfo)
+ {
+ MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
+ switch (keyInfo.Key) {
+ case ConsoleKey.Escape:
+ return MapKeyModifiers (keyInfo, Key.Esc);
+ case ConsoleKey.Tab:
+ return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
+ case ConsoleKey.Home:
+ return MapKeyModifiers (keyInfo, Key.Home);
+ case ConsoleKey.End:
+ return MapKeyModifiers (keyInfo, Key.End);
+ case ConsoleKey.LeftArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorLeft);
+ case ConsoleKey.RightArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorRight);
+ case ConsoleKey.UpArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorUp);
+ case ConsoleKey.DownArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorDown);
+ case ConsoleKey.PageUp:
+ return MapKeyModifiers (keyInfo, Key.PageUp);
+ case ConsoleKey.PageDown:
+ return MapKeyModifiers (keyInfo, Key.PageDown);
+ case ConsoleKey.Enter:
+ return MapKeyModifiers (keyInfo, Key.Enter);
+ case ConsoleKey.Spacebar:
+ return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
+ case ConsoleKey.Backspace:
+ return MapKeyModifiers (keyInfo, Key.Backspace);
+ case ConsoleKey.Delete:
+ return MapKeyModifiers (keyInfo, Key.DeleteChar);
+ case ConsoleKey.Insert:
+ return MapKeyModifiers (keyInfo, Key.InsertChar);
+
+ case ConsoleKey.Oem1:
+ case ConsoleKey.Oem2:
+ case ConsoleKey.Oem3:
+ case ConsoleKey.Oem4:
+ case ConsoleKey.Oem5:
+ case ConsoleKey.Oem6:
+ case ConsoleKey.Oem7:
+ case ConsoleKey.Oem8:
+ case ConsoleKey.Oem102:
+ case ConsoleKey.OemPeriod:
+ case ConsoleKey.OemComma:
+ case ConsoleKey.OemPlus:
+ case ConsoleKey.OemMinus:
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+
+ 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.CtrlMask) | ((uint)Key.A + delta));
+ }
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta));
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
+ }
}
- if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
- mouseFlag |= MouseFlags.ReportMousePosition;
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+ if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
+ var delta = key - ConsoleKey.D0;
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
}
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
- mouseFlag |= MouseFlags.ButtonShift;
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
}
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
- mouseFlag |= MouseFlags.ButtonCtrl;
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+ }
}
- if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
- mouseFlag |= MouseFlags.ButtonAlt;
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+ if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) {
+ var delta = key - ConsoleKey.F1;
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
}
- return new MouseEvent () {
- X = me.Position.X,
- Y = me.Position.Y,
- Flags = mouseFlag
- };
+ return (Key)((uint)Key.F1 + delta);
}
-
- ///
- public override bool GetCursorVisibility (out CursorVisibility visibility)
- {
- visibility = savedCursorVisibility ?? CursorVisibility.Default;
- return visibility == CursorVisibility.Default;
+ if (keyInfo.KeyChar != 0) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
}
- ///
- public override bool SetCursorVisibility (CursorVisibility visibility)
- {
- savedCursorVisibility = visibility;
- var isVisible = Console.CursorVisible = visibility == CursorVisibility.Default;
- if (isVisible) {
- Console.Out.Write ("\x1b[?25h");
- } else {
- Console.Out.Write ("\x1b[?25l");
- }
- return isVisible;
- }
+ return (Key)(0xffffffff);
+ }
- ///
- public override bool EnsureCursorVisibility ()
- {
- if (!(ccol >= 0 && crow >= 0 && ccol < Cols && crow < Rows)) {
- GetCursorVisibility (out CursorVisibility cursorVisibility);
- savedCursorVisibility = cursorVisibility;
- SetCursorVisibility (CursorVisibility.Invisible);
- return false;
- }
+ KeyModifiers _keyModifiers;
- SetCursorVisibility (savedCursorVisibility ?? CursorVisibility.Default);
- return savedCursorVisibility == CursorVisibility.Default;
+ Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+ {
+ _keyModifiers ??= new KeyModifiers ();
+ Key keyMod = new Key ();
+ if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
+ keyMod = Key.ShiftMask;
+ _keyModifiers.Shift = true;
+ }
+ if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
+ keyMod |= Key.CtrlMask;
+ _keyModifiers.Ctrl = true;
+ }
+ if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
+ keyMod |= Key.AltMask;
+ _keyModifiers.Alt = true;
}
- public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
- {
- NetEvents.InputResult input = new NetEvents.InputResult ();
- input.EventType = NetEvents.EventType.Key;
- input.ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control);
+ return keyMod != Key.Null ? keyMod | key : key;
+ }
- try {
- ProcessInput (input);
- } catch (OverflowException) { }
- }
+ Action _keyHandler;
+ Action _keyDownHandler;
+ Action _keyUpHandler;
+ Action _mouseHandler;
+ NetMainLoop _mainLoop;
- public override bool GetColors (int value, out Color foreground, out Color background)
- {
- bool hasColor = false;
- foreground = default;
- background = default;
- IEnumerable values = Enum.GetValues (typeof (ConsoleColor))
- .OfType ()
- .Select (s => (int)s);
- if (values.Contains (value & 0xffff)) {
- hasColor = true;
- background = (Color)(ConsoleColor)(value & 0xffff);
+ public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
+ {
+ _keyHandler = keyHandler;
+ _keyDownHandler = keyDownHandler;
+ _keyUpHandler = keyUpHandler;
+ _mouseHandler = mouseHandler;
+
+ var mLoop = _mainLoop = mainLoop.MainLoopDriver as NetMainLoop;
+
+ // Note: .Net API doesn't support keydown/up events and thus any passed keyDown/UpHandlers will be simulated to be called.
+ mLoop.ProcessInput = (e) => ProcessInput (e);
+ }
+
+ volatile bool _winSizeChanging;
+
+ void ProcessInput (NetEvents.InputResult inputEvent)
+ {
+ switch (inputEvent.EventType) {
+ case NetEvents.EventType.Key:
+ ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
+ if (consoleKeyInfo.Key == ConsoleKey.Packet) {
+ consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
}
- if (values.Contains ((value >> 16) & 0xffff)) {
- hasColor = true;
- foreground = (Color)(ConsoleColor)((value >> 16) & 0xffff);
+ _keyModifiers = new KeyModifiers ();
+ var map = MapKey (consoleKeyInfo);
+ if (map == (Key)0xffffffff) {
+ return;
}
- return hasColor;
+ if (map == Key.Null) {
+ _keyDownHandler (new KeyEvent (map, _keyModifiers));
+ _keyUpHandler (new KeyEvent (map, _keyModifiers));
+ } else {
+ _keyDownHandler (new KeyEvent (map, _keyModifiers));
+ _keyHandler (new KeyEvent (map, _keyModifiers));
+ _keyUpHandler (new KeyEvent (map, _keyModifiers));
+ }
+ break;
+ case NetEvents.EventType.Mouse:
+ _mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
+ break;
+ case NetEvents.EventType.WindowSize:
+ _winSizeChanging = true;
+ Top = 0;
+ Left = 0;
+ Cols = inputEvent.WindowSizeEvent.Size.Width;
+ Rows = Math.Max (inputEvent.WindowSizeEvent.Size.Height, 0); ;
+ ResizeScreen ();
+ ClearContents ();
+ _winSizeChanging = false;
+ TerminalResized?.Invoke ();
+ break;
+ case NetEvents.EventType.RequestResponse:
+ // BUGBUG: What is this for? It does not seem to be used anywhere.
+ // It is also not clear what it does. View.Data is documented as "This property is not used internally"
+ Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
+ break;
+ case NetEvents.EventType.WindowPosition:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException ();
}
+ }
- #region Unused
- public override void SetColors (ConsoleColor foreground, ConsoleColor background)
- {
- }
+ MouseEvent ToDriverMouse (NetEvents.MouseEvent me)
+ {
+ //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
- public override void SetColors (short foregroundColorId, short backgroundColorId)
- {
- }
+ MouseFlags mouseFlag = 0;
- public override void CookMouse ()
- {
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1Pressed) != 0) {
+ mouseFlag |= MouseFlags.Button1Pressed;
}
-
- public override void UncookMouse ()
- {
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1Released) != 0) {
+ mouseFlag |= MouseFlags.Button1Released;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1Clicked) != 0) {
+ mouseFlag |= MouseFlags.Button1Clicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1DoubleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button1DoubleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button1TripleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button1TripleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2Pressed) != 0) {
+ mouseFlag |= MouseFlags.Button2Pressed;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2Released) != 0) {
+ mouseFlag |= MouseFlags.Button2Released;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2Clicked) != 0) {
+ mouseFlag |= MouseFlags.Button2Clicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2DoubleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button2DoubleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button2TripleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button2TripleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3Pressed) != 0) {
+ mouseFlag |= MouseFlags.Button3Pressed;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3Released) != 0) {
+ mouseFlag |= MouseFlags.Button3Released;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3Clicked) != 0) {
+ mouseFlag |= MouseFlags.Button3Clicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3DoubleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button3DoubleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button3TripleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button3TripleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledUp) != 0) {
+ mouseFlag |= MouseFlags.WheeledUp;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledDown) != 0) {
+ mouseFlag |= MouseFlags.WheeledDown;
}
- #endregion
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledLeft) != 0) {
+ mouseFlag |= MouseFlags.WheeledLeft;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonWheeledRight) != 0) {
+ mouseFlag |= MouseFlags.WheeledRight;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4Pressed) != 0) {
+ mouseFlag |= MouseFlags.Button4Pressed;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4Released) != 0) {
+ mouseFlag |= MouseFlags.Button4Released;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4Clicked) != 0) {
+ mouseFlag |= MouseFlags.Button4Clicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4DoubleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button4DoubleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.Button4TripleClicked) != 0) {
+ mouseFlag |= MouseFlags.Button4TripleClicked;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ReportMousePosition) != 0) {
+ mouseFlag |= MouseFlags.ReportMousePosition;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonShift) != 0) {
+ mouseFlag |= MouseFlags.ButtonShift;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonCtrl) != 0) {
+ mouseFlag |= MouseFlags.ButtonCtrl;
+ }
+ if ((me.ButtonState & NetEvents.MouseButtonState.ButtonAlt) != 0) {
+ mouseFlag |= MouseFlags.ButtonAlt;
+ }
+
+ return new MouseEvent () {
+ X = me.Position.X,
+ Y = me.Position.Y,
+ Flags = mouseFlag
+ };
+ }
+
+ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
+ {
+ NetEvents.InputResult input = new NetEvents.InputResult {
+ EventType = NetEvents.EventType.Key,
+ ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control)
+ };
+
+ try {
+ ProcessInput (input);
+ } catch (OverflowException) { }
+ }
- //
- // These are for the .NET driver, but running natively on Windows, wont run
- // on the Mono emulation
- //
+ #region Not Implemented
+ public override void Suspend ()
+ {
+ throw new NotImplementedException ();
}
+ #endregion
+
+}
+
+///
+/// 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 NetDriver.
+///
+internal class NetMainLoop : IMainLoopDriver {
+ ManualResetEventSlim _keyReady = new ManualResetEventSlim (false);
+ ManualResetEventSlim _waitForProbe = new ManualResetEventSlim (false);
+ Queue _inputResult = new Queue ();
+ MainLoop _mainLoop;
+ CancellationTokenSource _tokenSource = new CancellationTokenSource ();
+ internal NetEvents _netEvents;
///
- /// 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.
+ /// Invoked when a Key is pressed.
+ ///
+ internal Action ProcessInput;
+
+ ///
+ /// Initializes the class with the console driver.
///
///
- /// This implementation is used for NetDriver.
+ /// Passing a consoleDriver is provided to capture windows resizing.
///
- internal class NetMainLoop : IMainLoopDriver {
- ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
- ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
- Queue inputResult = new Queue ();
- MainLoop mainLoop;
- CancellationTokenSource tokenSource = new CancellationTokenSource ();
- internal NetEvents netEvents;
-
- ///
- /// Invoked when a Key is pressed.
- ///
- public Action ProcessInput;
-
- ///
- /// Initializes the class with the console driver.
- ///
- ///
- /// Passing a consoleDriver is provided to capture windows resizing.
- ///
- /// The console driver used by this Net main loop.
- public NetMainLoop (ConsoleDriver consoleDriver = null)
- {
- if (consoleDriver == null) {
- throw new ArgumentNullException ("Console driver instance must be provided.");
- }
- netEvents = new NetEvents (consoleDriver);
- }
+ /// The console driver used by this Net main loop.
+ ///
+ public NetMainLoop (ConsoleDriver consoleDriver = null)
+ {
+ if (consoleDriver == null) {
+ throw new ArgumentNullException (nameof (consoleDriver));
+ }
+ _netEvents = new NetEvents (consoleDriver);
+ }
- void NetInputHandler ()
- {
- while (true) {
- waitForProbe.Wait ();
- waitForProbe.Reset ();
- if (inputResult.Count == 0) {
- inputResult.Enqueue (netEvents.ReadConsoleInput ());
- }
- try {
- while (inputResult.Peek () == null) {
- inputResult.Dequeue ();
- }
- if (inputResult.Count > 0) {
- keyReady.Set ();
- }
- } catch (InvalidOperationException) { }
+ void NetInputHandler ()
+ {
+ while (true) {
+ _waitForProbe.Wait ();
+ _waitForProbe.Reset ();
+ if (_inputResult.Count == 0) {
+ _inputResult.Enqueue (_netEvents.ReadConsoleInput ());
}
+ try {
+ while (_inputResult.Peek () == null) {
+ _inputResult.Dequeue ();
+ }
+ if (_inputResult.Count > 0) {
+ _keyReady.Set ();
+ }
+ } catch (InvalidOperationException) { }
}
+ }
- void IMainLoopDriver.Setup (MainLoop mainLoop)
- {
- this.mainLoop = mainLoop;
- Task.Run (NetInputHandler);
- }
-
- void IMainLoopDriver.Wakeup ()
- {
- keyReady.Set ();
- }
+ void IMainLoopDriver.Setup (MainLoop mainLoop)
+ {
+ _mainLoop = mainLoop;
+ Task.Run (NetInputHandler);
+ }
- bool IMainLoopDriver.EventsPending (bool wait)
- {
- waitForProbe.Set ();
+ void IMainLoopDriver.Wakeup ()
+ {
+ _keyReady.Set ();
+ }
- if (CheckTimers (wait, out var waitTimeout)) {
- return true;
- }
+ bool IMainLoopDriver.EventsPending (bool wait)
+ {
+ _waitForProbe.Set ();
- try {
- if (!tokenSource.IsCancellationRequested) {
- keyReady.Wait (waitTimeout, tokenSource.Token);
- }
- } catch (OperationCanceledException) {
- return true;
- } finally {
- keyReady.Reset ();
- }
+ if (CheckTimers (wait, out var waitTimeout)) {
+ return true;
+ }
- if (!tokenSource.IsCancellationRequested) {
- return inputResult.Count > 0 || CheckTimers (wait, out _);
+ try {
+ if (!_tokenSource.IsCancellationRequested) {
+ _keyReady.Wait (waitTimeout, _tokenSource.Token);
}
-
- tokenSource.Dispose ();
- tokenSource = new CancellationTokenSource ();
+ } catch (OperationCanceledException) {
return true;
+ } finally {
+ _keyReady.Reset ();
}
- bool CheckTimers (bool wait, out int waitTimeout)
- {
- long now = DateTime.UtcNow.Ticks;
+ if (!_tokenSource.IsCancellationRequested) {
+ return _inputResult.Count > 0 || CheckTimers (wait, out _);
+ }
- if (mainLoop.timeouts.Count > 0) {
- waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
- if (waitTimeout < 0)
- return true;
- } else {
- waitTimeout = -1;
- }
+ _tokenSource.Dispose ();
+ _tokenSource = new CancellationTokenSource ();
+ return true;
+ }
- if (!wait)
- waitTimeout = 0;
+ bool CheckTimers (bool wait, out int waitTimeout)
+ {
+ var now = DateTime.UtcNow.Ticks;
- int ic;
- lock (mainLoop.idleHandlers) {
- ic = mainLoop.idleHandlers.Count;
+ if (_mainLoop.timeouts.Count > 0) {
+ waitTimeout = (int)((_mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
+ if (waitTimeout < 0) {
+ return true;
}
+ } else {
+ waitTimeout = -1;
+ }
- return ic > 0;
+ if (!wait) {
+ waitTimeout = 0;
}
- void IMainLoopDriver.Iteration ()
- {
- while (inputResult.Count > 0) {
- ProcessInput?.Invoke (inputResult.Dequeue ().Value);
- }
+ int ic;
+ lock (_mainLoop.idleHandlers) {
+ ic = _mainLoop.idleHandlers.Count;
+ }
+
+ return ic > 0;
+ }
+
+ void IMainLoopDriver.Iteration ()
+ {
+ while (_inputResult.Count > 0) {
+ ProcessInput?.Invoke (_inputResult.Dequeue ().Value);
}
}
+ public void TearDown ()
+ {
+ //throw new NotImplementedException ();
+ }
}
\ No newline at end of file
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index 16741743b8..0cec3e9c8f 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -10,616 +10,671 @@
using System.Threading;
using System.Threading.Tasks;
-namespace Terminal.Gui {
+namespace Terminal.Gui;
+
+internal class WindowsConsole {
+ public const int STD_OUTPUT_HANDLE = -11;
+ public const int STD_INPUT_HANDLE = -10;
+ public const int STD_ERROR_HANDLE = -12;
+
+ IntPtr _inputHandle, _outputHandle;
+ IntPtr _screenBuffer;
+ readonly uint _originalConsoleMode;
+ CursorVisibility? _initialCursorVisibility = null;
+ CursorVisibility? _currentCursorVisibility = null;
+ CursorVisibility? _pendingCursorVisibility = null;
+ readonly StringBuilder _stringBuilder = new StringBuilder (256 * 1024);
+
+ public WindowsConsole ()
+ {
+ _inputHandle = GetStdHandle (STD_INPUT_HANDLE);
+ _outputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
+ _originalConsoleMode = ConsoleMode;
+ var newConsoleMode = _originalConsoleMode;
+ newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
+ newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
+ newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
+ ConsoleMode = newConsoleMode;
+ }
- internal class WindowsConsole {
- public const int STD_OUTPUT_HANDLE = -11;
- public const int STD_INPUT_HANDLE = -10;
- public const int STD_ERROR_HANDLE = -12;
+ CharInfo [] _originalStdOutChars;
- internal IntPtr InputHandle, OutputHandle;
- IntPtr ScreenBuffer;
- readonly uint originalConsoleMode;
- CursorVisibility? initialCursorVisibility = null;
- CursorVisibility? currentCursorVisibility = null;
- CursorVisibility? pendingCursorVisibility = null;
+ public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord coords, SmallRect window, bool useTrueColor)
+ {
+ if (_screenBuffer == IntPtr.Zero) {
+ ReadFromConsoleOutput (size, coords, ref window);
+ }
- public WindowsConsole ()
- {
- InputHandle = GetStdHandle (STD_INPUT_HANDLE);
- OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
- originalConsoleMode = ConsoleMode;
- var newConsoleMode = originalConsoleMode;
- newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
- newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
- newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
- ConsoleMode = newConsoleMode;
+ if (!useTrueColor) {
+ int i = 0;
+ CharInfo [] ci = new CharInfo [charInfoBuffer.Length];
+ foreach (ExtendedCharInfo info in charInfoBuffer) {
+ ci [i++] = new CharInfo () {
+ Char = new CharUnion () { UnicodeChar = info.Char },
+ Attributes = (ushort)info.Attribute.Value
+ };
+ }
+ return WriteConsoleOutput (_screenBuffer, ci, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
}
- public CharInfo [] OriginalStdOutChars;
- public bool WriteToConsole (Size size, CharInfo [] charInfoBuffer, Coord coords, SmallRect window)
- {
- if (ScreenBuffer == IntPtr.Zero) {
- ReadFromConsoleOutput (size, coords, ref window);
- }
+ return WriteConsoleTrueColorOutput (charInfoBuffer);
+ }
- return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
- }
+ private bool WriteConsoleTrueColorOutput (ExtendedCharInfo [] charInfoBuffer)
+ {
+ _stringBuilder.Clear ();
- public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window)
- {
- ScreenBuffer = CreateConsoleScreenBuffer (
- DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
- ShareMode.FileShareRead | ShareMode.FileShareWrite,
- IntPtr.Zero,
- 1,
- IntPtr.Zero
- );
- if (ScreenBuffer == INVALID_HANDLE_VALUE) {
- var err = Marshal.GetLastWin32Error ();
-
- if (err != 0)
- throw new System.ComponentModel.Win32Exception (err);
- }
+ _stringBuilder.Append (EscSeqUtils.CSI_SaveCursorPosition);
+ _stringBuilder.Append (EscSeqUtils.CSI_SetCursorPosition (0, 0));
- if (!initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) {
- initialCursorVisibility = visibility;
- }
+ Attribute? prev = null;
+ foreach (var info in charInfoBuffer) {
+ var attr = info.Attribute;
- if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ if (attr != prev) {
+ prev = attr;
+
+ _stringBuilder.Append (EscSeqUtils.CSI_SetForegroundColorRGB (attr.TrueColorForeground.Value.Red, attr.TrueColorForeground.Value.Green, attr.TrueColorForeground.Value.Blue));
+ _stringBuilder.Append (EscSeqUtils.CSI_SetBackgroundColorRGB (attr.TrueColorBackground.Value.Red, attr.TrueColorBackground.Value.Green, attr.TrueColorBackground.Value.Blue));
}
- OriginalStdOutChars = new CharInfo [size.Height * size.Width];
+ if (info.Char != '\x1b') {
+ if (!info.Empty) {
+ _stringBuilder.Append (info.Char);
+ }
- if (!ReadConsoleOutput (ScreenBuffer, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ } else {
+ _stringBuilder.Append (' ');
}
}
- public bool SetCursorPosition (Coord position)
- {
- return SetConsoleCursorPosition (ScreenBuffer, position);
- }
+ _stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition);
- public void SetInitialCursorVisibility ()
- {
- if (initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) {
- initialCursorVisibility = visibility;
+ string s = _stringBuilder.ToString ();
+
+ return WriteConsole (_screenBuffer, s, (uint)(s.Length), out uint _, null);
+ }
+
+ public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window)
+ {
+ _screenBuffer = CreateConsoleScreenBuffer (
+ DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
+ ShareMode.FileShareRead | ShareMode.FileShareWrite,
+ IntPtr.Zero,
+ 1,
+ IntPtr.Zero
+ );
+ if (_screenBuffer == INVALID_HANDLE_VALUE) {
+ var err = Marshal.GetLastWin32Error ();
+
+ if (err != 0) {
+ throw new System.ComponentModel.Win32Exception (err);
}
}
- 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) {
- throw new System.ComponentModel.Win32Exception (err);
- }
- visibility = Gui.CursorVisibility.Default;
+ if (!_initialCursorVisibility.HasValue && GetCursorVisibility (out CursorVisibility visibility)) {
+ _initialCursorVisibility = visibility;
+ }
- return false;
- }
+ if (!SetConsoleActiveScreenBuffer (_screenBuffer)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ }
- if (!info.bVisible)
- visibility = CursorVisibility.Invisible;
- else if (info.dwSize > 50)
- visibility = CursorVisibility.Box;
- else
- visibility = CursorVisibility.Underline;
+ _originalStdOutChars = new CharInfo [size.Height * size.Width];
- return true;
+ if (!ReadConsoleOutput (_screenBuffer, _originalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
+ }
- public bool EnsureCursorVisibility ()
- {
- if (initialCursorVisibility.HasValue && pendingCursorVisibility.HasValue && SetCursorVisibility (pendingCursorVisibility.Value)) {
- pendingCursorVisibility = null;
+ public bool SetCursorPosition (Coord position)
+ {
+ return SetConsoleCursorPosition (_screenBuffer, position);
+ }
- return true;
- }
+ public void SetInitialCursorVisibility ()
+ {
+ if (_initialCursorVisibility.HasValue == false && GetCursorVisibility (out CursorVisibility visibility)) {
+ _initialCursorVisibility = visibility;
+ }
+ }
+ public bool GetCursorVisibility (out CursorVisibility visibility)
+ {
+ if (_screenBuffer == IntPtr.Zero) {
+ visibility = CursorVisibility.Invisible;
return false;
}
-
- public void ForceRefreshCursorVisibility ()
- {
- if (currentCursorVisibility.HasValue) {
- pendingCursorVisibility = currentCursorVisibility;
- currentCursorVisibility = null;
+ if (!GetConsoleCursorInfo (_screenBuffer, out ConsoleCursorInfo info)) {
+ var err = Marshal.GetLastWin32Error ();
+ if (err != 0) {
+ throw new System.ComponentModel.Win32Exception (err);
}
+ visibility = Gui.CursorVisibility.Default;
+
+ return false;
}
- public bool SetCursorVisibility (CursorVisibility visibility)
- {
- if (initialCursorVisibility.HasValue == false) {
- pendingCursorVisibility = visibility;
+ if (!info.bVisible) {
+ visibility = CursorVisibility.Invisible;
+ } else if (info.dwSize > 50) {
+ visibility = CursorVisibility.Box;
+ } else {
+ visibility = CursorVisibility.Underline;
+ }
- return false;
- }
+ return true;
+ }
- if (currentCursorVisibility.HasValue == false || currentCursorVisibility.Value != visibility) {
- ConsoleCursorInfo info = new ConsoleCursorInfo {
- dwSize = (uint)visibility & 0x00FF,
- bVisible = ((uint)visibility & 0xFF00) != 0
- };
+ public bool EnsureCursorVisibility ()
+ {
+ if (_initialCursorVisibility.HasValue && _pendingCursorVisibility.HasValue && SetCursorVisibility (_pendingCursorVisibility.Value)) {
+ _pendingCursorVisibility = null;
- if (!SetConsoleCursorInfo (ScreenBuffer, ref info))
- return false;
+ return true;
+ }
- currentCursorVisibility = visibility;
- }
+ return false;
+ }
- return true;
+ public void ForceRefreshCursorVisibility ()
+ {
+ if (_currentCursorVisibility.HasValue) {
+ _pendingCursorVisibility = _currentCursorVisibility;
+ _currentCursorVisibility = null;
}
+ }
- public void Cleanup ()
- {
- if (initialCursorVisibility.HasValue) {
- SetCursorVisibility (initialCursorVisibility.Value);
- }
+ public bool SetCursorVisibility (CursorVisibility visibility)
+ {
+ if (_initialCursorVisibility.HasValue == false) {
+ _pendingCursorVisibility = visibility;
+
+ return false;
+ }
- SetConsoleOutputWindow (out _);
+ if (_currentCursorVisibility.HasValue == false || _currentCursorVisibility.Value != visibility) {
+ ConsoleCursorInfo info = new ConsoleCursorInfo {
+ dwSize = (uint)visibility & 0x00FF,
+ bVisible = ((uint)visibility & 0xFF00) != 0
+ };
- ConsoleMode = originalConsoleMode;
- //ContinueListeningForConsoleEvents = false;
- if (!SetConsoleActiveScreenBuffer (OutputHandle)) {
- var err = Marshal.GetLastWin32Error ();
- Console.WriteLine ("Error: {0}", err);
+ if (!SetConsoleCursorInfo (_screenBuffer, ref info)) {
+ return false;
}
- if (ScreenBuffer != IntPtr.Zero)
- CloseHandle (ScreenBuffer);
+ _currentCursorVisibility = visibility;
+ }
+
+ return true;
+ }
- ScreenBuffer = IntPtr.Zero;
+ public void Cleanup ()
+ {
+ if (_initialCursorVisibility.HasValue) {
+ SetCursorVisibility (_initialCursorVisibility.Value);
}
- internal Size GetConsoleBufferWindow (out Point position)
- {
- if (ScreenBuffer == IntPtr.Zero) {
- position = Point.Empty;
- return Size.Empty;
- }
+ SetConsoleOutputWindow (out _);
- var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
- csbi.cbSize = (uint)Marshal.SizeOf (csbi);
- if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
- //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- position = Point.Empty;
- return Size.Empty;
- }
- var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
- csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
- position = new Point (csbi.srWindow.Left, csbi.srWindow.Top);
+ ConsoleMode = _originalConsoleMode;
+ if (!SetConsoleActiveScreenBuffer (_outputHandle)) {
+ var err = Marshal.GetLastWin32Error ();
+ Console.WriteLine ("Error: {0}", err);
+ }
- return sz;
+ if (_screenBuffer != IntPtr.Zero) {
+ CloseHandle (_screenBuffer);
}
- internal Size GetConsoleOutputWindow (out Point position)
- {
- var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
- csbi.cbSize = (uint)Marshal.SizeOf (csbi);
- if (!GetConsoleScreenBufferInfoEx (OutputHandle, ref csbi)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- }
- var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
- csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
- position = new Point (csbi.srWindow.Left, csbi.srWindow.Top);
+ _screenBuffer = IntPtr.Zero;
+ }
- return sz;
+ internal Size GetConsoleBufferWindow (out Point position)
+ {
+ if (_screenBuffer == IntPtr.Zero) {
+ position = Point.Empty;
+ return Size.Empty;
}
- internal Size SetConsoleWindow (short cols, short rows)
- {
- var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
- csbi.cbSize = (uint)Marshal.SizeOf (csbi);
- if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- }
- var maxWinSize = GetLargestConsoleWindowSize (ScreenBuffer);
- var newCols = Math.Min (cols, maxWinSize.X);
- var newRows = Math.Min (rows, maxWinSize.Y);
- csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1));
- csbi.srWindow = new SmallRect (0, 0, newCols, newRows);
- csbi.dwMaximumWindowSize = new Coord (newCols, newRows);
- if (!SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- }
- var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0));
- if (!SetConsoleWindowInfo (OutputHandle, true, ref winRect)) {
- //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- return new Size (cols, rows);
- }
- SetConsoleOutputWindow (csbi);
- return new Size (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
+ var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
+ csbi.cbSize = (uint)Marshal.SizeOf (csbi);
+ if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) {
+ //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ position = Point.Empty;
+ return Size.Empty;
}
+ var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
+ csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
+ position = new Point (csbi.srWindow.Left, csbi.srWindow.Top);
- void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
- {
- if (ScreenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- }
+ return sz;
+ }
+
+ internal Size GetConsoleOutputWindow (out Point position)
+ {
+ var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
+ csbi.cbSize = (uint)Marshal.SizeOf (csbi);
+ if (!GetConsoleScreenBufferInfoEx (_outputHandle, ref csbi)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
+ var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
+ csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
+ position = new Point (csbi.srWindow.Left, csbi.srWindow.Top);
- internal Size SetConsoleOutputWindow (out Point position)
- {
- if (ScreenBuffer == IntPtr.Zero) {
- position = Point.Empty;
- return Size.Empty;
- }
+ return sz;
+ }
- var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
- csbi.cbSize = (uint)Marshal.SizeOf (csbi);
- if (!GetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi)) {
- throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
- }
- var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
- Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0));
- 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 ());
- }
+ internal Size SetConsoleWindow (short cols, short rows)
+ {
+ var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
+ csbi.cbSize = (uint)Marshal.SizeOf (csbi);
+
+ if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ }
+ var maxWinSize = GetLargestConsoleWindowSize (_screenBuffer);
+ var newCols = Math.Min (cols, maxWinSize.X);
+ var newRows = Math.Min (rows, maxWinSize.Y);
+ csbi.dwSize = new Coord (newCols, Math.Max (newRows, (short)1));
+ csbi.srWindow = new SmallRect (0, 0, newCols, newRows);
+ csbi.dwMaximumWindowSize = new Coord (newCols, newRows);
+ if (!SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ }
+ var winRect = new SmallRect (0, 0, (short)(newCols - 1), (short)Math.Max (newRows - 1, 0));
+ if (!SetConsoleWindowInfo (_outputHandle, true, ref winRect)) {
+ //throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ return new Size (cols, rows);
+ }
+ SetConsoleOutputWindow (csbi);
+ return new Size (winRect.Right + 1, newRows - 1 < 0 ? 0 : winRect.Bottom + 1);
+ }
- return sz;
+ void SetConsoleOutputWindow (CONSOLE_SCREEN_BUFFER_INFOEX csbi)
+ {
+ if (_screenBuffer != IntPtr.Zero && !SetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
}
+ }
- //bool ContinueListeningForConsoleEvents = true;
+ internal Size SetConsoleOutputWindow (out Point position)
+ {
+ if (_screenBuffer == IntPtr.Zero) {
+ position = Point.Empty;
+ return Size.Empty;
+ }
- public uint ConsoleMode {
- get {
- GetConsoleMode (InputHandle, out uint v);
- return v;
- }
- set {
- SetConsoleMode (InputHandle, value);
- }
+ var csbi = new CONSOLE_SCREEN_BUFFER_INFOEX ();
+ csbi.cbSize = (uint)Marshal.SizeOf (csbi);
+ if (!GetConsoleScreenBufferInfoEx (_screenBuffer, ref csbi)) {
+ throw new System.ComponentModel.Win32Exception (Marshal.GetLastWin32Error ());
+ }
+ var sz = new Size (csbi.srWindow.Right - csbi.srWindow.Left + 1,
+ Math.Max (csbi.srWindow.Bottom - csbi.srWindow.Top + 1, 0));
+ 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 ());
}
- [Flags]
- public enum ConsoleModes : uint {
- EnableProcessedInput = 1,
- EnableMouseInput = 16,
- EnableQuickEditMode = 64,
- EnableExtendedFlags = 128,
- }
-
- [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
- public struct KeyEventRecord {
- [FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
- public bool bKeyDown;
- [FieldOffset (4), MarshalAs (UnmanagedType.U2)]
- public ushort wRepeatCount;
- [FieldOffset (6), MarshalAs (UnmanagedType.U2)]
- public ushort wVirtualKeyCode;
- [FieldOffset (8), MarshalAs (UnmanagedType.U2)]
- public ushort wVirtualScanCode;
- [FieldOffset (10)]
- public char UnicodeChar;
- [FieldOffset (12), MarshalAs (UnmanagedType.U4)]
- public ControlKeyState dwControlKeyState;
- }
-
- [Flags]
- public enum ButtonState {
- Button1Pressed = 1,
- Button2Pressed = 4,
- Button3Pressed = 8,
- Button4Pressed = 16,
- RightmostButtonPressed = 2
- }
-
- [Flags]
- public enum ControlKeyState {
- RightAltPressed = 1,
- LeftAltPressed = 2,
- RightControlPressed = 4,
- LeftControlPressed = 8,
- ShiftPressed = 16,
- NumlockOn = 32,
- ScrolllockOn = 64,
- CapslockOn = 128,
- EnhancedKey = 256
- }
-
- [Flags]
- public enum EventFlags {
- MouseMoved = 1,
- DoubleClick = 2,
- MouseWheeled = 4,
- MouseHorizontalWheeled = 8
- }
-
- [StructLayout (LayoutKind.Explicit)]
- public struct MouseEventRecord {
- [FieldOffset (0)]
- public Coord MousePosition;
- [FieldOffset (4)]
- public ButtonState ButtonState;
- [FieldOffset (8)]
- public ControlKeyState ControlKeyState;
- [FieldOffset (12)]
- public EventFlags EventFlags;
-
- public override string ToString ()
- {
- return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}";
- }
+ return sz;
+ }
+
+ uint ConsoleMode {
+ get {
+ GetConsoleMode (_inputHandle, out uint v);
+ return v;
+ }
+ set {
+ SetConsoleMode (_inputHandle, value);
}
+ }
- public struct WindowBufferSizeRecord {
- public Coord size;
+ [Flags]
+ public enum ConsoleModes : uint {
+ EnableProcessedInput = 1,
+ EnableMouseInput = 16,
+ EnableQuickEditMode = 64,
+ EnableExtendedFlags = 128,
+ }
- public WindowBufferSizeRecord (short x, short y)
- {
- this.size = new Coord (x, y);
- }
+ [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
+ public struct KeyEventRecord {
+ [FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
+ public bool bKeyDown;
+ [FieldOffset (4), MarshalAs (UnmanagedType.U2)]
+ public ushort wRepeatCount;
+ [FieldOffset (6), MarshalAs (UnmanagedType.U2)]
+ public ushort wVirtualKeyCode;
+ [FieldOffset (8), MarshalAs (UnmanagedType.U2)]
+ public ushort wVirtualScanCode;
+ [FieldOffset (10)]
+ public char UnicodeChar;
+ [FieldOffset (12), MarshalAs (UnmanagedType.U4)]
+ public ControlKeyState dwControlKeyState;
+ }
- public override string ToString () => $"[WindowBufferSize{size}";
- }
-
- [StructLayout (LayoutKind.Sequential)]
- public struct MenuEventRecord {
- public uint dwCommandId;
- }
-
- [StructLayout (LayoutKind.Sequential)]
- public struct FocusEventRecord {
- public uint bSetFocus;
- }
-
- public enum EventType : ushort {
- Focus = 0x10,
- Key = 0x1,
- Menu = 0x8,
- Mouse = 2,
- WindowBufferSize = 4
- }
-
- [StructLayout (LayoutKind.Explicit)]
- public struct InputRecord {
- [FieldOffset (0)]
- public EventType EventType;
- [FieldOffset (4)]
- public KeyEventRecord KeyEvent;
- [FieldOffset (4)]
- public MouseEventRecord MouseEvent;
- [FieldOffset (4)]
- public WindowBufferSizeRecord WindowBufferSizeEvent;
- [FieldOffset (4)]
- public MenuEventRecord MenuEvent;
- [FieldOffset (4)]
- public FocusEventRecord FocusEvent;
-
- public override string ToString ()
- {
- switch (EventType) {
- case EventType.Focus:
- return FocusEvent.ToString ();
- case EventType.Key:
- return KeyEvent.ToString ();
- case EventType.Menu:
- return MenuEvent.ToString ();
- case EventType.Mouse:
- return MouseEvent.ToString ();
- case EventType.WindowBufferSize:
- return WindowBufferSizeEvent.ToString ();
- default:
- return "Unknown event type: " + EventType;
- }
- }
- };
+ [Flags]
+ public enum ButtonState {
+ Button1Pressed = 1,
+ Button2Pressed = 4,
+ Button3Pressed = 8,
+ Button4Pressed = 16,
+ RightmostButtonPressed = 2
+ }
- [Flags]
- enum ShareMode : uint {
- FileShareRead = 1,
- FileShareWrite = 2,
- }
+ [Flags]
+ public enum ControlKeyState {
+ RightAltPressed = 1,
+ LeftAltPressed = 2,
+ RightControlPressed = 4,
+ LeftControlPressed = 8,
+ ShiftPressed = 16,
+ NumlockOn = 32,
+ ScrolllockOn = 64,
+ CapslockOn = 128,
+ EnhancedKey = 256
+ }
- [Flags]
- enum DesiredAccess : uint {
- GenericRead = 2147483648,
- GenericWrite = 1073741824,
- }
+ [Flags]
+ public enum EventFlags {
+ MouseMoved = 1,
+ DoubleClick = 2,
+ MouseWheeled = 4,
+ MouseHorizontalWheeled = 8
+ }
- [StructLayout (LayoutKind.Sequential)]
- public struct ConsoleScreenBufferInfo {
- public Coord dwSize;
- public Coord dwCursorPosition;
- public ushort wAttributes;
- public SmallRect srWindow;
- public Coord dwMaximumWindowSize;
- }
+ [StructLayout (LayoutKind.Explicit)]
+ public struct MouseEventRecord {
+ [FieldOffset (0)]
+ public Coord MousePosition;
+ [FieldOffset (4)]
+ public ButtonState ButtonState;
+ [FieldOffset (8)]
+ public ControlKeyState ControlKeyState;
+ [FieldOffset (12)]
+ public EventFlags EventFlags;
- [StructLayout (LayoutKind.Sequential)]
- public struct Coord {
- public short X;
- public short Y;
+ public override readonly string ToString () => $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}";
- public Coord (short X, short Y)
- {
- this.X = X;
- this.Y = Y;
- }
- public override string ToString () => $"({X},{Y})";
- };
+ }
+
+ public struct WindowBufferSizeRecord {
+ public Coord _size;
- [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
- public struct CharUnion {
- [FieldOffset (0)] public char UnicodeChar;
- [FieldOffset (0)] public byte AsciiChar;
+ public WindowBufferSizeRecord (short x, short y)
+ {
+ _size = new Coord (x, y);
}
- [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
- public struct CharInfo {
- [FieldOffset (0)] public CharUnion Char;
- [FieldOffset (2)] public ushort Attributes;
+ public override readonly string ToString () => $"[WindowBufferSize{_size}";
+ }
+
+ [StructLayout (LayoutKind.Sequential)]
+ public struct MenuEventRecord {
+ public uint dwCommandId;
+ }
+
+ [StructLayout (LayoutKind.Sequential)]
+ public struct FocusEventRecord {
+ public uint bSetFocus;
+ }
+
+ public enum EventType : ushort {
+ Focus = 0x10,
+ Key = 0x1,
+ Menu = 0x8,
+ Mouse = 2,
+ WindowBufferSize = 4
+ }
+
+ [StructLayout (LayoutKind.Explicit)]
+ public struct InputRecord {
+ [FieldOffset (0)]
+ public EventType EventType;
+ [FieldOffset (4)]
+ public KeyEventRecord KeyEvent;
+ [FieldOffset (4)]
+ public MouseEventRecord MouseEvent;
+ [FieldOffset (4)]
+ public WindowBufferSizeRecord WindowBufferSizeEvent;
+ [FieldOffset (4)]
+ public MenuEventRecord MenuEvent;
+ [FieldOffset (4)]
+ public FocusEventRecord FocusEvent;
+
+ public override readonly string ToString ()
+ {
+ return EventType switch {
+ EventType.Focus => FocusEvent.ToString (),
+ EventType.Key => KeyEvent.ToString (),
+ EventType.Menu => MenuEvent.ToString (),
+ EventType.Mouse => MouseEvent.ToString (),
+ EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (),
+ _ => "Unknown event type: " + EventType
+ };
}
+ };
- [StructLayout (LayoutKind.Sequential)]
- public struct SmallRect {
- public short Left;
- public short Top;
- public short Right;
- public short Bottom;
+ [Flags]
+ enum ShareMode : uint {
+ FileShareRead = 1,
+ FileShareWrite = 2,
+ }
- public SmallRect (short left, short top, short right, short bottom)
- {
- Left = left;
- Top = top;
- Right = right;
- Bottom = bottom;
- }
+ [Flags]
+ enum DesiredAccess : uint {
+ GenericRead = 2147483648,
+ GenericWrite = 1073741824,
+ }
- public static void MakeEmpty (ref SmallRect rect)
- {
- rect.Left = -1;
- }
+ [StructLayout (LayoutKind.Sequential)]
+ public struct ConsoleScreenBufferInfo {
+ public Coord dwSize;
+ public Coord dwCursorPosition;
+ public ushort wAttributes;
+ public SmallRect srWindow;
+ public Coord dwMaximumWindowSize;
+ }
- public static void Update (ref SmallRect rect, short col, short row)
- {
- if (rect.Left == -1) {
- //System.Diagnostics.Debugger.Log (0, "debug", $"damager From Empty {col},{row}\n");
- rect.Left = rect.Right = col;
- rect.Bottom = rect.Top = row;
- return;
- }
- if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom)
- return;
- if (col < rect.Left)
- rect.Left = col;
- if (col > rect.Right)
- rect.Right = col;
- if (row < rect.Top)
- rect.Top = row;
- if (row > rect.Bottom)
- rect.Bottom = row;
- //System.Diagnostics.Debugger.Log (0, "debug", $"Expanding {rect.ToString ()}\n");
- }
+ [StructLayout (LayoutKind.Sequential)]
+ public struct Coord {
+ public short X;
+ public short Y;
- public override string ToString ()
- {
- return $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}";
- }
+ public Coord (short x, short y)
+ {
+ X = x;
+ Y = y;
}
+ public override readonly string ToString () => $"({X},{Y})";
+ };
+
+ [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
+ public struct CharUnion {
+ [FieldOffset (0)] public char UnicodeChar;
+ [FieldOffset (0)] public byte AsciiChar;
+ }
+
+ [StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
+ public struct CharInfo {
+ [FieldOffset (0)] public CharUnion Char;
+ [FieldOffset (2)] public ushort Attributes;
+ }
- [StructLayout (LayoutKind.Sequential)]
- public struct ConsoleKeyInfoEx {
- public ConsoleKeyInfo consoleKeyInfo;
- public bool CapsLock;
- public bool NumLock;
- public bool Scrolllock;
+ public struct ExtendedCharInfo {
+ public char Char { get; set; }
+ public Attribute Attribute { get; set; }
+ public bool Empty { get; set; } // TODO: Temp hack until virutal terminal sequences
- public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock)
- {
- this.consoleKeyInfo = consoleKeyInfo;
- CapsLock = capslock;
- NumLock = numlock;
- Scrolllock = scrolllock;
- }
+ public ExtendedCharInfo (char character, Attribute attribute)
+ {
+ Char = character;
+ Attribute = attribute;
+ Empty = false;
}
+ }
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern IntPtr GetStdHandle (int nStdHandle);
+ [StructLayout (LayoutKind.Sequential)]
+ public struct SmallRect {
+ public short Left;
+ public short Top;
+ public short Right;
+ public short Bottom;
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool CloseHandle (IntPtr handle);
+ public SmallRect (short left, short top, short right, short bottom)
+ {
+ Left = left;
+ Top = top;
+ Right = right;
+ Bottom = bottom;
+ }
- [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
- public static extern bool ReadConsoleInput (
- IntPtr hConsoleInput,
- IntPtr lpBuffer,
- uint nLength,
- out uint lpNumberOfEventsRead);
+ public static void MakeEmpty (ref SmallRect rect)
+ {
+ rect.Left = -1;
+ }
- [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- static extern bool ReadConsoleOutput (
- IntPtr hConsoleOutput,
- [Out] CharInfo [] lpBuffer,
- Coord dwBufferSize,
- Coord dwBufferCoord,
- ref SmallRect lpReadRegion
- );
+ public static void Update (ref SmallRect rect, short col, short row)
+ {
+ if (rect.Left == -1) {
+ rect.Left = rect.Right = col;
+ rect.Bottom = rect.Top = row;
+ return;
+ }
+ if (col >= rect.Left && col <= rect.Right && row >= rect.Top && row <= rect.Bottom)
+ return;
+ if (col < rect.Left)
+ rect.Left = col;
+ if (col > rect.Right)
+ rect.Right = col;
+ if (row < rect.Top)
+ rect.Top = row;
+ if (row > rect.Bottom)
+ rect.Bottom = row;
+ }
- [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutput", SetLastError = true, CharSet = CharSet.Unicode)]
- static extern bool WriteConsoleOutput (
- IntPtr hConsoleOutput,
- CharInfo [] lpBuffer,
- Coord dwBufferSize,
- Coord dwBufferCoord,
- ref SmallRect lpWriteRegion
- );
+ public override readonly string ToString () => $"Left={Left},Top={Top},Right={Right},Bottom={Bottom}";
+ }
- [DllImport ("kernel32.dll")]
- static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition);
+ [StructLayout (LayoutKind.Sequential)]
+ public struct ConsoleKeyInfoEx {
+ public ConsoleKeyInfo ConsoleKeyInfo;
+ public bool CapsLock;
+ public bool NumLock;
+ public bool ScrollLock;
- [StructLayout (LayoutKind.Sequential)]
- public struct ConsoleCursorInfo {
- public uint dwSize;
- public bool bVisible;
+ public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock, bool scrolllock)
+ {
+ ConsoleKeyInfo = consoleKeyInfo;
+ CapsLock = capslock;
+ NumLock = numlock;
+ ScrollLock = scrolllock;
}
+ }
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool SetConsoleCursorInfo (IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo);
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern IntPtr GetStdHandle (int nStdHandle);
+
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool CloseHandle (IntPtr handle);
+
+ [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
+ public static extern bool ReadConsoleInput (
+ IntPtr hConsoleInput,
+ IntPtr lpBuffer,
+ uint nLength,
+ out uint lpNumberOfEventsRead);
+
+ [DllImport ("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ static extern bool ReadConsoleOutput (
+ IntPtr hConsoleOutput,
+ [Out] CharInfo [] lpBuffer,
+ Coord dwBufferSize,
+ Coord dwBufferCoord,
+ ref SmallRect lpReadRegion
+ );
+
+ // TODO: This API is obsolete. See https://learn.microsoft.com/en-us/windows/console/writeconsoleoutput
+ [DllImport ("kernel32.dll", EntryPoint = "WriteConsoleOutputW", SetLastError = true, CharSet = CharSet.Unicode)]
+ static extern bool WriteConsoleOutput (
+ IntPtr hConsoleOutput,
+ CharInfo [] lpBuffer,
+ Coord dwBufferSize,
+ Coord dwBufferCoord,
+ ref SmallRect lpWriteRegion
+ );
+
+ [DllImport ("kernel32.dll", EntryPoint = "WriteConsole", SetLastError = true, CharSet = CharSet.Unicode)]
+ static extern bool WriteConsole (
+ IntPtr hConsoleOutput,
+ String lpbufer,
+ UInt32 NumberOfCharsToWriten,
+ out UInt32 lpNumberOfCharsWritten,
+ object lpReserved
+ );
+
+ [DllImport ("kernel32.dll")]
+ static extern bool SetConsoleCursorPosition (IntPtr hConsoleOutput, Coord dwCursorPosition);
+
+ [StructLayout (LayoutKind.Sequential)]
+ public struct ConsoleCursorInfo {
+ public uint dwSize;
+ public bool bVisible;
+ }
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool GetConsoleCursorInfo (IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo);
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool SetConsoleCursorInfo (IntPtr hConsoleOutput, [In] ref ConsoleCursorInfo lpConsoleCursorInfo);
- [DllImport ("kernel32.dll")]
- static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool GetConsoleCursorInfo (IntPtr hConsoleOutput, out ConsoleCursorInfo lpConsoleCursorInfo);
- [DllImport ("kernel32.dll")]
- static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
+ [DllImport ("kernel32.dll")]
+ static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern IntPtr CreateConsoleScreenBuffer (
- DesiredAccess dwDesiredAccess,
- ShareMode dwShareMode,
- IntPtr secutiryAttributes,
- uint flags,
- IntPtr screenBufferData
- );
+ [DllImport ("kernel32.dll")]
+ static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
- internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1);
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern IntPtr CreateConsoleScreenBuffer (
+ DesiredAccess dwDesiredAccess,
+ ShareMode dwShareMode,
+ IntPtr secutiryAttributes,
+ uint flags,
+ IntPtr screenBufferData
+ );
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle);
+ internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1);
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents);
- public uint InputEventCount {
- get {
- GetNumberOfConsoleInputEvents (InputHandle, out uint v);
- return v;
- }
- }
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool SetConsoleActiveScreenBuffer (IntPtr Handle);
- public InputRecord [] ReadConsoleInput ()
- {
- const int bufferSize = 1;
- var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize);
- try {
- ReadConsoleInput (InputHandle, pRecord, bufferSize,
- out var numberEventsRead);
-
- return numberEventsRead == 0
- ? null
- : new [] { Marshal.PtrToStructure (pRecord) };
- } catch (Exception) {
- return null;
- } finally {
- Marshal.FreeHGlobal (pRecord);
- }
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents);
+
+ public InputRecord [] ReadConsoleInput ()
+ {
+ const int bufferSize = 1;
+ var pRecord = Marshal.AllocHGlobal (Marshal.SizeOf () * bufferSize);
+ try {
+ ReadConsoleInput (_inputHandle, pRecord, bufferSize,
+ out var numberEventsRead);
+
+ return numberEventsRead == 0
+ ? null
+ : 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.
[DllImport ("kernel32.dll", ExactSpelling = true)]
@@ -639,1509 +694,1258 @@ internal void ShowWindow (int state)
ShowWindow (thisConsole, state);
}
#endif
- // See: https://github.com/gui-cs/Terminal.Gui/issues/357
-
- [StructLayout (LayoutKind.Sequential)]
- public struct CONSOLE_SCREEN_BUFFER_INFOEX {
- public uint cbSize;
- public Coord dwSize;
- public Coord dwCursorPosition;
- public ushort wAttributes;
- public SmallRect srWindow;
- public Coord dwMaximumWindowSize;
- public ushort wPopupAttributes;
- public bool bFullscreenSupported;
-
- [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)]
- public COLORREF [] ColorTable;
- }
-
- [StructLayout (LayoutKind.Explicit, Size = 4)]
- public struct COLORREF {
- public COLORREF (byte r, byte g, byte b)
- {
- Value = 0;
- R = r;
- G = g;
- B = b;
- }
-
- public COLORREF (uint value)
- {
- R = 0;
- G = 0;
- B = 0;
- Value = value & 0x00FFFFFF;
- }
-
- [FieldOffset (0)]
- public byte R;
- [FieldOffset (1)]
- public byte G;
- [FieldOffset (2)]
- public byte B;
-
- [FieldOffset (0)]
- public uint Value;
- }
-
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi);
-
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo);
-
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern bool SetConsoleWindowInfo (
- IntPtr hConsoleOutput,
- bool bAbsolute,
- [In] ref SmallRect lpConsoleWindow);
-
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern Coord GetLargestConsoleWindowSize (
- IntPtr hConsoleOutput);
+ // See: https://github.com/gui-cs/Terminal.Gui/issues/357
+
+ [StructLayout (LayoutKind.Sequential)]
+ public struct CONSOLE_SCREEN_BUFFER_INFOEX {
+ public uint cbSize;
+ public Coord dwSize;
+ public Coord dwCursorPosition;
+ public ushort wAttributes;
+ public SmallRect srWindow;
+ public Coord dwMaximumWindowSize;
+ public ushort wPopupAttributes;
+ public bool bFullscreenSupported;
+
+ [MarshalAs (UnmanagedType.ByValArray, SizeConst = 16)]
+ public COLORREF [] ColorTable;
}
- internal class WindowsDriver : ConsoleDriver {
- static bool sync = false;
- WindowsConsole.CharInfo [] OutputBuffer;
- int cols, rows, left, top;
- WindowsConsole.SmallRect damageRegion;
- IClipboard clipboard;
- int [,,] contents;
-
- public override int Cols => cols;
- public override int Rows => rows;
- public override int Left => left;
- public override int Top => top;
- public override bool EnableConsoleScrolling { get; set; }
- public override IClipboard Clipboard => clipboard;
- public override int [,,] Contents => contents;
-
- public WindowsConsole WinConsole { get; private set; }
-
- Action keyHandler;
- Action keyDownHandler;
- Action keyUpHandler;
- Action mouseHandler;
-
- public WindowsDriver ()
- {
- WinConsole = new WindowsConsole ();
- clipboard = new WindowsClipboard ();
- }
-
- public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
+ [StructLayout (LayoutKind.Explicit, Size = 4)]
+ public struct COLORREF {
+ public COLORREF (byte r, byte g, byte b)
{
- this.keyHandler = keyHandler;
- this.keyDownHandler = keyDownHandler;
- this.keyUpHandler = keyUpHandler;
- this.mouseHandler = mouseHandler;
-
- var mLoop = mainLoop.Driver as WindowsMainLoop;
-
- mLoop.ProcessInput = (e) => ProcessInput (e);
-
- mLoop.WinChanged = (s, e) => {
- ChangeWin (e.Size);
- };
+ Value = 0;
+ R = r;
+ G = g;
+ B = b;
}
- private void ChangeWin (Size e)
+ public COLORREF (uint value)
{
- if (!EnableConsoleScrolling) {
- var w = e.Width;
- if (w == cols - 3 && e.Height < rows) {
- w += 3;
- }
- var newSize = WinConsole.SetConsoleWindow (
- (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0));
- left = 0;
- top = 0;
- cols = newSize.Width;
- rows = newSize.Height;
- ResizeScreen ();
- UpdateOffScreen ();
- TerminalResized.Invoke ();
- }
+ R = 0;
+ G = 0;
+ B = 0;
+ Value = value & 0x00FFFFFF;
}
- void ProcessInput (WindowsConsole.InputRecord inputEvent)
- {
- switch (inputEvent.EventType) {
- case WindowsConsole.EventType.Key:
- var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet;
- if (fromPacketKey) {
- inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
- }
- var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
- //var ke = inputEvent.KeyEvent;
- //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}");
- //if (ke.UnicodeChar == '\0') {
- // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'");
- //} else if (ke.UnicodeChar == 13) {
- // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'");
- //} else {
- // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'");
- //}
- //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}");
- //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}");
- //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}");
- //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}");
- //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}");
-
- if (map == (Key)0xffffffff) {
- KeyEvent key = new KeyEvent ();
-
- // Shift = VK_SHIFT = 0x10
- // Ctrl = VK_CONTROL = 0x11
- // Alt = VK_MENU = 0x12
-
- if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) {
- inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn;
- }
-
- if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) {
- inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn;
- }
+ [FieldOffset (0)]
+ public byte R;
+ [FieldOffset (1)]
+ public byte G;
+ [FieldOffset (2)]
+ public byte B;
- if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) {
- inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn;
- }
+ [FieldOffset (0)]
+ public uint Value;
+ }
- switch (inputEvent.KeyEvent.dwControlKeyState) {
- case WindowsConsole.ControlKeyState.RightAltPressed:
- case WindowsConsole.ControlKeyState.RightAltPressed |
- WindowsConsole.ControlKeyState.LeftControlPressed |
- WindowsConsole.ControlKeyState.EnhancedKey:
- case WindowsConsole.ControlKeyState.EnhancedKey:
- key = new KeyEvent (Key.CtrlMask | Key.AltMask, keyModifiers);
- break;
- case WindowsConsole.ControlKeyState.LeftAltPressed:
- key = new KeyEvent (Key.AltMask, keyModifiers);
- break;
- case WindowsConsole.ControlKeyState.RightControlPressed:
- case WindowsConsole.ControlKeyState.LeftControlPressed:
- key = new KeyEvent (Key.CtrlMask, keyModifiers);
- break;
- case WindowsConsole.ControlKeyState.ShiftPressed:
- key = new KeyEvent (Key.ShiftMask, keyModifiers);
- break;
- case WindowsConsole.ControlKeyState.NumlockOn:
- break;
- case WindowsConsole.ControlKeyState.ScrolllockOn:
- break;
- case WindowsConsole.ControlKeyState.CapslockOn:
- break;
- default:
- switch (inputEvent.KeyEvent.wVirtualKeyCode) {
- case 0x10:
- key = new KeyEvent (Key.ShiftMask, keyModifiers);
- break;
- case 0x11:
- key = new KeyEvent (Key.CtrlMask, keyModifiers);
- break;
- case 0x12:
- key = new KeyEvent (Key.AltMask, keyModifiers);
- break;
- default:
- key = new KeyEvent (Key.Unknown, keyModifiers);
- break;
- }
- break;
- }
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool GetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX csbi);
- if (inputEvent.KeyEvent.bKeyDown)
- keyDownHandler (key);
- else
- keyUpHandler (key);
- } else {
- if (inputEvent.KeyEvent.bKeyDown) {
- // May occurs using SendKeys
- if (keyModifiers == null)
- keyModifiers = new KeyModifiers ();
- // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
- keyDownHandler (new KeyEvent (map, keyModifiers));
- keyHandler (new KeyEvent (map, keyModifiers));
- } else {
- keyUpHandler (new KeyEvent (map, keyModifiers));
- }
- }
- if (!inputEvent.KeyEvent.bKeyDown) {
- keyModifiers = null;
- }
- break;
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool SetConsoleScreenBufferInfoEx (IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFOEX ConsoleScreenBufferInfo);
- case WindowsConsole.EventType.Mouse:
- var me = ToDriverMouse (inputEvent.MouseEvent);
- mouseHandler (me);
- if (processButtonClick) {
- mouseHandler (
- new MouseEvent () {
- X = me.X,
- Y = me.Y,
- Flags = ProcessButtonClick (inputEvent.MouseEvent)
- });
- }
- break;
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern bool SetConsoleWindowInfo (
+ IntPtr hConsoleOutput,
+ bool bAbsolute,
+ [In] ref SmallRect lpConsoleWindow);
- case WindowsConsole.EventType.WindowBufferSize:
- var winSize = WinConsole.GetConsoleBufferWindow (out Point pos);
- left = pos.X;
- top = pos.Y;
- cols = inputEvent.WindowBufferSizeEvent.size.X;
- if (EnableConsoleScrolling) {
- rows = Math.Max (inputEvent.WindowBufferSizeEvent.size.Y, rows);
- } else {
- rows = inputEvent.WindowBufferSizeEvent.size.Y;
- }
- //System.Diagnostics.Debug.WriteLine ($"{EnableConsoleScrolling},{cols},{rows}");
- ResizeScreen ();
- UpdateOffScreen ();
- TerminalResized?.Invoke ();
- break;
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern Coord GetLargestConsoleWindowSize (
+ IntPtr hConsoleOutput);
+}
- case WindowsConsole.EventType.Focus:
- keyModifiers = null;
- break;
- }
- }
+internal class WindowsDriver : ConsoleDriver {
+ WindowsConsole.ExtendedCharInfo [] _outputBuffer;
+ WindowsConsole.SmallRect _damageRegion;
+ Action _keyHandler;
+ Action _keyDownHandler;
+ Action _keyUpHandler;
+ Action _mouseHandler;
- WindowsConsole.ButtonState? lastMouseButtonPressed = null;
- bool isButtonPressed = false;
- bool isButtonReleased = false;
- bool isButtonDoubleClicked = false;
- Point? point;
- Point pointMove;
- //int buttonPressedCount;
- bool isOneFingerDoubleClicked = false;
- bool processButtonClick;
+ public WindowsConsole WinConsole { get; private set; }
- MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
- {
- MouseFlags mouseFlag = MouseFlags.AllEvents;
+ public override bool SupportsTrueColor => Environment.OSVersion.Version.Build >= 14931;
- //System.Diagnostics.Debug.WriteLine (
- // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}");
+ public WindowsDriver ()
+ {
+ WinConsole = new WindowsConsole ();
+ Clipboard = new WindowsClipboard ();
+ }
- if (isButtonDoubleClicked || isOneFingerDoubleClicked) {
- Application.MainLoop.AddIdle (() => {
- Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
- return false;
- });
- }
+ public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler)
+ {
+ _keyHandler = keyHandler;
+ _keyDownHandler = keyDownHandler;
+ _keyUpHandler = keyUpHandler;
+ _mouseHandler = mouseHandler;
- // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
- // This will tell when a mouse button is pressed. When the button is released this event will
- // be fired with it's bit set to 0. So when the button is up ButtonState will be 0.
- // To map to the correct driver events we save the last pressed mouse button so we can
- // map to the correct clicked event.
- if ((lastMouseButtonPressed != null || isButtonReleased) && mouseEvent.ButtonState != 0) {
- lastMouseButtonPressed = null;
- //isButtonPressed = false;
- isButtonReleased = false;
- }
+ var mLoop = mainLoop.MainLoopDriver as WindowsMainLoop;
- var p = new Point () {
- X = mouseEvent.MousePosition.X,
- Y = mouseEvent.MousePosition.Y
- };
+ mLoop.ProcessInput = (e) => ProcessInput (e);
- //if (!isButtonPressed && buttonPressedCount < 2
- // && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved
- // && (mouseEvent.ButtonState == WindowsConsole.ButtonState.Button1Pressed
- // || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button2Pressed
- // || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button3Pressed)) {
+ mLoop.WinChanged = (s, e) => {
+ ChangeWin (e.Size);
+ };
+ }
- // lastMouseButtonPressed = mouseEvent.ButtonState;
- // buttonPressedCount++;
- //} else if (!isButtonPressed && buttonPressedCount > 0 && mouseEvent.ButtonState == 0
- // && mouseEvent.EventFlags == 0) {
+ private void ChangeWin (Size e)
+ {
+ var w = e.Width;
+ if (w == Cols - 3 && e.Height < Rows) {
+ w += 3;
+ }
+ var newSize = WinConsole.SetConsoleWindow (
+ (short)Math.Max (w, 16), (short)Math.Max (e.Height, 0));
+ Left = 0;
+ Top = 0;
+ Cols = newSize.Width;
+ Rows = newSize.Height;
+ ResizeScreen ();
+ ClearContents ();
+ TerminalResized.Invoke ();
+ }
- // buttonPressedCount++;
+ void ProcessInput (WindowsConsole.InputRecord inputEvent)
+ {
+ switch (inputEvent.EventType) {
+ case WindowsConsole.EventType.Key:
+ var fromPacketKey = inputEvent.KeyEvent.wVirtualKeyCode == (uint)ConsoleKey.Packet;
+ if (fromPacketKey) {
+ inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
+ }
+ var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
+ //var ke = inputEvent.KeyEvent;
+ //System.Diagnostics.Debug.WriteLine ($"fromPacketKey: {fromPacketKey}");
+ //if (ke.UnicodeChar == '\0') {
+ // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 0'\\0'");
+ //} else if (ke.UnicodeChar == 13) {
+ // System.Diagnostics.Debug.WriteLine ("UnicodeChar: 13'\\n'");
+ //} else {
+ // System.Diagnostics.Debug.WriteLine ($"UnicodeChar: {(uint)ke.UnicodeChar}'{ke.UnicodeChar}'");
//}
- //System.Diagnostics.Debug.WriteLine ($"isButtonPressed: {isButtonPressed};buttonPressedCount: {buttonPressedCount};lastMouseButtonPressed: {lastMouseButtonPressed}");
- //System.Diagnostics.Debug.WriteLine ($"isOneFingerDoubleClicked: {isOneFingerDoubleClicked}");
-
- //if (buttonPressedCount == 1 && lastMouseButtonPressed != null && p == point
- // && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed
- // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed
- // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) {
-
- // switch (lastMouseButtonPressed) {
- // case WindowsConsole.ButtonState.Button1Pressed:
- // mouseFlag = MouseFlags.Button1DoubleClicked;
- // break;
-
- // case WindowsConsole.ButtonState.Button2Pressed:
- // mouseFlag = MouseFlags.Button2DoubleClicked;
- // break;
-
- // case WindowsConsole.ButtonState.Button3Pressed:
- // mouseFlag = MouseFlags.Button3DoubleClicked;
- // break;
- // }
- // isOneFingerDoubleClicked = true;
-
- //} else if (buttonPressedCount == 3 && lastMouseButtonPressed != null && isOneFingerDoubleClicked && p == point
- // && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed
- // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed
- // || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) {
-
- // switch (lastMouseButtonPressed) {
- // case WindowsConsole.ButtonState.Button1Pressed:
- // mouseFlag = MouseFlags.Button1TripleClicked;
- // break;
-
- // case WindowsConsole.ButtonState.Button2Pressed:
- // mouseFlag = MouseFlags.Button2TripleClicked;
- // break;
-
- // case WindowsConsole.ButtonState.Button3Pressed:
- // mouseFlag = MouseFlags.Button3TripleClicked;
- // break;
- // }
- // buttonPressedCount = 0;
- // lastMouseButtonPressed = null;
- // isOneFingerDoubleClicked = false;
- // isButtonReleased = false;
+ //System.Diagnostics.Debug.WriteLine ($"bKeyDown: {ke.bKeyDown}");
+ //System.Diagnostics.Debug.WriteLine ($"dwControlKeyState: {ke.dwControlKeyState}");
+ //System.Diagnostics.Debug.WriteLine ($"wRepeatCount: {ke.wRepeatCount}");
+ //System.Diagnostics.Debug.WriteLine ($"wVirtualKeyCode: {ke.wVirtualKeyCode}");
+ //System.Diagnostics.Debug.WriteLine ($"wVirtualScanCode: {ke.wVirtualScanCode}");
- //}
- if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && lastMouseButtonPressed == null && !isButtonDoubleClicked) ||
- (lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) &&
- mouseEvent.ButtonState != 0 && !isButtonReleased && !isButtonDoubleClicked)) {
- switch (mouseEvent.ButtonState) {
- case WindowsConsole.ButtonState.Button1Pressed:
- mouseFlag = MouseFlags.Button1Pressed;
- break;
+ if (map == (Key)0xffffffff) {
+ KeyEvent key = new KeyEvent ();
- case WindowsConsole.ButtonState.Button2Pressed:
- mouseFlag = MouseFlags.Button2Pressed;
- break;
+ // Shift = VK_SHIFT = 0x10
+ // Ctrl = VK_CONTROL = 0x11
+ // Alt = VK_MENU = 0x12
- case WindowsConsole.ButtonState.RightmostButtonPressed:
- mouseFlag = MouseFlags.Button3Pressed;
- break;
+ if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.CapslockOn)) {
+ inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.CapslockOn;
}
- if (point == null)
- point = p;
-
- if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
- mouseFlag |= MouseFlags.ReportMousePosition;
- isButtonReleased = false;
- processButtonClick = false;
+ if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ScrolllockOn)) {
+ inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.ScrolllockOn;
}
- lastMouseButtonPressed = mouseEvent.ButtonState;
- isButtonPressed = true;
-
- if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
- Application.MainLoop.AddIdle (() => {
- Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
- return false;
- });
- }
-
- } else if (lastMouseButtonPressed != null && mouseEvent.EventFlags == 0
- && !isButtonReleased && !isButtonDoubleClicked && !isOneFingerDoubleClicked) {
- switch (lastMouseButtonPressed) {
- case WindowsConsole.ButtonState.Button1Pressed:
- mouseFlag = MouseFlags.Button1Released;
- break;
-
- case WindowsConsole.ButtonState.Button2Pressed:
- mouseFlag = MouseFlags.Button2Released;
- break;
- case WindowsConsole.ButtonState.RightmostButtonPressed:
- mouseFlag = MouseFlags.Button3Released;
- break;
- }
- isButtonPressed = false;
- isButtonReleased = true;
- if (point != null && (((Point)point).X == mouseEvent.MousePosition.X && ((Point)point).Y == mouseEvent.MousePosition.Y)) {
- processButtonClick = true;
- } else {
- point = null;
+ if (inputEvent.KeyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.NumlockOn)) {
+ inputEvent.KeyEvent.dwControlKeyState &= ~WindowsConsole.ControlKeyState.NumlockOn;
}
- } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved
- && !isOneFingerDoubleClicked && isButtonReleased && p == point) {
- mouseFlag = ProcessButtonClick (mouseEvent);
-
- } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) {
- switch (mouseEvent.ButtonState) {
- case WindowsConsole.ButtonState.Button1Pressed:
- mouseFlag = MouseFlags.Button1DoubleClicked;
+ switch (inputEvent.KeyEvent.dwControlKeyState) {
+ case WindowsConsole.ControlKeyState.RightAltPressed:
+ case WindowsConsole.ControlKeyState.RightAltPressed |
+ WindowsConsole.ControlKeyState.LeftControlPressed |
+ WindowsConsole.ControlKeyState.EnhancedKey:
+ case WindowsConsole.ControlKeyState.EnhancedKey:
+ key = new KeyEvent (Key.CtrlMask | Key.AltMask, _keyModifiers);
break;
-
- case WindowsConsole.ButtonState.Button2Pressed:
- mouseFlag = MouseFlags.Button2DoubleClicked;
+ case WindowsConsole.ControlKeyState.LeftAltPressed:
+ key = new KeyEvent (Key.AltMask, _keyModifiers);
break;
-
- case WindowsConsole.ButtonState.RightmostButtonPressed:
- mouseFlag = MouseFlags.Button3DoubleClicked;
+ case WindowsConsole.ControlKeyState.RightControlPressed:
+ case WindowsConsole.ControlKeyState.LeftControlPressed:
+ key = new KeyEvent (Key.CtrlMask, _keyModifiers);
break;
- }
- isButtonDoubleClicked = true;
- } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && isButtonDoubleClicked) {
- switch (mouseEvent.ButtonState) {
- case WindowsConsole.ButtonState.Button1Pressed:
- mouseFlag = MouseFlags.Button1TripleClicked;
+ case WindowsConsole.ControlKeyState.ShiftPressed:
+ key = new KeyEvent (Key.ShiftMask, _keyModifiers);
break;
-
- case WindowsConsole.ButtonState.Button2Pressed:
- mouseFlag = MouseFlags.Button2TripleClicked;
+ case WindowsConsole.ControlKeyState.NumlockOn:
break;
-
- case WindowsConsole.ButtonState.RightmostButtonPressed:
- mouseFlag = MouseFlags.Button3TripleClicked;
+ case WindowsConsole.ControlKeyState.ScrolllockOn:
break;
- }
- isButtonDoubleClicked = false;
- } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) {
- switch ((int)mouseEvent.ButtonState) {
- case int v when v > 0:
- mouseFlag = MouseFlags.WheeledUp;
+ case WindowsConsole.ControlKeyState.CapslockOn:
break;
-
- case int v when v < 0:
- mouseFlag = MouseFlags.WheeledDown;
+ default:
+ key = inputEvent.KeyEvent.wVirtualKeyCode switch {
+ 0x10 => new KeyEvent (Key.ShiftMask, _keyModifiers),
+ 0x11 => new KeyEvent (Key.CtrlMask, _keyModifiers),
+ 0x12 => new KeyEvent (Key.AltMask, _keyModifiers),
+ _ => new KeyEvent (Key.Unknown, _keyModifiers)
+ };
break;
}
- } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled &&
- mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) {
- switch ((int)mouseEvent.ButtonState) {
- case int v when v > 0:
- mouseFlag = MouseFlags.WheeledLeft;
- break;
-
- case int v when v < 0:
- mouseFlag = MouseFlags.WheeledRight;
- break;
+ if (inputEvent.KeyEvent.bKeyDown) {
+ _keyDownHandler (key);
+ } else {
+ _keyUpHandler (key);
+ }
+ } else {
+ if (inputEvent.KeyEvent.bKeyDown) {
+ // May occurs using SendKeys
+ _keyModifiers ??= new KeyModifiers ();
+ // Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
+ _keyDownHandler (new KeyEvent (map, _keyModifiers));
+ _keyHandler (new KeyEvent (map, _keyModifiers));
+ } else {
+ _keyUpHandler (new KeyEvent (map, _keyModifiers));
}
+ }
+ if (!inputEvent.KeyEvent.bKeyDown && inputEvent.KeyEvent.dwControlKeyState == 0) {
+ _keyModifiers = null;
+ }
+ break;
- } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) {
- switch ((int)mouseEvent.ButtonState) {
- case int v when v < 0:
- mouseFlag = MouseFlags.WheeledLeft;
- break;
+ case WindowsConsole.EventType.Mouse:
+ var me = ToDriverMouse (inputEvent.MouseEvent);
+ _mouseHandler (me);
+ if (_processButtonClick) {
+ _mouseHandler (
+ new MouseEvent () {
+ X = me.X,
+ Y = me.Y,
+ Flags = ProcessButtonClick (inputEvent.MouseEvent)
+ });
+ }
+ break;
- case int v when v > 0:
- mouseFlag = MouseFlags.WheeledRight;
- break;
- }
-
- } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
- mouseFlag = MouseFlags.ReportMousePosition;
- if (mouseEvent.MousePosition.X != pointMove.X || mouseEvent.MousePosition.Y != pointMove.Y) {
- pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y);
- }
- } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) {
- mouseFlag = 0;
- }
-
- mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
+ case WindowsConsole.EventType.Focus:
+ break;
+ }
+ }
- //System.Diagnostics.Debug.WriteLine (
- // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}");
+ WindowsConsole.ButtonState? _lastMouseButtonPressed = null;
+ bool _isButtonPressed = false;
+ bool _isButtonReleased = false;
+ bool _isButtonDoubleClicked = false;
+ Point? _point;
+ Point _pointMove;
+ bool _isOneFingerDoubleClicked = false;
+ bool _processButtonClick;
+
+ MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
+ {
+ MouseFlags mouseFlag = MouseFlags.AllEvents;
+
+ //System.Diagnostics.Debug.WriteLine (
+ // $"X:{mouseEvent.MousePosition.X};Y:{mouseEvent.MousePosition.Y};ButtonState:{mouseEvent.ButtonState};EventFlags:{mouseEvent.EventFlags}");
+
+ if (_isButtonDoubleClicked || _isOneFingerDoubleClicked) {
+ Application.MainLoop.AddIdle (() => {
+ Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
+ return false;
+ });
+ }
- return new MouseEvent () {
- X = mouseEvent.MousePosition.X,
- Y = mouseEvent.MousePosition.Y,
- Flags = mouseFlag
- };
+ // The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
+ // This will tell when a mouse button is pressed. When the button is released this event will
+ // be fired with it's bit set to 0. So when the button is up ButtonState will be 0.
+ // To map to the correct driver events we save the last pressed mouse button so we can
+ // map to the correct clicked event.
+ if ((_lastMouseButtonPressed != null || _isButtonReleased) && mouseEvent.ButtonState != 0) {
+ _lastMouseButtonPressed = null;
+ //isButtonPressed = false;
+ _isButtonReleased = false;
}
- MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent)
- {
- MouseFlags mouseFlag = 0;
- switch (lastMouseButtonPressed) {
+ var p = new Point () {
+ X = mouseEvent.MousePosition.X,
+ Y = mouseEvent.MousePosition.Y
+ };
+
+ if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && _lastMouseButtonPressed == null && !_isButtonDoubleClicked) ||
+ (_lastMouseButtonPressed == null && mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.MouseMoved) &&
+ mouseEvent.ButtonState != 0 && !_isButtonReleased && !_isButtonDoubleClicked)) {
+ switch (mouseEvent.ButtonState) {
case WindowsConsole.ButtonState.Button1Pressed:
- mouseFlag = MouseFlags.Button1Clicked;
+ mouseFlag = MouseFlags.Button1Pressed;
break;
case WindowsConsole.ButtonState.Button2Pressed:
- mouseFlag = MouseFlags.Button2Clicked;
+ mouseFlag = MouseFlags.Button2Pressed;
break;
case WindowsConsole.ButtonState.RightmostButtonPressed:
- mouseFlag = MouseFlags.Button3Clicked;
+ mouseFlag = MouseFlags.Button3Pressed;
break;
}
- point = new Point () {
- X = mouseEvent.MousePosition.X,
- Y = mouseEvent.MousePosition.Y
- };
- lastMouseButtonPressed = null;
- isButtonReleased = false;
- processButtonClick = false;
- point = null;
- return mouseFlag;
- }
- async Task ProcessButtonDoubleClickedAsync ()
- {
- await Task.Delay (300);
- isButtonDoubleClicked = false;
- isOneFingerDoubleClicked = false;
- //buttonPressedCount = 0;
- }
+ if (_point == null) {
+ _point = p;
+ }
- async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
- {
- while (isButtonPressed) {
- await Task.Delay (100);
- var me = new MouseEvent () {
- X = pointMove.X,
- Y = pointMove.Y,
- Flags = mouseFlag
- };
+ if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
+ mouseFlag |= MouseFlags.ReportMousePosition;
+ _isButtonReleased = false;
+ _processButtonClick = false;
+ }
+ _lastMouseButtonPressed = mouseEvent.ButtonState;
+ _isButtonPressed = true;
- var view = Application.WantContinuousButtonPressedView;
- if (view == null) {
- break;
- }
- if (isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
- Application.MainLoop.Invoke (() => mouseHandler (me));
- }
+ if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+ Application.MainLoop.AddIdle (() => {
+ Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseFlag));
+ return false;
+ });
}
- }
- static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag)
- {
- if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) ||
- mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed))
- mouseFlag |= MouseFlags.ButtonCtrl;
+ } else if (_lastMouseButtonPressed != null && mouseEvent.EventFlags == 0
+ && !_isButtonReleased && !_isButtonDoubleClicked && !_isOneFingerDoubleClicked) {
+ switch (_lastMouseButtonPressed) {
+ case WindowsConsole.ButtonState.Button1Pressed:
+ mouseFlag = MouseFlags.Button1Released;
+ break;
- if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed))
- mouseFlag |= MouseFlags.ButtonShift;
+ case WindowsConsole.ButtonState.Button2Pressed:
+ mouseFlag = MouseFlags.Button2Released;
+ break;
- if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
- mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed))
- mouseFlag |= MouseFlags.ButtonAlt;
- return mouseFlag;
- }
+ case WindowsConsole.ButtonState.RightmostButtonPressed:
+ mouseFlag = MouseFlags.Button3Released;
+ break;
+ }
+ _isButtonPressed = false;
+ _isButtonReleased = true;
+ if (_point != null && (((Point)_point).X == mouseEvent.MousePosition.X && ((Point)_point).Y == mouseEvent.MousePosition.Y)) {
+ _processButtonClick = true;
+ } else {
+ _point = null;
+ }
+ } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved
+ && !_isOneFingerDoubleClicked && _isButtonReleased && p == _point) {
- KeyModifiers keyModifiers;
+ mouseFlag = ProcessButtonClick (mouseEvent);
- public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
- {
- var state = keyEvent.dwControlKeyState;
-
- bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
- bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
- bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
- bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0;
- bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0;
- bool scrolllock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0;
-
- if (keyModifiers == null)
- keyModifiers = new KeyModifiers ();
- if (shift)
- keyModifiers.Shift = shift;
- if (alt)
- keyModifiers.Alt = alt;
- if (control)
- keyModifiers.Ctrl = control;
- if (capslock)
- keyModifiers.Capslock = capslock;
- if (numlock)
- keyModifiers.Numlock = numlock;
- if (scrolllock)
- keyModifiers.Scrolllock = scrolllock;
-
- var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
-
- return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock, scrolllock);
- }
-
- public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
- {
- if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) {
- return keyEvent;
- }
+ } else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) {
+ switch (mouseEvent.ButtonState) {
+ case WindowsConsole.ButtonState.Button1Pressed:
+ mouseFlag = MouseFlags.Button1DoubleClicked;
+ break;
- var mod = new ConsoleModifiers ();
- if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) {
- mod |= ConsoleModifiers.Shift;
- }
- if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
- keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) {
- mod |= ConsoleModifiers.Alt;
- }
- if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) ||
- keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) {
- mod |= ConsoleModifiers.Control;
- }
- var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode);
-
- return new WindowsConsole.KeyEventRecord {
- UnicodeChar = (char)keyChar,
- bKeyDown = keyEvent.bKeyDown,
- dwControlKeyState = keyEvent.dwControlKeyState,
- wRepeatCount = keyEvent.wRepeatCount,
- wVirtualKeyCode = (ushort)virtualKey,
- wVirtualScanCode = (ushort)scanCode
- };
- }
+ case WindowsConsole.ButtonState.Button2Pressed:
+ mouseFlag = MouseFlags.Button2DoubleClicked;
+ break;
- public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
- {
- var keyInfo = keyInfoEx.consoleKeyInfo;
- switch (keyInfo.Key) {
- case ConsoleKey.Escape:
- return MapKeyModifiers (keyInfo, Key.Esc);
- case ConsoleKey.Tab:
- return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
- case ConsoleKey.Clear:
- return MapKeyModifiers (keyInfo, Key.Clear);
- case ConsoleKey.Home:
- return MapKeyModifiers (keyInfo, Key.Home);
- case ConsoleKey.End:
- return MapKeyModifiers (keyInfo, Key.End);
- case ConsoleKey.LeftArrow:
- return MapKeyModifiers (keyInfo, Key.CursorLeft);
- case ConsoleKey.RightArrow:
- return MapKeyModifiers (keyInfo, Key.CursorRight);
- case ConsoleKey.UpArrow:
- return MapKeyModifiers (keyInfo, Key.CursorUp);
- case ConsoleKey.DownArrow:
- return MapKeyModifiers (keyInfo, Key.CursorDown);
- case ConsoleKey.PageUp:
- return MapKeyModifiers (keyInfo, Key.PageUp);
- case ConsoleKey.PageDown:
- return MapKeyModifiers (keyInfo, Key.PageDown);
- case ConsoleKey.Enter:
- return MapKeyModifiers (keyInfo, Key.Enter);
- case ConsoleKey.Spacebar:
- return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
- case ConsoleKey.Backspace:
- return MapKeyModifiers (keyInfo, Key.Backspace);
- case ConsoleKey.Delete:
- return MapKeyModifiers (keyInfo, Key.DeleteChar);
- case ConsoleKey.Insert:
- return MapKeyModifiers (keyInfo, Key.InsertChar);
- case ConsoleKey.PrintScreen:
- return MapKeyModifiers (keyInfo, Key.PrintScreen);
-
- case ConsoleKey.NumPad0:
- return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar;
- case ConsoleKey.NumPad1:
- return keyInfoEx.NumLock ? Key.D1 : Key.End;
- case ConsoleKey.NumPad2:
- return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown;
- case ConsoleKey.NumPad3:
- return keyInfoEx.NumLock ? Key.D3 : Key.PageDown;
- case ConsoleKey.NumPad4:
- return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft;
- case ConsoleKey.NumPad5:
- return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar);
- case ConsoleKey.NumPad6:
- return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight;
- case ConsoleKey.NumPad7:
- return keyInfoEx.NumLock ? Key.D7 : Key.Home;
- case ConsoleKey.NumPad8:
- return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp;
- case ConsoleKey.NumPad9:
- return keyInfoEx.NumLock ? Key.D9 : Key.PageUp;
-
- case ConsoleKey.Oem1:
- case ConsoleKey.Oem2:
- case ConsoleKey.Oem3:
- case ConsoleKey.Oem4:
- case ConsoleKey.Oem5:
- case ConsoleKey.Oem6:
- case ConsoleKey.Oem7:
- case ConsoleKey.Oem8:
- case ConsoleKey.Oem102:
- case ConsoleKey.OemPeriod:
- case ConsoleKey.OemComma:
- case ConsoleKey.OemPlus:
- case ConsoleKey.OemMinus:
- if (keyInfo.KeyChar == 0)
- return Key.Unknown;
-
- return (Key)((uint)keyInfo.KeyChar);
+ case WindowsConsole.ButtonState.RightmostButtonPressed:
+ mouseFlag = MouseFlags.Button3DoubleClicked;
+ break;
}
+ _isButtonDoubleClicked = true;
+ } else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && _isButtonDoubleClicked) {
+ switch (mouseEvent.ButtonState) {
+ case WindowsConsole.ButtonState.Button1Pressed:
+ mouseFlag = MouseFlags.Button1TripleClicked;
+ break;
- var key = keyInfo.Key;
- //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
+ case WindowsConsole.ButtonState.Button2Pressed:
+ mouseFlag = MouseFlags.Button2TripleClicked;
+ break;
- if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
- var delta = key - ConsoleKey.A;
- 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 | ConsoleModifiers.Alt)) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
- }
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
- }
- }
- //return (Key)((uint)alphaBase + delta);
- return (Key)((uint)keyInfo.KeyChar);
+ case WindowsConsole.ButtonState.RightmostButtonPressed:
+ mouseFlag = MouseFlags.Button3TripleClicked;
+ break;
}
- if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
- var delta = key - ConsoleKey.D0;
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
- return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
- }
- if (keyInfo.Modifiers == ConsoleModifiers.Control) {
- return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
- }
- if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
- }
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
- }
- }
- return (Key)((uint)keyInfo.KeyChar);
+ _isButtonDoubleClicked = false;
+ } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) {
+ switch ((int)mouseEvent.ButtonState) {
+ case int v when v > 0:
+ mouseFlag = MouseFlags.WheeledUp;
+ break;
+
+ case int v when v < 0:
+ mouseFlag = MouseFlags.WheeledDown;
+ break;
}
- if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
- var delta = key - ConsoleKey.F1;
- if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
- }
- return (Key)((uint)Key.F1 + delta);
+ } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled &&
+ mouseEvent.ControlKeyState == WindowsConsole.ControlKeyState.ShiftPressed) {
+ switch ((int)mouseEvent.ButtonState) {
+ case int v when v > 0:
+ mouseFlag = MouseFlags.WheeledLeft;
+ break;
+
+ case int v when v < 0:
+ mouseFlag = MouseFlags.WheeledRight;
+ break;
}
- if (keyInfo.KeyChar != 0) {
- return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
+
+ } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseHorizontalWheeled) {
+ switch ((int)mouseEvent.ButtonState) {
+ case int v when v < 0:
+ mouseFlag = MouseFlags.WheeledLeft;
+ break;
+
+ case int v when v > 0:
+ mouseFlag = MouseFlags.WheeledRight;
+ break;
}
- return (Key)(0xffffffff);
+ } else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
+ mouseFlag = MouseFlags.ReportMousePosition;
+ if (mouseEvent.MousePosition.X != _pointMove.X || mouseEvent.MousePosition.Y != _pointMove.Y) {
+ _pointMove = new Point (mouseEvent.MousePosition.X, mouseEvent.MousePosition.Y);
+ }
+ } else if (mouseEvent.ButtonState == 0 && mouseEvent.EventFlags == 0) {
+ mouseFlag = 0;
}
- private 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;
+ mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
- return keyMod != Key.Null ? keyMod | key : key;
- }
+ //System.Diagnostics.Debug.WriteLine (
+ // $"point.X:{(point != null ? ((Point)point).X : -1)};point.Y:{(point != null ? ((Point)point).Y : -1)}");
- public override void Init (Action terminalResized)
- {
- TerminalResized = terminalResized;
+ return new MouseEvent () {
+ X = mouseEvent.MousePosition.X,
+ Y = mouseEvent.MousePosition.Y,
+ Flags = mouseFlag
+ };
+ }
- try {
- // Needed for Windows Terminal
- // 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 () is not needed. See https://stackoverflow.com/a/20450486/297526
-
- 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 ();
-
- CurrentAttribute = MakeColor (Color.White, Color.Black);
- InitalizeColorSchemes ();
-
- ResizeScreen ();
- UpdateOffScreen ();
- } catch (Win32Exception e) {
- throw new InvalidOperationException ("The Windows Console output window is not available.", e);
- }
- }
+ MouseFlags ProcessButtonClick (WindowsConsole.MouseEventRecord mouseEvent)
+ {
+ MouseFlags mouseFlag = 0;
+ switch (_lastMouseButtonPressed) {
+ case WindowsConsole.ButtonState.Button1Pressed:
+ mouseFlag = MouseFlags.Button1Clicked;
+ 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
+ };
+ _lastMouseButtonPressed = null;
+ _isButtonReleased = false;
+ _processButtonClick = false;
+ _point = null;
+ return mouseFlag;
+ }
- public override void ResizeScreen ()
- {
- OutputBuffer = new WindowsConsole.CharInfo [Rows * Cols];
- Clip = new Rect (0, 0, Cols, Rows);
- damageRegion = new WindowsConsole.SmallRect () {
- Top = 0,
- Left = 0,
- Bottom = (short)Rows,
- Right = (short)Cols
+ async Task ProcessButtonDoubleClickedAsync ()
+ {
+ await Task.Delay (300);
+ _isButtonDoubleClicked = false;
+ _isOneFingerDoubleClicked = false;
+ //buttonPressedCount = 0;
+ }
+
+ async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
+ {
+ while (_isButtonPressed) {
+ await Task.Delay (100);
+ var me = new MouseEvent () {
+ X = _pointMove.X,
+ Y = _pointMove.Y,
+ Flags = mouseFlag
};
- 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");
+
+ var view = Application.WantContinuousButtonPressedView;
+ if (view == null) {
+ break;
+ }
+ if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+ Application.MainLoop.Invoke (() => _mouseHandler (me));
}
}
+ }
- public override void UpdateOffScreen ()
- {
- contents = new int [rows, cols, 3];
- for (int row = 0; row < rows; row++) {
- for (int col = 0; col < cols; col++) {
- int position = row * cols + col;
- OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
- OutputBuffer [position].Char.UnicodeChar = ' ';
- contents [row, col, 0] = OutputBuffer [position].Char.UnicodeChar;
- contents [row, col, 1] = OutputBuffer [position].Attributes;
- contents [row, col, 2] = 0;
- }
- }
+ static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag)
+ {
+ if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) ||
+ mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed)) {
+ mouseFlag |= MouseFlags.ButtonCtrl;
}
- int ccol, crow;
- public override void Move (int col, int row)
- {
- ccol = col;
- crow = row;
+ if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) {
+ mouseFlag |= MouseFlags.ButtonShift;
}
- int GetOutputBufferPosition ()
- {
- return crow * Cols + ccol;
+ if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
+ mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) {
+ mouseFlag |= MouseFlags.ButtonAlt;
}
+ return mouseFlag;
+ }
- public override bool IsRuneSupported (Rune rune)
- {
- // See Issue #2610
- return base.IsRuneSupported (rune) && rune.IsBmp;
+ KeyModifiers _keyModifiers;
+
+ public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
+ {
+ var state = keyEvent.dwControlKeyState;
+
+ var shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
+ var alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
+ var control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
+ var capsLock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0;
+ var numLock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0;
+ var scrollLock = (state & (WindowsConsole.ControlKeyState.ScrolllockOn)) != 0;
+
+ _keyModifiers ??= new KeyModifiers ();
+ if (shift) {
+ _keyModifiers.Shift = true;
+ }
+ if (alt) {
+ _keyModifiers.Alt = true;
+ }
+ if (control) {
+ _keyModifiers.Ctrl = true;
+ }
+ if (capsLock) {
+ _keyModifiers.Capslock = true;
+ }
+ if (numLock) {
+ _keyModifiers.Numlock = true;
+ }
+ if (scrollLock) {
+ _keyModifiers.Scrolllock = true;
}
- public override void AddRune (Rune rune)
- {
- if (!IsRuneSupported (rune)) {
- rune = Rune.ReplacementChar;
- }
+ var consoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
- rune = rune.MakePrintable ();
- var runeWidth = rune.GetColumns ();
- var position = GetOutputBufferPosition ();
- var validClip = IsValidContent (ccol, crow, Clip);
-
- if (validClip) {
- if (runeWidth == 0 && ccol > 0) {
- var r = contents [crow, ccol - 1, 0];
- var s = new string (new char [] { (char)r, (char)rune.Value });
- string sn;
- if (!s.IsNormalized ()) {
- sn = s.Normalize ();
- } else {
- sn = s;
- }
- var c = sn [0];
- 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;
- contents [crow, ccol - 1, 2] = 1;
- WindowsConsole.SmallRect.Update (ref damageRegion, (short)(ccol - 1), (short)crow);
- } else {
- if (runeWidth < 2 && ccol > 0
- && ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
+ return new WindowsConsole.ConsoleKeyInfoEx (consoleKeyInfo, capsLock, numLock, scrollLock);
+ }
- var prevPosition = crow * Cols + (ccol - 1);
- OutputBuffer [prevPosition].Char.UnicodeChar = ' ';
- contents [crow, ccol - 1, 0] = (int)(uint)' ';
+ public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
+ {
+ if (keyEvent.wVirtualKeyCode != (uint)ConsoleKey.Packet) {
+ return keyEvent;
+ }
- } else if (runeWidth < 2 && ccol <= Clip.Right - 1
- && ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
+ var mod = new ConsoleModifiers ();
+ if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed)) {
+ mod |= ConsoleModifiers.Shift;
+ }
+ if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
+ keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed)) {
+ mod |= ConsoleModifiers.Alt;
+ }
+ if (keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed) ||
+ keyEvent.dwControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed)) {
+ mod |= ConsoleModifiers.Control;
+ }
+ var keyChar = ConsoleKeyMapping.GetKeyCharFromConsoleKey (keyEvent.UnicodeChar, mod, out uint virtualKey, out uint scanCode);
- var prevPosition = GetOutputBufferPosition () + 1;
- OutputBuffer [prevPosition].Char.UnicodeChar = (char)' ';
- contents [crow, ccol + 1, 0] = (int)(uint)' ';
+ return new WindowsConsole.KeyEventRecord {
+ UnicodeChar = (char)keyChar,
+ bKeyDown = keyEvent.bKeyDown,
+ dwControlKeyState = keyEvent.dwControlKeyState,
+ wRepeatCount = keyEvent.wRepeatCount,
+ wVirtualKeyCode = (ushort)virtualKey,
+ wVirtualScanCode = (ushort)scanCode
+ };
+ }
- }
- if (runeWidth > 1 && ccol == Clip.Right - 1) {
- OutputBuffer [position].Char.UnicodeChar = (char)' ';
- contents [crow, ccol, 0] = (int)(uint)' ';
- } else {
- OutputBuffer [position].Char.UnicodeChar = (char)rune.Value;
- contents [crow, ccol, 0] = (int)(uint)rune.Value;
- }
- OutputBuffer [position].Attributes = (ushort)CurrentAttribute;
- contents [crow, ccol, 1] = CurrentAttribute;
- contents [crow, ccol, 2] = 1;
- WindowsConsole.SmallRect.Update (ref damageRegion, (short)ccol, (short)crow);
+ public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
+ {
+ var keyInfo = keyInfoEx.ConsoleKeyInfo;
+ switch (keyInfo.Key) {
+ case ConsoleKey.Escape:
+ return MapKeyModifiers (keyInfo, Key.Esc);
+ case ConsoleKey.Tab:
+ return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
+ case ConsoleKey.Clear:
+ return MapKeyModifiers (keyInfo, Key.Clear);
+ case ConsoleKey.Home:
+ return MapKeyModifiers (keyInfo, Key.Home);
+ case ConsoleKey.End:
+ return MapKeyModifiers (keyInfo, Key.End);
+ case ConsoleKey.LeftArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorLeft);
+ case ConsoleKey.RightArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorRight);
+ case ConsoleKey.UpArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorUp);
+ case ConsoleKey.DownArrow:
+ return MapKeyModifiers (keyInfo, Key.CursorDown);
+ case ConsoleKey.PageUp:
+ return MapKeyModifiers (keyInfo, Key.PageUp);
+ case ConsoleKey.PageDown:
+ return MapKeyModifiers (keyInfo, Key.PageDown);
+ case ConsoleKey.Enter:
+ return MapKeyModifiers (keyInfo, Key.Enter);
+ case ConsoleKey.Spacebar:
+ return MapKeyModifiers (keyInfo, keyInfo.KeyChar == 0 ? Key.Space : (Key)keyInfo.KeyChar);
+ case ConsoleKey.Backspace:
+ return MapKeyModifiers (keyInfo, Key.Backspace);
+ case ConsoleKey.Delete:
+ return MapKeyModifiers (keyInfo, Key.DeleteChar);
+ case ConsoleKey.Insert:
+ return MapKeyModifiers (keyInfo, Key.InsertChar);
+ case ConsoleKey.PrintScreen:
+ return MapKeyModifiers (keyInfo, Key.PrintScreen);
+
+ case ConsoleKey.NumPad0:
+ return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar;
+ case ConsoleKey.NumPad1:
+ return keyInfoEx.NumLock ? Key.D1 : Key.End;
+ case ConsoleKey.NumPad2:
+ return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown;
+ case ConsoleKey.NumPad3:
+ return keyInfoEx.NumLock ? Key.D3 : Key.PageDown;
+ case ConsoleKey.NumPad4:
+ return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft;
+ case ConsoleKey.NumPad5:
+ return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar);
+ case ConsoleKey.NumPad6:
+ return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight;
+ case ConsoleKey.NumPad7:
+ return keyInfoEx.NumLock ? Key.D7 : Key.Home;
+ case ConsoleKey.NumPad8:
+ return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp;
+ case ConsoleKey.NumPad9:
+ return keyInfoEx.NumLock ? Key.D9 : Key.PageUp;
+
+ case ConsoleKey.Oem1:
+ case ConsoleKey.Oem2:
+ case ConsoleKey.Oem3:
+ case ConsoleKey.Oem4:
+ case ConsoleKey.Oem5:
+ case ConsoleKey.Oem6:
+ case ConsoleKey.Oem7:
+ case ConsoleKey.Oem8:
+ case ConsoleKey.Oem102:
+ case ConsoleKey.OemPeriod:
+ case ConsoleKey.OemComma:
+ case ConsoleKey.OemPlus:
+ case ConsoleKey.OemMinus:
+ if (keyInfo.KeyChar == 0) {
+ return Key.Unknown;
+ }
+
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+
+ var key = keyInfo.Key;
+ //var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
+
+ if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
+ var delta = key - ConsoleKey.A;
+ 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 | ConsoleModifiers.Alt)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
}
}
-
- if (runeWidth < 0 || runeWidth > 0) {
- ccol++;
+ //return (Key)((uint)alphaBase + delta);
+ return (Key)((uint)keyInfo.KeyChar);
+ }
+ if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
+ var delta = key - ConsoleKey.D0;
+ if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
+ return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta));
}
-
- if (runeWidth > 1) {
- if (validClip && ccol < Clip.Right) {
- position = GetOutputBufferPosition ();
- 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, 2] = 0;
- }
- ccol++;
+ if (keyInfo.Modifiers == ConsoleModifiers.Control) {
+ return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta));
}
-
- if (sync) {
- UpdateScreen ();
+ if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+ }
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == ((uint)Key.D0 + delta)) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
+ }
}
+ return (Key)((uint)keyInfo.KeyChar);
}
+ if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
+ var delta = key - ConsoleKey.F1;
+ if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
+ }
- public override void AddStr (string str)
- {
- foreach (var rune in str.EnumerateRunes ())
- AddRune (rune);
+ return (Key)((uint)Key.F1 + delta);
}
-
- public override void SetAttribute (Attribute c)
- {
- base.SetAttribute (c);
+ if (keyInfo.KeyChar != 0) {
+ return MapKeyModifiers (keyInfo, (Key)((uint)keyInfo.KeyChar));
}
- public override Attribute MakeColor (Color foreground, Color background)
- {
- return MakeColor ((ConsoleColor)foreground, (ConsoleColor)background);
- }
+ return (Key)(0xffffffff);
+ }
- Attribute MakeColor (ConsoleColor f, ConsoleColor b)
- {
- // Encode the colors into the int value.
- return new Attribute (
- value: ((int)f | (int)b << 4),
- foreground: (Color)f,
- background: (Color)b
- );
+ private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+ {
+ Key keyMod = new Key ();
+ if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
+ keyMod = Key.ShiftMask;
}
-
- public override Attribute MakeAttribute (Color fore, Color back)
- {
- return MakeColor ((ConsoleColor)fore, (ConsoleColor)back);
+ if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
+ keyMod |= Key.CtrlMask;
+ }
+ if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
+ keyMod |= Key.AltMask;
}
- public override void Refresh ()
- {
- UpdateScreen ();
+ return keyMod != Key.Null ? keyMod | key : key;
+ }
- WinConsole.SetInitialCursorVisibility ();
+ public override bool IsRuneSupported (Rune rune)
+ {
+ return base.IsRuneSupported (rune) && rune.IsBmp;
+ }
- UpdateCursor ();
-#if false
- var bufferCoords = new WindowsConsole.Coord (){
- X = (short)Clip.Width,
- Y = (short)Clip.Height
- };
+ public override void Init (Action terminalResized)
+ {
+ TerminalResized = terminalResized;
- var window = new WindowsConsole.SmallRect (){
- Top = 0,
- Left = 0,
- Right = (short)Clip.Right,
- Bottom = (short)Clip.Bottom
- };
+ try {
+ // Needed for Windows Terminal
+ Console.Out.Write (EscSeqUtils.CSI_ActivateAltBufferNoBackscroll);
- UpdateCursor();
- WinConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
-#endif
+ var winSize = WinConsole.GetConsoleOutputWindow (out Point pos);
+ Cols = winSize.Width;
+ Rows = winSize.Height;
+ WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion);
+
+ } catch (Win32Exception) {
+ // Likely running unit tests. Set WinConsole to null so we can test it elsewhere.
+ WinConsole = null;
}
- public override void UpdateScreen ()
- {
- if (damageRegion.Left == -1)
- return;
+ CurrentAttribute = MakeColor (Color.White, Color.Black);
+ InitializeColorSchemes ();
- if (!EnableConsoleScrolling) {
- var windowSize = WinConsole.GetConsoleBufferWindow (out _);
- if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows))
- return;
- }
+ _outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols];
+ Clip = new Rect (0, 0, Cols, Rows);
+ _damageRegion = new WindowsConsole.SmallRect () {
+ Top = 0,
+ Left = 0,
+ Bottom = (short)Rows,
+ Right = (short)Cols
+ };
- var bufferCoords = new WindowsConsole.Coord () {
- X = (short)Clip.Width,
- Y = (short)Clip.Height
- };
+ ClearContents ();
+ }
+
+ public virtual void ResizeScreen ()
+ {
+ if (WinConsole == null) {
+ return;
+ }
- //var window = new WindowsConsole.SmallRect () {
- // Top = 0,
- // Left = 0,
- // Right = (short)Clip.Right,
- // Bottom = (short)Clip.Bottom
- //};
+ _outputBuffer = new WindowsConsole.ExtendedCharInfo [Rows * Cols];
+ Clip = new Rect (0, 0, Cols, Rows);
+ _damageRegion = new WindowsConsole.SmallRect () {
+ Top = 0,
+ Left = 0,
+ Bottom = (short)Rows,
+ Right = (short)Cols
+ };
+ _dirtyLines = new bool [Rows];
- WinConsole.WriteToConsole (new Size (Cols, Rows), OutputBuffer, bufferCoords, damageRegion);
+ WinConsole.ForceRefreshCursorVisibility ();
+ }
- // System.Diagnostics.Debugger.Log (0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
- WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
+
+ public override void UpdateScreen ()
+ {
+ var windowSize = WinConsole.GetConsoleBufferWindow (out _);
+ if (!windowSize.IsEmpty && (windowSize.Width != Cols || windowSize.Height != Rows)) {
+ return;
}
- CursorVisibility savedCursorVisibility;
+ var bufferCoords = new WindowsConsole.Coord () {
+ X = (short)Clip.Width,
+ Y = (short)Clip.Height
+ };
- public override void UpdateCursor ()
- {
- if (ccol < 0 || crow < 0 || ccol > Cols || crow > Rows) {
- GetCursorVisibility (out CursorVisibility cursorVisibility);
- savedCursorVisibility = cursorVisibility;
- SetCursorVisibility (CursorVisibility.Invisible);
- return;
+ for (int row = 0; row < Rows; row++) {
+ if (!_dirtyLines [row]) {
+ continue;
}
+ _dirtyLines [row] = false;
- SetCursorVisibility (savedCursorVisibility);
- var position = new WindowsConsole.Coord () {
- X = (short)ccol,
- Y = (short)crow
- };
- WinConsole.SetCursorPosition (position);
+ for (int col = 0; col < Cols; col++) {
+ int position = row * Cols + col;
+ _outputBuffer [position].Attribute = Contents [row, col].Attribute.GetValueOrDefault ();
+ if (Contents [row, col].IsDirty == false) {
+ _outputBuffer [position].Empty = true;
+ _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value;
+ continue;
+ }
+ _outputBuffer [position].Empty = false;
+ if (Contents [row, col].Runes [0].IsBmp) {
+ _outputBuffer [position].Char = (char)Contents [row, col].Runes [0].Value;
+ } else {
+ //_outputBuffer [position].Empty = true;
+ _outputBuffer [position].Char = (char)Rune.ReplacementChar.Value;
+ if (Contents [row, col].Runes [0].GetColumns () > 1 && col + 1 < Cols) {
+ // TODO: This is a hack to deal with non-BMP and wide characters.
+ col++;
+ position = row * Cols + col;
+ _outputBuffer [position].Empty = false;
+ _outputBuffer [position].Char = ' ';
+ }
+ }
+ }
}
- public override void End ()
- {
- WinConsole.Cleanup ();
- WinConsole = null;
+ WinConsole.WriteToConsole (new Size (Cols, Rows), _outputBuffer, bufferCoords, _damageRegion, UseTrueColor);
+ WindowsConsole.SmallRect.MakeEmpty (ref _damageRegion);
+ }
- // Needed for Windows Terminal
- // 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");
+ public override void Refresh ()
+ {
+ UpdateScreen ();
+ WinConsole.SetInitialCursorVisibility ();
+ UpdateCursor ();
+ }
- // Disable alternative screen buffer.
- Console.Out.Write ("\x1b[?1047l");
+ #region Color Handling
- // Console.Out.Flush () is not needed. See https://stackoverflow.com/a/20450486/297526
- }
+ ///
+ /// In the WindowsDriver, colors are encoded as an int.
+ /// The background color is stored in the least significant 4 bits,
+ /// and the foreground color is stored in the next 4 bits.
+ ///
+ public override Attribute MakeColor (Color foreground, Color background)
+ {
+ // Encode the colors into the int value.
+ return new Attribute (
+ value: (((int)foreground) | ((int)background << 4)),
+ foreground: foreground,
+ background: background
+ );
+ }
- ///
- public override bool GetCursorVisibility (out CursorVisibility visibility)
- {
- return WinConsole.GetCursorVisibility (out visibility);
- }
+ ///
+ /// Extracts the foreground and background colors from the encoded value.
+ /// Assumes a 4-bit encoded value for both foreground and background colors.
+ ///
+ internal override void GetColors (int value, out Color foreground, out Color background)
+ {
+ // Assume a 4-bit encoded value for both foreground and background colors.
+ foreground = (Color)((value >> 16) & 0xF);
+ background = (Color)(value & 0xF);
+ }
- ///
- public override bool SetCursorVisibility (CursorVisibility visibility)
- {
- savedCursorVisibility = visibility;
- return WinConsole.SetCursorVisibility (visibility);
- }
+ #endregion
- ///
- public override bool EnsureCursorVisibility ()
- {
- return WinConsole.EnsureCursorVisibility ();
- }
+ CursorVisibility savedCursorVisibility;
- public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
- {
- WindowsConsole.InputRecord input = new WindowsConsole.InputRecord {
- EventType = WindowsConsole.EventType.Key
- };
+ public override void UpdateCursor ()
+ {
+ if (Col < 0 || Row < 0 || Col > Cols || Row > Rows) {
+ GetCursorVisibility (out CursorVisibility cursorVisibility);
+ savedCursorVisibility = cursorVisibility;
+ SetCursorVisibility (CursorVisibility.Invisible);
+ return;
+ }
- WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord {
- bKeyDown = true
- };
- WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState ();
- if (shift) {
- controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
- keyEvent.UnicodeChar = '\0';
- keyEvent.wVirtualKeyCode = 16;
- }
- if (alt) {
- controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed;
- controlKey |= WindowsConsole.ControlKeyState.RightAltPressed;
- keyEvent.UnicodeChar = '\0';
- keyEvent.wVirtualKeyCode = 18;
- }
- if (control) {
- controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed;
- controlKey |= WindowsConsole.ControlKeyState.RightControlPressed;
- keyEvent.UnicodeChar = '\0';
- keyEvent.wVirtualKeyCode = 17;
- }
- keyEvent.dwControlKeyState = controlKey;
+ SetCursorVisibility (savedCursorVisibility);
+ var position = new WindowsConsole.Coord () {
+ X = (short)Col,
+ Y = (short)Row
+ };
+ WinConsole.SetCursorPosition (position);
+ }
- input.KeyEvent = keyEvent;
+ ///
+ public override bool GetCursorVisibility (out CursorVisibility visibility)
+ {
+ return WinConsole.GetCursorVisibility (out visibility);
+ }
- if (shift || alt || control) {
- ProcessInput (input);
- }
+ ///
+ public override bool SetCursorVisibility (CursorVisibility visibility)
+ {
+ savedCursorVisibility = visibility;
+ return WinConsole.SetCursorVisibility (visibility);
+ }
- keyEvent.UnicodeChar = keyChar;
- if ((uint)key < 255) {
- keyEvent.wVirtualKeyCode = (ushort)key;
- } else {
- keyEvent.wVirtualKeyCode = '\0';
- }
+ ///
+ public override bool EnsureCursorVisibility ()
+ {
+ return WinConsole.EnsureCursorVisibility ();
+ }
- input.KeyEvent = keyEvent;
+ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
+ {
+ WindowsConsole.InputRecord input = new WindowsConsole.InputRecord {
+ EventType = WindowsConsole.EventType.Key
+ };
- try {
- ProcessInput (input);
- } catch (OverflowException) { } finally {
- keyEvent.bKeyDown = false;
- input.KeyEvent = keyEvent;
- ProcessInput (input);
- }
+ WindowsConsole.KeyEventRecord keyEvent = new WindowsConsole.KeyEventRecord {
+ bKeyDown = true
+ };
+ WindowsConsole.ControlKeyState controlKey = new WindowsConsole.ControlKeyState ();
+ if (shift) {
+ controlKey |= WindowsConsole.ControlKeyState.ShiftPressed;
+ keyEvent.UnicodeChar = '\0';
+ keyEvent.wVirtualKeyCode = 16;
}
-
- public override bool GetColors (int value, out Color foreground, out Color background)
- {
- bool hasColor = false;
- foreground = default;
- background = default;
- IEnumerable values = Enum.GetValues (typeof (ConsoleColor))
- .OfType ()
- .Select (s => (int)s);
- if (values.Contains ((value >> 4) & 0xffff)) {
- hasColor = true;
- background = (Color)(ConsoleColor)((value >> 4) & 0xffff);
- }
- if (values.Contains (value - ((int)background << 4))) {
- hasColor = true;
- foreground = (Color)(ConsoleColor)(value - ((int)background << 4));
- }
- return hasColor;
+ if (alt) {
+ controlKey |= WindowsConsole.ControlKeyState.LeftAltPressed;
+ controlKey |= WindowsConsole.ControlKeyState.RightAltPressed;
+ keyEvent.UnicodeChar = '\0';
+ keyEvent.wVirtualKeyCode = 18;
}
-
- #region Unused
- public override void SetColors (ConsoleColor foreground, ConsoleColor background)
- {
+ if (control) {
+ controlKey |= WindowsConsole.ControlKeyState.LeftControlPressed;
+ controlKey |= WindowsConsole.ControlKeyState.RightControlPressed;
+ keyEvent.UnicodeChar = '\0';
+ keyEvent.wVirtualKeyCode = 17;
}
+ keyEvent.dwControlKeyState = controlKey;
- public override void SetColors (short foregroundColorId, short backgroundColorId)
- {
- }
+ input.KeyEvent = keyEvent;
- public override void Suspend ()
- {
+ if (shift || alt || control) {
+ ProcessInput (input);
}
- public override void StartReportingMouseMoves ()
- {
+ keyEvent.UnicodeChar = keyChar;
+ if ((uint)key < 255) {
+ keyEvent.wVirtualKeyCode = (ushort)key;
+ } else {
+ keyEvent.wVirtualKeyCode = '\0';
}
- public override void StopReportingMouseMoves ()
- {
- }
+ input.KeyEvent = keyEvent;
- public override void UncookMouse ()
- {
+ try {
+ ProcessInput (input);
+ } catch (OverflowException) { } finally {
+ keyEvent.bKeyDown = false;
+ input.KeyEvent = keyEvent;
+ ProcessInput (input);
}
+ }
- public override void CookMouse ()
- {
- }
- #endregion
+ public override void End ()
+ {
+ WinConsole?.Cleanup ();
+ WinConsole = null;
+
+ // Disable alternative screen buffer.
+ Console.Out.Write (EscSeqUtils.CSI_RestoreAltBufferWithBackscroll);
}
+ #region Not Implemented
+ public override void Suspend ()
+ {
+ throw new NotImplementedException ();
+ }
+ #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;
+ Size _windowSize;
+ CancellationTokenSource _tokenSource = new CancellationTokenSource ();
+
+ // The records that we keep fetching
+ Queue _resultQueue = new Queue ();
+
///
- /// Mainloop intended to be used with the , and can
- /// only be used on Windows.
+ /// Invoked when a Key is pressed or released.
///
- ///
- /// 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;
- Size windowSize;
- CancellationTokenSource tokenSource = new CancellationTokenSource ();
-
- // The records that we keep fetching
- Queue resultQueue = new Queue ();
-
- ///
- /// Invoked when a Key is pressed or released.
- ///
- public Action ProcessInput;
-
- ///
- /// Invoked when the window is changed.
- ///
- public EventHandler WinChanged;
-
- public WindowsMainLoop (ConsoleDriver consoleDriver = null)
- {
- this.consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided.");
- winConsole = ((WindowsDriver)consoleDriver).WinConsole;
- }
+ public Action ProcessInput;
- void IMainLoopDriver.Setup (MainLoop mainLoop)
- {
- this.mainLoop = mainLoop;
- Task.Run (WindowsInputHandler);
- Task.Run (CheckWinChange);
- }
+ ///
+ /// Invoked when the window is changed.
+ ///
+ public EventHandler WinChanged;
- void WindowsInputHandler ()
- {
- while (true) {
- waitForProbe.Wait ();
- waitForProbe.Reset ();
+ public WindowsMainLoop (ConsoleDriver consoleDriver = null)
+ {
+ _consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided.");
+ _winConsole = ((WindowsDriver)consoleDriver).WinConsole;
+ }
- if (resultQueue?.Count == 0) {
- resultQueue.Enqueue (winConsole.ReadConsoleInput ());
- }
+ void IMainLoopDriver.Setup (MainLoop mainLoop)
+ {
+ _mainLoop = mainLoop;
+ Task.Run (WindowsInputHandler);
+ Task.Run (CheckWinChange);
+ }
- eventReady.Set ();
- }
- }
+ void WindowsInputHandler ()
+ {
+ while (true) {
+ _waitForProbe.Wait ();
+ _waitForProbe.Reset ();
- void CheckWinChange ()
- {
- while (true) {
- winChange.Wait ();
- winChange.Reset ();
- WaitWinChange ();
- winChanged = true;
- eventReady.Set ();
+ if (_resultQueue?.Count == 0) {
+ _resultQueue.Enqueue (_winConsole.ReadConsoleInput ());
}
+
+ _eventReady.Set ();
}
+ }
- void WaitWinChange ()
- {
- while (true) {
- Thread.Sleep (100);
- if (!consoleDriver.EnableConsoleScrolling) {
- windowSize = winConsole.GetConsoleBufferWindow (out _);
- //System.Diagnostics.Debug.WriteLine ($"{consoleDriver.EnableConsoleScrolling},{windowSize.Width},{windowSize.Height}");
- if (windowSize != Size.Empty && windowSize.Width != consoleDriver.Cols
- || windowSize.Height != consoleDriver.Rows) {
- return;
- }
- }
- }
+ void CheckWinChange ()
+ {
+ while (true) {
+ _winChange.Wait ();
+ _winChange.Reset ();
+ WaitWinChange ();
+ _winChanged = true;
+ _eventReady.Set ();
}
+ }
- void IMainLoopDriver.Wakeup ()
- {
- //tokenSource.Cancel ();
- eventReady.Set ();
+ void WaitWinChange ()
+ {
+ while (true) {
+ Task.Delay (500).Wait ();
+ _windowSize = _winConsole.GetConsoleBufferWindow (out _);
+ if (_windowSize != Size.Empty && _windowSize.Width != _consoleDriver.Cols
+ || _windowSize.Height != _consoleDriver.Rows) {
+ return;
+ }
}
+ }
- bool IMainLoopDriver.EventsPending (bool wait)
- {
- waitForProbe.Set ();
- winChange.Set ();
+ void IMainLoopDriver.Wakeup ()
+ {
+ //tokenSource.Cancel ();
+ _eventReady.Set ();
+ }
- if (CheckTimers (wait, out var waitTimeout)) {
- return true;
- }
+ bool IMainLoopDriver.EventsPending (bool wait)
+ {
+ _waitForProbe.Set ();
+ _winChange.Set ();
- try {
- if (!tokenSource.IsCancellationRequested) {
- eventReady.Wait (waitTimeout, tokenSource.Token);
- }
- } catch (OperationCanceledException) {
- return true;
- } finally {
- eventReady.Reset ();
- }
+ if (CheckTimers (wait, out var waitTimeout)) {
+ return true;
+ }
- if (!tokenSource.IsCancellationRequested) {
- return resultQueue.Count > 0 || CheckTimers (wait, out _) || winChanged;
+ try {
+ if (!_tokenSource.IsCancellationRequested) {
+ _eventReady.Wait (waitTimeout, _tokenSource.Token);
}
-
- tokenSource.Dispose ();
- tokenSource = new CancellationTokenSource ();
+ } catch (OperationCanceledException) {
return true;
+ } finally {
+ _eventReady.Reset ();
}
- bool CheckTimers (bool wait, out int waitTimeout)
- {
- long now = DateTime.UtcNow.Ticks;
+ if (!_tokenSource.IsCancellationRequested) {
+ return _resultQueue.Count > 0 || CheckTimers (wait, out _) || _winChanged;
+ }
- if (mainLoop.timeouts.Count > 0) {
- waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
- if (waitTimeout < 0)
- return true;
- } else {
- waitTimeout = -1;
- }
+ _tokenSource.Dispose ();
+ _tokenSource = new CancellationTokenSource ();
+ return true;
+ }
- if (!wait)
- waitTimeout = 0;
+ bool CheckTimers (bool wait, out int waitTimeout)
+ {
+ long now = DateTime.UtcNow.Ticks;
- int ic;
- lock (mainLoop.idleHandlers) {
- ic = mainLoop.idleHandlers.Count;
- }
+ 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;
- return ic > 0;
+ int ic;
+ lock (_mainLoop.idleHandlers) {
+ ic = _mainLoop.idleHandlers.Count;
}
- void IMainLoopDriver.Iteration ()
- {
- while (resultQueue.Count > 0) {
- var inputRecords = resultQueue.Dequeue ();
- if (inputRecords != null && inputRecords.Length > 0) {
- var inputEvent = inputRecords [0];
- ProcessInput?.Invoke (inputEvent);
- }
- }
- if (winChanged) {
- winChanged = false;
- WinChanged?.Invoke (this, new SizeChangedEventArgs (windowSize));
+ return ic > 0;
+ }
+
+ void IMainLoopDriver.Iteration ()
+ {
+ while (_resultQueue.Count > 0) {
+ var inputRecords = _resultQueue.Dequeue ();
+ if (inputRecords != null && inputRecords.Length > 0) {
+ var inputEvent = inputRecords [0];
+ ProcessInput?.Invoke (inputEvent);
}
}
+ if (_winChanged) {
+ _winChanged = false;
+ WinChanged?.Invoke (this, new SizeChangedEventArgs (_windowSize));
+ }
+ }
+ public void TearDown ()
+ {
+ //throw new NotImplementedException ();
}
+}
- class WindowsClipboard : ClipboardBase {
- public WindowsClipboard ()
- {
- IsSupported = IsClipboardFormatAvailable (cfUnicodeText);
- }
+class WindowsClipboard : ClipboardBase {
+ public WindowsClipboard ()
+ {
+ IsSupported = IsClipboardFormatAvailable (_cfUnicodeText);
+ }
- public override bool IsSupported { get; }
+ public override bool IsSupported { get; }
- protected override string GetClipboardDataImpl ()
- {
- try {
- if (!OpenClipboard (IntPtr.Zero)) {
- return string.Empty;
- }
+ protected override string GetClipboardDataImpl ()
+ {
+ try {
+ if (!OpenClipboard (IntPtr.Zero)) {
+ return string.Empty;
+ }
- IntPtr handle = GetClipboardData (cfUnicodeText);
- if (handle == IntPtr.Zero) {
- return string.Empty;
- }
+ IntPtr handle = GetClipboardData (_cfUnicodeText);
+ if (handle == IntPtr.Zero) {
+ return string.Empty;
+ }
- IntPtr pointer = IntPtr.Zero;
+ IntPtr pointer = IntPtr.Zero;
- try {
- pointer = GlobalLock (handle);
- if (pointer == IntPtr.Zero) {
- return string.Empty;
- }
+ try {
+ pointer = GlobalLock (handle);
+ if (pointer == IntPtr.Zero) {
+ return string.Empty;
+ }
- int size = GlobalSize (handle);
- byte [] buff = new byte [size];
+ int size = GlobalSize (handle);
+ byte [] buff = new byte [size];
- Marshal.Copy (pointer, buff, 0, size);
+ Marshal.Copy (pointer, buff, 0, size);
- return Encoding.Unicode.GetString (buff).TrimEnd ('\0');
- } finally {
- if (pointer != IntPtr.Zero) {
- GlobalUnlock (handle);
- }
- }
+ return System.Text.Encoding.Unicode.GetString (buff).TrimEnd ('\0');
} finally {
- CloseClipboard ();
+ if (pointer != IntPtr.Zero) {
+ GlobalUnlock (handle);
+ }
}
+ } finally {
+ CloseClipboard ();
}
+ }
- protected override void SetClipboardDataImpl (string text)
- {
- OpenClipboard ();
-
- EmptyClipboard ();
- IntPtr hGlobal = default;
- try {
- var bytes = (text.Length + 1) * 2;
- hGlobal = Marshal.AllocHGlobal (bytes);
-
- if (hGlobal == default) {
- ThrowWin32 ();
- }
+ protected override void SetClipboardDataImpl (string text)
+ {
+ OpenClipboard ();
- var target = GlobalLock (hGlobal);
+ EmptyClipboard ();
+ IntPtr hGlobal = default;
+ try {
+ var bytes = (text.Length + 1) * 2;
+ hGlobal = Marshal.AllocHGlobal (bytes);
- if (target == default) {
- ThrowWin32 ();
- }
+ if (hGlobal == default) {
+ ThrowWin32 ();
+ }
- try {
- Marshal.Copy (text.ToCharArray (), 0, target, text.Length);
- } finally {
- GlobalUnlock (target);
- }
+ var target = GlobalLock (hGlobal);
- if (SetClipboardData (cfUnicodeText, hGlobal) == default) {
- ThrowWin32 ();
- }
+ if (target == default) {
+ ThrowWin32 ();
+ }
- hGlobal = default;
+ try {
+ Marshal.Copy (text.ToCharArray (), 0, target, text.Length);
} finally {
- if (hGlobal != default) {
- Marshal.FreeHGlobal (hGlobal);
- }
+ GlobalUnlock (target);
+ }
- CloseClipboard ();
+ if (SetClipboardData (_cfUnicodeText, hGlobal) == default) {
+ ThrowWin32 ();
}
- }
- void OpenClipboard ()
- {
- var num = 10;
- while (true) {
- if (OpenClipboard (default)) {
- break;
- }
+ hGlobal = default;
+ } finally {
+ if (hGlobal != default) {
+ Marshal.FreeHGlobal (hGlobal);
+ }
- if (--num == 0) {
- ThrowWin32 ();
- }
+ CloseClipboard ();
+ }
+ }
- Thread.Sleep (100);
+ void OpenClipboard ()
+ {
+ var num = 10;
+ while (true) {
+ if (OpenClipboard (default)) {
+ break;
}
- }
- const uint cfUnicodeText = 13;
+ if (--num == 0) {
+ ThrowWin32 ();
+ }
- void ThrowWin32 ()
- {
- throw new Win32Exception (Marshal.GetLastWin32Error ());
+ Thread.Sleep (100);
}
+ }
- [DllImport ("User32.dll", SetLastError = true)]
- [return: MarshalAs (UnmanagedType.Bool)]
- static extern bool IsClipboardFormatAvailable (uint format);
+ const uint _cfUnicodeText = 13;
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern int GlobalSize (IntPtr handle);
+ void ThrowWin32 ()
+ {
+ throw new Win32Exception (Marshal.GetLastWin32Error ());
+ }
- [DllImport ("kernel32.dll", SetLastError = true)]
- static extern IntPtr GlobalLock (IntPtr hMem);
+ [DllImport ("User32.dll", SetLastError = true)]
+ [return: MarshalAs (UnmanagedType.Bool)]
+ static extern bool IsClipboardFormatAvailable (uint format);
- [DllImport ("kernel32.dll", SetLastError = true)]
- [return: MarshalAs (UnmanagedType.Bool)]
- static extern bool GlobalUnlock (IntPtr hMem);
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern int GlobalSize (IntPtr handle);
- [DllImport ("user32.dll", SetLastError = true)]
- [return: MarshalAs (UnmanagedType.Bool)]
- static extern bool OpenClipboard (IntPtr hWndNewOwner);
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ static extern IntPtr GlobalLock (IntPtr hMem);
- [DllImport ("user32.dll", SetLastError = true)]
- [return: MarshalAs (UnmanagedType.Bool)]
- static extern bool CloseClipboard ();
+ [DllImport ("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs (UnmanagedType.Bool)]
+ static extern bool GlobalUnlock (IntPtr hMem);
- [DllImport ("user32.dll", SetLastError = true)]
- static extern IntPtr SetClipboardData (uint uFormat, IntPtr data);
+ [DllImport ("user32.dll", SetLastError = true)]
+ [return: MarshalAs (UnmanagedType.Bool)]
+ static extern bool OpenClipboard (IntPtr hWndNewOwner);
- [DllImport ("user32.dll")]
- static extern bool EmptyClipboard ();
+ [DllImport ("user32.dll", SetLastError = true)]
+ [return: MarshalAs (UnmanagedType.Bool)]
+ static extern bool CloseClipboard ();
- [DllImport ("user32.dll", SetLastError = true)]
- static extern IntPtr GetClipboardData (uint uFormat);
- }
-}
+ [DllImport ("user32.dll", SetLastError = true)]
+ static extern IntPtr SetClipboardData (uint uFormat, IntPtr data);
+
+ [DllImport ("user32.dll")]
+ static extern bool EmptyClipboard ();
+
+ [DllImport ("user32.dll", SetLastError = true)]
+ static extern IntPtr GetClipboardData (uint uFormat);
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Drawing/Cell.cs b/Terminal.Gui/Drawing/Cell.cs
index 8d511b5ded..f6094b337f 100644
--- a/Terminal.Gui/Drawing/Cell.cs
+++ b/Terminal.Gui/Drawing/Cell.cs
@@ -1,20 +1,29 @@
-using System.Text;
+using System.Collections.Generic;
+using System.Text;
namespace Terminal.Gui;
///
-/// Represents a single row/column within the . Includes the glyph and the foreground/background colors.
+/// Represents a single row/column in a Terminal.Gui rendering surface
+/// (e.g. and ).
///
public class Cell {
///
- /// The glyph to draw.
+ /// The list of Runes to draw in this cell. If the list is empty, the cell is blank. If the list contains
+ /// more than one Rune, the cell is a combining sequence.
+ /// (See #2616 - Support combining sequences that don't normalize)
///
- public Rune? Rune { get; set; }
+ public List Runes { get; set; } = new List ();
///
- /// The foreground color to draw the glyph with.
+ /// The attributes to use when drawing the Glyph.
///
public Attribute? Attribute { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this has
+ /// been modified since the last time it was drawn.
+ ///
+ public bool IsDirty { get; set; }
}
diff --git a/Terminal.Gui/Drawing/Color.cs b/Terminal.Gui/Drawing/Color.cs
new file mode 100644
index 0000000000..f617dab498
--- /dev/null
+++ b/Terminal.Gui/Drawing/Color.cs
@@ -0,0 +1,845 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+
+namespace Terminal.Gui {
+ ///
+ /// 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.
+ ///
+ [JsonConverter (typeof (ColorJsonConverter))]
+ public enum Color {
+ ///
+ /// The black color.
+ ///
+ Black,
+ ///
+ /// The blue color.
+ ///
+ Blue,
+ ///
+ /// The green color.
+ ///
+ Green,
+ ///
+ /// The cyan color.
+ ///
+ Cyan,
+ ///
+ /// The red color.
+ ///
+ Red,
+ ///
+ /// The magenta color.
+ ///
+ Magenta,
+ ///
+ /// The brown color.
+ ///
+ Brown,
+ ///
+ /// The gray color.
+ ///
+ Gray,
+ ///
+ /// The dark gray color.
+ ///
+ DarkGray,
+ ///
+ /// The bright bBlue color.
+ ///
+ BrightBlue,
+ ///
+ /// The bright green color.
+ ///
+ BrightGreen,
+ ///
+ /// The bright cyan color.
+ ///
+ BrightCyan,
+ ///
+ /// The bright red color.
+ ///
+ BrightRed,
+ ///
+ /// The bright magenta color.
+ ///
+ BrightMagenta,
+ ///
+ /// The bright yellow color.
+ ///
+ BrightYellow,
+ ///
+ /// The White color.
+ ///
+ White
+ }
+
+ ///
+ /// Indicates the RGB for true colors.
+ ///
+ [JsonConverter (typeof (TrueColorJsonConverter))]
+ public readonly struct TrueColor : IEquatable {
+ private static readonly ImmutableDictionary TrueColorToConsoleColorMap = 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},
+ }.ToImmutableDictionary ();
+
+ ///
+ /// 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;
+ }
+
+ ///
+ /// Converts the provided text to a .
+ ///
+ /// The text to analyze.
+ /// The parsed value.
+ /// A boolean value indcating whether it was successful.
+ public static bool TryParse (string text, [NotNullWhen (true)] out TrueColor? trueColor)
+ {
+ // empty color
+ if ((text == null) || (text.Length == 0)) {
+ trueColor = null;
+ return false;
+ }
+
+ // #RRGGBB or #RGB
+ if ((text [0] == '#') &&
+ ((text.Length == 7) || (text.Length == 4))) {
+ if (text.Length == 7) {
+ var r = Convert.ToInt32 (text.Substring (1, 2), 16);
+ var g = Convert.ToInt32 (text.Substring (3, 2), 16);
+ var b = Convert.ToInt32 (text.Substring (5, 2), 16);
+ trueColor = new TrueColor (r, g, b);
+ } else {
+ var rText = char.ToString (text [1]);
+ var gText = char.ToString (text [2]);
+ var bText = char.ToString (text [3]);
+
+ var r = Convert.ToInt32 (rText + rText, 16);
+ var g = Convert.ToInt32 (gText + gText, 16);
+ var b = Convert.ToInt32 (bText + bText, 16);
+ trueColor = new TrueColor (r, g, b);
+ }
+ return true;
+ }
+
+ // rgb(XX,YY,ZZ)
+ var match = Regex.Match (text, @"rgb\((\d+),(\d+),(\d+)\)");
+ if (match.Success) {
+ var r = int.Parse (match.Groups [1].Value);
+ var g = int.Parse (match.Groups [2].Value);
+ var b = int.Parse (match.Groups [3].Value);
+ trueColor = new TrueColor (r, g, b);
+ return true;
+ }
+
+ trueColor = null;
+ return false;
+ }
+
+ ///
+ /// Converts a to a using a default mapping.
+ ///
+ /// The to convert.
+ ///
+ public static TrueColor? FromConsoleColor (Color consoleColor)
+ {
+ return consoleColor switch {
+ Color.Black => new TrueColor (0, 0, 0),
+ Color.Blue => new TrueColor (0, 0, 0x80),
+ Color.Green => new TrueColor (0, 0x80, 0),
+ Color.Cyan => new TrueColor (0, 0x80, 0x80),
+ Color.Red => new TrueColor (0x80, 0, 0),
+ Color.Magenta => new TrueColor (0x80, 0, 0x80),
+ Color.Brown => new TrueColor (0xC1, 0x9C, 0x00) // TODO confirm this
+ ,
+ Color.Gray => new TrueColor (0xC0, 0xC0, 0xC0),
+ Color.DarkGray => new TrueColor (0x80, 0x80, 0x80),
+ Color.BrightBlue => new TrueColor (0, 0, 0xFF),
+ Color.BrightGreen => new TrueColor (0, 0xFF, 0),
+ Color.BrightCyan => new TrueColor (0, 0xFF, 0xFF),
+ Color.BrightRed => new TrueColor (0xFF, 0, 0),
+ Color.BrightMagenta => new TrueColor (0xFF, 0, 0xFF),
+ Color.BrightYellow => new TrueColor (0xFF, 0xFF, 0),
+ Color.White => new TrueColor (0xFF, 0xFF, 0xFF),
+ var _ => null
+ };
+ ;
+ }
+
+ ///
+ /// Converts the provided to using a default mapping.
+ ///
+ ///
+ ///
+ public static Color ToConsoleColor (TrueColor? trueColor)
+ {
+ if (trueColor.HasValue) {
+ return TrueColorToConsoleColorMap.MinBy (kv => CalculateDistance (kv.Key, trueColor.Value)).Value;
+ } else {
+ return (Color)(-1);
+ }
+ }
+
+ private static 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);
+ }
+
+ ///
+ public static bool operator == (TrueColor left, TrueColor right)
+ {
+ return left.Equals (right);
+ }
+
+ ///
+ public static bool operator != (TrueColor left, TrueColor right)
+ {
+ return !left.Equals (right);
+ }
+
+ ///
+ public override bool Equals (object obj)
+ {
+ return obj is TrueColor other && Equals (other);
+ }
+
+ ///
+ public bool Equals (TrueColor other)
+ {
+ return
+ Red == other.Red &&
+ Green == other.Green &&
+ Blue == other.Blue;
+ }
+
+ ///
+ public override int GetHashCode ()
+ {
+ return HashCode.Combine (Red, Green, Blue);
+ }
+
+ ///
+ public override string ToString ()
+ {
+ return $"#{Red:X2}{Green:X2}{Blue:X2}";
+ }
+ }
+
+ ///
+ /// Attributes represent how text is styled when displayed in the terminal.
+ ///
+ ///
+ /// provides a platform independent representation of colors (and someday other forms of text styling).
+ /// 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.
+ ///
+ [JsonConverter (typeof (AttributeJsonConverter))]
+ public struct Attribute : IEquatable {
+
+ ///
+ /// Default empty attribute.
+ ///
+ public static readonly Attribute Default = new Attribute (Color.White, Color.Black);
+
+ ///
+ /// The -specific color 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.
+ ///
+ [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
+ internal int Value { get; }
+
+ ///
+ /// The foreground color.
+ ///
+ [JsonConverter (typeof (ColorJsonConverter))]
+ public Color Foreground { get; private init; }
+
+ ///
+ /// The background color.
+ ///
+ [JsonConverter (typeof (ColorJsonConverter))]
+ public Color Background { get; private init; }
+
+ ///
+ /// Gets the TrueColor foreground color.
+ ///
+ [JsonConverter (typeof (TrueColorJsonConverter))]
+ public TrueColor? TrueColorForeground { get; private init; }
+
+ ///
+ /// Gets the TrueColor background color.
+ ///
+ [JsonConverter (typeof (TrueColorJsonConverter))]
+ public TrueColor? TrueColorBackground { get; private init; }
+
+ ///
+ /// Initializes a new instance with a platform-specific color value.
+ ///
+ /// Value.
+ internal 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;
+ Background = background;
+ TrueColorForeground = TrueColor.FromConsoleColor (foreground);
+ TrueColorBackground = TrueColor.FromConsoleColor (background);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// platform-dependent color value.
+ /// Foreground
+ /// Background
+ public Attribute (int value, Color foreground, Color background)
+ {
+ Foreground = foreground;
+ Background = background;
+ TrueColorForeground = TrueColor.FromConsoleColor (foreground);
+ TrueColorBackground = TrueColor.FromConsoleColor (background);
+ Value = value;
+ Initialized = true;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Foreground
+ /// Background
+ public Attribute (Color foreground = new Color (), Color background = new Color ())
+ {
+ Foreground = foreground;
+ Background = background;
+ TrueColorForeground = TrueColor.FromConsoleColor (foreground);
+ TrueColorBackground = TrueColor.FromConsoleColor (background);
+
+ var make = Make (foreground, background);
+ Initialized = make.Initialized;
+ Value = make.Value;
+ }
+
+ ///
+ /// Initializes a new instance of the class. Populates
+ /// and . Also computes
+ /// and (basic console colors) in case
+ /// driver does not support true color rendering.
+ ///
+ ///
+ ///
+ public Attribute (TrueColor? trueColorForeground, TrueColor? trueColorBackground)
+ {
+ Foreground = TrueColor.ToConsoleColor (trueColorForeground);
+ Background = TrueColor.ToConsoleColor (trueColorBackground);
+ TrueColorForeground = trueColorForeground;
+ TrueColorBackground = trueColorBackground;
+ var make = Make (Foreground, Background);
+ Value = make.Value;
+ Initialized = make.Initialized;
+ }
+
+ ///
+ ///
+ /// Initializes a new instance of the class. Populates
+ /// and with explicit
+ /// fallback values for and (in case
+ /// driver does not support true color rendering).
+ ///
+ /// If you do not want to manually specify the fallback colors use
+ /// instead which auto calculates these.
+ ///
+ /// True color RGB values you would like to use.
+ /// True color RGB values you would like to use.
+ /// Simple console color replacement if driver does not support true color.
+ /// Simple console color replacement if driver does not support true color.
+ public Attribute (TrueColor trueColorForeground, TrueColor trueColorBackground, Color foreground, Color background)
+ {
+ Foreground = foreground;
+ Background = background;
+ TrueColorForeground = trueColorForeground;
+ TrueColorBackground = trueColorBackground;
+ var make = Make (Foreground, Background);
+ Value = make.Value;
+ Initialized = make.Initialized;
+ }
+
+ ///
+ /// Initializes a new instance of the struct
+ /// with the same colors for the foreground and background.
+ ///
+ /// The color.
+ public Attribute (Color color) : this (color, color) { }
+
+
+ ///
+ /// Compares two attributes for equality.
+ ///
+ ///
+ ///
+ ///
+ public static bool operator == (Attribute left, Attribute right) => left.Equals (right);
+
+ ///
+ /// Compares two attributes for inequality.
+ ///
+ ///
+ ///
+ ///
+ public static bool operator != (Attribute left, Attribute right) => !(left == right);
+
+ ///
+ public override bool Equals (object obj)
+ {
+ return obj is Attribute other && Equals (other);
+ }
+
+ ///
+ public bool Equals (Attribute other)
+ {
+ if (TrueColorForeground.HasValue || TrueColorBackground.HasValue) {
+ return
+ TrueColorForeground == other.TrueColorForeground &&
+ TrueColorBackground == other.TrueColorBackground;
+ }
+
+ return Value == other.Value &&
+ Foreground == other.Foreground &&
+ Background == other.Background;
+ }
+
+ ///
+ public override int GetHashCode () => HashCode.Combine (Value, Foreground, Background, TrueColorForeground, TrueColorBackground);
+
+ ///
+ /// Creates an from the specified foreground and background colors.
+ ///
+ ///
+ /// 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) {
+ // Create the attribute, but show it's not been initialized
+ return new Attribute () {
+ Initialized = false,
+ Foreground = foreground,
+ Background = background
+ };
+ }
+ return Application.Driver.MakeAttribute (foreground, background);
+ }
+
+ ///
+ /// Gets the current from the driver.
+ ///
+ /// The current attribute.
+ public static Attribute Get ()
+ {
+ if (Application.Driver == null) {
+ 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.
+ ///
+ [JsonIgnore]
+ public bool Initialized { get; internal set; }
+
+ ///
+ /// Returns if the Attribute is valid (both foreground and background have valid color values).
+ ///
+ ///
+ [JsonIgnore]
+ public bool HasValidColors => (int)Foreground > -1 && (int)Background > -1;
+
+ ///
+ public override string ToString ()
+ {
+ // Note, Unit tests are dependent on this format
+ return $"{Foreground},{Background}";
+ }
+ }
+
+ ///
+ /// 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: .
+ ///
+ [JsonConverter (typeof (ColorSchemeJsonConverter))]
+ public class ColorScheme : IEquatable {
+ Attribute _normal = Attribute.Default;
+ Attribute _focus = Attribute.Default;
+ Attribute _hotNormal = Attribute.Default;
+ Attribute _hotFocus = Attribute.Default;
+ Attribute _disabled = Attribute.Default;
+
+ ///
+ /// Used by and to track which ColorScheme
+ /// is being accessed.
+ ///
+ internal string schemeBeingSet = "";
+
+ ///
+ /// Creates a new instance.
+ ///
+ public ColorScheme () { }
+
+ ///
+ /// Creates a new instance, initialized with the values from .
+ ///
+ /// The scheme to initialize the new instance with.
+ public ColorScheme (ColorScheme scheme) : base ()
+ {
+ if (scheme != null) {
+ _normal = scheme.Normal;
+ _focus = scheme.Focus;
+ _hotNormal = scheme.HotNormal;
+ _disabled = scheme.Disabled;
+ _hotFocus = scheme.HotFocus;
+ }
+ }
+
+ ///
+ /// Creates a new instance, initialized with the values from .
+ ///
+ /// The attribute to initialize the new instance with.
+ public ColorScheme (Attribute attribute)
+ {
+ _normal = attribute;
+ _focus = attribute;
+ _hotNormal = attribute;
+ _disabled = attribute;
+ _hotFocus = attribute;
+ }
+
+ ///
+ /// The foreground and background color for text when the view is not focused, hot, or disabled.
+ ///
+ public Attribute Normal {
+ get { return _normal; }
+ set {
+ if (!value.HasValidColors) {
+ return;
+ }
+ _normal = value;
+ }
+ }
+
+ ///
+ /// The foreground and background color for text when the view has the focus.
+ ///
+ public Attribute Focus {
+ get { return _focus; }
+ set {
+ if (!value.HasValidColors) {
+ return;
+ }
+ _focus = value;
+ }
+ }
+
+ ///
+ /// The foreground and background color for text when the view is highlighted (hot).
+ ///
+ public Attribute HotNormal {
+ get { return _hotNormal; }
+ set {
+ if (!value.HasValidColors) {
+ return;
+ }
+ _hotNormal = value;
+ }
+ }
+
+ ///
+ /// The foreground and background color for text when the view is highlighted (hot) and has focus.
+ ///
+ 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.
+ ///
+ ///
+ /// true if the two objects are equal
+ public override bool Equals (object obj)
+ {
+ return Equals (obj as ColorScheme);
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// true if the two objects are equal
+ public bool Equals (ColorScheme other)
+ {
+ return other != null &&
+ EqualityComparer.Default.Equals (_normal, other._normal) &&
+ EqualityComparer.Default.Equals (_focus, other._focus) &&
+ EqualityComparer.Default.Equals (_hotNormal, other._hotNormal) &&
+ EqualityComparer.Default.Equals (_hotFocus, other._hotFocus) &&
+ EqualityComparer.Default.Equals (_disabled, other._disabled);
+ }
+
+ ///
+ /// Returns a hashcode for this instance.
+ ///
+ /// hashcode for this instance
+ public override int GetHashCode ()
+ {
+ int hashCode = -1242460230;
+ hashCode = hashCode * -1521134295 + _normal.GetHashCode ();
+ hashCode = hashCode * -1521134295 + _focus.GetHashCode ();
+ hashCode = hashCode * -1521134295 + _hotNormal.GetHashCode ();
+ hashCode = hashCode * -1521134295 + _hotFocus.GetHashCode ();
+ hashCode = hashCode * -1521134295 + _disabled.GetHashCode ();
+ return hashCode;
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ ///
+ /// true if the two objects are equivalent
+ public static bool operator == (ColorScheme left, ColorScheme right)
+ {
+ return EqualityComparer.Default.Equals (left, right);
+ }
+
+ ///
+ /// Compares two objects for inequality.
+ ///
+ ///
+ ///
+ /// true if the two objects are not equivalent
+ public static bool operator != (ColorScheme left, ColorScheme right)
+ {
+ 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 string.Equals (x, y, StringComparison.InvariantCultureIgnoreCase);
+ }
+ 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.
+ return typeof (Colors).GetProperties ()
+ .Where (p => p.PropertyType == typeof (ColorScheme))
+ .Select (p => new KeyValuePair (p.Name, new ColorScheme ()))
+ .ToDictionary (t => t.Key, t => t.Value, comparer: new SchemeNameComparerIgnoreCase ());
+ }
+
+ ///
+ /// The application Toplevel color scheme, for the default Toplevel views.
+ ///
+ ///
+ ///
+ /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["TopLevel"];
+ ///
+ ///
+ public static ColorScheme TopLevel { get => GetColorScheme (); set => SetColorScheme (value); }
+
+ ///
+ /// The base color scheme, for the default Toplevel views.
+ ///
+ ///
+ ///
+ /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Base"];
+ ///
+ ///
+ public static ColorScheme Base { get => GetColorScheme (); set => SetColorScheme (value); }
+
+ ///
+ /// The dialog color scheme, for standard popup dialog boxes
+ ///
+ ///
+ ///
+ /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Dialog"];
+ ///
+ ///
+ public static ColorScheme Dialog { get => GetColorScheme (); set => SetColorScheme (value); }
+
+ ///
+ /// The menu bar color
+ ///
+ ///
+ ///
+ /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Menu"];
+ ///
+ ///
+ public static ColorScheme Menu { get => GetColorScheme (); set => SetColorScheme (value); }
+
+ ///
+ /// The color scheme for showing errors.
+ ///
+ ///
+ ///
+ /// This API will be deprecated in the future. Use instead (e.g. edit.ColorScheme = Colors.ColorSchemes["Error"];
+ ///
+ ///
+ public static ColorScheme Error { get => GetColorScheme (); set => SetColorScheme (value); }
+
+ static ColorScheme GetColorScheme ([CallerMemberName] string schemeBeingSet = null)
+ {
+ return ColorSchemes [schemeBeingSet];
+ }
+
+ static void SetColorScheme (ColorScheme colorScheme, [CallerMemberName] string schemeBeingSet = null)
+ {
+ ColorSchemes [schemeBeingSet] = colorScheme;
+ colorScheme.schemeBeingSet = schemeBeingSet;
+ }
+
+ ///
+ /// Provides the defined s.
+ ///
+ [SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
+ [JsonConverter (typeof (DictionaryJsonConverter))]
+ public static Dictionary ColorSchemes { get; private set; }
+ }
+
+}
diff --git a/Terminal.Gui/Drawing/Glyphs.cs b/Terminal.Gui/Drawing/Glyphs.cs
index 6edb70107f..fa81604418 100644
--- a/Terminal.Gui/Drawing/Glyphs.cs
+++ b/Terminal.Gui/Drawing/Glyphs.cs
@@ -139,6 +139,11 @@ public class GlyphDefinitions {
///
public Rune Collapse { get; set; } = (Rune)'-';
+ ///
+ /// Identical To (U+226)
+ ///
+ public Rune IdenticalTo { get; set; } = (Rune)'≡';
+
///
/// Apple (non-BMP). Because snek. And because it's an example of a non-BMP surrogate pair. See Issue #2610.
///
@@ -166,6 +171,16 @@ public class GlyphDefinitions {
///
public Rune File { get; set; } = (Rune)'☰';
+ ///
+ /// Horizontal Ellipsis - … U+2026
+ ///
+ public Rune HorizontalEllipsis { get; set; } = (Rune)'…';
+
+ ///
+ /// Vertical Four Dots - ⁞ U+205e
+ ///
+ public Rune VerticalFourDots { get; set; } = (Rune)'⁞';
+
#region ----------------- Lines -----------------
///
/// Box Drawings Horizontal Line - Light (U+2500) - ─
diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs
index e7ceec8903..698b32e89c 100644
--- a/Terminal.Gui/Drawing/LineCanvas.cs
+++ b/Terminal.Gui/Drawing/LineCanvas.cs
@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -73,7 +74,7 @@ public LineCanvas()
ConfigurationManager.Applied += ConfigurationManager_Applied;
}
- private void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs e)
+ private void ConfigurationManager_Applied (object? sender, ConfigurationManagerEventArgs e)
{
foreach (var irr in runeResolvers) {
irr.Value.SetGlyphs ();
@@ -142,7 +143,7 @@ public StraightLine RemoveLastLine()
_lines.Remove(l);
}
- return l;
+ return l!;
}
///
@@ -553,14 +554,17 @@ public override void SetGlyphs ()
}
- private Cell GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
+ private Cell? GetCellForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
{
if (!intersects.Any ()) {
return null;
}
var cell = new Cell ();
- cell.Rune = GetRuneForIntersects (driver, intersects);
+ var rune = GetRuneForIntersects (driver, intersects);
+ if (rune.HasValue) {
+ cell.Runes.Add (rune.Value);
+ }
cell.Attribute = GetAttributeForIntersects (intersects);
return cell;
}
diff --git a/Terminal.Gui/Input/Command.cs b/Terminal.Gui/Input/Command.cs
index 29de444eea..b6ce90623c 100644
--- a/Terminal.Gui/Input/Command.cs
+++ b/Terminal.Gui/Input/Command.cs
@@ -340,7 +340,7 @@ public enum Command {
QuitToplevel,
///
- /// Suspend a application (used on Linux).
+ /// Suspend a application (Only implemented in ).
///
Suspend,
diff --git a/Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs b/Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs
deleted file mode 100644
index eaa3084ad9..0000000000
--- a/Terminal.Gui/Input/EscSeqUtils/EscSeqReq.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Terminal.Gui {
- ///
- /// 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 EscSeqReqStatus {
- ///
- /// Gets the terminating.
- ///
- public string Terminating { get; }
- ///
- /// Gets the number of requests.
- ///
- public int NumRequests { get; }
- ///
- /// Gets information about unfinished requests.
- ///
- public int NumOutstanding { get; set; }
-
- ///
- /// Creates a new state of escape sequence request.
- ///
- /// The terminating.
- /// The number of requests.
- public EscSeqReqStatus (string terminating, int numOfReq)
- {
- Terminating = terminating;
- NumRequests = NumOutstanding = numOfReq;
- }
- }
-
- ///
- /// Manages a list of .
- ///
- 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 EscSeqReqStatus (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.
- ///
- /// The terminating string.
- public void Remove (string terminating)
- {
- lock (EscSeqReqStats) {
- var found = EscSeqReqStats.Find (x => x.Terminating == terminating);
- if (found == null) {
- return;
- }
- if (found != null && found.NumOutstanding == 0) {
- EscSeqReqStats.Remove (found);
- } else if (found != null && found.NumOutstanding > 0) {
- found.NumOutstanding--;
- if (found.NumOutstanding == 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.NumOutstanding > 0) {
- return true;
- } else {
- EscSeqReqStats.Remove (found);
- }
- return false;
- }
- }
- }
-}
diff --git a/Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs
deleted file mode 100644
index 21a9aac594..0000000000
--- a/Terminal.Gui/Input/EscSeqUtils/EscSeqUtils.cs
+++ /dev/null
@@ -1,907 +0,0 @@
-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;
- }
- }
-}
\ No newline at end of file
diff --git a/Terminal.Gui/MainLoop.cs b/Terminal.Gui/MainLoop.cs
index 05179fbfbb..0934e15c49 100644
--- a/Terminal.Gui/MainLoop.cs
+++ b/Terminal.Gui/MainLoop.cs
@@ -77,8 +77,8 @@ public ReadOnlyCollection> IdleHandlers {
///
/// The current in use.
///
- /// The driver.
- public IMainLoopDriver Driver { get; }
+ /// The main loop driver.
+ public IMainLoopDriver MainLoopDriver { get; }
///
/// Invoked when a new timeout is added. To be used in the case
@@ -93,7 +93,7 @@ public ReadOnlyCollection> IdleHandlers {
/// (one of the implementations FakeMainLoop, UnixMainLoop, NetMainLoop or WindowsMainLoop).
public MainLoop (IMainLoopDriver driver)
{
- Driver = driver;
+ MainLoopDriver = driver;
driver.Setup (this);
}
@@ -128,7 +128,7 @@ public Func AddIdle (Func idleHandler)
idleHandlers.Add (idleHandler);
}
- Driver.Wakeup ();
+ MainLoopDriver.Wakeup ();
return idleHandler;
}
@@ -140,8 +140,9 @@ public Func AddIdle (Func idleHandler)
/// This method also returns false if the idle handler is not found.
public bool RemoveIdle (Func token)
{
- lock (_idleHandlersLock)
+ lock (_idleHandlersLock) {
return idleHandlers.Remove (token);
+ }
}
void AddTimeout (TimeSpan time, Timeout timeout)
@@ -149,7 +150,7 @@ void AddTimeout (TimeSpan time, Timeout timeout)
lock (_timeoutsLockToken) {
var k = (DateTime.UtcNow + time).Ticks;
timeouts.Add (NudgeToUniqueKey (k), timeout);
- TimeoutAdded?.Invoke (this, new TimeoutEventArgs(timeout, k));
+ TimeoutAdded?.Invoke (this, new TimeoutEventArgs (timeout, k));
}
}
@@ -166,8 +167,9 @@ void AddTimeout (TimeSpan time, Timeout timeout)
///
public object AddTimeout (TimeSpan time, Func callback)
{
- if (callback == null)
+ if (callback == null) {
throw new ArgumentNullException (nameof (callback));
+ }
var timeout = new Timeout () {
Span = time,
Callback = callback
@@ -188,8 +190,9 @@ public bool RemoveTimeout (object token)
{
lock (_timeoutsLockToken) {
var idx = timeouts.IndexOfValue (token as Timeout);
- if (idx == -1)
+ if (idx == -1) {
return false;
+ }
timeouts.RemoveAt (idx);
}
return true;
@@ -213,8 +216,9 @@ void RunTimers ()
var k = t.Key;
var timeout = t.Value;
if (k < now) {
- if (timeout.Callback (this))
+ if (timeout.Callback (this)) {
AddTimeout (timeout.Span, timeout);
+ }
} else {
lock (_timeoutsLockToken) {
timeouts.Add (NudgeToUniqueKey (k), timeout);
@@ -249,21 +253,25 @@ void RunIdle ()
}
foreach (var idle in iterate) {
- if (idle ())
- lock (_idleHandlersLock)
+ if (idle ()) {
+ lock (_idleHandlersLock) {
idleHandlers.Add (idle);
+ }
+ }
}
}
bool _running;
+ // BUGBUG: Stop is only called from MainLoopUnitTests.cs. As a result, the mainloop
+ // will never exit during other unit tests or normal execution.
///
/// Stops the mainloop.
///
public void Stop ()
{
_running = false;
- Driver.Wakeup ();
+ MainLoopDriver.Wakeup ();
}
///
@@ -276,7 +284,7 @@ public void Stop ()
///
public bool EventsPending (bool wait = false)
{
- return Driver.EventsPending (wait);
+ return MainLoopDriver.EventsPending (wait);
}
///
@@ -291,10 +299,11 @@ public bool EventsPending (bool wait = false)
///
public void RunIteration ()
{
- if (timeouts.Count > 0)
+ if (timeouts.Count > 0) {
RunTimers ();
+ }
- Driver.Iteration ();
+ MainLoopDriver.Iteration ();
bool runIdle = false;
lock (_idleHandlersLock) {
diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json
index 49e669d53c..d1c69247d8 100644
--- a/Terminal.Gui/Resources/config.json
+++ b/Terminal.Gui/Resources/config.json
@@ -28,7 +28,6 @@
"Ctrl"
]
},
- "Application.EnableConsoleScrolling": false,
"Application.QuitKey": {
"Key": "Q",
"Modifiers": [
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index f487887ce6..30592f23d0 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -27,16 +27,12 @@
-
-
-
+
+
+
+
-
-
-
-
-
diff --git a/Terminal.Gui/Text/RuneExtensions.cs b/Terminal.Gui/Text/RuneExtensions.cs
index 86781ff77a..8fc1e2abd3 100644
--- a/Terminal.Gui/Text/RuneExtensions.cs
+++ b/Terminal.Gui/Text/RuneExtensions.cs
@@ -1,5 +1,6 @@
using System.Globalization;
using System.Text;
+using Wcwidth;
namespace Terminal.Gui;
@@ -26,21 +27,7 @@ public static class RuneExtensions {
///
public static int GetColumns (this Rune rune)
{
- // TODO: I believe there is a way to do this without using our own tables, using Rune.
- var codePoint = rune.Value;
- switch (codePoint) {
- case < 0x20:
- case >= 0x7f and < 0xa0:
- return -1;
- case < 0x7f:
- return 1;
- }
- /* binary search in table of non-spacing characters */
- if (BiSearch (codePoint, _combining, _combining.GetLength (0) - 1) != 0) {
- return 0;
- }
- /* if we arrive here, ucs is not a combining or C0/C1 control character */
- return 1 + (BiSearch (codePoint, _combiningWideChars, _combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0);
+ return UnicodeCalculator.GetWidth (rune);
}
///
@@ -179,133 +166,4 @@ public static bool CanBeEncodedAsRune (byte [] buffer)
}
return true;
}
-
- // ---------------- implementation details ------------------
- // TODO: Can this be handled by the new .NET 8 Rune type?
- static readonly int [,] _combining = new int [,] {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
- { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
- { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
- { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
- { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
- { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
- { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
- { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
- { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
- { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
- { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
- { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x2E9A, 0x2E9A },
- { 0x2EF4, 0x2EFF }, { 0x2FD6, 0x2FEF }, { 0x2FFC, 0x2FFF },
- { 0x31E4, 0x31EF }, { 0x321F, 0x321F }, { 0xA48D, 0xA48F },
- { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 },
- { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE1A, 0xFE1F },
- { 0xFE20, 0xFE23 }, { 0xFE53, 0xFE53 }, { 0xFE67, 0xFE67 },
- { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
- { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
- { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
- { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
- { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
- { 0xE0100, 0xE01EF }
- };
-
- static readonly int [,] _combiningWideChars = new int [,] {
- /* Hangul Jamo init. consonants - 0x1100, 0x11ff */
- /* Miscellaneous Technical - 0x2300, 0x23ff */
- /* Hangul Syllables - 0x11a8, 0x11c2 */
- /* CJK Compatibility Ideographs - f900, fad9 */
- /* Vertical forms - fe10, fe19 */
- /* CJK Compatibility Forms - fe30, fe4f */
- /* Fullwidth Forms - ff01, ffee */
- /* Alphabetic Presentation Forms - 0xFB00, 0xFb4f */
- /* Chess Symbols - 0x1FA00, 0x1FA0f */
-
- { 0x1100, 0x115f }, { 0x231a, 0x231b }, { 0x2329, 0x232a },
- { 0x23e9, 0x23ec }, { 0x23f0, 0x23f0 }, { 0x23f3, 0x23f3 },
- { 0x25fd, 0x25fe }, { 0x2614, 0x2615 }, { 0x2648, 0x2653 },
- { 0x267f, 0x267f }, { 0x2693, 0x2693 }, { 0x26a1, 0x26a1 },
- { 0x26aa, 0x26ab }, { 0x26bd, 0x26be }, { 0x26c4, 0x26c5 },
- { 0x26ce, 0x26ce }, { 0x26d4, 0x26d4 }, { 0x26ea, 0x26ea },
- { 0x26f2, 0x26f3 }, { 0x26f5, 0x26f5 }, { 0x26fa, 0x26fa },
- { 0x26fd, 0x26fd }, { 0x2705, 0x2705 }, { 0x270a, 0x270b },
- { 0x2728, 0x2728 }, { 0x274c, 0x274c }, { 0x274e, 0x274e },
- { 0x2753, 0x2755 }, { 0x2757, 0x2757 }, { 0x2795, 0x2797 },
- { 0x27b0, 0x27b0 }, { 0x27bf, 0x27bf }, { 0x2b1b, 0x2b1c },
- { 0x2b50, 0x2b50 }, { 0x2b55, 0x2b55 }, { 0x2e80, 0x303e },
- { 0x3041, 0x3096 }, { 0x3099, 0x30ff }, { 0x3105, 0x312f },
- { 0x3131, 0x318e }, { 0x3190, 0x3247 }, { 0x3250, 0x4dbf },
- { 0x4e00, 0xa4c6 }, { 0xa960, 0xa97c }, { 0xac00, 0xd7a3 },
- { 0xf900, 0xfaff }, { 0xfe10, 0xfe1f }, { 0xfe30, 0xfe6b },
- { 0xff01, 0xff60 }, { 0xffe0, 0xffe6 },
- { 0x16fe0, 0x16fe4 }, { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 },
- { 0x18800, 0x18cd5 }, { 0x18d00, 0x18d08 }, { 0x1aff0, 0x1affc },
- { 0x1b000, 0x1b122 }, { 0x1b150, 0x1b152 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1d538, 0x1d550 },
- { 0x1f004, 0x1f004 }, { 0x1f0cf, 0x1f0cf }, /*{ 0x1f100, 0x1f10a },*/
- //{ 0x1f110, 0x1f12d }, { 0x1f130, 0x1f169 }, { 0x1f170, 0x1f1ac },
- { 0x1f18f, 0x1f199 },
- { 0x1f1e6, 0x1f1ff }, { 0x1f200, 0x1f202 }, { 0x1f210, 0x1f23b },
- { 0x1f240, 0x1f248 }, { 0x1f250, 0x1f251 }, { 0x1f260, 0x1f265 },
- { 0x1f300, 0x1f320 }, { 0x1f32d, 0x1f33e }, { 0x1f340, 0x1f37e },
- { 0x1f380, 0x1f393 }, { 0x1f3a0, 0x1f3ca }, { 0x1f3cf, 0x1f3d3 },
- { 0x1f3e0, 0x1f3f0 }, { 0x1f3f4, 0x1f3f4 }, { 0x1f3f8, 0x1f43e },
- { 0x1f440, 0x1f44e }, { 0x1f450, 0x1f4fc }, { 0x1f4ff, 0x1f53d },
- { 0x1f54b, 0x1f54e }, { 0x1f550, 0x1f567 }, { 0x1f57a, 0x1f57a },
- { 0x1f595, 0x1f596 }, { 0x1f5a4, 0x1f5a4 }, { 0x1f5fb, 0x1f606 },
- { 0x1f607, 0x1f64f }, { 0x1f680, 0x1f6c5 }, { 0x1f6cc, 0x1f6cc },
- { 0x1f6d0, 0x1f6d2 }, { 0x1f6d5, 0x1f6d7 }, { 0x1f6dd, 0x1f6df }, { 0x1f6eb, 0x1f6ec },
- { 0x1f6f4, 0x1f6fc }, { 0x1f7e0, 0x1f7eb }, { 0x1f7f0, 0x1f7f0 }, { 0x1f90c, 0x1f93a },
- { 0x1f93c, 0x1f945 }, { 0x1f947, 0x1f97f }, { 0x1f980, 0x1f9cc },
- { 0x1f9cd, 0x1f9ff }, { 0x1fa70, 0x1fa74 }, { 0x1fa78, 0x1fa7c }, { 0x1fa80, 0x1fa86 },
- { 0x1fa90, 0x1faac }, { 0x1fab0, 0x1faba }, { 0x1fac0, 0x1fac5 },
- { 0x1fad0, 0x1fad9 }, { 0x1fae0, 0x1fae7 }, { 0x1faf0, 0x1faf6 }, { 0x20000, 0x2fffd }, { 0x30000, 0x3fffd },
- //{ 0xe0100, 0xe01ef }, { 0xf0000, 0xffffd }, { 0x100000, 0x10fffd }
- };
-
- static int BiSearch (int rune, int [,] table, int max)
- {
- var min = 0;
-
- if (rune < table [0, 0] || rune > table [max, 1]) {
- return 0;
- }
- while (max >= min) {
- var mid = (min + max) / 2;
- if (rune > table [mid, 1]) {
- min = mid + 1;
- } else if (rune < table [mid, 0]) {
- max = mid - 1;
- } else {
- return 1;
- }
- }
-
- return 0;
- }
}
\ No newline at end of file
diff --git a/Terminal.Gui/View/Frame.cs b/Terminal.Gui/View/Frame.cs
index 83ecb68923..6183819b29 100644
--- a/Terminal.Gui/View/Frame.cs
+++ b/Terminal.Gui/View/Frame.cs
@@ -126,7 +126,9 @@ public virtual void OnDrawSubViews (Rect clipRect)
///
public override void OnDrawContent (Rect contentArea)
{
- if (Thickness == Thickness.Empty) return;
+ if (Thickness == Thickness.Empty) {
+ return;
+ }
if (ColorScheme != null) {
Driver.SetAttribute (GetNormalColor ());
@@ -139,9 +141,6 @@ public override void OnDrawContent (Rect contentArea)
}
//Driver.SetAttribute (Colors.Error.Normal);
-
- var prevClip = SetClip (Frame);
-
var screenBounds = ViewToScreen (Frame);
// This just draws/clears the thickness, not the insides.
@@ -305,7 +304,7 @@ public override void OnDrawContent (Rect contentArea)
}
}
- Driver.Clip = prevClip;
+ ClearNeedsDisplay ();
}
// TODO: v2 - Frame.BorderStyle is temporary - Eventually the border will be drawn by a "BorderView" that is a subview of the Frame.
diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs
index 7413616098..17a725e16b 100644
--- a/Terminal.Gui/View/ViewDrawing.cs
+++ b/Terminal.Gui/View/ViewDrawing.cs
@@ -9,7 +9,7 @@ public partial class View {
///
/// The color scheme for this view, if it is not defined, it returns the 's
- /// color scheme.
+ /// color scheme.
///
public virtual ColorScheme ColorScheme {
get {
@@ -34,7 +34,11 @@ public virtual ColorScheme ColorScheme {
/// If it's overridden can return other values.
public virtual Attribute GetNormalColor ()
{
- return Enabled ? ColorScheme.Normal : ColorScheme.Disabled;
+ ColorScheme cs = ColorScheme;
+ if (ColorScheme == null) {
+ cs = new ColorScheme ();
+ }
+ return Enabled ? cs.Normal : cs.Disabled;
}
///
@@ -76,106 +80,126 @@ public void AddRune (int col, int row, Rune ch)
}
///
- /// Removes the and the setting on this view.
+ /// Clears and .
///
protected void ClearNeedsDisplay ()
{
- _needsDisplay = Rect.Empty;
+ _needsDisplayRect = Rect.Empty;
_subViewNeedsDisplay = false;
}
- // The view-relative region that needs to be redrawn
- internal Rect _needsDisplay { get; private set; } = Rect.Empty;
+ // The view-relative region that needs to be redrawn. Marked internal for unit tests.
+ internal Rect _needsDisplayRect = Rect.Empty;
+
+ ///
+ /// Gets or sets whether the view needs to be redrawn.
+ ///
+ public bool NeedsDisplay {
+ get => _needsDisplayRect != Rect.Empty;
+ set {
+ if (value) {
+ SetNeedsDisplay ();
+ } else {
+ ClearNeedsDisplay ();
+ }
+ }
+ }
///
- /// Sets a flag indicating this view needs to be redisplayed because its state has changed.
+ /// Sets the area of this view needing to be redrawn to .
///
public void SetNeedsDisplay ()
{
- if (!IsInitialized) return;
+ if (!IsInitialized) {
+ return;
+ }
SetNeedsDisplay (Bounds);
}
///
- /// Flags the view-relative region on this View as needing to be redrawn.
+ /// Expands the area of this view needing to be redrawn to include .
///
/// The view-relative region that needs to be redrawn.
public void SetNeedsDisplay (Rect region)
{
- if (_needsDisplay.IsEmpty) {
- _needsDisplay = region;
+ if (_needsDisplayRect.IsEmpty) {
+ _needsDisplayRect = region;
} else {
- var x = Math.Min (_needsDisplay.X, region.X);
- var y = Math.Min (_needsDisplay.Y, region.Y);
- var w = Math.Max (_needsDisplay.Width, region.Width);
- var h = Math.Max (_needsDisplay.Height, region.Height);
- _needsDisplay = new Rect (x, y, w, h);
+ var x = Math.Min (_needsDisplayRect.X, region.X);
+ var y = Math.Min (_needsDisplayRect.Y, region.Y);
+ var w = Math.Max (_needsDisplayRect.Width, region.Width);
+ var h = Math.Max (_needsDisplayRect.Height, region.Height);
+ _needsDisplayRect = new Rect (x, y, w, h);
}
_superView?.SetSubViewNeedsDisplay ();
- if (_subviews == null)
+ if (_needsDisplayRect.X < Bounds.X ||
+ _needsDisplayRect.Y < Bounds.Y ||
+ _needsDisplayRect.Width > Bounds.Width ||
+ _needsDisplayRect.Height > Bounds.Height) {
+ Margin?.SetNeedsDisplay (Margin.Bounds);
+ Border?.SetNeedsDisplay (Border.Bounds);
+ Padding?.SetNeedsDisplay (Padding.Bounds);
+ }
+
+ if (_subviews == null) {
return;
+ }
- foreach (var view in _subviews)
- if (view.Frame.IntersectsWith (region)) {
- var childRegion = Rect.Intersect (view.Frame, region);
- childRegion.X -= view.Frame.X;
- childRegion.Y -= view.Frame.Y;
- view.SetNeedsDisplay (childRegion);
+ foreach (var subview in _subviews) {
+ if (subview.Frame.IntersectsWith (region)) {
+ var subviewRegion = Rect.Intersect (subview.Frame, region);
+ subviewRegion.X -= subview.Frame.X;
+ subviewRegion.Y -= subview.Frame.Y;
+ subview.SetNeedsDisplay (subviewRegion);
}
+ }
+ }
+
+ ///
+ /// Gets whether any Subviews need to be redrawn.
+ ///
+ public bool SubViewNeedsDisplay {
+ get => _subViewNeedsDisplay;
}
- internal bool _subViewNeedsDisplay { get; private set; }
+ bool _subViewNeedsDisplay;
///
/// Indicates that any Subviews (in the list) need to be repainted.
///
public void SetSubViewNeedsDisplay ()
{
- if (_subViewNeedsDisplay) {
- return;
- }
_subViewNeedsDisplay = true;
- if (_superView != null && !_superView._subViewNeedsDisplay)
+ if (_superView != null && !_superView._subViewNeedsDisplay) {
_superView.SetSubViewNeedsDisplay ();
+ }
}
///
- /// Clears the view region with the current color.
+ /// Clears the with the normal background color.
///
///
///
- /// This clears the entire region used by this view.
+ /// This clears the Bounds used by this view.
///
///
- public void Clear ()
- {
- 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++)
- Driver.AddRune ((Rune)' ');
- }
- }
+ public void Clear () => Clear (ViewToScreen(Bounds));
- // BUGBUG: Stupid that this takes screen-relative. We should have a tenet that says
- // "View APIs only deal with View-relative coords".
+ // BUGBUG: This version of the Clear API should be removed. We should have a tenet that says
+ // "View APIs only deal with View-relative coords". This is only used by ComboBox which can
+ // be refactored to use the View-relative version.
///
- /// Clears the specified region with the current color.
+ /// Clears the specified screen-relative rectangle with the normal background.
///
///
///
- /// The screen-relative region to clear.
+ /// The screen-relative rectangle to clear.
public void Clear (Rect regionScreen)
{
- var h = regionScreen.Height;
- var w = regionScreen.Width;
- for (var line = regionScreen.Y; line < regionScreen.Y + h; line++) {
- Driver.Move (regionScreen.X, line);
- for (var col = 0; col < w; col++)
- Driver.AddRune ((Rune)' ');
- }
+ var prev = Driver.SetAttribute (GetNormalColor ());
+ Driver.FillRect (regionScreen);
+ Driver.SetAttribute (prev);
}
// Clips a rectangle in screen coordinates to the dimensions currently available on the screen
@@ -190,35 +214,18 @@ internal Rect ScreenClip (Rect regionScreen)
}
///
- /// Sets the 's clip region to .
+ /// Expands the 's clip region to include .
///
/// The current screen-relative clip region, which can be then re-applied by setting .
///
///
- /// is View-relative.
- ///
- ///
/// If and do not intersect, the clip region will be set to .
///
///
public Rect ClipToBounds ()
- {
- return SetClip (Bounds);
- }
-
- // BUGBUG: v2 - SetClip should return VIEW-relative so that it can be used to reset it; using Driver.Clip directly should not be necessary.
- ///
- /// Sets the clip region to the specified view-relative region.
- ///
- /// The current screen-relative clip region, which can be then re-applied by setting .
- /// View-relative clip region.
- ///
- /// If and do not intersect, the clip region will be set to .
- ///
- public Rect SetClip (Rect region)
{
var previous = Driver.Clip;
- Driver.Clip = Rect.Intersect (previous, ViewToScreen (region));
+ Driver.Clip = Rect.Intersect (previous, ViewToScreen (Bounds));
return previous;
}
@@ -304,22 +311,11 @@ public virtual bool OnDrawFrames ()
return false;
}
- var prevClip = Driver.Clip;
- Driver.Clip = ViewToScreen (Frame);
-
- // TODO: Figure out what we should do if we have no superview
- //if (SuperView != null) {
- // TODO: Clipping is disabled for now to ensure we see errors
- Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);// screenBounds;// SuperView.ClipToBounds ();
- //}
-
// Each of these renders lines to either this View's LineCanvas
// Those lines will be finally rendered in OnRenderLineCanvas
- Margin?.OnDrawContent (Bounds);
- Border?.OnDrawContent (Bounds);
- Padding?.OnDrawContent (Bounds);
-
- Driver.Clip = prevClip;
+ Margin?.OnDrawContent (Margin.Bounds);
+ Border?.OnDrawContent (Border.Bounds);
+ Padding?.OnDrawContent (Padding.Bounds);
return true;
}
@@ -346,7 +342,6 @@ public void Draw ()
if (!CanBeVisible (this)) {
return;
}
-
OnDrawFrames ();
var prevClip = ClipToBounds ();
@@ -367,7 +362,6 @@ public void Draw ()
Driver.Clip = prevClip;
OnRenderLineCanvas ();
-
// Invoke DrawContentCompleteEvent
OnDrawContentComplete (Bounds);
@@ -389,29 +383,29 @@ public virtual bool OnRenderLineCanvas ()
return false;
}
- //Driver.SetAttribute (new Attribute(Color.White, Color.Black));
-
// If we have a SuperView, it'll render our frames.
if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rect.Empty) {
foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map
- Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal);
+ Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
Driver.Move (p.Key.X, p.Key.Y);
- Driver.AddRune (p.Value.Rune.Value);
+ // TODO: #2616 - Support combining sequences that don't normalize
+ Driver.AddRune (p.Value.Runes [0]);
}
LineCanvas.Clear ();
}
- if (Subviews.Where (s => s.SuperViewRendersLineCanvas).Count () > 0) {
+ if (Subviews.Any (s => s.SuperViewRendersLineCanvas)) {
foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas == true)) {
- // Combine the LineCavas'
+ // Combine the LineCanvas'
LineCanvas.Merge (subview.LineCanvas);
subview.LineCanvas.Clear ();
}
foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map
- Driver.SetAttribute (p.Value.Attribute?.Value ?? ColorScheme.Normal);
+ Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
Driver.Move (p.Key.X, p.Key.Y);
- Driver.AddRune (p.Value.Rune.Value);
+ // TODO: #2616 - Support combining sequences that don't normalize
+ Driver.AddRune (p.Value.Runes [0]);
}
LineCanvas.Clear ();
}
@@ -441,38 +435,45 @@ public virtual bool OnRenderLineCanvas ()
///
public virtual void OnDrawContent (Rect contentArea)
{
- if (SuperView != null) {
- Clear (ViewToScreen (Bounds));
- }
+ if (NeedsDisplay) {
+ if (SuperView != null) {
+ Clear (ViewToScreen (Bounds));
+ }
- if (!string.IsNullOrEmpty (TextFormatter.Text)) {
- if (TextFormatter != null) {
- TextFormatter.NeedsFormat = true;
+ if (!string.IsNullOrEmpty (TextFormatter.Text)) {
+ if (TextFormatter != null) {
+ TextFormatter.NeedsFormat = true;
+ }
}
// This should NOT clear
TextFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
- HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
- Rect.Empty, false);
+ HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+ Rect.Empty, false);
SetSubViewNeedsDisplay ();
}
// Draw subviews
// TODO: Implement OnDrawSubviews (cancelable);
- if (_subviews != null) {
- foreach (var view in _subviews) {
- if (view.Visible) { //!view._needsDisplay.IsEmpty || view._childNeedsDisplay || view.LayoutNeeded) {
- if (true) { //view.Frame.IntersectsWith (bounds)) { // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
- if (view.LayoutNeeded) {
- view.LayoutSubviews ();
- }
-
- // Draw the subview
- // Use the view's bounds (view-relative; Location will always be (0,0)
- //if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) {
- view.Draw ();
- //}
- }
+ if (_subviews != null && SubViewNeedsDisplay) {
+ var subviewsNeedingDraw = _subviews.Where (
+ view => view.Visible &&
+ (view.NeedsDisplay ||
+ view.SubViewNeedsDisplay ||
+ view.LayoutNeeded)
+ );
+
+ foreach (var view in subviewsNeedingDraw) {
+ //view.Frame.IntersectsWith (bounds)) {
+ // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
+ if (view.LayoutNeeded) {
+ view.LayoutSubviews ();
}
+
+ // Draw the subview
+ // Use the view's bounds (view-relative; Location will always be (0,0)
+ //if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) {
+ view.Draw ();
+ //}
}
}
}
diff --git a/Terminal.Gui/View/ViewLayout.cs b/Terminal.Gui/View/ViewLayout.cs
index a8fed3a265..3b720339c6 100644
--- a/Terminal.Gui/View/ViewLayout.cs
+++ b/Terminal.Gui/View/ViewLayout.cs
@@ -470,16 +470,15 @@ protected virtual void OnResizeNeeded ()
internal void SetNeedsLayout ()
{
- if (LayoutNeeded)
+ if (LayoutNeeded) {
return;
+ }
LayoutNeeded = true;
foreach (var view in Subviews) {
view.SetNeedsLayout ();
}
TextFormatter.NeedsFormat = true;
- if (SuperView == null)
- return;
- SuperView.SetNeedsLayout ();
+ SuperView?.SetNeedsLayout ();
}
///
@@ -541,7 +540,7 @@ public virtual void ViewToScreen (int col, int row, out int rcol, out int rrow,
///
/// Converts a region in view-relative coordinates to screen-relative coordinates.
///
- internal Rect ViewToScreen (Rect region)
+ public Rect ViewToScreen (Rect region)
{
ViewToScreen (region.X, region.Y, out var x, out var y, clamped: false);
return new Rect (x, y, region.Width, region.Height);
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index 01226fd809..1657e09ed0 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -5,7 +5,7 @@
using System.Text;
namespace Terminal.Gui {
- public partial class View {
+ public partial class View {
static readonly IList _empty = new List (0).AsReadOnly ();
View _superView = null;
@@ -128,7 +128,7 @@ public virtual void OnAdded (SuperViewChangedEventArgs e)
}
///
- /// Gets information if the view was already added to the .
+ /// Indicates whether the view was added to .
///
public bool IsAdded { get; private set; }
@@ -420,10 +420,12 @@ public override bool OnEnter (View view)
{
var args = new FocusEventArgs (view);
Enter?.Invoke (this, args);
- if (args.Handled)
+ if (args.Handled) {
return true;
- if (base.OnEnter (view))
+ }
+ if (base.OnEnter (view)) {
return true;
+ }
return false;
}
@@ -433,11 +435,14 @@ public override bool OnLeave (View view)
{
var args = new FocusEventArgs (view);
Leave?.Invoke (this, args);
- if (args.Handled)
+ if (args.Handled) {
return true;
- if (base.OnLeave (view))
+ }
+ if (base.OnLeave (view)) {
return true;
+ }
+ Driver?.SetCursorVisibility (CursorVisibility.Invisible);
return false;
}
diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs
index 0968b1d337..e8eaf8088f 100644
--- a/Terminal.Gui/Views/CheckBox.cs
+++ b/Terminal.Gui/Views/CheckBox.cs
@@ -119,11 +119,11 @@ protected override void UpdateTextFormatterText ()
Rune GetCheckedState ()
{
- switch (Checked) {
- case true: return charChecked;
- case false: return charUnChecked;
- default: return charNullChecked;
- }
+ return Checked switch {
+ true => charChecked,
+ false => charUnChecked,
+ var _ => charNullChecked
+ };
}
string GetFormatterText ()
@@ -157,9 +157,7 @@ public bool AllowNullChecked {
get => allowNullChecked;
set {
allowNullChecked = value;
- if (Checked == null) {
- Checked = false;
- }
+ Checked ??= false;
}
}
diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs
index c9d0cb9b4b..36bcf3b636 100644
--- a/Terminal.Gui/Views/ColorPicker.cs
+++ b/Terminal.Gui/Views/ColorPicker.cs
@@ -68,14 +68,7 @@ public int BoxHeight {
}
}
}
- private int _boxHeight = 2;
-
- // Cursor runes.
- private static readonly Rune [] _cursorRunes = new Rune []
- {
- (Rune)0x250C, (Rune) 0x2500, (Rune) 0x2500, (Rune) 0x2510,
- (Rune) 0x2514, (Rune) 0x2500, (Rune) 0x2500, (Rune) 0x2518
- };
+ int _boxHeight = 2;
///
/// Cursor for the selected color.
diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs
index e01a41fd52..b425e5f073 100644
--- a/Terminal.Gui/Views/HexView.cs
+++ b/Terminal.Gui/Views/HexView.cs
@@ -213,8 +213,8 @@ public override void OnDrawContent (Rect contentArea)
Source.Position = displayStart;
var n = source.Read (data, 0, data.Length);
- int activeColor = ColorScheme.HotNormal;
- int trackingColor = ColorScheme.HotFocus;
+ var activeColor = ColorScheme.HotNormal;
+ var trackingColor = ColorScheme.HotFocus;
for (int line = 0; line < frame.Height; line++) {
var lineRect = new Rect (0, line, frame.Width, 1);
diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs
index 5e18a55927..74af49e30e 100644
--- a/Terminal.Gui/Views/Menu.cs
+++ b/Terminal.Gui/Views/Menu.cs
@@ -573,7 +573,6 @@ public override void OnDrawContent (Rect contentArea)
}
var savedClip = Driver.Clip;
Driver.Clip = new Rect (0, 0, Driver.Cols, Driver.Rows);
-
Driver.SetAttribute (GetNormalColor ());
OnDrawFrames ();
diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs
index f8cd3a4739..bb83466360 100644
--- a/Terminal.Gui/Views/ScrollView.cs
+++ b/Terminal.Gui/Views/ScrollView.cs
@@ -14,597 +14,596 @@
using System;
using System.Linq;
-namespace Terminal.Gui {
- ///
- /// Scrollviews are views that present a window into a virtual space where subviews are added. Similar to the iOS UIScrollView.
- ///
- ///
- ///
- /// The subviews that are added to this are offset by the
- /// property. The view itself is a window into the
- /// space represented by the .
- ///
- ///
- /// Use the
- ///
- ///
- public class ScrollView : View {
-
- // The ContentView is the view that contains the subviews and content that are being scrolled
- // The ContentView is the size of the ContentSize and is offset by the ContentOffset
- private class ContentView : View {
- public ContentView (Rect frame) : base (frame)
- {
- Id = "ScrollView.ContentView";
- CanFocus = true;
- }
+namespace Terminal.Gui;
+///
+/// Scrollviews are views that present a window into a virtual space where subviews are added. Similar to the iOS UIScrollView.
+///
+///
+///
+/// The subviews that are added to this are offset by the
+/// property. The view itself is a window into the
+/// space represented by the .
+///
+///
+/// Use the
+///
+///
+public class ScrollView : View {
+
+ // The ContentView is the view that contains the subviews and content that are being scrolled
+ // The ContentView is the size of the ContentSize and is offset by the ContentOffset
+ private class ContentView : View {
+ public ContentView (Rect frame) : base (frame)
+ {
+ Id = "ScrollView.ContentView";
+ CanFocus = true;
}
+ }
- ContentView _contentView;
- ScrollBarView _vertical, _horizontal;
+ ContentView _contentView;
+ ScrollBarView _vertical, _horizontal;
- ///
- /// Initializes a new instance of the class using positioning.
- ///
- ///
- public ScrollView (Rect frame) : base (frame)
- {
- SetInitialProperties (frame);
- }
+ ///
+ /// Initializes a new instance of the class using positioning.
+ ///
+ ///
+ public ScrollView (Rect frame) : base (frame)
+ {
+ SetInitialProperties (frame);
+ }
- ///
- /// Initializes a new instance of the class using positioning.
- ///
- public ScrollView () : base ()
- {
- SetInitialProperties (Rect.Empty);
- }
+ ///
+ /// Initializes a new instance of the class using positioning.
+ ///
+ public ScrollView () : base ()
+ {
+ SetInitialProperties (Rect.Empty);
+ }
- void SetInitialProperties (Rect frame)
- {
- _contentView = new ContentView (frame);
- _vertical = new ScrollBarView (1, 0, isVertical: true) {
- X = Pos.AnchorEnd (1),
- Y = 0,
- Width = 1,
- Height = Dim.Fill (_showHorizontalScrollIndicator ? 1 : 0),
- Host = this
+ void SetInitialProperties (Rect frame)
+ {
+ _contentView = new ContentView (frame);
+ _vertical = new ScrollBarView (1, 0, isVertical: true) {
+ X = Pos.AnchorEnd (1),
+ Y = 0,
+ Width = 1,
+ Height = Dim.Fill (_showHorizontalScrollIndicator ? 1 : 0),
+ Host = this
+ };
+
+ _horizontal = new ScrollBarView (1, 0, isVertical: false) {
+ X = 0,
+ Y = Pos.AnchorEnd (1),
+ Width = Dim.Fill (_showVerticalScrollIndicator ? 1 : 0),
+ Height = 1,
+ Host = this
+ };
+
+ _vertical.OtherScrollBarView = _horizontal;
+ _horizontal.OtherScrollBarView = _vertical;
+ base.Add (_contentView);
+ CanFocus = true;
+
+ MouseEnter += View_MouseEnter;
+ MouseLeave += View_MouseLeave;
+ _contentView.MouseEnter += View_MouseEnter;
+ _contentView.MouseLeave += View_MouseLeave;
+
+ // Things this view knows how to do
+ AddCommand (Command.ScrollUp, () => ScrollUp (1));
+ AddCommand (Command.ScrollDown, () => ScrollDown (1));
+ AddCommand (Command.ScrollLeft, () => ScrollLeft (1));
+ AddCommand (Command.ScrollRight, () => ScrollRight (1));
+ AddCommand (Command.PageUp, () => ScrollUp (Bounds.Height));
+ AddCommand (Command.PageDown, () => ScrollDown (Bounds.Height));
+ AddCommand (Command.PageLeft, () => ScrollLeft (Bounds.Width));
+ AddCommand (Command.PageRight, () => ScrollRight (Bounds.Width));
+ AddCommand (Command.TopHome, () => ScrollUp (_contentSize.Height));
+ AddCommand (Command.BottomEnd, () => ScrollDown (_contentSize.Height));
+ AddCommand (Command.LeftHome, () => ScrollLeft (_contentSize.Width));
+ AddCommand (Command.RightEnd, () => ScrollRight (_contentSize.Width));
+
+ // Default keybindings for this view
+ AddKeyBinding (Key.CursorUp, Command.ScrollUp);
+ AddKeyBinding (Key.CursorDown, Command.ScrollDown);
+ AddKeyBinding (Key.CursorLeft, Command.ScrollLeft);
+ AddKeyBinding (Key.CursorRight, Command.ScrollRight);
+
+ AddKeyBinding (Key.PageUp, Command.PageUp);
+ AddKeyBinding ((Key)'v' | Key.AltMask, Command.PageUp);
+
+ AddKeyBinding (Key.PageDown, Command.PageDown);
+ AddKeyBinding (Key.V | Key.CtrlMask, Command.PageDown);
+
+ AddKeyBinding (Key.PageUp | Key.CtrlMask, Command.PageLeft);
+ AddKeyBinding (Key.PageDown | Key.CtrlMask, Command.PageRight);
+ AddKeyBinding (Key.Home, Command.TopHome);
+ AddKeyBinding (Key.End, Command.BottomEnd);
+ AddKeyBinding (Key.Home | Key.CtrlMask, Command.LeftHome);
+ AddKeyBinding (Key.End | Key.CtrlMask, Command.RightEnd);
+
+ Initialized += (s, e) => {
+ if (!_vertical.IsInitialized) {
+ _vertical.BeginInit ();
+ _vertical.EndInit ();
+ }
+ if (!_horizontal.IsInitialized) {
+ _horizontal.BeginInit ();
+ _horizontal.EndInit ();
+ }
+ SetContentOffset (_contentOffset);
+ _contentView.Frame = new Rect (ContentOffset, ContentSize);
+ _vertical.ChangedPosition += delegate {
+ ContentOffset = new Point (ContentOffset.X, _vertical.Position);
};
-
- _horizontal = new ScrollBarView (1, 0, isVertical: false) {
- X = 0,
- Y = Pos.AnchorEnd (1),
- Width = Dim.Fill (_showVerticalScrollIndicator ? 1 : 0),
- Height = 1,
- Host = this
+ _horizontal.ChangedPosition += delegate {
+ ContentOffset = new Point (_horizontal.Position, ContentOffset.Y);
};
+ };
+ }
- _vertical.OtherScrollBarView = _horizontal;
- _horizontal.OtherScrollBarView = _vertical;
- base.Add (_contentView);
- CanFocus = true;
+ //public override void BeginInit ()
+ //{
+ // SetContentOffset (contentOffset);
+ // base.BeginInit ();
+ //}
- MouseEnter += View_MouseEnter;
- MouseLeave += View_MouseLeave;
- _contentView.MouseEnter += View_MouseEnter;
- _contentView.MouseLeave += View_MouseLeave;
-
- // Things this view knows how to do
- AddCommand (Command.ScrollUp, () => ScrollUp (1));
- AddCommand (Command.ScrollDown, () => ScrollDown (1));
- AddCommand (Command.ScrollLeft, () => ScrollLeft (1));
- AddCommand (Command.ScrollRight, () => ScrollRight (1));
- AddCommand (Command.PageUp, () => ScrollUp (Bounds.Height));
- AddCommand (Command.PageDown, () => ScrollDown (Bounds.Height));
- AddCommand (Command.PageLeft, () => ScrollLeft (Bounds.Width));
- AddCommand (Command.PageRight, () => ScrollRight (Bounds.Width));
- AddCommand (Command.TopHome, () => ScrollUp (_contentSize.Height));
- AddCommand (Command.BottomEnd, () => ScrollDown (_contentSize.Height));
- AddCommand (Command.LeftHome, () => ScrollLeft (_contentSize.Width));
- AddCommand (Command.RightEnd, () => ScrollRight (_contentSize.Width));
-
- // Default keybindings for this view
- AddKeyBinding (Key.CursorUp, Command.ScrollUp);
- AddKeyBinding (Key.CursorDown, Command.ScrollDown);
- AddKeyBinding (Key.CursorLeft, Command.ScrollLeft);
- AddKeyBinding (Key.CursorRight, Command.ScrollRight);
-
- AddKeyBinding (Key.PageUp, Command.PageUp);
- AddKeyBinding ((Key)'v' | Key.AltMask, Command.PageUp);
-
- AddKeyBinding (Key.PageDown, Command.PageDown);
- AddKeyBinding (Key.V | Key.CtrlMask, Command.PageDown);
-
- AddKeyBinding (Key.PageUp | Key.CtrlMask, Command.PageLeft);
- AddKeyBinding (Key.PageDown | Key.CtrlMask, Command.PageRight);
- AddKeyBinding (Key.Home, Command.TopHome);
- AddKeyBinding (Key.End, Command.BottomEnd);
- AddKeyBinding (Key.Home | Key.CtrlMask, Command.LeftHome);
- AddKeyBinding (Key.End | Key.CtrlMask, Command.RightEnd);
-
- Initialized += (s, e) => {
- if (!_vertical.IsInitialized) {
- _vertical.BeginInit ();
- _vertical.EndInit ();
- }
- if (!_horizontal.IsInitialized) {
- _horizontal.BeginInit ();
- _horizontal.EndInit ();
- }
- SetContentOffset (_contentOffset);
- _contentView.Frame = new Rect (ContentOffset, ContentSize);
- _vertical.ChangedPosition += delegate {
- ContentOffset = new Point (ContentOffset.X, _vertical.Position);
- };
- _horizontal.ChangedPosition += delegate {
- ContentOffset = new Point (_horizontal.Position, ContentOffset.Y);
- };
- };
- }
+ Size _contentSize;
+ Point _contentOffset;
+ bool _showHorizontalScrollIndicator;
+ bool _showVerticalScrollIndicator;
+ bool _keepContentAlwaysInViewport = true;
+ bool _autoHideScrollBars = true;
- //public override void BeginInit ()
- //{
- // SetContentOffset (contentOffset);
- // base.BeginInit ();
- //}
-
- Size _contentSize;
- Point _contentOffset;
- bool _showHorizontalScrollIndicator;
- bool _showVerticalScrollIndicator;
- bool _keepContentAlwaysInViewport = true;
- bool _autoHideScrollBars = true;
-
- ///
- /// Represents the contents of the data shown inside the scrollview
- ///
- /// The size of the content.
- public Size ContentSize {
- get {
- return _contentSize;
- }
- set {
- if (_contentSize != value) {
- _contentSize = value;
- _contentView.Frame = new Rect (_contentOffset, value);
- _vertical.Size = _contentSize.Height;
- _horizontal.Size = _contentSize.Width;
- SetNeedsDisplay ();
- }
+ ///
+ /// Represents the contents of the data shown inside the scrollview
+ ///
+ /// The size of the content.
+ public Size ContentSize {
+ get {
+ return _contentSize;
+ }
+ set {
+ if (_contentSize != value) {
+ _contentSize = value;
+ _contentView.Frame = new Rect (_contentOffset, value);
+ _vertical.Size = _contentSize.Height;
+ _horizontal.Size = _contentSize.Width;
+ SetNeedsDisplay ();
}
}
+ }
- ///
- /// Represents the top left corner coordinate that is displayed by the scrollview
- ///
- /// The content offset.
- public Point ContentOffset {
- get {
- return _contentOffset;
+ ///
+ /// Represents the top left corner coordinate that is displayed by the scrollview
+ ///
+ /// The content offset.
+ public Point ContentOffset {
+ get {
+ return _contentOffset;
+ }
+ set {
+ if (!IsInitialized) {
+ // We're not initialized so we can't do anything fancy. Just cache value.
+ _contentOffset = new Point (-Math.Abs (value.X), -Math.Abs (value.Y)); ;
+ return;
}
- set {
- if (!IsInitialized) {
- // We're not initialized so we can't do anything fancy. Just cache value.
- _contentOffset = new Point (-Math.Abs (value.X), -Math.Abs (value.Y)); ;
- return;
- }
- SetContentOffset (value);
- }
+ SetContentOffset (value);
}
+ }
- private void SetContentOffset (Point offset)
- {
- var co = new Point (-Math.Abs (offset.X), -Math.Abs (offset.Y));
- _contentOffset = co;
- _contentView.Frame = new Rect (_contentOffset, _contentSize);
- var p = Math.Max (0, -_contentOffset.Y);
- if (_vertical.Position != p) {
- _vertical.Position = Math.Max (0, -_contentOffset.Y);
- }
- p = Math.Max (0, -_contentOffset.X);
- if (_horizontal.Position != p) {
- _horizontal.Position = Math.Max (0, -_contentOffset.X);
- }
- SetNeedsDisplay ();
- }
-
- ///
- /// If true the vertical/horizontal scroll bars won't be showed if it's not needed.
- ///
- public bool AutoHideScrollBars {
- get => _autoHideScrollBars;
- set {
- if (_autoHideScrollBars != value) {
- _autoHideScrollBars = value;
- if (Subviews.Contains (_vertical)) {
- _vertical.AutoHideScrollBars = value;
- }
- if (Subviews.Contains (_horizontal)) {
- _horizontal.AutoHideScrollBars = value;
- }
- SetNeedsDisplay ();
+ private void SetContentOffset (Point offset)
+ {
+ var co = new Point (-Math.Abs (offset.X), -Math.Abs (offset.Y));
+ _contentOffset = co;
+ _contentView.Frame = new Rect (_contentOffset, _contentSize);
+ var p = Math.Max (0, -_contentOffset.Y);
+ if (_vertical.Position != p) {
+ _vertical.Position = Math.Max (0, -_contentOffset.Y);
+ }
+ p = Math.Max (0, -_contentOffset.X);
+ if (_horizontal.Position != p) {
+ _horizontal.Position = Math.Max (0, -_contentOffset.X);
+ }
+ SetNeedsDisplay ();
+ }
+
+ ///
+ /// If true the vertical/horizontal scroll bars won't be showed if it's not needed.
+ ///
+ public bool AutoHideScrollBars {
+ get => _autoHideScrollBars;
+ set {
+ if (_autoHideScrollBars != value) {
+ _autoHideScrollBars = value;
+ if (Subviews.Contains (_vertical)) {
+ _vertical.AutoHideScrollBars = value;
+ }
+ if (Subviews.Contains (_horizontal)) {
+ _horizontal.AutoHideScrollBars = value;
}
+ SetNeedsDisplay ();
}
}
+ }
- ///
- /// Get or sets if the view-port is kept always visible in the area of this
- ///
- public bool KeepContentAlwaysInViewport {
- get { return _keepContentAlwaysInViewport; }
- set {
- if (_keepContentAlwaysInViewport != value) {
- _keepContentAlwaysInViewport = value;
- _vertical.OtherScrollBarView.KeepContentAlwaysInViewport = value;
- _horizontal.OtherScrollBarView.KeepContentAlwaysInViewport = value;
- Point p = default;
- if (value && -_contentOffset.X + Bounds.Width > _contentSize.Width) {
- p = new Point (_contentSize.Width - Bounds.Width + (_showVerticalScrollIndicator ? 1 : 0), -_contentOffset.Y);
- }
- if (value && -_contentOffset.Y + Bounds.Height > _contentSize.Height) {
- if (p == default) {
- p = new Point (-_contentOffset.X, _contentSize.Height - Bounds.Height + (_showHorizontalScrollIndicator ? 1 : 0));
- } else {
- p.Y = _contentSize.Height - Bounds.Height + (_showHorizontalScrollIndicator ? 1 : 0);
- }
- }
- if (p != default) {
- ContentOffset = p;
+ ///
+ /// Get or sets if the view-port is kept always visible in the area of this
+ ///
+ public bool KeepContentAlwaysInViewport {
+ get { return _keepContentAlwaysInViewport; }
+ set {
+ if (_keepContentAlwaysInViewport != value) {
+ _keepContentAlwaysInViewport = value;
+ _vertical.OtherScrollBarView.KeepContentAlwaysInViewport = value;
+ _horizontal.OtherScrollBarView.KeepContentAlwaysInViewport = value;
+ Point p = default;
+ if (value && -_contentOffset.X + Bounds.Width > _contentSize.Width) {
+ p = new Point (_contentSize.Width - Bounds.Width + (_showVerticalScrollIndicator ? 1 : 0), -_contentOffset.Y);
+ }
+ if (value && -_contentOffset.Y + Bounds.Height > _contentSize.Height) {
+ if (p == default) {
+ p = new Point (-_contentOffset.X, _contentSize.Height - Bounds.Height + (_showHorizontalScrollIndicator ? 1 : 0));
+ } else {
+ p.Y = _contentSize.Height - Bounds.Height + (_showHorizontalScrollIndicator ? 1 : 0);
}
}
+ if (p != default) {
+ ContentOffset = p;
+ }
}
}
+ }
- View _contentBottomRightCorner;
+ View _contentBottomRightCorner;
- ///
- /// Adds the view to the scrollview.
- ///
- /// The view to add to the scrollview.
- public override void Add (View view)
- {
- if (view.Id == "contentBottomRightCorner") {
- _contentBottomRightCorner = view;
- base.Add (view);
- } else {
- if (!IsOverridden (view, "MouseEvent")) {
- view.MouseEnter += View_MouseEnter;
- view.MouseLeave += View_MouseLeave;
- }
- _contentView.Add (view);
- }
- SetNeedsLayout ();
+ ///
+ /// Adds the view to the scrollview.
+ ///
+ /// The view to add to the scrollview.
+ public override void Add (View view)
+ {
+ if (view.Id == "contentBottomRightCorner") {
+ _contentBottomRightCorner = view;
+ base.Add (view);
+ } else {
+ if (!IsOverridden (view, "MouseEvent")) {
+ view.MouseEnter += View_MouseEnter;
+ view.MouseLeave += View_MouseLeave;
+ }
+ _contentView.Add (view);
}
+ SetNeedsLayout ();
+ }
- void View_MouseLeave (object sender, MouseEventEventArgs e)
- {
- if (Application.MouseGrabView != null && Application.MouseGrabView != _vertical && Application.MouseGrabView != _horizontal) {
- Application.UngrabMouse ();
- }
+ void View_MouseLeave (object sender, MouseEventEventArgs e)
+ {
+ if (Application.MouseGrabView != null && Application.MouseGrabView != _vertical && Application.MouseGrabView != _horizontal) {
+ Application.UngrabMouse ();
}
+ }
- void View_MouseEnter (object sender, MouseEventEventArgs e)
- {
- Application.GrabMouse (this);
- }
-
- ///
- /// Gets or sets the visibility for the horizontal scroll indicator.
- ///
- /// true if show horizontal scroll indicator; otherwise, false.
- public bool ShowHorizontalScrollIndicator {
- get => _showHorizontalScrollIndicator;
- set {
- if (value != _showHorizontalScrollIndicator) {
- _showHorizontalScrollIndicator = value;
- SetNeedsLayout ();
- if (value) {
- _horizontal.OtherScrollBarView = _vertical;
- base.Add (_horizontal);
- _horizontal.ShowScrollIndicator = value;
- _horizontal.AutoHideScrollBars = _autoHideScrollBars;
- _horizontal.OtherScrollBarView.ShowScrollIndicator = value;
- _horizontal.MouseEnter += View_MouseEnter;
- _horizontal.MouseLeave += View_MouseLeave;
- } else {
- base.Remove (_horizontal);
- _horizontal.OtherScrollBarView = null;
- _horizontal.MouseEnter -= View_MouseEnter;
- _horizontal.MouseLeave -= View_MouseLeave;
- }
+ void View_MouseEnter (object sender, MouseEventEventArgs e)
+ {
+ Application.GrabMouse (this);
+ }
+
+ ///
+ /// Gets or sets the visibility for the horizontal scroll indicator.
+ ///
+ /// true if show horizontal scroll indicator; otherwise, false.
+ public bool ShowHorizontalScrollIndicator {
+ get => _showHorizontalScrollIndicator;
+ set {
+ if (value != _showHorizontalScrollIndicator) {
+ _showHorizontalScrollIndicator = value;
+ SetNeedsLayout ();
+ if (value) {
+ _horizontal.OtherScrollBarView = _vertical;
+ base.Add (_horizontal);
+ _horizontal.ShowScrollIndicator = value;
+ _horizontal.AutoHideScrollBars = _autoHideScrollBars;
+ _horizontal.OtherScrollBarView.ShowScrollIndicator = value;
+ _horizontal.MouseEnter += View_MouseEnter;
+ _horizontal.MouseLeave += View_MouseLeave;
+ } else {
+ base.Remove (_horizontal);
+ _horizontal.OtherScrollBarView = null;
+ _horizontal.MouseEnter -= View_MouseEnter;
+ _horizontal.MouseLeave -= View_MouseLeave;
}
- _vertical.Height = Dim.Fill (_showHorizontalScrollIndicator ? 1 : 0);
}
+ _vertical.Height = Dim.Fill (_showHorizontalScrollIndicator ? 1 : 0);
}
+ }
- ///
- /// Removes all widgets from this container.
- ///
- ///
- ///
- public override void RemoveAll ()
- {
- _contentView.RemoveAll ();
- }
-
- ///
- /// Gets or sets the visibility for the vertical scroll indicator.
- ///
- /// true if show vertical scroll indicator; otherwise, false.
- public bool ShowVerticalScrollIndicator {
- get => _showVerticalScrollIndicator;
- set {
- if (value != _showVerticalScrollIndicator) {
- _showVerticalScrollIndicator = value;
- SetNeedsLayout ();
- if (value) {
- _vertical.OtherScrollBarView = _horizontal;
- base.Add (_vertical);
- _vertical.ShowScrollIndicator = value;
- _vertical.AutoHideScrollBars = _autoHideScrollBars;
- _vertical.OtherScrollBarView.ShowScrollIndicator = value;
- _vertical.MouseEnter += View_MouseEnter;
- _vertical.MouseLeave += View_MouseLeave;
- } else {
- Remove (_vertical);
- _vertical.OtherScrollBarView = null;
- _vertical.MouseEnter -= View_MouseEnter;
- _vertical.MouseLeave -= View_MouseLeave;
- }
+ ///
+ /// Removes all widgets from this container.
+ ///
+ ///
+ ///
+ public override void RemoveAll ()
+ {
+ _contentView.RemoveAll ();
+ }
+
+ ///
+ /// Gets or sets the visibility for the vertical scroll indicator.
+ ///
+ /// true if show vertical scroll indicator; otherwise, false.
+ public bool ShowVerticalScrollIndicator {
+ get => _showVerticalScrollIndicator;
+ set {
+ if (value != _showVerticalScrollIndicator) {
+ _showVerticalScrollIndicator = value;
+ SetNeedsLayout ();
+ if (value) {
+ _vertical.OtherScrollBarView = _horizontal;
+ base.Add (_vertical);
+ _vertical.ShowScrollIndicator = value;
+ _vertical.AutoHideScrollBars = _autoHideScrollBars;
+ _vertical.OtherScrollBarView.ShowScrollIndicator = value;
+ _vertical.MouseEnter += View_MouseEnter;
+ _vertical.MouseLeave += View_MouseLeave;
+ } else {
+ Remove (_vertical);
+ _vertical.OtherScrollBarView = null;
+ _vertical.MouseEnter -= View_MouseEnter;
+ _vertical.MouseLeave -= View_MouseLeave;
}
- _horizontal.Width = Dim.Fill (_showVerticalScrollIndicator ? 1 : 0);
}
+ _horizontal.Width = Dim.Fill (_showVerticalScrollIndicator ? 1 : 0);
}
+ }
- ///
- public override void OnDrawContent (Rect contentArea)
- {
- SetViewsNeedsDisplay ();
+ ///
+ public override void OnDrawContent (Rect contentArea)
+ {
+ SetViewsNeedsDisplay ();
- var savedClip = ClipToBounds ();
- Driver.SetAttribute (GetNormalColor ());
- Clear ();
+ var savedClip = ClipToBounds ();
+ // TODO: It's bad practice for views to always clear a view. It negates clipping.
+ Clear ();
- if (!string.IsNullOrEmpty (_contentView.Text) || _contentView.Subviews.Count > 0) {
- _contentView.Draw ();
- }
+ if (!string.IsNullOrEmpty (_contentView.Text) || _contentView.Subviews.Count > 0) {
+ _contentView.Draw ();
+ }
- DrawScrollBars ();
+ DrawScrollBars ();
- Driver.Clip = savedClip;
- }
+ Driver.Clip = savedClip;
+ }
- private void DrawScrollBars ()
- {
- if (_autoHideScrollBars) {
- ShowHideScrollBars ();
- } else {
- if (ShowVerticalScrollIndicator) {
- _vertical.Draw ();
- }
- if (ShowHorizontalScrollIndicator) {
- _horizontal.Draw ();
- }
- if (ShowVerticalScrollIndicator && ShowHorizontalScrollIndicator) {
- SetContentBottomRightCornerVisibility ();
- _contentBottomRightCorner.Draw ();
- }
+ private void DrawScrollBars ()
+ {
+ if (_autoHideScrollBars) {
+ ShowHideScrollBars ();
+ } else {
+ if (ShowVerticalScrollIndicator) {
+ _vertical.Draw ();
+ }
+ if (ShowHorizontalScrollIndicator) {
+ _horizontal.Draw ();
+ }
+ if (ShowVerticalScrollIndicator && ShowHorizontalScrollIndicator) {
+ SetContentBottomRightCornerVisibility ();
+ _contentBottomRightCorner.Draw ();
}
}
+ }
- private void SetContentBottomRightCornerVisibility ()
- {
- if (_showHorizontalScrollIndicator && _showVerticalScrollIndicator) {
- _contentBottomRightCorner.Visible = true;
- } else if (_horizontal.IsAdded || _vertical.IsAdded) {
- _contentBottomRightCorner.Visible = false;
- }
+ private void SetContentBottomRightCornerVisibility ()
+ {
+ if (_showHorizontalScrollIndicator && _showVerticalScrollIndicator) {
+ _contentBottomRightCorner.Visible = true;
+ } else if (_horizontal.IsAdded || _vertical.IsAdded) {
+ _contentBottomRightCorner.Visible = false;
}
+ }
- void ShowHideScrollBars ()
- {
- bool v = false, h = false; bool p = false;
+ void ShowHideScrollBars ()
+ {
+ bool v = false, h = false; bool p = false;
- if (Bounds.Height == 0 || Bounds.Height > _contentSize.Height) {
- if (ShowVerticalScrollIndicator) {
- ShowVerticalScrollIndicator = false;
- }
- v = false;
- } else if (Bounds.Height > 0 && Bounds.Height == _contentSize.Height) {
- p = true;
- } else {
+ if (Bounds.Height == 0 || Bounds.Height > _contentSize.Height) {
+ if (ShowVerticalScrollIndicator) {
+ ShowVerticalScrollIndicator = false;
+ }
+ v = false;
+ } else if (Bounds.Height > 0 && Bounds.Height == _contentSize.Height) {
+ p = true;
+ } else {
+ if (!ShowVerticalScrollIndicator) {
+ ShowVerticalScrollIndicator = true;
+ }
+ v = true;
+ }
+ if (Bounds.Width == 0 || Bounds.Width > _contentSize.Width) {
+ if (ShowHorizontalScrollIndicator) {
+ ShowHorizontalScrollIndicator = false;
+ }
+ h = false;
+ } else if (Bounds.Width > 0 && Bounds.Width == _contentSize.Width && p) {
+ if (ShowHorizontalScrollIndicator) {
+ ShowHorizontalScrollIndicator = false;
+ }
+ h = false;
+ if (ShowVerticalScrollIndicator) {
+ ShowVerticalScrollIndicator = false;
+ }
+ v = false;
+ } else {
+ if (p) {
if (!ShowVerticalScrollIndicator) {
ShowVerticalScrollIndicator = true;
}
v = true;
}
- if (Bounds.Width == 0 || Bounds.Width > _contentSize.Width) {
- if (ShowHorizontalScrollIndicator) {
- ShowHorizontalScrollIndicator = false;
- }
- h = false;
- } else if (Bounds.Width > 0 && Bounds.Width == _contentSize.Width && p) {
- if (ShowHorizontalScrollIndicator) {
- ShowHorizontalScrollIndicator = false;
- }
- h = false;
- if (ShowVerticalScrollIndicator) {
- ShowVerticalScrollIndicator = false;
- }
- v = false;
- } else {
- if (p) {
- if (!ShowVerticalScrollIndicator) {
- ShowVerticalScrollIndicator = true;
- }
- v = true;
- }
- if (!ShowHorizontalScrollIndicator) {
- ShowHorizontalScrollIndicator = true;
- }
- h = true;
- }
- var dim = Dim.Fill (h ? 1 : 0);
- if (!_vertical.Height.Equals (dim)) {
- _vertical.Height = dim;
- }
- dim = Dim.Fill (v ? 1 : 0);
- if (!_horizontal.Width.Equals (dim)) {
- _horizontal.Width = dim;
- }
-
- if (v) {
- _vertical.SetRelativeLayout (Bounds);
- _vertical.Draw ();
- }
- if (h) {
- _horizontal.SetRelativeLayout (Bounds);
- _horizontal.Draw ();
- }
- SetContentBottomRightCornerVisibility ();
- if (v && h) {
- _contentBottomRightCorner.SetRelativeLayout (Bounds);
- _contentBottomRightCorner.Draw ();
+ if (!ShowHorizontalScrollIndicator) {
+ ShowHorizontalScrollIndicator = true;
}
+ h = true;
+ }
+ var dim = Dim.Fill (h ? 1 : 0);
+ if (!_vertical.Height.Equals (dim)) {
+ _vertical.Height = dim;
+ }
+ dim = Dim.Fill (v ? 1 : 0);
+ if (!_horizontal.Width.Equals (dim)) {
+ _horizontal.Width = dim;
}
- void SetViewsNeedsDisplay ()
- {
- foreach (View view in _contentView.Subviews) {
- view.SetNeedsDisplay ();
- }
+ if (v) {
+ _vertical.SetRelativeLayout (Bounds);
+ _vertical.Draw ();
+ }
+ if (h) {
+ _horizontal.SetRelativeLayout (Bounds);
+ _horizontal.Draw ();
+ }
+ SetContentBottomRightCornerVisibility ();
+ if (v && h) {
+ _contentBottomRightCorner.SetRelativeLayout (Bounds);
+ _contentBottomRightCorner.Draw ();
}
+ }
- ///
- public override void PositionCursor ()
- {
- if (InternalSubviews.Count == 0)
- Move (0, 0);
- else
- base.PositionCursor ();
- }
-
- ///
- /// Scrolls the view up.
- ///
- /// true, if left was scrolled, false otherwise.
- /// Number of lines to scroll.
- public bool ScrollUp (int lines)
- {
- if (_contentOffset.Y < 0) {
- ContentOffset = new Point (_contentOffset.X, Math.Min (_contentOffset.Y + lines, 0));
- return true;
- }
- return false;
+ void SetViewsNeedsDisplay ()
+ {
+ foreach (View view in _contentView.Subviews) {
+ view.SetNeedsDisplay ();
}
+ }
- ///
- /// Scrolls the view to the left
- ///
- /// true, if left was scrolled, false otherwise.
- /// Number of columns to scroll by.
- public bool ScrollLeft (int cols)
- {
- if (_contentOffset.X < 0) {
- ContentOffset = new Point (Math.Min (_contentOffset.X + cols, 0), _contentOffset.Y);
- return true;
- }
- return false;
+ ///
+ public override void PositionCursor ()
+ {
+ if (InternalSubviews.Count == 0)
+ Move (0, 0);
+ else
+ base.PositionCursor ();
+ }
+
+ ///
+ /// Scrolls the view up.
+ ///
+ /// true, if left was scrolled, false otherwise.
+ /// Number of lines to scroll.
+ public bool ScrollUp (int lines)
+ {
+ if (_contentOffset.Y < 0) {
+ ContentOffset = new Point (_contentOffset.X, Math.Min (_contentOffset.Y + lines, 0));
+ return true;
}
+ return false;
+ }
- ///
- /// Scrolls the view down.
- ///
- /// true, if left was scrolled, false otherwise.
- /// Number of lines to scroll.
- public bool ScrollDown (int lines)
- {
- if (_vertical.CanScroll (lines, out _, true)) {
- ContentOffset = new Point (_contentOffset.X, _contentOffset.Y - lines);
- return true;
- }
- return false;
+ ///
+ /// Scrolls the view to the left
+ ///
+ /// true, if left was scrolled, false otherwise.
+ /// Number of columns to scroll by.
+ public bool ScrollLeft (int cols)
+ {
+ if (_contentOffset.X < 0) {
+ ContentOffset = new Point (Math.Min (_contentOffset.X + cols, 0), _contentOffset.Y);
+ return true;
}
+ return false;
+ }
- ///
- /// Scrolls the view to the right.
- ///
- /// true, if right was scrolled, false otherwise.
- /// Number of columns to scroll by.
- public bool ScrollRight (int cols)
- {
- if (_horizontal.CanScroll (cols, out _)) {
- ContentOffset = new Point (_contentOffset.X - cols, _contentOffset.Y);
- return true;
- }
- return false;
+ ///
+ /// Scrolls the view down.
+ ///
+ /// true, if left was scrolled, false otherwise.
+ /// Number of lines to scroll.
+ public bool ScrollDown (int lines)
+ {
+ if (_vertical.CanScroll (lines, out _, true)) {
+ ContentOffset = new Point (_contentOffset.X, _contentOffset.Y - lines);
+ return true;
}
+ return false;
+ }
- ///
- public override bool ProcessKey (KeyEvent kb)
- {
- if (base.ProcessKey (kb))
- return true;
+ ///
+ /// Scrolls the view to the right.
+ ///
+ /// true, if right was scrolled, false otherwise.
+ /// Number of columns to scroll by.
+ public bool ScrollRight (int cols)
+ {
+ if (_horizontal.CanScroll (cols, out _)) {
+ ContentOffset = new Point (_contentOffset.X - cols, _contentOffset.Y);
+ return true;
+ }
+ return false;
+ }
- var result = InvokeKeybindings (kb);
- if (result != null)
- return (bool)result;
+ ///
+ public override bool ProcessKey (KeyEvent kb)
+ {
+ if (base.ProcessKey (kb))
+ return true;
- return false;
- }
+ var result = InvokeKeybindings (kb);
+ if (result != null)
+ return (bool)result;
- ///
- public override bool MouseEvent (MouseEvent me)
- {
- if (me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp &&
- me.Flags != MouseFlags.WheeledRight && me.Flags != MouseFlags.WheeledLeft &&
- // me.Flags != MouseFlags.Button1Pressed && me.Flags != MouseFlags.Button1Clicked &&
- !me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
+ return false;
+ }
- return false;
- }
+ ///
+ public override bool MouseEvent (MouseEvent me)
+ {
+ if (me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp &&
+ me.Flags != MouseFlags.WheeledRight && me.Flags != MouseFlags.WheeledLeft &&
+ // me.Flags != MouseFlags.Button1Pressed && me.Flags != MouseFlags.Button1Clicked &&
+ !me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
- if (me.Flags == MouseFlags.WheeledDown && ShowVerticalScrollIndicator) {
- ScrollDown (1);
- } else if (me.Flags == MouseFlags.WheeledUp && ShowVerticalScrollIndicator) {
- ScrollUp (1);
- } else if (me.Flags == MouseFlags.WheeledRight && _showHorizontalScrollIndicator) {
- ScrollRight (1);
- } else if (me.Flags == MouseFlags.WheeledLeft && ShowVerticalScrollIndicator) {
- ScrollLeft (1);
- } else if (me.X == _vertical.Frame.X && ShowVerticalScrollIndicator) {
- _vertical.MouseEvent (me);
- } else if (me.Y == _horizontal.Frame.Y && ShowHorizontalScrollIndicator) {
- _horizontal.MouseEvent (me);
- } else if (IsOverridden (me.View, "MouseEvent")) {
- Application.UngrabMouse ();
- }
- return true;
+ return false;
}
- ///
- protected override void Dispose (bool disposing)
- {
- if (!_showVerticalScrollIndicator) {
- // It was not added to SuperView, so it won't get disposed automatically
- _vertical?.Dispose ();
- }
- if (!_showHorizontalScrollIndicator) {
- // It was not added to SuperView, so it won't get disposed automatically
- _horizontal?.Dispose ();
- }
- base.Dispose (disposing);
+ if (me.Flags == MouseFlags.WheeledDown && ShowVerticalScrollIndicator) {
+ ScrollDown (1);
+ } else if (me.Flags == MouseFlags.WheeledUp && ShowVerticalScrollIndicator) {
+ ScrollUp (1);
+ } else if (me.Flags == MouseFlags.WheeledRight && _showHorizontalScrollIndicator) {
+ ScrollRight (1);
+ } else if (me.Flags == MouseFlags.WheeledLeft && ShowVerticalScrollIndicator) {
+ ScrollLeft (1);
+ } else if (me.X == _vertical.Frame.X && ShowVerticalScrollIndicator) {
+ _vertical.MouseEvent (me);
+ } else if (me.Y == _horizontal.Frame.Y && ShowHorizontalScrollIndicator) {
+ _horizontal.MouseEvent (me);
+ } else if (IsOverridden (me.View, "MouseEvent")) {
+ Application.UngrabMouse ();
}
+ return true;
+ }
- ///
- public override bool OnEnter (View view)
- {
- if (Subviews.Count == 0 || !Subviews.Any (subview => subview.CanFocus)) {
- Application.Driver?.SetCursorVisibility (CursorVisibility.Invisible);
- }
+ ///
+ protected override void Dispose (bool disposing)
+ {
+ if (!_showVerticalScrollIndicator) {
+ // It was not added to SuperView, so it won't get disposed automatically
+ _vertical?.Dispose ();
+ }
+ if (!_showHorizontalScrollIndicator) {
+ // It was not added to SuperView, so it won't get disposed automatically
+ _horizontal?.Dispose ();
+ }
+ base.Dispose (disposing);
+ }
- return base.OnEnter (view);
+ ///
+ public override bool OnEnter (View view)
+ {
+ if (Subviews.Count == 0 || !Subviews.Any (subview => subview.CanFocus)) {
+ Application.Driver?.SetCursorVisibility (CursorVisibility.Invisible);
}
+
+ return base.OnEnter (view);
}
}
diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs
index 4e4fd77325..8fb9a21569 100644
--- a/Terminal.Gui/Views/StatusBar.cs
+++ b/Terminal.Gui/Views/StatusBar.cs
@@ -121,8 +121,9 @@ public override void OnDrawContent (Rect contentArea)
{
Move (0, 0);
Driver.SetAttribute (GetNormalColor ());
- for (int i = 0; i < Frame.Width; i++)
+ for (int i = 0; i < Frame.Width; i++) {
Driver.AddRune ((Rune)' ');
+ }
Move (1, 0);
var scheme = GetNormalColor ();
diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs
index b4ae68ab4c..86a558b82d 100644
--- a/Terminal.Gui/Views/TabView.cs
+++ b/Terminal.Gui/Views/TabView.cs
@@ -171,7 +171,7 @@ public void ApplyStyleChanges ()
// Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0
tabsBar.Y = Pos.Percent (0);
}
-
+ LayoutSubviews ();
SetNeedsDisplay ();
}
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index 9907d9480f..d9f3d29f1d 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -461,7 +461,7 @@ public override void OnDrawContent (Rect contentArea)
var roc = GetReadOnlyColor ();
for (int idx = p; idx < tcount; idx++) {
var rune = _text [idx];
- var cols = ((Rune)rune).GetColumns ();
+ var cols = rune.GetColumns ();
if (idx == _point && HasFocus && !Used && _length == 0 && !ReadOnly) {
Driver.SetAttribute (selColor);
} else if (ReadOnly) {
@@ -474,7 +474,7 @@ public override void OnDrawContent (Rect contentArea)
Driver.SetAttribute (idx >= _start && _length > 0 && idx < _start + _length ? selColor : ColorScheme.Focus);
}
if (col + cols <= width) {
- Driver.AddRune ((Rune)(Secret ? CM.Glyphs.Dot : rune));
+ Driver.AddRune ((Secret ? CM.Glyphs.Dot : rune));
}
if (!TextModel.SetCol (ref col, width, cols)) {
break;
@@ -567,7 +567,7 @@ void Adjust ()
return;
int offB = OffSetBackground ();
- bool need = !_needsDisplay.IsEmpty || !Used;
+ bool need = NeedsDisplay || !Used;
if (_point < _first) {
_first = _point;
need = true;
@@ -1233,14 +1233,11 @@ public virtual void Cut ()
List DeleteSelectedText ()
{
- string actualText = Text;
SetSelectedStartSelectedLength ();
int selStart = SelectedStart > -1 ? _start : _point;
- (var size, var _) = TextModel.DisplaySize (_text, 0, selStart, false);
- (var size2, var _) = TextModel.DisplaySize (_text, selStart, selStart + _length, false);
- (var size3, var _) = TextModel.DisplaySize (_text, selStart + _length, actualText.GetRuneCount (), false);
- var newText = actualText [..size] +
- actualText.Substring (size + size2, size3);
+ var newText = StringExtensions.ToString (_text.GetRange (0, selStart)) +
+ StringExtensions.ToString (_text.GetRange (selStart + _length, _text.Count - (selStart + _length)));
+
ClearAllSelection ();
_point = selStart >= newText.GetRuneCount () ? newText.GetRuneCount () : selStart;
return newText.ToRuneList ();
@@ -1257,14 +1254,11 @@ public virtual void Paste ()
SetSelectedStartSelectedLength ();
int selStart = _start == -1 ? CursorPosition : _start;
- string actualText = Text;
- (int size, int _) = TextModel.DisplaySize (_text, 0, selStart, false);
- (var size2, var _) = TextModel.DisplaySize (_text, selStart, selStart + _length, false);
- (var size3, var _) = TextModel.DisplaySize (_text, selStart + _length, actualText.GetRuneCount (), false);
string cbTxt = Clipboard.Contents.Split ("\n") [0] ?? "";
- Text = actualText [..size] +
+ Text = StringExtensions.ToString (_text.GetRange (0, selStart)) +
cbTxt +
- actualText.Substring (size + size2, size3);
+ StringExtensions.ToString (_text.GetRange (selStart + _length, _text.Count - (selStart + _length)));
+
_point = selStart + cbTxt.GetRuneCount ();
ClearAllSelection ();
SetNeedsDisplay ();
@@ -1375,4 +1369,4 @@ protected override void SetCursorPosition (int column)
((TextField)HostControl).CursorPosition = column;
}
}
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index c6f111c6cd..5a1f49293b 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -2407,7 +2407,11 @@ void ClearRegion (int left, int top, int right, int bottom)
///
public override Attribute GetNormalColor ()
{
- return Enabled ? ColorScheme.Focus : ColorScheme.Disabled;
+ ColorScheme cs = ColorScheme;
+ if (ColorScheme == null) {
+ cs = new ColorScheme ();
+ }
+ return Enabled ? cs.Focus : cs.Disabled;
}
///
@@ -3063,10 +3067,10 @@ public void InsertText (string toAdd)
InsertText (new KeyEvent () { Key = key });
}
- if (_needsDisplay.IsEmpty) {
- PositionCursor ();
- } else {
+ if (NeedsDisplay) {
Adjust ();
+ } else {
+ PositionCursor ();
}
}
@@ -3235,7 +3239,7 @@ void Adjust ()
{
var offB = OffSetBackground ();
var line = GetCurrentLine ();
- bool need = !_needsDisplay.IsEmpty || _wrapNeeded || !Used;
+ bool need = NeedsDisplay || _wrapNeeded || !Used;
var tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth);
var dSize = TextModel.DisplaySize (line, _leftColumn, _currentColumn, true, TabWidth);
if (!_wordWrap && _currentColumn < _leftColumn) {
@@ -4395,10 +4399,10 @@ public override bool OnKeyUp (KeyEvent kb)
void DoNeededAction ()
{
- if (_needsDisplay.IsEmpty) {
- PositionCursor ();
- } else {
+ if (NeedsDisplay) {
Adjust ();
+ } else {
+ PositionCursor ();
}
}
diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs
index 01ffc6f0ac..aa660203bc 100644
--- a/Terminal.Gui/Views/TileView.cs
+++ b/Terminal.Gui/Views/TileView.cs
@@ -898,7 +898,7 @@ public override bool MouseEvent (MouseEvent mouseEvent)
// End Drag
Application.UngrabMouse ();
- Driver.UncookMouse ();
+ //Driver.UncookMouse ();
FinalisePosition (
dragOrignalPos,
Orientation == Orientation.Horizontal ? Y : X);
diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs
index d567f25786..159774984d 100644
--- a/Terminal.Gui/Views/Toplevel.cs
+++ b/Terminal.Gui/Views/Toplevel.cs
@@ -757,9 +757,10 @@ public override void OnDrawContent (Rect contentArea)
return;
}
- if (!_needsDisplay.IsEmpty || _subViewNeedsDisplay || LayoutNeeded) {
- Driver.SetAttribute (GetNormalColor ());
- Clear (ViewToScreen (Bounds));
+ if (NeedsDisplay || SubViewNeedsDisplay || LayoutNeeded) {
+ //Driver.SetAttribute (GetNormalColor ());
+ // TODO: It's bad practice for views to always clear. Defeats the purpose of clipping etc...
+ Clear ();
LayoutSubviews ();
PositionToplevels ();
@@ -776,9 +777,10 @@ public override void OnDrawContent (Rect contentArea)
}
}
+ // This should not be here, but in base
foreach (var view in Subviews) {
if (view.Frame.IntersectsWith (Bounds) && !OutsideTopFrame (this)) {
- view.SetNeedsLayout ();
+ //view.SetNeedsLayout ();
view.SetNeedsDisplay (view.Bounds);
view.SetSubViewNeedsDisplay ();
}
diff --git a/Terminal.Gui/Views/ToplevelOverlapped.cs b/Terminal.Gui/Views/ToplevelOverlapped.cs
index c28f4d7c79..1c95d66477 100644
--- a/Terminal.Gui/Views/ToplevelOverlapped.cs
+++ b/Terminal.Gui/Views/ToplevelOverlapped.cs
@@ -97,7 +97,7 @@ static bool OverlappedChildNeedsDisplay ()
}
foreach (var top in _toplevels) {
- if (top != Current && top.Visible && (!top._needsDisplay.IsEmpty || top._subViewNeedsDisplay || top.LayoutNeeded)) {
+ if (top != Current && top.Visible && (top.NeedsDisplay || top.SubViewNeedsDisplay || top.LayoutNeeded)) {
OverlappedTop.SetSubViewNeedsDisplay ();
return true;
}
diff --git a/Terminal.sln b/Terminal.sln
index cd242ab364..b0837d7501 100644
--- a/Terminal.sln
+++ b/Terminal.sln
@@ -14,15 +14,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example.
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E143FB1F-0B88-48CB-9086-72CDCECFCD22}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
.gitignore = .gitignore
+ .github\CODEOWNERS = .github\CODEOWNERS
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
CONTRIBUTING.md = CONTRIBUTING.md
.github\workflows\dotnet-core.yml = .github\workflows\dotnet-core.yml
- .github\GitVersion.yml = .github\GitVersion.yml
.github\workflows\publish.yml = .github\workflows\publish.yml
README.md = README.md
- Release.ps1 = Release.ps1
testenvironments.json = testenvironments.json
EndProjectSection
EndProject
diff --git a/UICatalog/Properties/launchSettings.json b/UICatalog/Properties/launchSettings.json
index 4fe02b923b..00e9d9e71f 100644
--- a/UICatalog/Properties/launchSettings.json
+++ b/UICatalog/Properties/launchSettings.json
@@ -50,6 +50,9 @@
"Windows & FrameViews": {
"commandName": "Project",
"commandLineArgs": "\"Windows & FrameViews\""
+ },
+ "Profile 1": {
+ "commandName": "Executable"
}
}
}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/BasicColors.cs b/UICatalog/Scenarios/BasicColors.cs
index a8fe04e4c2..9284659f74 100644
--- a/UICatalog/Scenarios/BasicColors.cs
+++ b/UICatalog/Scenarios/BasicColors.cs
@@ -87,8 +87,8 @@ public override void Setup ()
Application.RootMouseEvent = (e) => {
if (e.View != null) {
- var colorValue = e.View.GetNormalColor ().Value;
- Application.Driver.GetColors (colorValue, out Color fore, out Color back);
+ var fore = e.View.GetNormalColor ().Foreground;
+ var back = e.View.GetNormalColor ().Background;
lblForeground.Text = fore.ToString ();
viewForeground.ColorScheme.Normal = new Attribute (fore, fore);
lblBackground.Text = back.ToString ();
diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs
index ca1d792342..7c220312d2 100644
--- a/UICatalog/Scenarios/CharacterMap.cs
+++ b/UICatalog/Scenarios/CharacterMap.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Globalization;
using System.Linq;
using System.Net.Http;
@@ -11,6 +12,7 @@
using System.Text.Unicode;
using System.Threading.Tasks;
using Terminal.Gui;
+using static Terminal.Gui.SpinnerStyle;
using static Terminal.Gui.TableView;
namespace UICatalog.Scenarios;
@@ -27,24 +29,31 @@ namespace UICatalog.Scenarios;
[ScenarioCategory ("ScrollView")]
public class CharacterMap : Scenario {
CharMap _charMap;
- Label _errorLabel;
+ public Label _errorLabel;
TableView _categoryList;
+ // Don't create a Window, just return the top-level view
+ public override void Init ()
+ {
+ Application.Init ();
+ Application.Top.ColorScheme = Colors.Base;
+ }
+
public override void Setup ()
{
_charMap = new CharMap () {
X = 0,
- Y = 0,
+ Y = 1,
Height = Dim.Fill ()
};
- Win.Add (_charMap);
+ Application.Top.Add (_charMap);
var jumpLabel = new Label ("Jump To Code Point:") { X = Pos.Right (_charMap) + 1, Y = Pos.Y (_charMap) };
- Win.Add (jumpLabel);
+ Application.Top.Add (jumpLabel);
var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3" };
- Win.Add (jumpEdit);
- _errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] };
- Win.Add (_errorLabel);
+ Application.Top.Add (jumpEdit);
+ _errorLabel = new Label ("err") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] };
+ Application.Top.Add (_errorLabel);
jumpEdit.TextChanged += JumpEdit_TextChanged;
@@ -96,15 +105,42 @@ public override void Setup ()
_charMap.StartCodePoint = table.Data.ToArray () [args.NewRow].Start;
};
- Win.Add (_categoryList);
+ Application.Top.Add (_categoryList);
_charMap.SelectedCodePoint = 0;
//jumpList.Refresh ();
_charMap.SetFocus ();
_charMap.Width = Dim.Fill () - _categoryList.Width;
+
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("_Quit", $"{Application.QuitKey}", () => Application.RequestStop ()),
+ }),
+ new MenuBarItem ("_Options", new MenuItem [] {
+ CreateMenuShowWidth (),
+ })
+ });
+ Application.Top.Add (menu);
+
+ //_charMap.Hover += (s, a) => {
+ // _errorLabel.Text = $"U+{a.Item:x5} {(Rune)a.Item}";
+ //};
}
+ MenuItem CreateMenuShowWidth ()
+ {
+ var item = new MenuItem {
+ Title = "_Show Glyph Width",
+ };
+ item.CheckType |= MenuItemCheckStyle.Checked;
+ item.Checked = _charMap?.ShowGlyphWidths;
+ item.Action += () => {
+ _charMap.ShowGlyphWidths = (bool)(item.Checked = !item.Checked);
+ };
+
+ return item;
+ }
EnumerableTableSource CreateCategoryTable (int sortByColumn, bool descending)
{
@@ -177,7 +213,7 @@ private void JumpEdit_TextChanged (object sender, TextChangedEventArgs e)
_errorLabel.Text = $"Beyond maximum codepoint";
return;
}
- _errorLabel.Text = $"U+{result:x4}";
+ _errorLabel.Text = $"U+{result:x5}";
var table = (EnumerableTableSource)_categoryList.Table;
_categoryList.SelectedRow = table.Data
@@ -200,8 +236,7 @@ public int StartCodePoint {
get => _start;
set {
_start = value;
- _selected = value;
- ContentOffset = new Point (0, (int)(_start / 16));
+ SelectedCodePoint = value;
SetNeedsDisplay ();
}
}
@@ -216,15 +251,16 @@ public int SelectedCodePoint {
get => _selected;
set {
_selected = value;
- var col = Cursor.X;
- var row = Cursor.Y;
- var height = (Bounds.Height / ROW_HEIGHT) - (ShowHorizontalScrollIndicator ? 2 : 1);
+ var row = (SelectedCodePoint / 16 * _rowHeight);
+ var col = (SelectedCodePoint % 16 * COLUMN_WIDTH);
+
+ var height = (Bounds.Height) - (ShowHorizontalScrollIndicator ? 2 : 1);
if (row + ContentOffset.Y < 0) {
// Moving up.
ContentOffset = new Point (ContentOffset.X, row);
} else if (row + ContentOffset.Y >= height) {
// Moving down.
- ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + ROW_HEIGHT));
+ ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + _rowHeight));
}
var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth);
if (col + ContentOffset.X < 0) {
@@ -239,10 +275,15 @@ public int SelectedCodePoint {
}
}
+ public event EventHandler Hover;
+
+ ///
+ /// Gets the coordinates of the Cursor based on the SelectedCodePoint in screen coordinates
+ ///
public Point Cursor {
get {
- var row = SelectedCodePoint / 16;
- var col = (SelectedCodePoint - row * 16) * COLUMN_WIDTH;
+ var row = (SelectedCodePoint / 16 * _rowHeight) + ContentOffset.Y + 1;
+ var col = (SelectedCodePoint % 16 * COLUMN_WIDTH) + ContentOffset.X + RowLabelWidth + 1; // + 1 for padding
return new Point (col, row);
}
set => throw new NotImplementedException ();
@@ -250,35 +291,42 @@ public Point Cursor {
public override void PositionCursor ()
{
- if (HasFocus && Cursor.X + ContentOffset.X + RowLabelWidth + 1 >= RowLabelWidth &&
- Cursor.X + ContentOffset.X + RowLabelWidth + 1 < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) &&
- Cursor.Y + ContentOffset.Y + 1 > 0 &&
- Cursor.Y + ContentOffset.Y + 1 < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0)) {
-
+ if (HasFocus &&
+ Cursor.X >= RowLabelWidth &&
+ Cursor.X < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0) &&
+ Cursor.Y > 0 &&
+ Cursor.Y < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0)) {
Driver.SetCursorVisibility (CursorVisibility.Default);
- Move (Cursor.X + ContentOffset.X + RowLabelWidth + 1, Cursor.Y + ContentOffset.Y + 1);
+ Move (Cursor.X, Cursor.Y);
} else {
Driver.SetCursorVisibility (CursorVisibility.Invisible);
}
}
+ public bool ShowGlyphWidths {
+ get => _rowHeight == 2;
+ set {
+ _rowHeight = value ? 2 : 1;
+ SetNeedsDisplay ();
+ }
+ }
int _start = 0;
int _selected = 0;
- public const int COLUMN_WIDTH = 3;
- public const int ROW_HEIGHT = 1;
+ const int COLUMN_WIDTH = 3;
+ int _rowHeight = 1;
public static int MaxCodePoint => 0x10FFFF;
- public static int RowLabelWidth => $"U+{MaxCodePoint:x5}".Length + 1;
- public static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16);
+ static int RowLabelWidth => $"U+{MaxCodePoint:x5}".Length + 1;
+ static int RowWidth => RowLabelWidth + (COLUMN_WIDTH * 16);
public CharMap ()
{
ColorScheme = Colors.Dialog;
CanFocus = true;
- ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1)));
+ ContentSize = new Size (CharMap.RowWidth, (int)((MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1)) * _rowHeight));
AddCommand (Command.ScrollUp, () => {
if (SelectedCodePoint >= 16) {
@@ -305,12 +353,12 @@ public CharMap ()
return true;
});
AddCommand (Command.PageUp, () => {
- var page = (Bounds.Height / ROW_HEIGHT - 1) * 16;
+ var page = (Bounds.Height / _rowHeight - 1) * 16;
SelectedCodePoint -= Math.Min (page, SelectedCodePoint);
return true;
});
AddCommand (Command.PageDown, () => {
- var page = (Bounds.Height / ROW_HEIGHT - 1) * 16;
+ var page = (Bounds.Height / _rowHeight - 1) * 16;
SelectedCodePoint += Math.Min (page, MaxCodePoint - SelectedCodePoint);
return true;
});
@@ -336,26 +384,34 @@ public CharMap ()
public override void OnDrawContent (Rect contentArea)
{
- if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePoint / 16 + 2)) {
- ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 2));
- var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth);
- if (Cursor.X + ContentOffset.X >= width) {
- // Snap to the selected glyph.
- ContentOffset = new Point (Math.Min (Cursor.X, Cursor.X - width + COLUMN_WIDTH), ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y);
- } else {
- ContentOffset = new Point (ContentOffset.X - Cursor.X, ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y);
- }
- } else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePoint / 16 + 1)) {
- ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 1));
- // Snap 1st column into view if it's been scrolled horizontally
- ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y);
- }
+ //if (ShowHorizontalScrollIndicator && ContentSize.Height < (int)(MaxCodePoint / 16 + 2)) {
+ // //ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 2));
+ // //ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16) * _rowHeight + 2);
+ // var width = (Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH) - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth);
+ // if (Cursor.X + ContentOffset.X >= width) {
+ // // Snap to the selected glyph.
+ // ContentOffset = new Point (
+ // Math.Min (Cursor.X, Cursor.X - width + COLUMN_WIDTH),
+ // ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y);
+ // } else {
+ // ContentOffset = new Point (
+ // ContentOffset.X - Cursor.X,
+ // ContentOffset.Y == -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y);
+ // }
+ //} else if (!ShowHorizontalScrollIndicator && ContentSize.Height > (int)(MaxCodePoint / 16 + 1)) {
+ // //ContentSize = new Size (CharMap.RowWidth, (int)(MaxCodePoint / 16 + 1));
+ // // Snap 1st column into view if it's been scrolled horizontally
+ // ContentOffset = new Point (0, ContentOffset.Y < -ContentSize.Height + Bounds.Height ? ContentOffset.Y - 1 : ContentOffset.Y);
+ //}
base.OnDrawContent (contentArea);
}
//public void CharMap_DrawContent (object s, DrawEventArgs a)
public override void OnDrawContentComplete (Rect contentArea)
{
+ if (contentArea.Height == 0 || contentArea.Width == 0) {
+ return;
+ }
Rect viewport = new Rect (ContentOffset,
new Size (Math.Max (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0), 0),
Math.Max (Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0), 0)));
@@ -370,8 +426,8 @@ public override void OnDrawContentComplete (Rect contentArea)
Driver.Clip = new Rect (Driver.Clip.Location, new Size (Driver.Clip.Width - 1, Driver.Clip.Height));
}
- var cursorCol = Cursor.X;
- var cursorRow = Cursor.Y;
+ var cursorCol = Cursor.X - ContentOffset.X - RowLabelWidth - 1;
+ var cursorRow = Cursor.Y - ContentOffset.Y - 1;
Driver.SetAttribute (GetHotNormalColor ());
Move (0, 0);
@@ -382,7 +438,7 @@ public override void OnDrawContentComplete (Rect contentArea)
Move (x, 0);
Driver.SetAttribute (GetHotNormalColor ());
Driver.AddStr (" ");
- Driver.SetAttribute (HasFocus && (cursorCol + RowLabelWidth == x) ? ColorScheme.HotFocus : GetHotNormalColor ());
+ Driver.SetAttribute (HasFocus && (cursorCol + ContentOffset.X + RowLabelWidth == x) ? ColorScheme.HotFocus : GetHotNormalColor ());
Driver.AddStr ($"{hexDigit:x}");
Driver.SetAttribute (GetHotNormalColor ());
Driver.AddStr (" ");
@@ -390,7 +446,10 @@ public override void OnDrawContentComplete (Rect contentArea)
}
var firstColumnX = viewport.X + RowLabelWidth;
- for (int row = -ContentOffset.Y, y = 1; row <= (-ContentOffset.Y) + (Bounds.Height / ROW_HEIGHT); row++, y += ROW_HEIGHT) {
+ for (int y = 1; y < Bounds.Height; y++) {
+ // What row is this?
+ var row = (y - ContentOffset.Y - 1) / _rowHeight;
+
var val = (row) * 16;
if (val > MaxCodePoint) {
continue;
@@ -398,16 +457,26 @@ public override void OnDrawContentComplete (Rect contentArea)
Move (firstColumnX + COLUMN_WIDTH, y);
Driver.SetAttribute (GetNormalColor ());
for (int col = 0; col < 16; col++) {
+
var x = firstColumnX + COLUMN_WIDTH * col + 1;
+
Move (x, y);
if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) {
Driver.SetAttribute (GetFocusColor ());
}
+ var scalar = val + col;
+ Rune rune = (Rune)'?';
+ if (Rune.IsValid (scalar)) {
+ rune = new Rune (scalar);
+ }
+ var width = rune.GetColumns ();
- if (char.IsSurrogate ((char)(val + col))) {
- Driver.AddRune (Rune.ReplacementChar);
+ // are we at first row of the row?
+ if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0) {
+ Driver.AddRune (rune);
} else {
- Driver.AddRune (new Rune (val + col));
+ Driver.SetAttribute (ColorScheme.HotNormal);
+ Driver.AddStr ($"{width}");
}
if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus) {
@@ -416,8 +485,11 @@ public override void OnDrawContentComplete (Rect contentArea)
}
Move (0, y);
Driver.SetAttribute (HasFocus && (cursorRow + ContentOffset.Y + 1 == y) ? ColorScheme.HotFocus : ColorScheme.HotNormal);
- var rowLabel = $"U+{val / 16:x5}_ ";
- Driver.AddStr (rowLabel);
+ if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0) {
+ Driver.AddStr ($"U+{val / 16:x5}_ ");
+ } else {
+ Driver.AddStr (new string (' ', RowLabelWidth));
+ }
}
Driver.Clip = oldClip;
}
@@ -426,30 +498,38 @@ public override void OnDrawContentComplete (Rect contentArea)
void Handle_MouseClick (object sender, MouseEventEventArgs args)
{
var me = args.MouseEvent;
- if (me.Flags == MouseFlags.ReportMousePosition || (me.Flags != MouseFlags.Button1Clicked &&
- me.Flags != MouseFlags.Button1DoubleClicked)) { // && me.Flags != _contextMenu.MouseFlags)) {
+ if (me.Flags != MouseFlags.ReportMousePosition && me.Flags != MouseFlags.Button1Clicked &&
+ me.Flags != MouseFlags.Button1DoubleClicked) {
return;
}
- if (me.X < RowLabelWidth) {
- return;
+ if (me.Y == 0) {
+ me.Y = Cursor.Y;
}
- if (me.Y < 1) {
- return;
+ if (me.Y > 0) {
+ }
+
+ if (me.X < RowLabelWidth || me.X > RowLabelWidth + (16 * COLUMN_WIDTH) - 1) {
+ me.X = Cursor.X;
}
- var row = me.Y - 1;
+ var row = (me.Y - 1 - ContentOffset.Y) / _rowHeight; // -1 for header
var col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH;
- if (row < 0 || row > Bounds.Height || col < 0 || col > 15) {
- return;
+
+ if (col > 15) {
+ col = 15;
}
- var val = (row - ContentOffset.Y) * 16 + col;
+ var val = (row) * 16 + col;
if (val > MaxCodePoint) {
return;
}
+ if (me.Flags == MouseFlags.ReportMousePosition) {
+ Hover?.Invoke (this, new ListViewItemEventArgs (val, null));
+ }
+
if (me.Flags == MouseFlags.Button1Clicked) {
SelectedCodePoint = val;
return;
@@ -535,6 +615,7 @@ void ShowDetails ()
};
Application.Run (waitIndicator);
+
if (!string.IsNullOrEmpty (decResponse)) {
string name = string.Empty;
@@ -551,25 +632,156 @@ void ShowDetails ()
//&& property3Element.TryGetProperty ("nestedProperty", out JsonElement nestedPropertyElement)) {
// Console.WriteLine (nestedPropertyElement.GetString ());
//}
+ decResponse = JsonSerializer.Serialize (document.RootElement, new
+ JsonSerializerOptions {
+ WriteIndented = true
+ });
}
- var title = $"{ToCamelCase (name)} - {new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x4}";
- switch (MessageBox.Query (title, decResponse, "Copy _Glyph", "Copy Code _Point", "Cancel")) {
- case 0:
+ var title = $"{ToCamelCase (name)} - {new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x5}";
+
+ var copyGlyph = new Button ("Copy _Glyph");
+ var copyCP = new Button ("Copy Code _Point");
+ var cancel = new Button ("Cancel");
+
+ var dlg = new Dialog (copyGlyph, copyCP, cancel) {
+ Title = title
+ };
+
+ copyGlyph.Clicked += (s, a) => {
CopyGlyph ();
- break;
- case 1:
+ dlg.RequestStop ();
+ };
+ copyCP.Clicked += (s, a) => {
CopyCodePoint ();
- break;
- }
+ dlg.RequestStop ();
+ };
+ cancel.Clicked += (s, a) => dlg.RequestStop ();
+
+ var rune = (Rune)SelectedCodePoint;
+ var label = new Label () {
+ Text = "IsAscii: ",
+ X = 0,
+ Y = 0
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.IsAscii}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = ", Bmp: ",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.IsBmp}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = ", CombiningMark: ",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.IsCombiningMark ()}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = ", SurrogatePair: ",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.IsSurrogatePair ()}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = ", Plane: ",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.Plane}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = "Columns: ",
+ X = 0,
+ Y = Pos.Bottom (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.GetColumns ()}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = ", Utf16SequenceLength: ",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+
+ label = new Label () {
+ Text = $"{rune.Utf16SequenceLength}",
+ X = Pos.Right (label),
+ Y = Pos.Top (label)
+ };
+ dlg.Add (label);
+ label = new Label () {
+ Text = $"Code Point Information from {UcdApiClient.BaseUrl}codepoint/dec/{SelectedCodePoint}:",
+ X = 0,
+ Y = Pos.Bottom (label)
+ };
+ dlg.Add (label);
+
+ var json = new TextView () {
+ X = 0,
+ Y = Pos.Bottom (label),
+ Width = Dim.Fill (),
+ Height = Dim.Fill (2),
+ ReadOnly = true,
+ Text = decResponse
+ };
+ dlg.Add (json);
+
+ Application.Run (dlg);
+
} else {
- MessageBox.ErrorQuery ("Code Point API", $"{UcdApiClient.BaseUrl} did not return a result.", "Ok");
+ MessageBox.ErrorQuery ("Code Point API", $"{UcdApiClient.BaseUrl}codepoint/dec/{SelectedCodePoint} did not return a result for\r\n {new Rune (SelectedCodePoint)} U+{SelectedCodePoint:x5}.", "Ok");
}
// BUGBUG: This is a workaround for some weird ScrollView related mouse grab bug
Application.GrabMouse (this);
}
-
public override bool OnEnter (View view)
{
if (IsInitialized) {
@@ -577,6 +789,12 @@ public override bool OnEnter (View view)
}
return base.OnEnter (view);
}
+
+ public override bool OnLeave (View view)
+ {
+ Driver.SetCursorVisibility (CursorVisibility.Invisible);
+ return base.OnLeave (view);
+ }
}
public class UcdApiClient {
@@ -637,7 +855,6 @@ public static List GetRanges ()
new UnicodeRange (0x1F130, 0x1F149 ,"Squared Latin Capital Letters"),
new UnicodeRange (0x12400, 0x1240f ,"Cuneiform Numbers and Punctuation"),
- new UnicodeRange (0x1FA00, 0x1FA0f ,"Chess Symbols"),
new UnicodeRange (0x10000, 0x1007F ,"Linear B Syllabary"),
new UnicodeRange (0x10080, 0x100FF ,"Linear B Ideograms"),
new UnicodeRange (0x10100, 0x1013F ,"Aegean Numbers"),
diff --git a/UICatalog/Scenarios/Generic.cs b/UICatalog/Scenarios/Generic.cs
index f2719bccb5..9d4ce4e32b 100644
--- a/UICatalog/Scenarios/Generic.cs
+++ b/UICatalog/Scenarios/Generic.cs
@@ -12,12 +12,12 @@ public override void Init ()
// that reads "Press to Quit". Access this Window with `this.Win`.
// - Sets the Theme & the ColorScheme property of `this.Win` to `colorScheme`.
// To override this, implement an override of `Init`.
-
+
//base.Init ();
-
+
// A common, alternate, implementation where `this.Win` is not used is below. This code
// leverages ConfigurationManager to borrow the color scheme settings from UICatalog:
-
+
Application.Init ();
ConfigurationManager.Themes.Theme = Theme;
ConfigurationManager.Apply ();
@@ -37,7 +37,6 @@ public override void Setup ()
Y = Pos.Center (),
};
Application.Top.Add (button);
-
}
}
}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs
new file mode 100644
index 0000000000..ea74bea58c
--- /dev/null
+++ b/UICatalog/Scenarios/Images.cs
@@ -0,0 +1,136 @@
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using System;
+using System.Collections.Concurrent;
+using System.IO;
+using Terminal.Gui;
+using Attribute = Terminal.Gui.Attribute;
+
+
+
+namespace UICatalog.Scenarios {
+ [ScenarioMetadata (Name: "Images", Description: "Demonstration of how to render an image with/without true color support.")]
+ [ScenarioCategory ("Colors")]
+ public class Images : Scenario {
+ public override void Setup ()
+ {
+ base.Setup ();
+
+ var x = 0;
+ var y = 0;
+
+ var canTrueColor = Application.Driver.SupportsTrueColor;
+
+ var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") {
+ X = x,
+ Y = y++
+ };
+ Win.Add (lblDriverName);
+ y++;
+
+ var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") {
+ X = x,
+ Y = y++,
+ Checked = canTrueColor,
+ CanFocus = false
+ };
+ Win.Add (cbSupportsTrueColor);
+
+ var cbUseTrueColor = new CheckBox ("Use true color") {
+ X = x,
+ Y = y++,
+ Checked = Application.Driver.UseTrueColor,
+ Enabled = canTrueColor,
+ };
+ cbUseTrueColor.Toggled += (_, evt) => Application.Driver.UseTrueColor = evt.NewValue ?? false;
+ Win.Add (cbUseTrueColor);
+
+ var btnOpenImage = new Button ("Open Image") {
+ X = x,
+ Y = y++
+ };
+ Win.Add (btnOpenImage);
+
+ var imageView = new ImageView () {
+ X = x,
+ Y = y++,
+ Width = Dim.Fill (),
+ Height = Dim.Fill (),
+ };
+ Win.Add (imageView);
+
+
+ btnOpenImage.Clicked += (_, _) => {
+ var ofd = new OpenDialog ("Open Image") { AllowsMultipleSelection = false };
+ Application.Run (ofd);
+
+ if (ofd.Canceled)
+ return;
+
+ var path = ofd.FilePaths [0];
+
+ if (string.IsNullOrWhiteSpace (path)) {
+ return;
+ }
+
+ if (!File.Exists (path)) {
+ return;
+ }
+
+ Image img;
+
+ try {
+ img = Image.Load (File.ReadAllBytes (path));
+ } catch (Exception ex) {
+
+ MessageBox.ErrorQuery ("Could not open file", ex.Message, "Ok");
+ return;
+ }
+
+ imageView.SetImage (img);
+ };
+ }
+
+ class ImageView : View {
+
+ private Image fullResImage;
+ private Image matchSize;
+
+ ConcurrentDictionary cache = new ConcurrentDictionary ();
+
+ internal void SetImage (Image image)
+ {
+ fullResImage = image;
+ this.SetNeedsDisplay ();
+ }
+
+ public override void OnDrawContent(Rect bounds)
+ {
+ base.OnDrawContent (bounds);
+
+ if (fullResImage == null) {
+ return;
+ }
+
+ // if we have not got a cached resized image of this size
+ if (matchSize == null || bounds.Width != matchSize.Width || bounds.Height != matchSize.Height) {
+
+ // generate one
+ matchSize = fullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height));
+ }
+
+ for (int y = 0; y < bounds.Height; y++) {
+ for (int x = 0; x < bounds.Width; x++) {
+ var rgb = matchSize [x, y];
+
+ var attr = cache.GetOrAdd (rgb, (rgb) => new Attribute (new TrueColor (), new TrueColor (rgb.R, rgb.G, rgb.B)));
+
+ Driver.SetAttribute (attr);
+ AddRune (x, y, (System.Text.Rune)' ');
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/UICatalog/Scenarios/LineDrawing.cs b/UICatalog/Scenarios/LineDrawing.cs
index 9946a8c084..8682dce2e2 100644
--- a/UICatalog/Scenarios/LineDrawing.cs
+++ b/UICatalog/Scenarios/LineDrawing.cs
@@ -142,8 +142,9 @@ public override void OnDrawContentComplete (Rect contentArea)
foreach (var canvas in _layers) {
foreach (var c in canvas.GetCellMap ()) {
- Driver.SetAttribute (c.Value.Attribute?.Value ?? ColorScheme.Normal);
- this.AddRune (c.Key.X, c.Key.Y, c.Value.Rune.Value);
+ Driver.SetAttribute (c.Value.Attribute ?? ColorScheme.Normal);
+ // TODO: #2616 - Support combining sequences that don't normalize
+ this.AddRune (c.Key.X, c.Key.Y, c.Value.Runes [0]);
}
}
}
diff --git a/UICatalog/Scenarios/ProgressBarStyles.cs b/UICatalog/Scenarios/ProgressBarStyles.cs
index 9ff533fa83..022d0adba5 100644
--- a/UICatalog/Scenarios/ProgressBarStyles.cs
+++ b/UICatalog/Scenarios/ProgressBarStyles.cs
@@ -79,7 +79,7 @@ public override void Setup ()
_fractionTimer = null;
button.Enabled = true;
}
- Application.MainLoop.Driver.Wakeup ();
+ Application.MainLoop.MainLoopDriver.Wakeup ();
}, null, 0, _timerTick);
}
};
@@ -128,7 +128,7 @@ public override void Setup ()
marqueesBlocksPB.Text = marqueesContinuousPB.Text = DateTime.Now.TimeOfDay.ToString ();
marqueesBlocksPB.Pulse ();
marqueesContinuousPB.Pulse ();
- Application.MainLoop.Driver.Wakeup ();
+ Application.MainLoop.MainLoopDriver.Wakeup ();
}, null, 0, 300);
Application.Top.Unloaded += Top_Unloaded;
diff --git a/UICatalog/Scenarios/TableEditor.cs b/UICatalog/Scenarios/TableEditor.cs
index b392a72f73..d172d59b7a 100644
--- a/UICatalog/Scenarios/TableEditor.cs
+++ b/UICatalog/Scenarios/TableEditor.cs
@@ -786,7 +786,6 @@ public UnicodeRange (uint start, uint end, string category)
new UnicodeRange(0x2b00, 0x2bff,"Miscellaneous Symbols and Arrows"),
new UnicodeRange(0xFB00, 0xFb4f,"Alphabetic Presentation Forms"),
new UnicodeRange(0x12400, 0x1240f,"Cuneiform Numbers and Punctuation"),
- new UnicodeRange(0x1FA00, 0x1FA0f,"Chess Symbols"),
new UnicodeRange((uint)(CharMap.MaxCodePoint - 16), (uint)CharMap.MaxCodePoint,"End"),
new UnicodeRange (0x0020 ,0x007F ,"Basic Latin"),
diff --git a/UICatalog/Scenarios/TrueColors.cs b/UICatalog/Scenarios/TrueColors.cs
new file mode 100644
index 0000000000..e4c7df5005
--- /dev/null
+++ b/UICatalog/Scenarios/TrueColors.cs
@@ -0,0 +1,116 @@
+using System;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios {
+
+ [ScenarioMetadata (Name: "True Colors", Description: "Demonstration of true color support.")]
+ [ScenarioCategory ("Colors")]
+ public class TrueColors : Scenario {
+
+ public override void Setup ()
+ {
+ var x = 2;
+ var y = 1;
+
+ var canTrueColor = Application.Driver.SupportsTrueColor;
+
+ var lblDriverName = new Label ($"Current driver is {Application.Driver.GetType ().Name}") {
+ X = x,
+ Y = y++
+ };
+ Win.Add (lblDriverName);
+ y++;
+
+ var cbSupportsTrueColor = new CheckBox ("Driver supports true color ") {
+ X = x,
+ Y = y++,
+ Checked = canTrueColor,
+ CanFocus = false
+ };
+ Win.Add (cbSupportsTrueColor);
+
+ var cbUseTrueColor = new CheckBox ("Use true color") {
+ X = x,
+ Y = y++,
+ Checked = Application.Driver.UseTrueColor,
+ Enabled = canTrueColor,
+ };
+ cbUseTrueColor.Toggled += (_, evt) => Application.Driver.UseTrueColor = evt.NewValue ?? false;
+ Win.Add (cbUseTrueColor);
+
+ y += 2;
+ SetupGradient ("Red gradient", x, ref y, (i) => new TrueColor (i, 0, 0));
+ SetupGradient ("Green gradient", x, ref y, (i) => new TrueColor (0, i, 0));
+ SetupGradient ("Blue gradient", x, ref y, (i) => new TrueColor (0, 0, i));
+ SetupGradient ("Yellow gradient", x, ref y, (i) => new TrueColor (i, i, 0));
+ SetupGradient ("Magenta gradient", x, ref y, (i) => new TrueColor (i, 0, i));
+ SetupGradient ("Cyan gradient", x, ref y, (i) => new TrueColor (0, i, i));
+ SetupGradient ("Gray gradient", x, ref y, (i) => new TrueColor (i, i, i));
+
+ Win.Add (new Label ("Mouse over to get the gradient view color:") {
+ X = Pos.AnchorEnd (44),
+ Y = 2
+ });
+ Win.Add (new Label ("Red:") {
+ X = Pos.AnchorEnd (44),
+ Y = 4
+ });
+ Win.Add (new Label ("Green:") {
+ X = Pos.AnchorEnd (44),
+ Y = 5
+ });
+ Win.Add (new Label ("Blue:") {
+ X = Pos.AnchorEnd (44),
+ Y = 6
+ });
+
+ var lblRed = new Label ("na") {
+ X = Pos.AnchorEnd (32),
+ Y = 4
+ };
+ Win.Add (lblRed);
+ var lblGreen = new Label ("na") {
+ X = Pos.AnchorEnd (32),
+ Y = 5
+ };
+ Win.Add (lblGreen);
+ var lblBlue = new Label ("na") {
+ X = Pos.AnchorEnd (32),
+ Y = 6
+ };
+ Win.Add (lblBlue);
+
+ Application.RootMouseEvent = (e) => {
+ var normal = e.View.GetNormalColor ();
+ if (e.View != null) {
+ lblRed.Text = normal.TrueColorForeground.Value.Red.ToString ();
+ lblGreen.Text = normal.TrueColorForeground.Value.Green.ToString ();
+ lblBlue.Text = normal.TrueColorForeground.Value.Blue.ToString ();
+ }
+ };
+ }
+
+ private void SetupGradient (string name, int x, ref int y, Func colorFunc)
+ {
+ var gradient = new Label (name) {
+ X = x,
+ Y = y++,
+ };
+ Win.Add (gradient);
+ for (int dx = x, i = 0; i <= 256; i += 4) {
+ var l = new Label (" ") {
+ X = dx++,
+ Y = y,
+ ColorScheme = new ColorScheme () {
+ Normal = new Terminal.Gui.Attribute (
+ colorFunc (i > 255 ? 255 : i),
+ colorFunc (i > 255 ? 255 : i)
+ )
+ }
+ };
+ Win.Add (l);
+ }
+ y += 2;
+ }
+ }
+}
diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs
index 866679a615..e5e53095c8 100644
--- a/UICatalog/Scenarios/Unicode.cs
+++ b/UICatalog/Scenarios/Unicode.cs
@@ -9,16 +9,9 @@ namespace UICatalog.Scenarios {
public class UnicodeInMenu : Scenario {
public override void Setup ()
{
- const string IdenticalSign = "\u2261";
- const string ArrowUpSign = "\u2191";
- const string ArrowDownSign = "\u2193";
- const string EllipsesSign = "\u2026";
- const string StashSign = "\u205E";
-
- //string text = "Hello world, how are you today? Pretty neat!\nSecond line\n\nFourth Line.";
string unicode = "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου.";
- string gitString = $"gui.cs master {IdenticalSign} {ArrowDownSign}18 {ArrowUpSign}10 {StashSign}1 {EllipsesSign}";
+ string gitString = $"gui.cs 糊 (hú) {ConfigurationManager.Glyphs.IdenticalTo} {ConfigurationManager.Glyphs.DownArrow}18 {ConfigurationManager.Glyphs.UpArrow}10 {ConfigurationManager.Glyphs.VerticalFourDots}1 {ConfigurationManager.Glyphs.HorizontalEllipsis}";
var menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_Файл", new MenuItem [] {
@@ -30,7 +23,7 @@ public override void Setup ()
new MenuBarItem ("_Edit", new MenuItem [] {
new MenuItem ("_Copy", "", null),
new MenuItem ("C_ut", "", null),
- new MenuItem ("_Paste", "", null)
+ new MenuItem ("_糊", "hú (Paste)", null)
})
});
Application.Top.Add (menu);
@@ -60,11 +53,10 @@ public override void Setup ()
label = new Label ("CheckBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
Win.Add (label);
var checkBox = new CheckBox (gitString) { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) };
- var ckbAllowNull = new CheckBox ("Allow null checked") { X = Pos.Right (checkBox) + 1, Y = Pos.Y (label) };
- ckbAllowNull.Toggled += (s,e) => checkBox.AllowNullChecked = (bool)!e.OldValue;
- Win.Add (checkBox, ckbAllowNull);
+ var checkBoxRight = new CheckBox ($"Align Right - {gitString}") { X = 20, Y = Pos.Bottom (checkBox), Width = Dim.Percent (50), TextAlignment = TextAlignment.Right};
+ Win.Add (checkBox, checkBoxRight);
- label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (label) + 1 };
+ label = new Label ("ComboBox:") { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1 };
Win.Add (label);
var comboBox = new ComboBox () {
X = 20,
diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs
index 7e4bf420dc..141015d398 100644
--- a/UICatalog/UICatalog.cs
+++ b/UICatalog/UICatalog.cs
@@ -214,8 +214,6 @@ static Scenario RunUICatalogTopLevel ()
CM.Apply ();
}
- //Application.EnableConsoleScrolling = _enableConsoleScrolling;
-
Application.Run ();
Application.Shutdown ();
@@ -239,7 +237,6 @@ static Scenario RunUICatalogTopLevel ()
static bool _useSystemConsole = false;
static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
- //static bool _enableConsoleScrolling = false;
static bool _isFirstRunning = true;
static string _topLevelColorScheme = string.Empty;
@@ -254,7 +251,6 @@ public class UICatalogTopLevel : Toplevel {
public MenuItem? miUseSubMenusSingleFrame;
public MenuItem? miIsMenuBorderDisabled;
public MenuItem? miIsMouseDisabled;
- public MenuItem? miEnableConsoleScrolling;
public ListView CategoryList;
@@ -383,8 +379,8 @@ public UICatalogTopLevel ()
ScenarioList.KeyDown += (s, a) => {
if (CollectionNavigator.IsCompatibleKey (a.KeyEvent)) {
var newItem = _scenarioCollectionNav?.GetNextMatchingItem (ScenarioList.SelectedRow, (char)a.KeyEvent.KeyValue);
- if (newItem is int && newItem != -1) {
- ScenarioList.SelectedRow = (int)newItem;
+ if (newItem is int v && newItem != -1) {
+ ScenarioList.SelectedRow = v;
ScenarioList.EnsureSelectedCellIsVisible ();
ScenarioList.SetNeedsDisplay ();
a.Handled = true;
@@ -426,8 +422,7 @@ void LoadedHandler (object? sender, EventArgs? args)
ConfigChanged ();
miIsMouseDisabled!.Checked = Application.IsMouseDisabled;
- miEnableConsoleScrolling!.Checked = Application.EnableConsoleScrolling;
- DriverName.Title = $"Driver: {Driver.GetType ().Name}";
+ DriverName.Title = $"Driver: {Driver.GetVersionInfo()}";
OS.Title = $"OS: {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystem} {Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.OperatingSystemVersion}";
if (_selectedScenario != null) {
@@ -489,8 +484,7 @@ List