ソースを参照

Merge branch 'v2_develop' into copilot/fix-e6dde989-9ea1-4d83-8522-54ed8f70815a

Tig 2 ヶ月 前
コミット
78e319b595
100 ファイル変更1607 行追加4545 行削除
  1. 2 2
      .github/workflows/unit-tests.yml
  2. 132 0
      AGENTS.md
  3. 4 4
      Directory.Packages.props
  4. 20 1
      Examples/Example/Example.cs
  5. 30 46
      Examples/UICatalog/Properties/launchSettings.json
  6. 1 1
      Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs
  7. 5 5
      Examples/UICatalog/Scenarios/FileDialogExamples.cs
  8. 1 1
      Examples/UICatalog/Scenarios/Images.cs
  9. 219 0
      Examples/UICatalog/Scenarios/LineExample.cs
  10. 0 76
      Examples/UICatalog/Scenarios/LineViewExample.cs
  11. 0 1
      Examples/UICatalog/Scenarios/TileViewNesting.cs
  12. 38 13
      Examples/UICatalog/UICatalog.cs
  13. 1 1
      Terminal.Gui/App/Application.Driver.cs
  14. 47 38
      Terminal.Gui/App/Application.Initialization.cs
  15. 1 1
      Terminal.Gui/App/Application.Keyboard.cs
  16. 1 1
      Terminal.Gui/App/Application.Navigation.cs
  17. 2 5
      Terminal.Gui/App/Application.Run.cs
  18. 176 188
      Terminal.Gui/App/ApplicationImpl.cs
  19. 13 7
      Terminal.Gui/App/CWP/CWPPropertyHelper.cs
  20. 1 1
      Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs
  21. 2 9
      Terminal.Gui/App/IApplication.cs
  22. 20 5
      Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
  23. 13 4
      Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs
  24. 48 0
      Terminal.Gui/App/MainLoop/IMainLoopCoordinator.cs
  25. 24 0
      Terminal.Gui/App/MainLoop/IMainLoopDriver.cs
  26. 13 26
      Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs
  27. 17 13
      Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs
  28. 0 0
      Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs
  29. 4 3
      Terminal.Gui/App/NotInitializedException.cs
  30. 1 1
      Terminal.Gui/App/Toplevel/IToplevelTransitionManager.cs
  31. 2 1
      Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs
  32. 9 31
      Terminal.Gui/Drawing/Attribute.cs
  33. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequence.cs
  34. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequenceRequest.cs
  35. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiMouseParser.cs
  36. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs
  37. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs
  38. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParser.cs
  39. 0 0
      Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParserState.cs
  40. 0 0
      Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqReqStatus.cs
  41. 0 0
      Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqRequests.cs
  42. 55 5
      Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs
  43. 0 0
      Terminal.Gui/Drivers/AnsiHandling/GenericHeld.cs
  44. 0 0
      Terminal.Gui/Drivers/AnsiHandling/IAnsiResponseParser.cs
  45. 0 0
      Terminal.Gui/Drivers/AnsiHandling/IHeld.cs
  46. 0 0
      Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParser.cs
  47. 0 0
      Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParserPattern.cs
  48. 0 0
      Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiCursorPattern.cs
  49. 0 0
      Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiKeyPattern.cs
  50. 9 2
      Terminal.Gui/Drivers/AnsiHandling/Keyboard/EscAsAltPattern.cs
  51. 8 1
      Terminal.Gui/Drivers/AnsiHandling/Keyboard/Ss3Pattern.cs
  52. 0 0
      Terminal.Gui/Drivers/AnsiHandling/ReasonCannotSend.cs
  53. 0 0
      Terminal.Gui/Drivers/AnsiHandling/StringHeld.cs
  54. 0 0
      Terminal.Gui/Drivers/ComponentFactory.cs
  55. 8 25
      Terminal.Gui/Drivers/ConsoleDriver.cs
  56. 6 14
      Terminal.Gui/Drivers/ConsoleDriverFacade.cs
  57. 0 0
      Terminal.Gui/Drivers/ConsoleInput.cs
  58. 0 1040
      Terminal.Gui/Drivers/CursesDriver/CursesDriver.cs
  59. 0 5
      Terminal.Gui/Drivers/CursesDriver/README.md
  60. 0 256
      Terminal.Gui/Drivers/CursesDriver/UnixMainLoop.cs
  61. 0 95
      Terminal.Gui/Drivers/CursesDriver/UnmanagedLibrary.cs
  62. 0 746
      Terminal.Gui/Drivers/CursesDriver/binding.cs
  63. 0 177
      Terminal.Gui/Drivers/CursesDriver/constants.cs
  64. 0 86
      Terminal.Gui/Drivers/CursesDriver/handles.cs
  65. 0 0
      Terminal.Gui/Drivers/DotNetDriver/INetInput.cs
  66. 1 1
      Terminal.Gui/Drivers/DotNetDriver/NetComponentFactory.cs
  67. 16 0
      Terminal.Gui/Drivers/DotNetDriver/NetInput.cs
  68. 4 1
      Terminal.Gui/Drivers/DotNetDriver/NetInputProcessor.cs
  69. 0 0
      Terminal.Gui/Drivers/DotNetDriver/NetKeyConverter.cs
  70. 38 15
      Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs
  71. 0 0
      Terminal.Gui/Drivers/DotNetDriver/NetWinVTConsole.cs
  72. 49 0
      Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs
  73. 42 0
      Terminal.Gui/Drivers/FakeDriver/FakeConsoleInput.cs
  74. 88 0
      Terminal.Gui/Drivers/FakeDriver/FakeConsoleOutput.cs
  75. 6 4
      Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs
  76. 41 0
      Terminal.Gui/Drivers/FakeDriver/FakeWindowSizeMonitor.cs
  77. 1 0
      Terminal.Gui/Drivers/IComponentFactory.cs
  78. 3 3
      Terminal.Gui/Drivers/IConsoleDriver.cs
  79. 0 0
      Terminal.Gui/Drivers/IConsoleDriverFacade.cs
  80. 0 0
      Terminal.Gui/Drivers/IConsoleInput.cs
  81. 0 0
      Terminal.Gui/Drivers/IConsoleOutput.cs
  82. 5 0
      Terminal.Gui/Drivers/IInputProcessor.cs
  83. 0 0
      Terminal.Gui/Drivers/IKeyConverter.cs
  84. 0 0
      Terminal.Gui/Drivers/IOutputBuffer.cs
  85. 0 0
      Terminal.Gui/Drivers/IWindowSizeMonitor.cs
  86. 3 0
      Terminal.Gui/Drivers/InputProcessor.cs
  87. 0 0
      Terminal.Gui/Drivers/MouseButtonStateEx.cs
  88. 0 0
      Terminal.Gui/Drivers/MouseInterpreter.cs
  89. 0 739
      Terminal.Gui/Drivers/NetDriver/NetDriver.cs
  90. 0 618
      Terminal.Gui/Drivers/NetDriver/NetEvents.cs
  91. 0 167
      Terminal.Gui/Drivers/NetDriver/NetMainLoop.cs
  92. 10 10
      Terminal.Gui/Drivers/OutputBase.cs
  93. 2 19
      Terminal.Gui/Drivers/OutputBuffer.cs
  94. 0 0
      Terminal.Gui/Drivers/Platform.cs
  95. 26 0
      Terminal.Gui/Drivers/PlatformDetection.cs
  96. 3 0
      Terminal.Gui/Drivers/UnixDriver/IUnixInput.cs
  97. 3 31
      Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs
  98. 29 0
      Terminal.Gui/Drivers/UnixDriver/UnixComponentFactory.cs
  99. 266 0
      Terminal.Gui/Drivers/UnixDriver/UnixInput.cs
  100. 38 0
      Terminal.Gui/Drivers/UnixDriver/UnixInputProcessor.cs

+ 2 - 2
.github/workflows/unit-tests.yml

@@ -50,7 +50,7 @@ jobs:
 
     - name: Run UnitTests
       run: |
-       dotnet test Tests/UnitTests --no-build --verbosity normal --collect:"XPlat Code Coverage" --settings Tests/UnitTests/coverlet.runsettings --diag:logs/UnitTests/${{ runner.os }}/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always -- xunit.stopOnFail=true
+       dotnet test Tests/UnitTests --no-build --verbosity normal --collect:"XPlat Code Coverage" --settings Tests/UnitTests/coverlet.runsettings --diag:logs/UnitTests/${{ runner.os }}/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always -- xunit.stopOnFail=false
      
        # mv -v Tests/UnitTests/TestResults/*/*.* TestResults/UnitTests/
 
@@ -102,7 +102,7 @@ jobs:
 
     - name: Run UnitTestsParallelizable
       run: |
-       dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal --collect:"XPlat Code Coverage" --settings Tests/UnitTestsParallelizable/coverlet.runsettings --diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always -- xunit.stopOnFail=true
+       dotnet test Tests/UnitTestsParallelizable --no-build --verbosity normal --collect:"XPlat Code Coverage" --settings Tests/UnitTestsParallelizable/coverlet.runsettings --diag:logs/UnitTestsParallelizable/${{ runner.os }}/logs.txt --blame --blame-crash --blame-hang --blame-hang-timeout 60s --blame-crash-collect-always -- xunit.stopOnFail=false
      
        # mv -v Tests/UnitTestsParallelizable/TestResults/*/*.* TestResults/UnitTestsParallelizable/
 

+ 132 - 0
AGENTS.md

@@ -0,0 +1,132 @@
+# Terminal.Gui - GitHub Copilot Instructions
+
+This file provides instructions for GitHub Copilot when working with the Terminal.Gui project.
+
+## Project Overview
+
+**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. It provides a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system. The toolkit works across Windows, macOS, and Linux, leveraging platform-specific console capabilities where available.
+
+**Key characteristics:**
+- Cross-platform terminal/console UI framework for .NET
+- Supports Windows, macOS, and Linux
+- Rich GUI controls (buttons, dialogs, menus, text boxes, etc.)
+- Keyboard-first design with full mouse support
+- Follows Microsoft .NET Framework Design Guidelines, with some tweaks.
+- v2 is currently in Alpha with stable core API (v1 is in maintenance mode)
+
+## Documentation
+
+- Full documentation: [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui)
+- API Reference: [API Documentation](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.App.html)
+- Getting Started: [Getting Started Guide](https://gui-cs.github.io/Terminal.Gui/docs/getting-started)
+
+## Repository Structure
+
+- `/Terminal.Gui/` - Core library source code
+  - `App/` - Core application logic, `Application.cs` (static class managing `RunState` and `MainLoop`)
+  - `Configuration/` - `ConfigurationManager` for application settings
+  - `Drivers/` - Console driver implementations (`IConsoleDriver.cs`, `NetDriver`, `UnixDriver`, `WindowsDriver`)
+  - `Drawing/` - Rendering graphical elements in the console
+  - `Input/` - Keyboard and mouse input handling
+  - `View/` - Core `View` class hierarchy
+  - `Views/` - Specific sub-classes of `View` (Toplevel, Window, Dialog, etc.)
+- `/Examples/` - Sample applications and demos
+- `/Examples/UICatalog/` - Comprehensive demo app for manual testing
+- `/Tests/` - Unit and integration tests
+- `/docfx/` - Documentation source files (Deep Dive Articles and API docs)
+- `/Scripts/` - Build and utility scripts
+
+## Branching Model
+
+**Terminal.Gui uses GitFlow:**
+- `v2_develop` - Default branch for v2 development (active development)
+- `v2_release` - Stable release branches matching NuGet packages
+
+## Code Style and Standards
+
+### Code Style Tenets
+
+1. **Six-Year-Old Reading Level** - Prioritize readability over terseness. Use clear variable names and comments.
+2. **Consistency, Consistency, Consistency** - Follow established patterns ruthlessly.
+3. **Don't be Weird** - Follow Microsoft and .NET community conventions.
+4. **Set and Forget** - Use ReSharper/Rider for automated formatting.
+5. **Documentation is the Spec** - API documentation is the source of truth.
+
+### Coding Conventions
+
+- Based on [Microsoft C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
+- Project settings defined and enforced via `./Terminal.sln.DotSettings` and `./.editorconfig`
+- Use `var` only for the most basic dotnet types - prefer explicit types for clarity
+- Use target-typed new
+
+## API Design Guidelines
+
+### Public API Tenets
+
+1. **Stand on the shoulders of giants** - Follow [Microsoft .NET Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/)
+2. **Don't Break Existing Stuff** - Avoid breaking changes; find compatible ways to add features
+3. **Fail-fast** - Prefer early failure to expose bugs sooner
+4. **Standards Reduce Complexity** - Use standard .NET idioms, tweaked to match Terminal.Gui. 
+
+### API Documentation Requirements
+
+**All public APIs must have XML documentation:**
+- Clear, concise, and complete `<summary>` tags
+- Use `<see cref=""/>` liberally for cross-references
+- Add `<remarks>` for context and detailed explanations
+- Document complex topics in `docfx/articles/*.md` files
+- Use proper English and correct grammar
+- Provide sample code via `<example>` in cases where a sample is needed (not for very obvious things)
+
+### Events
+
+- Follow the [Events Deep Dive](https://gui-cs.github.io/Terminal.Gui/docs/events.html) documentation
+- Use the Cancellable Work Pattern for user-initiated actions
+- Use the CWPHelpers if possible
+- Name event handlers consistently (e.g., `On[EventName]`), following dotnet guidelines.
+
+## User Experience Tenets
+
+1. **Honor What's Come Before** - Follow established Mac/Windows GUI idioms (e.g., `Ctrl-C` for copy)
+2. **Consistency Matters** - Common UI patterns should be consistent (e.g., `Ctrl-Q` quits modals)
+3. **Honor the OS, but Work Everywhere** - Take advantage of platform capabilities while maintaining cross-platform support
+4. **Keyboard first, Mouse also** - Optimize for keyboard, but ensure everything also works with mouse
+
+## Testing
+
+### Unit Test Requirements
+
+- **Never decrease code coverage** - Aim for 70%+ coverage on new code
+- Write unit tests for all new functionality
+- Follow existing test patterns in `/Tests/`
+- Many existing unit tests are obtuse and not really unit tests. Anytime new tests are added or updated, strive to refactor the tests into more granular tests where each test covers the smallest area possible. 
+- Many existing unit tests in the `./Tests/UnitTests` project incorrectly require `Application.Init` and use `[AutoInitShutdown]`. Anytime new tests are added or updated, strive to remove these dependencies and make the tests parallelizable. This means not taking any dependency on static objects like `Application` and `ConfigurationManager`. 
+
+## Pull Request Guidelines
+
+- Titles should be of the form "Fixes #issue. Terse description." 
+- If the PR addresses multiple issues, use "Fixes #issue1, #issue2. Terse description."
+- First comment should include "- Fixes #issue" for each issue addressed. If an issue is only partially addressed, use "Partially addresses #issue".
+- First comment should include a thorough description of the change and any impact. 
+- Put temporary .md files in `/docfx/docs/drafts/` and remove before merging.
+
+## Building and Running
+
+### Build the Solution
+```powershell
+dotnet build
+```
+
+### Run Tests
+```powershell
+dotnet test
+```
+
+## Key Concepts
+
+`./docfx/docs` contains a set of architectural and key-concept deep-dives. 
+
+## Additional Guidelines
+1. Maintain existing code structure and organization unless explicitly told
+2. View sub-classes must not use private APIs
+3. Suggest changes to the `./docfx/docs/` folder when appropriate

+ 4 - 4
Directory.Packages.props

@@ -11,14 +11,14 @@
     <PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.11.0" />
     <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="[8,9)" />
     <PackageVersion Include="ColorHelper" Version="[1.8.1,2)" />
-    <PackageVersion Include="JetBrains.Annotations" Version="[2024.3.0,)" />
+    <PackageVersion Include="JetBrains.Annotations" Version="[2025.2.2,)" />
     <PackageVersion Include="Microsoft.CodeAnalysis" Version="4.11.0" />
     <PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.11.0" />
     <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
-    <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="[9.0.2,10)" />
+    <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="[9.0.0,10)" />
     <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
-    <PackageVersion Include="System.IO.Abstractions" Version="[22.0.11,23)" />
-    <PackageVersion Include="Wcwidth" Version="[2,3)" />
+    <PackageVersion Include="System.IO.Abstractions" Version="[22.0.16,23)" />
+    <PackageVersion Include="Wcwidth" Version="[3.0.0,)" />
     <PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="[1.21.2,2)" />
     <PackageVersion Include="Serilog" Version="4.2.0" />
     <PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.0" />

+ 20 - 1
Examples/Example/Example.cs

@@ -11,9 +11,11 @@ using Terminal.Gui.Views;
 using Attribute = Terminal.Gui.Drawing.Attribute;
 
 // Override the default configuration for the application to use the Light theme
-ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }""";
+//ConfigurationManager.RuntimeConfig = """{ "Theme": "Light" }""";
 ConfigurationManager.Enable(ConfigLocations.All);
 
+
+
 Application.Run<ExampleWindow> ().Dispose ();
 
 // Before the application exits, reset Terminal.Gui for clean shutdown
@@ -89,5 +91,22 @@ public class ExampleWindow : Window
 
         // Add the views to the Window
         Add (usernameLabel, userNameText, passwordLabel, passwordText, btnLogin);
+
+        ListView lv = new ListView ()
+        {
+            Y = Pos.AnchorEnd(),
+            Height= Dim.Auto(),
+            Width = Dim.Auto()
+        };
+        lv.SetSource (["One", "Two", "Three", "Four"]);
+        Add (lv);
+    }
+
+    public override void EndInit ()
+    {
+        base.EndInit ();
+        // Set the theme to "Anders" if it exists, otherwise use "Default"
+        ThemeManager.Theme = ThemeManager.GetThemeNames ().FirstOrDefault (x => x == "Anders") ?? "Default";
     }
 }
+ 

+ 30 - 46
Examples/UICatalog/Properties/launchSettings.json

@@ -4,25 +4,13 @@
       "commandName": "Project",
       "commandLineArgs": "--debug-log-level Debug"
     },
-    "UICatalog --driver NetDriver": {
+    "UICatalog --driver windows": {
       "commandName": "Project",
-      "commandLineArgs": "--driver NetDriver"
+      "commandLineArgs": "--driver windows -dl Trace"
     },
-    "UICatalog --driver WindowsDriver": {
+    "UICatalog --driver dotnet": {
       "commandName": "Project",
-      "commandLineArgs": "--driver WindowsDriver"
-    },
-    "UICatalog --driver v2": {
-      "commandName": "Project",
-      "commandLineArgs": "--driver v2 -dl Trace"
-    },
-    "UICatalog --driver v2win": {
-      "commandName": "Project",
-      "commandLineArgs": "--driver v2win -dl Trace"
-    },
-    "UICatalog --driver v2net": {
-      "commandName": "Project",
-      "commandLineArgs": "--driver v2net -dl Trace"
+      "commandLineArgs": "--driver dotnet -dl Trace"
     },
     "WSL: UICatalog": {
       "commandName": "Executable",
@@ -30,22 +18,16 @@
       "commandLineArgs": "dotnet UICatalog.dll",
       "distributionName": ""
     },
-    "WSL: UICatalog --driver NetDriver": {
+    "WSL: UICatalog --driver dotnet": {
       "commandName": "Executable",
       "executablePath": "wsl",
-      "commandLineArgs": "dotnet UICatalog.dll --driver NetDriver",
+      "commandLineArgs": "dotnet UICatalog.dll --driver dotnet",
       "distributionName": ""
     },
-    "WSL: UICatalog --driver v2": {
+    "WSL: UICatalog --driver unix": {
       "commandName": "Executable",
       "executablePath": "wsl",
-      "commandLineArgs": "dotnet UICatalog.dll --driver v2",
-      "distributionName": ""
-    },
-    "WSL: UICatalog --driver v2net": {
-      "commandName": "Executable",
-      "executablePath": "wsl",
-      "commandLineArgs": "dotnet UICatalog.dll --driver v2net",
+      "commandLineArgs": "dotnet UICatalog.dll --driver unix",
       "distributionName": ""
     },
     "WSL-Gnome: UICatalog": {
@@ -54,39 +36,29 @@
       "commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll; exec bash\"'",
       "distributionName": ""
     },
-    "WSL-Gnome: UICatalog --driver NetDriver": {
-      "commandName": "Executable",
-      "executablePath": "wsl",
-      "commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver NetDriver; exec bash\"'",
-      "distributionName": ""
-    },
-    "WSL-Gnome: UICatalog --driver v2": {
+    "WSL-Gnome: UICatalog --driver dotnet": {
       "commandName": "Executable",
       "executablePath": "wsl",
-      "commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver v2; exec bash\"'",
+      "commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver dotnet; exec bash\"'",
       "distributionName": ""
     },
-    "WSL-Gnome: UICatalog --driver v2net": {
+    "WSL-Gnome: UICatalog --driver unix": {
       "commandName": "Executable",
       "executablePath": "wsl",
-      "commandLineArgs": "bash -c 'while [ ! -e \"$XDG_RUNTIME_DIR/bus\" ]; do sleep 0.1; done; gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver v2net; exec bash\"'",
+      "commandLineArgs": "bash -c 'dbus-run-session -- gnome-terminal --wait -- bash -l -c \"dotnet UICatalog.dll --driver unix; exec bash\"'",
       "distributionName": ""
     },
     "Benchmark All": {
       "commandName": "Project",
       "commandLineArgs": "--benchmark"
     },
-    "Benchmark All --driver NetDriver": {
-      "commandName": "Project",
-      "commandLineArgs": "--driver NetDriver --benchmark"
-    },
-    "Benchmark All --driver v2win": {
+    "Benchmark All --driver dotnet": {
       "commandName": "Project",
-      "commandLineArgs": "--driver v2win --benchmark"
+      "commandLineArgs": "--driver dotnet --benchmark"
     },
-    "Benchmark All --driver v2net": {
+    "Benchmark All --driver windows": {
       "commandName": "Project",
-      "commandLineArgs": "--driver v2net --benchmark"
+      "commandLineArgs": "--driver windows --benchmark"
     },
     "WSL: Benchmark All": {
       "commandName": "Executable",
@@ -94,6 +66,18 @@
       "commandLineArgs": "dotnet UICatalog.dll --benchmark",
       "distributionName": ""
     },
+    "WSL: Benchmark All --driver unix": {
+      "commandName": "Executable",
+      "executablePath": "wsl",
+      "commandLineArgs": "dotnet UICatalog.dll --driver unix --benchmark",
+      "distributionName": ""
+    },
+    "WSL: Benchmark All --driver dotnet": {
+      "commandName": "Executable",
+      "executablePath": "wsl",
+      "commandLineArgs": "dotnet UICatalog.dll --driver dotnet --benchmark",
+      "distributionName": ""
+    },
     "Docker": {
       "commandName": "Docker"
     },
@@ -105,9 +89,9 @@
       "commandName": "Project",
       "commandLineArgs": "--disable-cm\r\n"
     },
-    "UICatalog --disable-cm --driver v2win": {
+    "UICatalog --disable-cm --driver windows": {
       "commandName": "Project",
-      "commandLineArgs": "--disable-cm --driver v2win"
+      "commandLineArgs": "--disable-cm --driver windows"
     },
     "Themes": {
       "commandName": "Project",

+ 1 - 1
Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs

@@ -129,7 +129,7 @@ public class CollectionNavigatorTester : Scenario
         _items = new (_items.OrderBy (i => i, StringComparer.OrdinalIgnoreCase));
 
         CreateListView ();
-        var vsep = new LineView (Orientation.Vertical) { X = Pos.Right (_listView), Y = 1, Height = Dim.Fill () };
+        var vsep = new Line { Orientation = Orientation.Vertical, X = Pos.Right (_listView), Y = 1, Height = Dim.Fill () };
         top.Add (vsep);
         CreateTreeView ();
 

+ 5 - 5
Examples/UICatalog/Scenarios/FileDialogExamples.cs

@@ -62,7 +62,7 @@ public class FileDialogExamples : Scenario
         x = 24;
 
         win.Add (
-                 new LineView (Orientation.Vertical) { X = x++, Y = 1, Height = 4 }
+                 new Line { Orientation = Orientation.Vertical, X = x++, Y = 1, Height = 4 }
                 );
         win.Add (new Label { X = x++, Y = y++, Text = "Caption" });
 
@@ -74,7 +74,7 @@ public class FileDialogExamples : Scenario
         x = 34;
 
         win.Add (
-                 new LineView (Orientation.Vertical) { X = x++, Y = 1, Height = 4 }
+                 new Line { Orientation = Orientation.Vertical, X = x++, Y = 1, Height = 4 }
                 );
         win.Add (new Label { X = x++, Y = y++, Text = "OpenMode" });
 
@@ -86,7 +86,7 @@ public class FileDialogExamples : Scenario
         x = 48;
 
         win.Add (
-                 new LineView (Orientation.Vertical) { X = x++, Y = 1, Height = 4 }
+                 new Line { Orientation = Orientation.Vertical, X = x++, Y = 1, Height = 4 }
                 );
         win.Add (new Label { X = x++, Y = y++, Text = "Icons" });
 
@@ -101,7 +101,7 @@ public class FileDialogExamples : Scenario
         x = 24;
 
         win.Add (
-                 new LineView (Orientation.Vertical) { X = x++, Y = y + 1, Height = 4 }
+                 new Line { Orientation = Orientation.Vertical, X = x++, Y = y + 1, Height = 4 }
                 );
         win.Add (new Label { X = x++, Y = y++, Text = "Allowed" });
 
@@ -113,7 +113,7 @@ public class FileDialogExamples : Scenario
         x = 45;
 
         win.Add (
-                 new LineView (Orientation.Vertical) { X = x++, Y = y + 1, Height = 4 }
+                 new Line { Orientation = Orientation.Vertical, X = x++, Y = y + 1, Height = 4 }
                 );
         win.Add (new Label { X = x++, Y = y++, Text = "Buttons" });
 

+ 1 - 1
Examples/UICatalog/Scenarios/Images.cs

@@ -532,7 +532,7 @@ public class Images : Scenario
             // Application.Driver?.Move (_screenLocationForSixel.X, _screenLocationForSixel.Y);
             // Application.Driver?.AddStr (_encodedSixelData);
 
-            // Works in NetDriver but results in screen flicker when moving mouse but vanish instantly
+            // Works in DotNetDriver but results in screen flicker when moving mouse but vanish instantly
             // Console.SetCursorPosition (_screenLocationForSixel.X, _screenLocationForSixel.Y);
             // Console.Write (_encodedSixelData);
         }

+ 219 - 0
Examples/UICatalog/Scenarios/LineExample.cs

@@ -0,0 +1,219 @@
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Line", "Demonstrates the Line view with LineCanvas integration.")]
+[ScenarioCategory ("Controls")]
+[ScenarioCategory ("Drawing")]
+[ScenarioCategory ("Adornments")]
+public class LineExample : Scenario
+{
+    public override void Main ()
+    {
+        Application.Init ();
+
+        var app = new Window
+        {
+            Title = GetQuitKeyAndName ()
+        };
+
+        // Section 1: Basic Lines
+        var basicLabel = new Label
+        {
+            X = 0,
+            Y = 0,
+            Text = "Basic Lines:"
+        };
+        app.Add (basicLabel);
+
+        // Horizontal line
+        var hLine = new Line
+        {
+            X = 0,
+            Y = 1,
+            Width = 30
+        };
+        app.Add (hLine);
+
+        // Vertical line
+        var vLine = new Line
+        {
+            X = 32,
+            Y = 0,
+            Height = 10,
+            Orientation = Orientation.Vertical
+        };
+        app.Add (vLine);
+
+        // Section 2: Different Line Styles
+        var stylesLabel = new Label
+        {
+            X = 0,
+            Y = 3,
+            Text = "Line Styles:"
+        };
+        app.Add (stylesLabel);
+
+        (LineStyle, string) [] styles = new []
+        {
+            (LineStyle.Single, "Single"),
+            (LineStyle.Double, "Double"),
+            (LineStyle.Heavy, "Heavy"),
+            (LineStyle.Rounded, "Rounded"),
+            (LineStyle.Dashed, "Dashed"),
+            (LineStyle.Dotted, "Dotted")
+        };
+
+        var yPos = 4;
+
+        foreach ((LineStyle style, string name) in styles)
+        {
+            app.Add (new Label { X = 0, Y = yPos, Width = 15, Text = name + ":" });
+            app.Add (new Line { X = 16, Y = yPos, Width = 14, Style = style });
+            yPos++;
+        }
+
+        // Section 3: Line Intersections
+        var intersectionLabel = new Label
+        {
+            X = 35,
+            Y = 3,
+            Text = "Line Intersections:"
+        };
+        app.Add (intersectionLabel);
+
+        // Create a grid of intersecting lines
+        var gridX = 35;
+        var gridY = 5;
+
+        // Horizontal lines in the grid
+        for (var i = 0; i < 5; i++)
+        {
+            app.Add (
+                     new Line
+                     {
+                         X = gridX,
+                         Y = gridY + i * 2,
+                         Width = 21,
+                         Style = LineStyle.Single
+                     });
+        }
+
+        // Vertical lines in the grid
+        for (var i = 0; i < 5; i++)
+        {
+            app.Add (
+                     new Line
+                     {
+                         X = gridX + i * 5,
+                         Y = gridY,
+                         Height = 9,
+                         Orientation = Orientation.Vertical,
+                         Style = LineStyle.Single
+                     });
+        }
+
+        // Section 4: Mixed Styles (shows how LineCanvas handles different line styles)
+        var mixedLabel = new Label
+        {
+            X = 60,
+            Y = 3,
+            Text = "Mixed Style Intersections:"
+        };
+        app.Add (mixedLabel);
+
+        // Double horizontal
+        app.Add (
+                 new Line
+                 {
+                     X = 60,
+                     Y = 5,
+                     Width = 20,
+                     Style = LineStyle.Double
+                 });
+
+        // Single vertical through double horizontal
+        app.Add (
+                 new Line
+                 {
+                     X = 70,
+                     Y = 4,
+                     Height = 3,
+                     Orientation = Orientation.Vertical,
+                     Style = LineStyle.Single
+                 });
+
+        // Heavy horizontal
+        app.Add (
+                 new Line
+                 {
+                     X = 60,
+                     Y = 8,
+                     Width = 20,
+                     Style = LineStyle.Heavy
+                 });
+
+        // Single vertical through heavy horizontal
+        app.Add (
+                 new Line
+                 {
+                     X = 70,
+                     Y = 7,
+                     Height = 3,
+                     Orientation = Orientation.Vertical,
+                     Style = LineStyle.Single
+                 });
+
+        // Section 5: Box Example (showing borders and lines working together)
+        var boxLabel = new Label
+        {
+            X = 0,
+            Y = 12,
+            Text = "Lines with Borders:"
+        };
+        app.Add (boxLabel);
+
+        var framedView = new FrameView
+        {
+            Title = "Frame",
+            X = 0,
+            Y = 13,
+            Width = 30,
+            Height = 8,
+            BorderStyle = LineStyle.Single
+        };
+
+        // Add a cross inside the frame
+        framedView.Add (
+                        new Line
+                        {
+                            X = 0,
+                            Y = 3,
+                            Width = Dim.Fill (),
+                            Style = LineStyle.Single
+                        });
+
+        framedView.Add (
+                        new Line
+                        {
+                            X = 14,
+                            Y = 0,
+                            Height = Dim.Fill (),
+                            Orientation = Orientation.Vertical,
+                            Style = LineStyle.Single
+                        });
+
+        app.Add (framedView);
+
+        // Add help text
+        var helpLabel = new Label
+        {
+            X = Pos.Center (),
+            Y = Pos.AnchorEnd (1),
+            Text = "Line integrates with LineCanvas for automatic intersection handling"
+        };
+        app.Add (helpLabel);
+
+        Application.Run (app);
+        app.Dispose ();
+        Application.Shutdown ();
+    }
+}

+ 0 - 76
Examples/UICatalog/Scenarios/LineViewExample.cs

@@ -1,76 +0,0 @@
-using System.Globalization;
-using System.Text;
-
-namespace UICatalog.Scenarios;
-
-[ScenarioMetadata ("Line View", "Demonstrates drawing lines using the LineView control.")]
-[ScenarioCategory ("Controls")]
-[ScenarioCategory ("LineView")]
-[ScenarioCategory ("Adornments")]
-public class LineViewExample : Scenario
-{
-    public override void Main ()
-    {
-        Application.Init ();
-
-        var appWindow = new Window()
-        {
-            Title = GetQuitKeyAndName (),
-        };
-
-        appWindow.Add (new Label { Y = 1, Text = "Regular Line" });
-
-        // creates a horizontal line
-        var line = new LineView { Y = 2 };
-
-        appWindow.Add (line);
-
-        appWindow.Add (new Label { Y = 3, Text = "Double Width Line" });
-
-        // creates a horizontal line
-        var doubleLine = new LineView { Y = 4, LineRune = (Rune)'\u2550' };
-
-        appWindow.Add (doubleLine);
-
-        appWindow.Add (new Label { Y = 5, Text = "Short Line" });
-
-        // creates a horizontal line
-        var shortLine = new LineView { Y = 5, Width = 10 };
-
-        appWindow.Add (shortLine);
-
-        appWindow.Add (new Label { Y = 7, Text = "Arrow Line" });
-
-        // creates a horizontal line
-        var arrowLine = new LineView
-        {
-            Y = 8, Width = 10, StartingAnchor = Glyphs.LeftTee, EndingAnchor = (Rune)'>'
-        };
-
-        appWindow.Add (arrowLine);
-
-        appWindow.Add (new Label { Y = 10, X = 11, Text = "Vertical Line" });
-
-        // creates a horizontal line
-        var verticalLine = new LineView (Orientation.Vertical) { X = 25 };
-
-        appWindow.Add (verticalLine);
-
-        appWindow.Add (new Label { Y = 12, X = 28, Text = "Vertical Arrow" });
-
-        // creates a horizontal line
-        var verticalArrow = new LineView (Orientation.Vertical)
-        {
-            X = 27, StartingAnchor = Glyphs.TopTee, EndingAnchor = (Rune)'V'
-        };
-
-        appWindow.Add (verticalArrow);
-
-        // Run - Start the application.
-        Application.Run (appWindow);
-        appWindow.Dispose ();
-
-        // Shutdown - Calling Application.Shutdown is required.
-        Application.Shutdown ();
-    }
-}

+ 0 - 1
Examples/UICatalog/Scenarios/TileViewNesting.cs

@@ -4,7 +4,6 @@ namespace UICatalog.Scenarios;
 
 [ScenarioMetadata ("Tile View Nesting", "Demonstrates recursive nesting of TileViews")]
 [ScenarioCategory ("Controls")]
-[ScenarioCategory ("LineView")]
 public class TileViewNesting : Scenario
 {
     private CheckBox _cbBorder;

+ 38 - 13
Examples/UICatalog/UICatalog.cs

@@ -18,6 +18,7 @@ using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Reflection;
+using System.Reflection.Metadata;
 using System.Text;
 using System.Text.Json;
 using Microsoft.Extensions.Logging;
@@ -76,12 +77,25 @@ public class UICatalog
         // Process command line args
 
         // If no driver is provided, the default driver is used.
-        Option<string> driverOption = new Option<string> ("--driver", "The IConsoleDriver to use.").FromAmong (
-             Application.GetDriverTypes ().Item2.ToArray ()!
-            );
+        // Get allowed driver names
+        string? [] allowedDrivers = Application.GetDriverTypes ().Item2.ToArray ();
+
+        Option<string> driverOption = new Option<string> ("--driver", "The IConsoleDriver to use.")
+            .FromAmong (allowedDrivers!);
+        driverOption.SetDefaultValue (string.Empty);
         driverOption.AddAlias ("-d");
         driverOption.AddAlias ("--d");
 
+        // Add validator separately (not chained)
+        driverOption.AddValidator (result =>
+        {
+            var value = result.GetValueOrDefault<string> ();
+            if (result.Tokens.Count > 0 && !allowedDrivers.Contains (value))
+            {
+                result.ErrorMessage = $"Invalid driver name '{value}'. Allowed values: {string.Join (", ", allowedDrivers)}";
+            }
+        });
+
         // Configuration Management
         Option<bool> disableConfigManagement = new (
                                                     "--disable-cm",
@@ -163,6 +177,17 @@ public class UICatalog
             return 0;
         }
 
+        var parseResult = parser.Parse (args);
+
+        if (parseResult.Errors.Count > 0)
+        {
+            foreach (var error in parseResult.Errors)
+            {
+                Console.Error.WriteLine (error.Message);
+            }
+            return 1; // Non-zero exit code for error
+        }
+
         Scenario.BenchmarkTimeout = Options.BenchmarkTimeout;
 
         Logging.Logger = CreateLogger ();
@@ -175,16 +200,16 @@ public class UICatalog
     public static LogEventLevel LogLevelToLogEventLevel (LogLevel logLevel)
     {
         return logLevel switch
-               {
-                   LogLevel.Trace => LogEventLevel.Verbose,
-                   LogLevel.Debug => LogEventLevel.Debug,
-                   LogLevel.Information => LogEventLevel.Information,
-                   LogLevel.Warning => LogEventLevel.Warning,
-                   LogLevel.Error => LogEventLevel.Error,
-                   LogLevel.Critical => LogEventLevel.Fatal,
-                   LogLevel.None => LogEventLevel.Fatal, // Default to Fatal if None is specified
-                   _ => LogEventLevel.Fatal // Default to Information for any unspecified LogLevel
-               };
+        {
+            LogLevel.Trace => LogEventLevel.Verbose,
+            LogLevel.Debug => LogEventLevel.Debug,
+            LogLevel.Information => LogEventLevel.Information,
+            LogLevel.Warning => LogEventLevel.Warning,
+            LogLevel.Error => LogEventLevel.Error,
+            LogLevel.Critical => LogEventLevel.Fatal,
+            LogLevel.None => LogEventLevel.Fatal, // Default to Fatal if None is specified
+            _ => LogEventLevel.Fatal // Default to Information for any unspecified LogLevel
+        };
     }
 
     private static ILogger CreateLogger ()

+ 1 - 1
Terminal.Gui/App/Application.Driver.cs

@@ -20,7 +20,7 @@ public static partial class Application // Driver abstractions
 
     // BUGBUG: ForceDriver should be nullable.
     /// <summary>
-    ///     Forces the use of the specified driver (one of "fake", "ansi", "curses", "net", or "windows"). If not
+    ///     Forces the use of the specified driver (one of "fake", "dotnet", "windows", or "unix"). If not
     ///     specified, the driver is selected based on the platform.
     /// </summary>
     /// <remarks>

+ 47 - 38
Terminal.Gui/App/Application.Initialization.cs

@@ -32,7 +32,7 @@ public static partial class Application // Initialization (Init/Shutdown)
     ///     <paramref name="driverName"/> are specified the default driver for the platform will be used.
     /// </param>
     /// <param name="driverName">
-    ///     The short name (e.g. "net", "windows", "ansi", "fake", or "curses") of the
+    ///     The short name (e.g. "dotnet", "windows", "unix", or "fake") of the
     ///     <see cref="IConsoleDriver"/> to use. If neither <paramref name="driver"/> or <paramref name="driverName"/> are
     ///     specified the default driver for the platform will be used.
     /// </param>
@@ -40,7 +40,27 @@ public static partial class Application // Initialization (Init/Shutdown)
     [RequiresDynamicCode ("AOT")]
     public static void Init (IConsoleDriver? driver = null, string? driverName = null)
     {
-        ApplicationImpl.Instance.Init (driver, driverName);
+        // Check if this is a request for a legacy driver (like FakeDriver)
+        // that isn't supported by the modern application architecture
+        if (driver is null)
+        {
+            var driverNameToCheck = string.IsNullOrWhiteSpace (driverName) ? ForceDriver : driverName;
+            if (!string.IsNullOrEmpty (driverNameToCheck))
+            {
+                (List<Type?> drivers, List<string?> driverTypeNames) = GetDriverTypes ();
+                Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (driverNameToCheck, StringComparison.InvariantCultureIgnoreCase));
+                
+                // If it's a legacy IConsoleDriver (not a Facade), use InternalInit which supports legacy drivers
+                if (driverType is { } && !typeof (IConsoleDriverFacade).IsAssignableFrom (driverType))
+                {
+                    InternalInit (driver, driverName);
+                    return;
+                }
+            }
+        }
+        
+        // Otherwise delegate to the ApplicationImpl instance (which uses the modern architecture)
+        ApplicationImpl.Instance.Init (driver, driverName ?? ForceDriver);
     }
 
     internal static int MainThreadId { get; set; } = -1;
@@ -90,44 +110,31 @@ public static partial class Application // Initialization (Init/Shutdown)
             ForceDriver = driverName;
         }
 
+        // Check if we need to use a legacy driver (like FakeDriver)
+        // or go through the modern application architecture
         if (Driver is null)
         {
-            PlatformID p = Environment.OSVersion.Platform;
-
-            if (string.IsNullOrEmpty (ForceDriver))
-            {
-                if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
-                {
-                    Driver = new WindowsDriver ();
-                }
-                else
-                {
-                    Driver = new CursesDriver ();
-                }
-            }
-            else
+            //// Try to find a legacy IConsoleDriver type that matches the driver name
+            //bool useLegacyDriver = false;
+            //if (!string.IsNullOrEmpty (ForceDriver))
+            //{
+            //    (List<Type?> drivers, List<string?> driverTypeNames) = GetDriverTypes ();
+            //    Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (ForceDriver, StringComparison.InvariantCultureIgnoreCase));
+                
+            //    if (driverType is { } && !typeof (IConsoleDriverFacade).IsAssignableFrom (driverType))
+            //    {
+            //        // This is a legacy driver (not a ConsoleDriverFacade)
+            //        Driver = (IConsoleDriver)Activator.CreateInstance (driverType)!;
+            //        useLegacyDriver = true;
+            //    }
+            //}
+            
+            //// Use the modern application architecture
+            //if (!useLegacyDriver)
             {
-                (List<Type?> drivers, List<string?> driverTypeNames) = GetDriverTypes ();
-                Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (ForceDriver, StringComparison.InvariantCultureIgnoreCase));
-
-                if (driverType is { })
-                {
-                    Driver = (IConsoleDriver)Activator.CreateInstance (driverType)!;
-                }
-                else if (ForceDriver?.StartsWith ("v2") ?? false)
-                {
-                    ApplicationImpl.ChangeInstance (new ApplicationV2 ());
-                    ApplicationImpl.Instance.Init (driver, ForceDriver);
-                    Debug.Assert (Driver is { });
-
-                    return;
-                }
-                else
-                {
-                    throw new ArgumentException (
-                                                 $"Invalid driver name: {ForceDriver}. Valid names are {string.Join (", ", drivers.Select (t => t!.Name))}"
-                                                );
-                }
+                ApplicationImpl.Instance.Init (driver, driverName);
+                Debug.Assert (Driver is { });
+                return;
             }
         }
 
@@ -217,9 +224,11 @@ public static partial class Application // Initialization (Init/Shutdown)
         List<string?> driverTypeNames = driverTypes
                                         .Where (d => !typeof (IConsoleDriverFacade).IsAssignableFrom (d))
                                         .Select (d => d!.Name)
-                                        .Union (["v2", "v2win", "v2net"])
+                                        .Union (["dotnet", "windows", "unix", "fake"])
                                         .ToList ()!;
 
+
+
         return (driverTypes, driverTypeNames);
     }
 

+ 1 - 1
Terminal.Gui/App/Application.Keyboard.cs

@@ -151,7 +151,7 @@ public static partial class Application // Keyboard handling
     ///     </para>
     /// </summary>
     /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Unix) do not support firing the
     ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
     ///     <para>Fired after <see cref="KeyDown"/> and before <see cref="KeyUp"/>.</para>
     /// </remarks>

+ 1 - 1
Terminal.Gui/App/Application.Navigation.cs

@@ -53,7 +53,7 @@ public static partial class Application // Navigation stuff
     ///     </para>
     /// </summary>
     /// <remarks>
-    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Curses) do not support firing the
+    ///     All drivers support firing the <see cref="KeyDown"/> event. Some drivers (Unix) do not support firing the
     ///     <see cref="KeyDown"/> and <see cref="KeyUp"/> events.
     ///     <para>Fired after <see cref="KeyDown"/>.</para>
     /// </remarks>

+ 2 - 5
Terminal.Gui/App/Application.Run.cs

@@ -333,8 +333,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     /// <param name="errorHandler"></param>
     /// <param name="driver">
     ///     The <see cref="IConsoleDriver"/> to use. If not specified the default driver for the platform will
-    ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
-    ///     <see langword="null"/> if <see cref="Init"/> has already been called.
+    ///     be used. Must be <see langword="null"/> if <see cref="Init"/> has already been called.
     /// </param>
     /// <returns>The created T object. The caller is responsible for disposing this object.</returns>
     [RequiresUnreferencedCode ("AOT")]
@@ -425,9 +424,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
     ///     If <see langword="true"/> the entire View hierarchy will be redrawn. The default is <see langword="false"/> and
     ///     should only be overriden for testing.
     /// </param>
-    public static void LayoutAndDraw (bool forceDraw = false) { ApplicationImpl.Instance.LayoutAndDraw (forceDraw); }
-
-    internal static void LayoutAndDrawImpl (bool forceDraw = false)
+    public static void LayoutAndDraw (bool forceDraw = false)
     {
         List<View> tops = [.. TopLevels];
 

+ 176 - 188
Terminal.Gui/App/ApplicationImpl.cs

@@ -1,14 +1,23 @@
-#nullable enable
+#nullable enable
+using System.Collections.Concurrent;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Logging;
+using Terminal.Gui.Drivers;
 
 namespace Terminal.Gui.App;
 
 /// <summary>
-/// Original Terminal.Gui implementation of core <see cref="Application"/> methods.
+/// Implementation of core <see cref="Application"/> methods using the modern
+/// main loop architecture with component factories for different platforms.
 /// </summary>
 public class ApplicationImpl : IApplication
 {
+    private readonly IComponentFactory? _componentFactory;
+    private IMainLoopCoordinator? _coordinator;
+    private string? _driverName;
+    private readonly ITimedEvents _timedEvents = new TimedEvents ();
+
     // Private static readonly Lazy instance of Application
     private static Lazy<IApplication> _lazyInstance = new (() => new ApplicationImpl ());
 
@@ -18,15 +27,28 @@ public class ApplicationImpl : IApplication
     /// </summary>
     public static IApplication Instance => _lazyInstance.Value;
 
-
     /// <inheritdoc/>
-    public virtual ITimedEvents? TimedEvents => Application.MainLoop?.TimedEvents;
+    public ITimedEvents? TimedEvents => _timedEvents;
+
+    internal IMainLoopCoordinator? Coordinator => _coordinator;
 
     /// <summary>
     /// Handles which <see cref="View"/> (if any) has captured the mouse
     /// </summary>
     public IMouseGrabHandler MouseGrabHandler { get; set; } = new MouseGrabHandler ();
 
+    /// <summary>
+    /// Creates a new instance of the Application backend.
+    /// </summary>
+    public ApplicationImpl ()
+    {
+    }
+
+    internal ApplicationImpl (IComponentFactory componentFactory)
+    {
+        _componentFactory = componentFactory;
+    }
+
     /// <summary>
     /// Change the singleton implementation, should not be called except before application
     /// startup. This method lets you provide alternative implementations of core static gateway
@@ -41,25 +63,117 @@ public class ApplicationImpl : IApplication
     /// <inheritdoc/>
     [RequiresUnreferencedCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
-    public virtual void Init (IConsoleDriver? driver = null, string? driverName = null)
+    public void Init (IConsoleDriver? driver = null, string? driverName = null)
+    {
+        if (Application.Initialized)
+        {
+            Logging.Logger.LogError ("Init called multiple times without shutdown, aborting.");
+
+            throw new InvalidOperationException ("Init called multiple times without Shutdown");
+        }
+
+        if (!string.IsNullOrWhiteSpace (driverName))
+        {
+            _driverName = driverName;
+        }
+
+        if (string.IsNullOrWhiteSpace (_driverName))
+        {
+            _driverName = Application.ForceDriver;
+        }
+
+        Debug.Assert(Application.Navigation is null);
+        Application.Navigation = new ();
+
+        Debug.Assert (Application.Popover is null);
+        Application.Popover = new ();
+
+        Application.AddKeyBindings ();
+
+        CreateDriver (driverName ?? _driverName);
+
+        Application.Initialized = true;
+
+        Application.OnInitializedChanged (this, new (true));
+        Application.SubscribeDriverEvents ();
+
+        SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ());
+        Application.MainThreadId = Thread.CurrentThread.ManagedThreadId;
+    }
+
+    private void CreateDriver (string? driverName)
     {
-        Application.InternalInit (driver, string.IsNullOrWhiteSpace (driverName) ? Application.ForceDriver : driverName);
+        PlatformID p = Environment.OSVersion.Platform;
+
+        // Check component factory type first - this takes precedence over driverName
+        bool factoryIsWindows = _componentFactory is IComponentFactory<WindowsConsole.InputRecord>;
+        bool factoryIsDotNet = _componentFactory is IComponentFactory<ConsoleKeyInfo>;
+        bool factoryIsUnix = _componentFactory is IComponentFactory<char>;
+        bool factoryIsFake = _componentFactory is IComponentFactory<ConsoleKeyInfo>;
+
+        // Then check driverName
+        bool nameIsWindows = driverName?.Contains ("win", StringComparison.OrdinalIgnoreCase) ?? false;
+        bool nameIsDotNet = (driverName?.Contains ("dotnet", StringComparison.OrdinalIgnoreCase) ?? false);
+        bool nameIsUnix = driverName?.Contains ("unix", StringComparison.OrdinalIgnoreCase) ?? false;
+        bool nameIsFake = driverName?.Contains ("fake", StringComparison.OrdinalIgnoreCase) ?? false;
+
+        // Decide which driver to use - component factory type takes priority
+        if (factoryIsFake || (!factoryIsWindows && !factoryIsDotNet && !factoryIsUnix && nameIsFake))
+        {
+            _coordinator = CreateSubcomponents (() => new FakeComponentFactory ());
+        }
+        else if (factoryIsWindows || (!factoryIsDotNet && !factoryIsUnix && nameIsWindows))
+        {
+            _coordinator = CreateSubcomponents (() => new WindowsComponentFactory ());
+        }
+        else if (factoryIsDotNet || (!factoryIsWindows && !factoryIsUnix && nameIsDotNet))
+        {
+            _coordinator = CreateSubcomponents (() => new NetComponentFactory ());
+        }
+        else if (factoryIsUnix || (!factoryIsWindows && !factoryIsDotNet && nameIsUnix))
+        {
+            _coordinator = CreateSubcomponents (() => new UnixComponentFactory ());
+        }
+        else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
+        {
+            _coordinator = CreateSubcomponents (() => new WindowsComponentFactory ());
+        }
+        else
+        {
+            _coordinator = CreateSubcomponents (() => new UnixComponentFactory ());
+        }
+
+        _coordinator.StartAsync ().Wait ();
+
+        if (Application.Driver == null)
+        {
+            throw new ("Application.Driver was null even after booting MainLoopCoordinator");
+        }
+    }
+
+    private IMainLoopCoordinator CreateSubcomponents<T> (Func<IComponentFactory<T>> fallbackFactory)
+    {
+        ConcurrentQueue<T> inputBuffer = new ();
+        ApplicationMainLoop<T> loop = new ();
+
+        IComponentFactory<T> cf;
+
+        if (_componentFactory is IComponentFactory<T> typedFactory)
+        {
+            cf = typedFactory;
+        }
+        else
+        {
+            cf = fallbackFactory ();
+        }
+
+        return new MainLoopCoordinator<T> (_timedEvents, inputBuffer, loop, cf);
     }
 
     /// <summary>
     ///     Runs the application by creating a <see cref="Toplevel"/> object and calling
     ///     <see cref="Run(Toplevel, Func{Exception, bool})"/>.
     /// </summary>
-    /// <remarks>
-    ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
-    ///     <para>
-    ///         <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has returned) to
-    ///         ensure resources are cleaned up and terminal settings restored.
-    ///     </para>
-    ///     <para>
-    ///         The caller is responsible for disposing the object returned by this method.
-    ///     </para>
-    /// </remarks>
     /// <returns>The created <see cref="Toplevel"/> object. The caller is responsible for disposing this object.</returns>
     [RequiresUnreferencedCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
@@ -69,168 +183,71 @@ public class ApplicationImpl : IApplication
     ///     Runs the application by creating a <see cref="Toplevel"/>-derived object of type <c>T</c> and calling
     ///     <see cref="Run(Toplevel, Func{Exception, bool})"/>.
     /// </summary>
-    /// <remarks>
-    ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
-    ///     <para>
-    ///         <see cref="Shutdown"/> must be called when the application is closing (typically after Run> has returned) to
-    ///         ensure resources are cleaned up and terminal settings restored.
-    ///     </para>
-    ///     <para>
-    ///         The caller is responsible for disposing the object returned by this method.
-    ///     </para>
-    /// </remarks>
     /// <param name="errorHandler"></param>
     /// <param name="driver">
     ///     The <see cref="IConsoleDriver"/> to use. If not specified the default driver for the platform will
-    ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
-    ///     <see langword="null"/> if <see cref="Init"/> has already been called.
+    ///     be used. Must be <see langword="null"/> if <see cref="Init"/> has already been called.
     /// </param>
     /// <returns>The created T object. The caller is responsible for disposing this object.</returns>
     [RequiresUnreferencedCode ("AOT")]
     [RequiresDynamicCode ("AOT")]
-    public virtual T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
+    public T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
         where T : Toplevel, new()
     {
         if (!Application.Initialized)
         {
-            // Init() has NOT been called.
-            Application.InternalInit (driver, Application.ForceDriver, true);
-        }
-
-        if (Instance is ApplicationV2)
-        {
-            return Instance.Run<T> (errorHandler, driver);
+            // Init() has NOT been called. Auto-initialize as per interface contract.
+            Init (driver, null);
         }
 
         var top = new T ();
-
         Run (top, errorHandler);
-
         return top;
     }
 
     /// <summary>Runs the Application using the provided <see cref="Toplevel"/> view.</summary>
-    /// <remarks>
-    ///     <para>
-    ///         This method is used to start processing events for the main application, but it is also used to run other
-    ///         modal <see cref="View"/>s such as <see cref="Dialog"/> boxes.
-    ///     </para>
-    ///     <para>
-    ///         To make a <see cref="Run(Toplevel,System.Func{System.Exception,bool})"/> stop execution, call
-    ///         <see cref="Application.RequestStop"/>.
-    ///     </para>
-    ///     <para>
-    ///         Calling <see cref="Run(Toplevel,System.Func{System.Exception,bool})"/> is equivalent to calling
-    ///         <see cref="Application.Begin(Toplevel)"/>, followed by <see cref="Application.RunLoop(RunState)"/>, and then calling
-    ///         <see cref="Application.End(RunState)"/>.
-    ///     </para>
-    ///     <para>
-    ///         Alternatively, to have a program control the main loop and process events manually, call
-    ///         <see cref="Application.Begin(Toplevel)"/> to set things up manually and then repeatedly call
-    ///         <see cref="Application.RunLoop(RunState)"/> with the wait parameter set to false. By doing this the
-    ///         <see cref="Application.RunLoop(RunState)"/> method will only process any pending events, timers handlers and then
-    ///         return control immediately.
-    ///     </para>
-    ///     <para>When using <see cref="Run{T}"/> or
-    ///         <see cref="Run(System.Func{System.Exception,bool},IConsoleDriver)"/>
-    ///         <see cref="Init"/> will be called automatically.
-    ///     </para>
-    ///     <para>
-    ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
-    ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
-    ///         returns <see langword="true"/> the <see cref="Application.RunLoop(RunState)"/> will resume; otherwise this method will
-    ///         exit.
-    ///     </para>
-    /// </remarks>
     /// <param name="view">The <see cref="Toplevel"/> to run as a modal.</param>
-    /// <param name="errorHandler">
-    ///     RELEASE builds only: Handler for any unhandled exceptions (resumes when returns true,
-    ///     rethrows when null).
-    /// </param>
-    public virtual void Run (Toplevel view, Func<Exception, bool>? errorHandler = null)
+    /// <param name="errorHandler">Handler for any unhandled exceptions.</param>
+    public void Run (Toplevel view, Func<Exception, bool>? errorHandler = null)
     {
+        Logging.Information ($"Run '{view}'");
         ArgumentNullException.ThrowIfNull (view);
 
-        if (Application.Initialized)
+        if (!Application.Initialized)
         {
-            if (Application.Driver is null)
-            {
-                // Disposing before throwing
-                view.Dispose ();
-
-                // This code path should be impossible because Init(null, null) will select the platform default driver
-                throw new InvalidOperationException (
-                                                     "Init() completed without a driver being set (this should be impossible); Run<T>() cannot be called."
-                                                    );
-            }
+            throw new NotInitializedException (nameof (Run));
         }
-        else
+
+        if (Application.Driver == null)
         {
-            // Init() has NOT been called.
-            throw new InvalidOperationException (
-                                                 "Init() has not been called. Only Run() or Run<T>() can be used without calling Init()."
-                                                );
+            throw new  InvalidOperationException ("Driver was inexplicably null when trying to Run view");
         }
 
-        var resume = true;
+        Application.Top = view;
 
-        while (resume)
+        RunState rs = Application.Begin (view);
+
+        Application.Top.Running = true;
+
+        while (Application.TopLevels.TryPeek (out Toplevel? found) && found == view && view.Running)
         {
-#if !DEBUG
-            try
+            if (_coordinator is null)
             {
-#endif
-                resume = false;
-                RunState runState = Application.Begin (view);
-
-                // If EndAfterFirstIteration is true then the user must dispose of the runToken
-                // by using NotifyStopRunState event.
-                Application.RunLoop (runState);
-
-                if (runState.Toplevel is null)
-                {
-#if DEBUG_IDISPOSABLE
-                if (View.EnableDebugIDisposableAsserts)
-                {
-                    Debug.Assert (Application.TopLevels.Count == 0);
-                }
-#endif
-                    runState.Dispose ();
-
-                    return;
-                }
-
-                if (!Application.EndAfterFirstIteration)
-                {
-                    Application.End (runState);
-                }
-#if !DEBUG
+                throw new ($"{nameof (IMainLoopCoordinator)} inexplicably became null during Run");
             }
-            catch (Exception error)
-            {
-                Logging.Warning ($"Release Build Exception: {error}");
-                if (errorHandler is null)
-                {
-                    throw;
-                }
 
-                resume = errorHandler (error);
-            }
-#endif
+            _coordinator.RunIteration ();
         }
+
+        Logging.Information ($"Run - Calling End");
+        Application.End (rs);
     }
 
     /// <summary>Shutdown an application initialized with <see cref="Init"/>.</summary>
-    /// <remarks>
-    ///     Shutdown must be called for every call to <see cref="Init"/> or
-    ///     <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to ensure all resources are cleaned
-    ///     up (Disposed)
-    ///     and terminal settings are restored.
-    /// </remarks>
-    public virtual void Shutdown ()
+    public void Shutdown ()
     {
-        // TODO: Throw an exception if Init hasn't been called.
-
+        _coordinator?.Stop ();
+        
         bool wasInitialized = Application.Initialized;
         Application.ResetState ();
         ConfigurationManager.PrintJsonErrors ();
@@ -238,19 +255,21 @@ public class ApplicationImpl : IApplication
         if (wasInitialized)
         {
             bool init = Application.Initialized;
-
             Application.OnInitializedChanged (this, new (in init));
         }
 
+        Application.Driver = null;
         _lazyInstance = new (() => new ApplicationImpl ());
     }
 
     /// <inheritdoc />
-    public virtual void RequestStop (Toplevel? top)
+    public void RequestStop (Toplevel? top)
     {
+        Logging.Logger.LogInformation ($"RequestStop '{(top is {} ? top : "null")}'");
+
         top ??= Application.Top;
 
-        if (!top!.Running)
+        if (top == null)
         {
             return;
         }
@@ -264,71 +283,40 @@ public class ApplicationImpl : IApplication
         }
 
         top.Running = false;
-        Application.OnNotifyStopRunState (top);
     }
 
     /// <inheritdoc />
-    public virtual void Invoke (Action action)
+    public void Invoke (Action action)
     {
-
         // If we are already on the main UI thread
         if (Application.MainThreadId == Thread.CurrentThread.ManagedThreadId)
         {
             action ();
-            WakeupMainLoop ();
-
-            return;
-        }
-
-        if (Application.MainLoop == null)
-        {
-            Logging.Warning ("Ignored Invoke because MainLoop is not initialized yet");
             return;
         }
 
-
-        Application.AddTimeout (TimeSpan.Zero,
-                           () =>
-                           {
-                               action ();
-
-                               return false;
-                           }
-                          );
-
-        WakeupMainLoop ();
-
-        void WakeupMainLoop ()
-        {
-            // Ensure the action is executed in the main loop
-            // Wakeup mainloop if it's waiting for events
-            Application.MainLoop?.Wakeup ();
-        }
+        _timedEvents.Add (TimeSpan.Zero,
+                              () =>
+                              {
+                                  action ();
+                                  return false;
+                              }
+                             );
     }
 
     /// <inheritdoc />
-    public bool IsLegacy { get; protected set; } = true;
+    public bool IsLegacy => false;
 
     /// <inheritdoc />
-    public virtual object AddTimeout (TimeSpan time, Func<bool> callback)
-    {
-        if (Application.MainLoop is null)
-        {
-            throw new NotInitializedException ("Cannot add timeout before main loop is initialized", null);
-        }
-
-        return Application.MainLoop.TimedEvents.Add (time, callback);
-    }
+    public object AddTimeout (TimeSpan time, Func<bool> callback) { return _timedEvents.Add (time, callback); }
 
     /// <inheritdoc />
-    public virtual bool RemoveTimeout (object token)
-    {
-        return Application.MainLoop?.TimedEvents.Remove (token) ?? false;
-    }
+    public bool RemoveTimeout (object token) { return _timedEvents.Remove (token); }
 
     /// <inheritdoc />
-    public virtual void LayoutAndDraw (bool forceDraw)
+    public void LayoutAndDraw (bool forceDraw)
     {
-        Application.LayoutAndDrawImpl (forceDraw);
+        Application.Top?.SetNeedsDraw();
+        Application.Top?.SetNeedsLayout ();
     }
 }

+ 13 - 7
Terminal.Gui/App/CWP/CWPPropertyHelper.cs

@@ -26,6 +26,7 @@ public static class CWPPropertyHelper
     /// <param name="newValue">The proposed new property value, which may be null for nullable types.</param>
     /// <param name="onChanging">The virtual method invoked before the change, returning true to cancel.</param>
     /// <param name="changingEvent">The pre-change event raised to allow modification or cancellation.</param>
+    /// <param name="doWork">The action that performs the actual work of setting the property (e.g., updating backing field, calling related methods).</param>
     /// <param name="onChanged">The virtual method invoked after the change.</param>
     /// <param name="changedEvent">The post-change event raised to notify of the completed change.</param>
     /// <param name="finalValue">
@@ -39,15 +40,15 @@ public static class CWPPropertyHelper
     /// </exception>
     /// <example>
     ///     <code>
-    ///         string? current = null;
+    ///         string? current = _schemeName;
     ///         string? proposed = "Base";
-    ///         Func&lt;ValueChangingEventArgs&lt;string?&gt;, bool&gt; onChanging = args =&gt; false;
-    ///         EventHandler&lt;ValueChangingEventArgs&lt;string?&gt;&gt;? changingEvent = null;
-    ///         Action&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? onChanged = args =&gt;
-    ///             Console.WriteLine($"SchemeName changed to {args.NewValue ?? "none"}.");
-    ///         EventHandler&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? changedEvent = null;
+    ///         Func&lt;ValueChangingEventArgs&lt;string?&gt;, bool&gt; onChanging = OnSchemeNameChanging;
+    ///         EventHandler&lt;ValueChangingEventArgs&lt;string?&gt;&gt;? changingEvent = SchemeNameChanging;
+    ///         Action&lt;string?&gt; doWork = value => _schemeName = value;
+    ///         Action&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? onChanged = OnSchemeNameChanged;
+    ///         EventHandler&lt;ValueChangedEventArgs&lt;string?&gt;&gt;? changedEvent = SchemeNameChanged;
     ///         bool changed = CWPPropertyHelper.ChangeProperty(
-    ///             current, proposed, onChanging, changingEvent, onChanged, changedEvent, out string? final);
+    ///             current, proposed, onChanging, changingEvent, doWork, onChanged, changedEvent, out string? final);
     ///     </code>
     /// </example>
     public static bool ChangeProperty<T> (
@@ -55,6 +56,7 @@ public static class CWPPropertyHelper
         T newValue,
         Func<ValueChangingEventArgs<T>, bool> onChanging,
         EventHandler<ValueChangingEventArgs<T>>? changingEvent,
+        Action<T> doWork,
         Action<ValueChangedEventArgs<T>>? onChanged,
         EventHandler<ValueChangedEventArgs<T>>? changedEvent,
         out T finalValue
@@ -93,6 +95,10 @@ public static class CWPPropertyHelper
         }
 
         finalValue = args.NewValue;
+        
+        // Do the work (set backing field, update related properties, etc.) BEFORE raising Changed events
+        doWork (finalValue);
+        
         ValueChangedEventArgs<T> changedArgs = new (currentValue, finalValue);
         onChanged?.Invoke (changedArgs);
         changedEvent?.Invoke (null, changedArgs);

+ 1 - 1
Terminal.Gui/App/Clipboard/ClipboardProcessRunner.cs

@@ -5,7 +5,7 @@ namespace Terminal.Gui.App;
 
 /// <summary>
 ///     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 IConsoleDriver.cs.
+///     UnixDriver, but also used in Unit tests which is why it is in IConsoleDriver.cs.
 /// </summary>
 internal static class ClipboardProcessRunner
 {

+ 2 - 9
Terminal.Gui/App/IApplication.cs

@@ -44,7 +44,7 @@ public interface IApplication
     ///     <paramref name="driverName"/> are specified the default driver for the platform will be used.
     /// </param>
     /// <param name="driverName">
-    ///     The short name (e.g. "net", "windows", "ansi", "fake", or "curses") of the
+    ///     The driver name (e.g. "dotnet", "windows", "fake", or "unix") of the
     ///     <see cref="IConsoleDriver"/> to use. If neither <paramref name="driver"/> or <paramref name="driverName"/> are
     ///     specified the default driver for the platform will be used.
     /// </param>
@@ -89,7 +89,7 @@ public interface IApplication
     /// <param name="errorHandler"></param>
     /// <param name="driver">
     ///     The <see cref="IConsoleDriver"/> to use. If not specified the default driver for the platform will
-    ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
+    ///     be used. Must be
     ///     <see langword="null"/> if <see cref="Init"/> has already been called.
     /// </param>
     /// <returns>The created T object. The caller is responsible for disposing this object.</returns>
@@ -187,11 +187,4 @@ public interface IApplication
     /// <see langword="false"/>
     /// if the timeout is not found.</returns>
     bool RemoveTimeout (object token);
-
-    /// <summary>
-    /// Causes any Toplevels that need layout to be laid out. Then draws any Toplevels that need display. Only Views that need to be laid out (see <see cref="View.NeedsLayout"/>) will be laid out.
-    /// Only Views that need to be drawn (see <see cref="View.NeedsDraw"/>) will be drawn.
-    /// </summary>
-    /// <param name="forceDraw">If <see langword="true"/> the entire View hierarchy will be redrawn. The default is <see langword="false"/> and should only be overriden for testing.</param>
-    void LayoutAndDraw (bool forceDraw);
 }

+ 20 - 5
Terminal.Gui/Drivers/V2/MainLoop.cs → Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs

@@ -1,11 +1,26 @@
 #nullable enable
+using Terminal.Gui.Drivers;
 using System.Collections.Concurrent;
 using System.Diagnostics;
 
-namespace Terminal.Gui.Drivers;
-
-/// <inheritdoc/>
-public class MainLoop<T> : IMainLoop<T>
+namespace Terminal.Gui.App;
+
+/// <summary>
+///     The main application loop that runs Terminal.Gui's UI rendering and event processing.
+/// </summary>
+/// <remarks>
+///     This class coordinates the Terminal.Gui application lifecycle by:
+///     <list type="bullet">
+///         <item>Processing buffered input events and translating them to UI events</item>
+///         <item>Executing user timeout callbacks at scheduled intervals</item>
+///         <item>Detecting which views need redrawing or layout updates</item>
+///         <item>Rendering UI changes to the console output buffer</item>
+///         <item>Managing cursor position and visibility</item>
+///         <item>Throttling iterations to respect <see cref="Application.MaximumIterationsPerSecond"/></item>
+///     </list>
+/// </remarks>
+/// <typeparam name="T">Type of raw input events, e.g. <see cref="ConsoleKeyInfo"/> for .NET driver</typeparam>
+public class ApplicationMainLoop<T> : IApplicationMainLoop<T>
 {
     private ITimedEvents? _timedEvents;
     private ConcurrentQueue<T>? _inputBuffer;
@@ -143,7 +158,7 @@ public class MainLoop<T> : IMainLoop<T>
             {
                 Logging.Redraws.Add (1);
 
-                Application.LayoutAndDrawImpl (true);
+                Application.LayoutAndDraw (true);
 
                 Out.Write (OutputBuffer);
 

+ 13 - 4
Terminal.Gui/Drivers/V2/IMainLoop.cs → Terminal.Gui/App/MainLoop/IApplicationMainLoop.cs

@@ -1,13 +1,22 @@
 #nullable enable
 using System.Collections.Concurrent;
 
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.App;
 
 /// <summary>
-///     Interface for main loop that runs the core Terminal.Gui UI loop.
+///     Interface for the main application loop that runs the core Terminal.Gui UI rendering and event processing.
 /// </summary>
-/// <typeparam name="T">Type of raw input events processed by the loop e.g. <see cref="ConsoleKeyInfo"/></typeparam>
-public interface IMainLoop<T> : IDisposable
+/// <remarks>
+///     This interface defines the contract for the main loop that coordinates:
+///     <list type="bullet">
+///         <item>Processing input events from the console</item>
+///         <item>Running user timeout callbacks</item>
+///         <item>Detecting UI changes that need redrawing</item>
+///         <item>Rendering UI updates to the console</item>
+///     </list>
+/// </remarks>
+/// <typeparam name="T">Type of raw input events processed by the loop, e.g. <see cref="ConsoleKeyInfo"/> for cross-platform .NET driver</typeparam>
+public interface IApplicationMainLoop<T> : IDisposable
 {
     /// <summary>
     ///     Gets the class responsible for servicing user timeouts

+ 48 - 0
Terminal.Gui/App/MainLoop/IMainLoopCoordinator.cs

@@ -0,0 +1,48 @@
+namespace Terminal.Gui.App;
+
+/// <summary>
+///     Interface for the main loop coordinator that manages UI loop initialization and threading.
+/// </summary>
+/// <remarks>
+///     The coordinator is responsible for:
+///     <list type="bullet">
+///         <item>Starting the asynchronous input reading thread</item>
+///         <item>Initializing the main UI loop on the application thread</item>
+///         <item>Building the <see cref="IConsoleDriver"/> facade</item>
+///         <item>Coordinating clean shutdown of both threads</item>
+///     </list>
+/// </remarks>
+public interface IMainLoopCoordinator
+{
+    /// <summary>
+    ///     Initializes all required subcomponents and starts the input thread.
+    /// </summary>
+    /// <remarks>
+    ///     This method:
+    ///     <list type="number">
+    ///         <item>Starts the input thread that reads console input asynchronously</item>
+    ///         <item>Initializes the main UI loop on the calling thread</item>
+    ///         <item>Waits for both to be ready before returning</item>
+    ///     </list>
+    /// </remarks>
+    /// <returns>A task that completes when initialization is done</returns>
+    public Task StartAsync ();
+
+    /// <summary>
+    ///     Stops the input thread and performs cleanup.
+    /// </summary>
+    /// <remarks>
+    ///     This method blocks until the input thread has exited.
+    ///     It must be called only from the main UI thread.
+    /// </remarks>
+    public void Stop ();
+
+    /// <summary>
+    ///     Executes a single iteration of the main UI loop.
+    /// </summary>
+    /// <remarks>
+    ///     Each iteration processes input, runs timeouts, checks for UI changes,
+    ///     and renders any necessary updates.
+    /// </remarks>
+    void RunIteration ();
+}

+ 24 - 0
Terminal.Gui/App/MainLoop/IMainLoopDriver.cs

@@ -0,0 +1,24 @@
+#nullable enable
+namespace Terminal.Gui.App;
+
+/// <summary>Interface to create a platform specific <see cref="MainLoop"/> driver.</summary>
+internal interface IMainLoopDriver
+{
+    /// <summary>Must report whether there are any events pending, or even block waiting for events.</summary>
+    /// <returns><see langword="true"/>, if there were pending events, <see langword="false"/> otherwise.</returns>
+    bool EventsPending ();
+
+    /// <summary>The iteration function.</summary>
+    void Iteration ();
+
+    /// <summary>Initializes the <see cref="MainLoop"/>, gets the calling main loop for the initialization.</summary>
+    /// <remarks>Call <see cref="TearDown"/> to release resources.</remarks>
+    /// <param name="mainLoop">Main loop.</param>
+    void Setup (MainLoop mainLoop);
+
+    /// <summary>Tears down the <see cref="MainLoop"/> driver. Releases resources created in <see cref="Setup"/>.</summary>
+    void TearDown ();
+
+    /// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input, must be thread safe.</summary>
+    void Wakeup ();
+}

+ 13 - 26
Terminal.Gui/App/MainLoop.cs → Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs

@@ -1,6 +1,6 @@
 #nullable enable
 //
-// MainLoop.cs: IMainLoopDriver and MainLoop for Terminal.Gui
+// LegacyMainLoopDriver.cs: IMainLoopDriver and MainLoop for legacy v1 driver based applications
 //
 // Authors:
 //   Miguel de Icaza ([email protected])
@@ -10,33 +10,20 @@ using System.Collections.ObjectModel;
 
 namespace Terminal.Gui.App;
 
-/// <summary>Interface to create a platform specific <see cref="MainLoop"/> driver.</summary>
-internal interface IMainLoopDriver
-{
-    /// <summary>Must report whether there are any events pending, or even block waiting for events.</summary>
-    /// <returns><see langword="true"/>, if there were pending events, <see langword="false"/> otherwise.</returns>
-    bool EventsPending ();
-
-    /// <summary>The iteration function.</summary>
-    void Iteration ();
-
-    /// <summary>Initializes the <see cref="MainLoop"/>, gets the calling main loop for the initialization.</summary>
-    /// <remarks>Call <see cref="TearDown"/> to release resources.</remarks>
-    /// <param name="mainLoop">Main loop.</param>
-    void Setup (MainLoop mainLoop);
-
-    /// <summary>Tears down the <see cref="MainLoop"/> driver. Releases resources created in <see cref="Setup"/>.</summary>
-    void TearDown ();
-
-    /// <summary>Wakes up the <see cref="MainLoop"/> that might be waiting on input, must be thread safe.</summary>
-    void Wakeup ();
-}
-
-/// <summary>The main event loop of v1 driver based applications.</summary>
+/// <summary>
+///     The main event loop of legacy v1 driver based applications.
+/// </summary>
 /// <remarks>
-///     Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this
-///     on Windows.
+///     <para>
+///         This class is provided for backward compatibility with the legacy FakeDriver implementation.
+///         New code should use the modern <see cref="ApplicationMainLoop{T}"/> architecture instead.
+///     </para>
+///     <para>
+///         Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this
+///         on Windows.
+///     </para>
 /// </remarks>
+[Obsolete ("This class is for legacy FakeDriver compatibility only. Use ApplicationMainLoop<T> for new code.")]
 public class MainLoop : IDisposable
 {
     /// <summary>

+ 17 - 13
Terminal.Gui/Drivers/V2/MainLoopCoordinator.cs → Terminal.Gui/App/MainLoop/MainLoopCoordinator.cs

@@ -1,21 +1,26 @@
 using System.Collections.Concurrent;
+using Terminal.Gui.Drivers;
 using Microsoft.Extensions.Logging;
 
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.App;
 
 /// <summary>
 ///     <para>
-///         Handles creating the input loop thread and bootstrapping the
-///         <see cref="MainLoop{T}"/> that handles layout/drawing/events etc.
+///         Coordinates the creation and startup of the main UI loop and input thread.
 ///     </para>
-///     <para>This class is designed to be managed by <see cref="ApplicationV2"/></para>
+///     <para>
+///         This class bootstraps the <see cref="ApplicationMainLoop{T}"/> that handles
+///         UI layout, drawing, and event processing while also managing a separate thread
+///         for reading console input asynchronously.
+///     </para>
+///     <para>This class is designed to be managed by <see cref="ApplicationImpl"/></para>
 /// </summary>
-/// <typeparam name="T"></typeparam>
+/// <typeparam name="T">Type of raw input events, e.g. <see cref="ConsoleKeyInfo"/> for .NET driver</typeparam>
 internal class MainLoopCoordinator<T> : IMainLoopCoordinator
 {
     private readonly ConcurrentQueue<T> _inputBuffer;
     private readonly IInputProcessor _inputProcessor;
-    private readonly IMainLoop<T> _loop;
+    private readonly IApplicationMainLoop<T> _loop;
     private readonly IComponentFactory<T> _componentFactory;
     private readonly CancellationTokenSource _tokenSource = new ();
     private IConsoleInput<T> _input;
@@ -28,17 +33,16 @@ internal class MainLoopCoordinator<T> : IMainLoopCoordinator
     private readonly SemaphoreSlim _startupSemaphore = new (0, 1);
 
     /// <summary>
-    ///     Creates a new coordinator
+    ///     Creates a new coordinator that will manage the main UI loop and input thread.
     /// </summary>
-    /// <param name="timedEvents"></param>
-    /// <param name="inputBuffer"></param>
-    /// <param name="loop"></param>
-    /// <param name="componentFactory">Factory for creating driver components
-    /// (<see cref="IConsoleOutput"/>, <see cref="IConsoleInput{T}"/> etc)</param>
+    /// <param name="timedEvents">Handles scheduling and execution of user timeout callbacks</param>
+    /// <param name="inputBuffer">Thread-safe queue for buffering raw console input</param>
+    /// <param name="loop">The main application loop instance</param>
+    /// <param name="componentFactory">Factory for creating driver-specific components (input, output, etc.)</param>
     public MainLoopCoordinator (
         ITimedEvents timedEvents,
         ConcurrentQueue<T> inputBuffer,
-        IMainLoop<T> loop,
+        IApplicationMainLoop<T> loop,
         IComponentFactory<T> componentFactory
     )
     {

+ 0 - 0
Terminal.Gui/App/MainLoopSyncContext.cs → Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs


+ 4 - 3
Terminal.Gui/Drivers/V2/NotInitializedException.cs → Terminal.Gui/App/NotInitializedException.cs

@@ -1,10 +1,11 @@
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.App;
 
 /// <summary>
 ///     Thrown when user code attempts to access a property or perform a method
-///     that is only supported after Initialization e.g. of an <see cref="IMainLoop{T}"/>
+///     Exception type thrown when trying to use a property or method
+///     that is only supported after initialization, e.g. of an <see cref="IApplicationMainLoop{T}"/>
 /// </summary>
-public class NotInitializedException : Exception
+public class NotInitializedException : InvalidOperationException
 {
     /// <summary>
     ///     Creates a new instance of the exception indicating that the class

+ 1 - 1
Terminal.Gui/Drivers/V2/IToplevelTransitionManager.cs → Terminal.Gui/App/Toplevel/IToplevelTransitionManager.cs

@@ -1,4 +1,4 @@
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.App;
 
 /// <summary>
 ///     Interface for class that handles bespoke behaviours that occur when application

+ 2 - 1
Terminal.Gui/Drivers/V2/ToplevelTransitionManager.cs → Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs

@@ -1,6 +1,7 @@
 #nullable enable
+using Terminal.Gui.Drivers;
 
-namespace Terminal.Gui.Drivers;
+namespace Terminal.Gui.App;
 
 /// <summary>
 ///     Handles bespoke behaviours that occur when application top level changes.

+ 9 - 31
Terminal.Gui/Drawing/Attribute.cs

@@ -25,11 +25,6 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
     [JsonIgnore]
     public static Attribute Default => new (Color.White, Color.Black);
 
-    // TODO: Once CursesDriver is dead, remove this property
-    /// <summary>INTERNAL: The <see cref="IConsoleDriver"/>-specific color value.</summary>
-    [JsonIgnore (Condition = JsonIgnoreCondition.Always)]
-    internal int PlatformColor { get; init; }
-
     /// <summary>
     ///     Gets the foreground <see cref="Color"/> used to render text.
     /// </summary>
@@ -51,24 +46,12 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
     /// <summary>
     ///     Initializes a new instance of the <see cref="Attribute"/> struct with default values.
     /// </summary>
-    public Attribute () { this = Default with { PlatformColor = -1 }; }
+    public Attribute () { this = Default; }
 
     /// <summary>
     ///     Initializes a new <see cref="Attribute"/> from an existing instance, preserving explicit state.
     /// </summary>
-    public Attribute (in Attribute attr) { this = attr with { PlatformColor = -1 }; }
-
-    /// <summary>INTERNAL: Initializes a new instance of the <see cref="Attribute"/> struct.</summary>
-    /// <param name="platformColor">platform-dependent color value.</param>
-    /// <param name="foreground">Foreground</param>
-    /// <param name="background">Background</param>
-    internal Attribute (in int platformColor, in Color foreground, in Color background)
-    {
-        Foreground = foreground;
-        Background = background;
-        PlatformColor = platformColor;
-        Style = TextStyle.None;
-    }
+    public Attribute (in Attribute attr) { this = attr; }
 
     /// <summary>
     ///     Initializes an instance using two named colors.
@@ -78,8 +61,6 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
         Foreground = foreground;
         Background = background;
 
-        // TODO: Once CursesDriver supports true color all the PlatformColor stuff goes away
-        PlatformColor = Application.Driver?.MakeColor (in foreground, in background).PlatformColor ?? -1;
         Style = TextStyle.None;
     }
 
@@ -91,9 +72,6 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
         Foreground = foreground;
         Background = background;
         Style = style;
-
-        // TODO: Once CursesDriver supports true color all the PlatformColor stuff goes away
-        PlatformColor = Application.Driver?.MakeColor (in foreground, in background).PlatformColor ?? -1;
     }
 
     /// <summary>
@@ -111,7 +89,6 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
         Style = style is { } && Enum.TryParse (style, true, out TextStyle parsedStyle)
                     ? parsedStyle
                     : TextStyle.None;
-        PlatformColor = Application.Driver?.MakeColor (Foreground, Background).PlatformColor ?? -1;
     }
 
     /// <summary>
@@ -127,7 +104,6 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
         Background = Color.Parse (background);
 
         Style = style;
-        PlatformColor = Application.Driver?.MakeColor (Foreground, Background).PlatformColor ?? -1;
     }
 
     /// <summary>
@@ -186,20 +162,22 @@ public readonly record struct Attribute : IEqualityOperators<Attribute, Attribut
     /// <summary>
     ///     Initializes a new instance with foreground and background colors and a <see cref="TextStyle"/>.
     /// </summary>
-    public Attribute (in StandardColor foreground, in StandardColor background, in TextStyle style) : this (new (in foreground), new Color (in background), style) { }
-
+    public Attribute (in StandardColor foreground, in StandardColor background, in TextStyle style) : this (
+                                                                                                            new (in foreground),
+                                                                                                            new Color (in background),
+                                                                                                            style)
+    { }
 
     /// <inheritdoc/>
     public bool Equals (Attribute other)
     {
-        return PlatformColor == other.PlatformColor
-               && Foreground.Equals (other.Foreground)
+        return Foreground.Equals (other.Foreground)
                && Background.Equals (other.Background)
                && Style == other.Style;
     }
 
     /// <inheritdoc/>
-    public override int GetHashCode () { return HashCode.Combine (PlatformColor, Foreground, Background, Style); }
+    public override int GetHashCode () { return HashCode.Combine (Foreground, Background, Style); }
 
     /// <inheritdoc/>
     public override string ToString ()

+ 0 - 0
Terminal.Gui/Drivers/AnsiEscapeSequence.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequence.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiEscapeSequenceRequest.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiEscapeSequenceRequest.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/AnsiMouseParser.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiMouseParser.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/AnsiRequestScheduler.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiRequestScheduler.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/AnsiResponseExpectation.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiResponseExpectation.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/AnsiResponseParser.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParser.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/AnsiResponseParserState.cs → Terminal.Gui/Drivers/AnsiHandling/AnsiResponseParserState.cs


+ 0 - 0
Terminal.Gui/Drivers/EscSeqUtils/EscSeqReqStatus.cs → Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqReqStatus.cs


+ 0 - 0
Terminal.Gui/Drivers/EscSeqUtils/EscSeqRequests.cs → Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqRequests.cs


+ 55 - 5
Terminal.Gui/Drivers/EscSeqUtils/EscSeqUtils.cs → Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs

@@ -384,7 +384,7 @@ public static class EscSeqUtils
                 else
                 {
                     // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/2803
-                    // This is caused by NetDriver depending on Console.KeyAvailable?
+                    // This is caused by DotNetDriver depending on Console.KeyAvailable?
                     //throw new InvalidOperationException ("CSI response, but there's no terminator");
 
                     IncompleteCkInfos = cki;
@@ -1029,6 +1029,16 @@ public static class EscSeqUtils
         //}
     }
 
+    /// <summary>
+    /// Helper to set the Control key states based on the char.
+    /// </summary>
+    /// <param name="ch">The char value.</param>
+    /// <returns></returns>
+    public static ConsoleKeyInfo MapChar (char ch)
+    {
+        return MapConsoleKeyInfo (new (ch, ConsoleKey.None, false, false, false));
+    }
+
     /// <summary>
     ///     Ensures a console key is mapped to one that works correctly with ANSI escape sequences.
     /// </summary>
@@ -1131,6 +1141,17 @@ public static class EscSeqUtils
                                              true);
                 }
 
+                break;
+            case uint n when n is >= '\u001c'  and <= '\u001f':
+                key = (ConsoleKey)(char)(consoleKeyInfo.KeyChar + 24);
+
+                newConsoleKeyInfo = new (
+                                         (char)key,
+                                         key,
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Shift) != 0,
+                                         (consoleKeyInfo.Modifiers & ConsoleModifiers.Alt) != 0,
+                                         true);
+
                 break;
             case 127: // DEL
                 key = ConsoleKey.Backspace;
@@ -1375,6 +1396,12 @@ public static class EscSeqUtils
     {
         switch (keyInfo.Key)
         {
+            case ConsoleKey.Multiply:
+            case ConsoleKey.Add:
+            case ConsoleKey.Separator:
+            case ConsoleKey.Subtract:
+            case ConsoleKey.Decimal:
+            case ConsoleKey.Divide:
             case ConsoleKey.OemPeriod:
             case ConsoleKey.OemComma:
             case ConsoleKey.OemPlus:
@@ -1391,8 +1418,31 @@ public static class EscSeqUtils
             case ConsoleKey.Oem102:
                 if (keyInfo.KeyChar == 0)
                 {
-                    // If the keyChar is 0, keyInfo.Key value is not a printable character.
-                    System.Diagnostics.Debug.Assert (keyInfo.Key == 0);
+                    // All Oem* produce a valid KeyChar and is not guaranteed to be printable ASCII, but it’s never just '\0' (null).
+                    // If that happens it's because Console.ReadKey is misreporting for AltGr + non-character keys
+                    // or if it's a combine key waiting for the next input which will determine the respective KeyChar.
+                    // This behavior only happens on Windows and not on Unix-like systems.
+                    if (keyInfo.Key != ConsoleKey.Multiply
+                        && keyInfo.Key != ConsoleKey.Add
+                        && keyInfo.Key != ConsoleKey.Decimal
+                        && keyInfo.Key != ConsoleKey.Subtract
+                        && keyInfo.Key != ConsoleKey.Divide
+                        && keyInfo.Key != ConsoleKey.OemPeriod
+                        && keyInfo.Key != ConsoleKey.OemComma
+                        && keyInfo.Key != ConsoleKey.OemPlus
+                        && keyInfo.Key != ConsoleKey.OemMinus
+                        && keyInfo.Key != ConsoleKey.Oem1
+                        && keyInfo.Key != ConsoleKey.Oem2
+                        && keyInfo.Key != ConsoleKey.Oem3
+                        && keyInfo.Key != ConsoleKey.Oem4
+                        && keyInfo.Key != ConsoleKey.Oem5
+                        && keyInfo.Key != ConsoleKey.Oem6
+                        && keyInfo.Key != ConsoleKey.Oem7
+                        && keyInfo.Key != ConsoleKey.Oem102)
+                    {
+                        // If the keyChar is 0, keyInfo.Key value is not a printable character.
+                        System.Diagnostics.Debug.Assert (keyInfo.Key == 0);
+                    }
 
                     return KeyCode.Null; // MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode)keyInfo.Key);
                 }
@@ -1411,7 +1461,7 @@ public static class EscSeqUtils
         // Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
         if (keyInfo.Key != ConsoleKey.None && Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key))
         {
-            if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control) && keyInfo.Key == ConsoleKey.I)
+            if (keyInfo is { Modifiers: ConsoleModifiers.Control, Key: ConsoleKey.I })
             {
                 return KeyCode.Tab;
             }
@@ -1453,7 +1503,7 @@ public static class EscSeqUtils
             if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt)
                 || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
             {
-                // NetDriver doesn't support Shift-Ctrl/Shift-Alt combos
+                // DotNetDriver doesn't support Shift-Ctrl/Shift-Alt combos
                 return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.Key);
             }
 

+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/GenericHeld.cs → Terminal.Gui/Drivers/AnsiHandling/GenericHeld.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/IAnsiResponseParser.cs → Terminal.Gui/Drivers/AnsiHandling/IAnsiResponseParser.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/IHeld.cs → Terminal.Gui/Drivers/AnsiHandling/IHeld.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/Keyboard/AnsiKeyboardParser.cs → Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParser.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/Keyboard/AnsiKeyboardParserPattern.cs → Terminal.Gui/Drivers/AnsiHandling/Keyboard/AnsiKeyboardParserPattern.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/Keyboard/CsiCursorPattern.cs → Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiCursorPattern.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/Keyboard/CsiKeyPattern.cs → Terminal.Gui/Drivers/AnsiHandling/Keyboard/CsiKeyPattern.cs


+ 9 - 2
Terminal.Gui/Drivers/AnsiResponseParser/Keyboard/EscAsAltPattern.cs → Terminal.Gui/Drivers/AnsiHandling/Keyboard/EscAsAltPattern.cs

@@ -8,7 +8,7 @@ internal class EscAsAltPattern : AnsiKeyboardParserPattern
     public EscAsAltPattern () { IsLastMinute = true; }
 
 #pragma warning disable IDE1006 // Naming Styles
-    private static readonly Regex _pattern = new (@"^\u001b([a-zA-Z0-9_])$");
+    private static readonly Regex _pattern = new (@"^\u001b([\u0001-\u001a\u001fa-zA-Z0-9_])$");
 #pragma warning restore IDE1006 // Naming Styles
 
     public override bool IsMatch (string? input) { return _pattern.IsMatch (input!); }
@@ -22,7 +22,14 @@ internal class EscAsAltPattern : AnsiKeyboardParserPattern
             return null;
         }
 
-        char key = match.Groups [1].Value [0];
+        char ch = match.Groups [1].Value [0];
+
+        Key key = ch switch
+                  {
+                      >= '\u0001' and <= '\u001a' => ((Key)(ch + 96)).WithCtrl,
+                      '\u001f' => Key.D7.WithCtrl.WithShift,
+                      _ => ch
+                  };
 
         return new Key (key).WithAlt;
     }

+ 8 - 1
Terminal.Gui/Drivers/AnsiResponseParser/Keyboard/Ss3Pattern.cs → Terminal.Gui/Drivers/AnsiHandling/Keyboard/Ss3Pattern.cs

@@ -10,7 +10,7 @@ namespace Terminal.Gui.Drivers;
 public class Ss3Pattern : AnsiKeyboardParserPattern
 {
 #pragma warning disable IDE1006 // Naming Styles
-    private static readonly Regex _pattern = new (@"^\u001bO([PQRStDCAB])$");
+    private static readonly Regex _pattern = new (@"^\u001bO([PQRStDCABOHFwqysu])$");
 #pragma warning restore IDE1006 // Naming Styles
 
     /// <inheritdoc/>
@@ -41,6 +41,13 @@ public class Ss3Pattern : AnsiKeyboardParserPattern
                    'C' => Key.CursorRight,
                    'A' => Key.CursorUp,
                    'B' => Key.CursorDown,
+                   'H' => Key.Home,
+                   'F' => Key.End,
+                   'w' => Key.Home,
+                   'q' => Key.End,
+                   'y' => Key.PageUp,
+                   's' => Key.PageDown,
+                   'u' => Key.Clear,
                    _ => null
                };
     }

+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/ReasonCannotSend.cs → Terminal.Gui/Drivers/AnsiHandling/ReasonCannotSend.cs


+ 0 - 0
Terminal.Gui/Drivers/AnsiResponseParser/StringHeld.cs → Terminal.Gui/Drivers/AnsiHandling/StringHeld.cs


+ 0 - 0
Terminal.Gui/Drivers/V2/ComponentFactory.cs → Terminal.Gui/Drivers/ComponentFactory.cs


+ 8 - 25
Terminal.Gui/Drivers/ConsoleDriver.cs

@@ -6,9 +6,11 @@ namespace Terminal.Gui.Drivers;
 
 /// <summary>Base class for Terminal.Gui IConsoleDriver implementations.</summary>
 /// <remarks>
-///     There are currently four implementations: - <see cref="CursesDriver"/> (for Unix and Mac) -
-///     <see cref="WindowsDriver"/> - <see cref="NetDriver"/> that uses the .NET Console API - <see cref="FakeConsole"/>
-///     for unit testing.
+///     There are currently four implementations:
+/// - DotNetDriver that uses the .NET Console API and works on all platforms
+/// - UnixDriver optimized for Unix and Mac.
+/// - WindowsDriver optimized for Windows.
+/// - FakeDriver for unit testing.
 /// </remarks>
 public abstract class ConsoleDriver : IConsoleDriver
 {
@@ -32,7 +34,7 @@ public abstract class ConsoleDriver : IConsoleDriver
 
     #region ANSI Esc Sequence Handling
 
-    // QUESTION: This appears to be an API to help in debugging. It's only implemented in CursesDriver and WindowsDriver.
+    // QUESTION: This appears to be an API to help in debugging. It's only implemented in UnixDriver and WindowsDriver.
     // QUESTION: Can it be factored such that it does not contaminate the ConsoleDriver API?
     /// <summary>
     ///     Provide proper writing to send escape sequence recognized by the <see cref="ConsoleDriver"/>.
@@ -535,7 +537,7 @@ public abstract class ConsoleDriver : IConsoleDriver
     #endregion Cursor Handling
 
     /// <summary>Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.</summary>
-    /// <remarks>This is only implemented in <see cref="CursesDriver"/>.</remarks>
+    /// <remarks>This is only implemented in <see cref="UnixDriver"/>.</remarks>
     public abstract void Suspend ();
 
     /// <summary>Sets the position of the terminal cursor to <see cref="Col"/> and <see cref="Row"/>.</summary>
@@ -579,7 +581,6 @@ public abstract class ConsoleDriver : IConsoleDriver
         set => Application.Force16Colors = value || !SupportsTrueColor;
     }
 
-    private Attribute _currentAttribute;
     private int _cols;
     private int _rows;
 
@@ -587,22 +588,7 @@ public abstract class ConsoleDriver : IConsoleDriver
     ///     The <see cref="Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/>
     ///     call.
     /// </summary>
-    public Attribute CurrentAttribute
-    {
-        get => _currentAttribute;
-        set
-        {
-            // TODO: This makes IConsoleDriver dependent on Application, which is not ideal. Once Attribute.PlatformColor is removed, this can be fixed.
-            if (Application.Driver is { })
-            {
-                _currentAttribute = new (value.Foreground, value.Background, value.Style);
-
-                return;
-            }
-
-            _currentAttribute = value;
-        }
-    }
+    public Attribute CurrentAttribute { get; set; }
 
     /// <summary>Selects the specified attribute as the attribute to use for future calls to AddRune and AddString.</summary>
     /// <remarks>Implementations should call <c>base.SetAttribute(c)</c>.</remarks>
@@ -619,8 +605,6 @@ public abstract class ConsoleDriver : IConsoleDriver
     /// <returns>The current attribute.</returns>
     public Attribute GetAttribute () { return CurrentAttribute; }
 
-    // TODO: This is only overridden by CursesDriver. Once CursesDriver supports 24-bit color, this virtual method can be
-    // removed (and Attribute can lose the platformColor property).
     /// <summary>Makes an <see cref="Attribute"/>.</summary>
     /// <param name="foreground">The foreground color.</param>
     /// <param name="background">The background color.</param>
@@ -629,7 +613,6 @@ public abstract class ConsoleDriver : IConsoleDriver
     {
         // Encode the colors into the int value.
         return new (
-                    0xFF, // only used by cursesdriver!
                     foreground,
                     background
                    );

+ 6 - 14
Terminal.Gui/Drivers/V2/ConsoleDriverFacade.cs → Terminal.Gui/Drivers/ConsoleDriverFacade.cs

@@ -50,7 +50,9 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
     {
         if (FakeDriver.FakeBehaviors.UseFakeClipboard)
         {
-            Clipboard = new FakeDriver.FakeClipboard ();
+            Clipboard = new FakeDriver.FakeClipboard (
+                FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException,
+                FakeDriver.FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse);
 
             return;
         }
@@ -65,7 +67,7 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
         {
             Clipboard = new MacOSXClipboard ();
         }
-        else if (CursesDriver.Is_WSL_Platform ())
+        else if (PlatformDetection.IsWSLPlatform ())
         {
             Clipboard = new WSLClipboard ();
         }
@@ -260,18 +262,9 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
     /// <inheritdoc/>
     public virtual string GetVersionInfo ()
     {
-        var type = "";
-
-        if (InputProcessor is WindowsInputProcessor)
-        {
-            type = "win";
-        }
-        else if (InputProcessor is NetInputProcessor)
-        {
-            type = "net";
-        }
+        string type = InputProcessor.DriverName ?? throw new ArgumentNullException (nameof (InputProcessor.DriverName));
 
-        return "v2" + type;
+        return type;
     }
 
     /// <summary>Tests if the specified rune is supported by the driver.</summary>
@@ -397,7 +390,6 @@ internal class ConsoleDriverFacade<T> : IConsoleDriver, IConsoleDriverFacade
     {
         // TODO: what even is this? why Attribute constructor wants to call Driver method which must return an instance of Attribute? ?!?!?!
         return new (
-                    0xFF, // only used by cursesdriver!
                     foreground,
                     background
                    );

+ 0 - 0
Terminal.Gui/Drivers/V2/ConsoleInput.cs → Terminal.Gui/Drivers/ConsoleInput.cs


+ 0 - 1040
Terminal.Gui/Drivers/CursesDriver/CursesDriver.cs

@@ -1,1040 +0,0 @@
-#nullable enable
-//
-// Driver.cs: Curses-based Driver
-//
-
-using System.Runtime.InteropServices;
-using Unix.Terminal;
-
-namespace Terminal.Gui.Drivers;
-
-/// <summary>A Linux/Mac driver based on the Curses library.</summary>
-internal class CursesDriver : ConsoleDriver
-{
-    public override string GetVersionInfo () { return $"{Curses.curses_version ()}"; }
-
-    public override int Cols
-    {
-        get => Curses.Cols;
-        set
-        {
-            Curses.Cols = value;
-            ClearContents ();
-        }
-    }
-
-    public override int Rows
-    {
-        get => Curses.Lines;
-        set
-        {
-            Curses.Lines = value;
-            ClearContents ();
-        }
-    }
-
-    public override bool IsRuneSupported (Rune rune)
-    {
-        // See Issue #2615 - CursesDriver is broken with non-BMP characters
-        return base.IsRuneSupported (rune) && rune.IsBmp;
-    }
-
-    public override void Move (int col, int row)
-    {
-        base.Move (col, row);
-
-        if (RunningUnitTests)
-        {
-            return;
-        }
-
-        if (IsValidLocation (default, 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
-            Rectangle clipRect = Clip!.GetBounds ();
-            Curses.move (clipRect.Y, clipRect.X);
-        }
-    }
-
-    public void StartReportingMouseMoves ()
-    {
-        if (!RunningUnitTests)
-        {
-            Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
-        }
-    }
-
-    public void StopReportingMouseMoves ()
-    {
-        if (!RunningUnitTests)
-        {
-            Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
-        }
-    }
-
-
-    public override void Suspend ()
-    {
-        StopReportingMouseMoves ();
-
-        if (!RunningUnitTests)
-        {
-            Platform.Suspend ();
-        }
-
-        StartReportingMouseMoves ();
-    }
-
-    public override void UpdateCursor ()
-    {
-        EnsureCursorVisibility ();
-
-        if (!RunningUnitTests && Col >= 0 && Col < Cols && Row >= 0 && Row < Rows)
-        {
-            _mainLoopDriver?.WriteRaw (EscSeqUtils.CSI_SetCursorPosition (Row + 1, Col + 1));
-        }
-    }
-
-    public override bool UpdateScreen ()
-    {
-        bool updated = false;
-        if (RunningUnitTests
-            || Console.WindowHeight < 1
-            || Contents?.Length != Rows * Cols
-            || Rows != Console.WindowHeight)
-        {
-            return updated;
-        }
-
-        var top = 0;
-        var left = 0;
-        int rows = Rows;
-        int cols = Cols;
-        var output = new StringBuilder ();
-        Attribute? redrawAttr = null;
-        int lastCol = -1;
-
-        CursorVisibility? savedVisibility = _currentCursorVisibility;
-        SetCursorVisibility (CursorVisibility.Invisible);
-
-        for (int row = top; row < rows; row++)
-        {
-            if (Console.WindowHeight < 1)
-            {
-                return updated;
-            }
-
-            if (!_dirtyLines! [row])
-            {
-                continue;
-            }
-
-            if (!SetCursorPosition (0, row))
-            {
-                return updated;
-            }
-
-            updated = true;
-            _dirtyLines [row] = false;
-            output.Clear ();
-
-            for (int 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;
-                    }
-
-                    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;
-
-                        if (Force16Colors)
-                        {
-                            output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
-                            output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
-                        }
-                        else
-                        {
-                            output.Append (
-                                           EscSeqUtils.CSI_SetForegroundColorRGB (
-                                                                                  attr.Foreground.R,
-                                                                                  attr.Foreground.G,
-                                                                                  attr.Foreground.B
-                                                                                 )
-                                          );
-
-                            output.Append (
-                                           EscSeqUtils.CSI_SetBackgroundColorRGB (
-                                                                                  attr.Background.R,
-                                                                                  attr.Background.G,
-                                                                                  attr.Background.B
-                                                                                 )
-                                          );
-                        }
-                    }
-
-                    outputWidth++;
-                    Rune rune = Contents [row, col].Rune;
-                    output.Append (rune);
-
-                    if (Contents [row, col].CombiningMarks.Count > 0)
-                    {
-                        // AtlasEngine does not support NON-NORMALIZED combining marks in a way
-                        // compatible with the driver architecture. Any CMs (except in the first col)
-                        // are correctly combined with the base char, but are ALSO treated as 1 column
-                        // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é  ]`.
-                        // 
-                        // For now, we just ignore the list of CMs.
-                        //foreach (var combMark in Contents [row, col].CombiningMarks) {
-                        //	output.Append (combMark);
-                        //}
-                        // WriteToConsole (output, ref lastCol, row, ref outputWidth);
-                    }
-                    else if (rune.IsSurrogatePair () && rune.GetColumns () < 2)
-                    {
-                        WriteToConsole (output, ref lastCol, row, ref outputWidth);
-                        SetCursorPosition (col - 1, row);
-                    }
-
-                    Contents [row, col].IsDirty = false;
-                }
-            }
-
-            if (output.Length > 0)
-            {
-                SetCursorPosition (lastCol, row);
-                Console.Write (output);
-            }
-
-            foreach (var s in Application.Sixel)
-            {
-                if (!string.IsNullOrWhiteSpace (s.SixelData))
-                {
-                    SetCursorPosition (s.ScreenPosition.X, s.ScreenPosition.Y);
-                    Console.Write (s.SixelData);
-                }
-            }
-        }
-
-        SetCursorPosition (0, 0);
-
-        _currentCursorVisibility = savedVisibility;
-
-        void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
-        {
-            SetCursorPosition (lastCol, row);
-            Console.Write (output);
-            output.Clear ();
-            lastCol += outputWidth;
-            outputWidth = 0;
-        }
-
-        return updated;
-    }
-
-    #region Color Handling
-
-    public override bool SupportsTrueColor => true;
-
-    /// <summary>Creates an Attribute from the provided curses-based foreground and background color numbers</summary>
-    /// <param name="foreground">Contains the curses color number for the foreground (color, plus any attributes)</param>
-    /// <param name="background">Contains the curses color number for the background (color, plus any attributes)</param>
-    /// <returns></returns>
-    private static Attribute MakeColor (short foreground, short background)
-    {
-        //var v = (short)((ushort)foreground | (background << 4));
-        var v = (short)(((ushort)(foreground & 0xffff) << 16) | (background & 0xffff));
-
-        // TODO: for TrueColor - Use InitExtendedPair
-        Curses.InitColorPair (v, foreground, background);
-
-        return new (
-                    Curses.ColorPair (v),
-                    CursesColorNumberToColorName16 (foreground),
-                    CursesColorNumberToColorName16 (background)
-                   );
-    }
-
-    private static short ColorNameToCursesColorNumber (ColorName16 color)
-    {
-        switch (color)
-        {
-            case ColorName16.Black:
-                return Curses.COLOR_BLACK;
-            case ColorName16.Blue:
-                return Curses.COLOR_BLUE;
-            case ColorName16.Green:
-                return Curses.COLOR_GREEN;
-            case ColorName16.Cyan:
-                return Curses.COLOR_CYAN;
-            case ColorName16.Red:
-                return Curses.COLOR_RED;
-            case ColorName16.Magenta:
-                return Curses.COLOR_MAGENTA;
-            case ColorName16.Yellow:
-                return Curses.COLOR_YELLOW;
-            case ColorName16.Gray:
-                return Curses.COLOR_WHITE;
-            case ColorName16.DarkGray:
-                return Curses.COLOR_GRAY;
-            case ColorName16.BrightBlue:
-                return Curses.COLOR_BLUE | Curses.COLOR_GRAY;
-            case ColorName16.BrightGreen:
-                return Curses.COLOR_GREEN | Curses.COLOR_GRAY;
-            case ColorName16.BrightCyan:
-                return Curses.COLOR_CYAN | Curses.COLOR_GRAY;
-            case ColorName16.BrightRed:
-                return Curses.COLOR_RED | Curses.COLOR_GRAY;
-            case ColorName16.BrightMagenta:
-                return Curses.COLOR_MAGENTA | Curses.COLOR_GRAY;
-            case ColorName16.BrightYellow:
-                return Curses.COLOR_YELLOW | Curses.COLOR_GRAY;
-            case ColorName16.White:
-                return Curses.COLOR_WHITE | Curses.COLOR_GRAY;
-        }
-
-        throw new ArgumentException ("Invalid color code");
-    }
-
-    private static ColorName16 CursesColorNumberToColorName16 (short color)
-    {
-        switch (color)
-        {
-            case Curses.COLOR_BLACK:
-                return ColorName16.Black;
-            case Curses.COLOR_BLUE:
-                return ColorName16.Blue;
-            case Curses.COLOR_GREEN:
-                return ColorName16.Green;
-            case Curses.COLOR_CYAN:
-                return ColorName16.Cyan;
-            case Curses.COLOR_RED:
-                return ColorName16.Red;
-            case Curses.COLOR_MAGENTA:
-                return ColorName16.Magenta;
-            case Curses.COLOR_YELLOW:
-                return ColorName16.Yellow;
-            case Curses.COLOR_WHITE:
-                return ColorName16.Gray;
-            case Curses.COLOR_GRAY:
-                return ColorName16.DarkGray;
-            case Curses.COLOR_BLUE | Curses.COLOR_GRAY:
-                return ColorName16.BrightBlue;
-            case Curses.COLOR_GREEN | Curses.COLOR_GRAY:
-                return ColorName16.BrightGreen;
-            case Curses.COLOR_CYAN | Curses.COLOR_GRAY:
-                return ColorName16.BrightCyan;
-            case Curses.COLOR_RED | Curses.COLOR_GRAY:
-                return ColorName16.BrightRed;
-            case Curses.COLOR_MAGENTA | Curses.COLOR_GRAY:
-                return ColorName16.BrightMagenta;
-            case Curses.COLOR_YELLOW | Curses.COLOR_GRAY:
-                return ColorName16.BrightYellow;
-            case Curses.COLOR_WHITE | Curses.COLOR_GRAY:
-                return ColorName16.White;
-        }
-
-        throw new ArgumentException ("Invalid curses color code");
-    }
-
-    #endregion
-
-    private CursorVisibility? _currentCursorVisibility;
-    private CursorVisibility? _initialCursorVisibility;
-
-
-    private void EnsureCursorVisibility ()
-    {
-        if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows))
-        {
-            GetCursorVisibility (out CursorVisibility cursorVisibility);
-            _currentCursorVisibility = cursorVisibility;
-            SetCursorVisibility (CursorVisibility.Invisible);
-
-            return;
-        }
-
-        SetCursorVisibility (_currentCursorVisibility ?? CursorVisibility.Default);
-    }
-
-    /// <inheritdoc/>
-    public override bool GetCursorVisibility (out CursorVisibility visibility)
-    {
-        visibility = CursorVisibility.Invisible;
-
-        if (!_currentCursorVisibility.HasValue)
-        {
-            return false;
-        }
-
-        visibility = _currentCursorVisibility.Value;
-
-        return true;
-    }
-
-    private EscSeqUtils.DECSCUSR_Style? _currentDecscusrStyle;
-
-    /// <inheritdoc/>
-    public override bool SetCursorVisibility (CursorVisibility visibility)
-    {
-        if (_initialCursorVisibility.HasValue == false)
-        {
-            return false;
-        }
-
-        if (!RunningUnitTests)
-        {
-            Curses.curs_set (((int)visibility >> 16) & 0x000000FF);
-            Curses.leaveok (_window!.Handle, !Force16Colors);
-        }
-
-        if (visibility != CursorVisibility.Invisible)
-        {
-            if (_currentDecscusrStyle is null || _currentDecscusrStyle != (EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF))
-            {
-                _currentDecscusrStyle = (EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF);
-
-                _mainLoopDriver?.WriteRaw (
-                                           EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)_currentDecscusrStyle)
-                                          );
-            }
-        }
-
-        _currentCursorVisibility = visibility;
-
-        return true;
-    }
-
-    private bool SetCursorPosition (int col, int row)
-    {
-        // + 1 is needed because non-Windows is based on 1 instead of 0 and
-        // Console.CursorTop/CursorLeft isn't reliable.
-        Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1));
-
-        return true;
-    }
-
-    #region Init/End/MainLoop
-
-    private Curses.Window? _window;
-    private UnixMainLoop? _mainLoopDriver;
-    private object? _processInputToken;
-
-    public override MainLoop Init ()
-    {
-        _mainLoopDriver = new (this);
-
-        if (!RunningUnitTests)
-        {
-            _window = Curses.initscr ();
-            Curses.set_escdelay (10);
-
-            // Ensures that all procedures are performed at some previous closing.
-            Curses.doupdate ();
-
-            // 
-            // 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;
-
-                case 2:
-                    _currentCursorVisibility = _initialCursorVisibility = CursorVisibility.Box;
-                    Curses.curs_set (2);
-
-                    break;
-
-                default:
-                    _currentCursorVisibility = _initialCursorVisibility = null;
-
-                    break;
-            }
-
-            if (!Curses.HasColors)
-            {
-                throw new InvalidOperationException ("V2 - This should never happen. File an Issue if it does.");
-            }
-
-            Curses.raw ();
-            Curses.noecho ();
-
-            Curses.Window.Standard.keypad (true);
-
-            Curses.StartColor ();
-            Curses.UseDefaultColors ();
-
-            if (!RunningUnitTests)
-            {
-                Curses.timeout (0);
-            }
-
-            _processInputToken = _mainLoopDriver.AddWatch (
-                                                           0,
-                                                           UnixMainLoop.Condition.PollIn,
-                                                           x =>
-                                                           {
-                                                               ProcessInput ();
-
-                                                               return true;
-                                                           }
-                                                          );
-        }
-
-        CurrentAttribute = new (ColorName16.White, ColorName16.Black);
-
-        if (Environment.OSVersion.Platform == PlatformID.Win32NT)
-        {
-            Clipboard = new FakeDriver.FakeClipboard ();
-        }
-        else
-        {
-            if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX))
-            {
-                Clipboard = new MacOSXClipboard ();
-            }
-            else
-            {
-                if (Is_WSL_Platform ())
-                {
-                    Clipboard = new WSLClipboard ();
-                }
-                else
-                {
-                    Clipboard = new CursesClipboard ();
-                }
-            }
-        }
-
-        ClearContents ();
-        StartReportingMouseMoves ();
-
-        if (!RunningUnitTests)
-        {
-            Curses.CheckWinChange ();
-
-            // On Init this call is needed no mater Force16Colors or not
-            Curses.refresh ();
-
-            EscSeqUtils.ContinuousButtonPressed += EscSeqUtils_ContinuousButtonPressed;
-        }
-
-        return new (_mainLoopDriver);
-    }
-
-    private readonly AnsiResponseParser _parser = new ();
-    /// <inheritdoc />
-    internal override IAnsiResponseParser GetParser () => _parser;
-
-    internal void ProcessInput ()
-    {
-        int wch;
-        int code = Curses.get_wch (out wch);
-
-        //System.Diagnostics.Debug.WriteLine ($"code: {code}; wch: {wch}");
-        if (code == Curses.ERR)
-        {
-            return;
-        }
-
-        var k = KeyCode.Null;
-
-        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)
-                {
-                    Key? kea = null;
-
-                    ConsoleKeyInfo [] cki =
-                    {
-                        new ((char)KeyCode.Esc, 0, false, false, false),
-                        new ('[', 0, false, false, false),
-                        new ('<', 0, false, false, false)
-                    };
-                    code = 0;
-                    HandleEscSeqResponse (ref code, ref k, ref wch2, ref kea!, ref cki!);
-                }
-
-                return;
-            }
-
-            k = MapCursesKey (wch);
-
-            if (wch >= 277 && wch <= 288)
-            {
-                // Shift+(F1 - F12)
-                wch -= 12;
-                k = KeyCode.ShiftMask | MapCursesKey (wch);
-            }
-            else if (wch >= 289 && wch <= 300)
-            {
-                // Ctrl+(F1 - F12)
-                wch -= 24;
-                k = KeyCode.CtrlMask | MapCursesKey (wch);
-            }
-            else if (wch >= 301 && wch <= 312)
-            {
-                // Ctrl+Shift+(F1 - F12)
-                wch -= 36;
-                k = KeyCode.CtrlMask | KeyCode.ShiftMask | MapCursesKey (wch);
-            }
-            else if (wch >= 313 && wch <= 324)
-            {
-                // Alt+(F1 - F12)
-                wch -= 48;
-                k = KeyCode.AltMask | MapCursesKey (wch);
-            }
-            else if (wch >= 325 && wch <= 327)
-            {
-                // Shift+Alt+(F1 - F3)
-                wch -= 60;
-                k = KeyCode.ShiftMask | KeyCode.AltMask | MapCursesKey (wch);
-            }
-            else if (wch == 520) // Ctrl+Delete
-            {
-                k = KeyCode.CtrlMask | KeyCode.Delete;
-            }
-
-            OnKeyDown (new Key (k));
-            OnKeyUp (new Key (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);
-
-            if (code == Curses.KEY_CODE_YES)
-            {
-                k = KeyCode.AltMask | MapCursesKey (wch);
-            }
-
-            Key? key = null;
-
-            if (code == 0)
-            {
-                // The ESC-number handling, debatable.
-                // Simulates the AltMask itself by pressing Alt + Space.
-                // Needed for macOS
-                if (wch2 == (int)KeyCode.Space)
-                {
-                    k = KeyCode.AltMask | KeyCode.Space;
-                }
-                else if (wch2 - (int)KeyCode.Space >= (uint)KeyCode.A
-                         && wch2 - (int)KeyCode.Space <= (uint)KeyCode.Z)
-                {
-                    k = (KeyCode)((uint)KeyCode.AltMask + (wch2 - (int)KeyCode.Space));
-                }
-                else if (wch2 >= (uint)KeyCode.A - 64 && wch2 <= (uint)KeyCode.Z - 64)
-                {
-                    k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + (wch2 + 64));
-                }
-                else if (wch2 >= (uint)KeyCode.D0 && wch2 <= (uint)KeyCode.D9)
-                {
-                    k = (KeyCode)((uint)KeyCode.AltMask + (uint)KeyCode.D0 + (wch2 - (uint)KeyCode.D0));
-                }
-                else
-                {
-                    ConsoleKeyInfo [] cki =
-                    [
-                        new ((char)KeyCode.Esc, 0, false, false, false), new ((char)wch2, 0, false, false, false)
-                    ];
-                    HandleEscSeqResponse (ref code, ref k, ref wch2, ref key!, ref cki!);
-
-                    return;
-                }
-                //else if (wch2 == Curses.KeyCSI)
-                //{
-                //    ConsoleKeyInfo [] cki =
-                //    {
-                //        new ((char)KeyCode.Esc, 0, false, false, false), new ('[', 0, false, false, false)
-                //    };
-                //    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 (((KeyCode)wch2 & KeyCode.CtrlMask) != 0)
-                //    {
-                //        k = (KeyCode)((uint)KeyCode.CtrlMask + (wch2 & ~(int)KeyCode.CtrlMask));
-                //    }
-
-                //    if (wch2 == 0)
-                //    {
-                //        k = KeyCode.CtrlMask | KeyCode.AltMask | KeyCode.Space;
-                //    }
-                //    //else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z)
-                //    //{
-                //    //    k = KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.Space;
-                //    //}
-                //    else if (wch2 < 256)
-                //    {
-                //        k = (KeyCode)wch2; // | KeyCode.AltMask;
-                //    }
-                //    else
-                //    {
-                //        k = (KeyCode)((uint)(KeyCode.AltMask | KeyCode.CtrlMask) + wch2);
-                //    }
-                //}
-
-                key = new Key (k);
-            }
-            else
-            {
-                key = Key.Esc;
-            }
-
-            OnKeyDown (key);
-            OnKeyUp (key);
-        }
-        else if (wch == 8) // Ctrl+Backspace
-        {
-            k = KeyCode.Backspace | KeyCode.CtrlMask;
-            OnKeyDown (new Key (k));
-            OnKeyUp (new Key (k));
-        }
-        else if (wch == Curses.KeyTab)
-        {
-            k = MapCursesKey (wch);
-            OnKeyDown (new Key (k));
-            OnKeyUp (new Key (k));
-        }
-        else if (wch == 127)
-        {
-            // Backspace needed for macOS
-            k = KeyCode.Backspace;
-            OnKeyDown (new Key (k));
-            OnKeyUp (new Key (k));
-        }
-        else
-        {
-            // Unfortunately there are no way to differentiate Ctrl+alfa and Ctrl+Shift+alfa.
-            k = (KeyCode)wch;
-
-            if (wch == 0)
-            {
-                k = KeyCode.CtrlMask | KeyCode.Space;
-            }
-            else if (wch >= (uint)KeyCode.A - 64 && wch <= (uint)KeyCode.Z - 64)
-            {
-                if ((KeyCode)(wch + 64) != KeyCode.J)
-                {
-                    k = KeyCode.CtrlMask | (KeyCode)(wch + 64);
-                }
-            }
-            else if (wch >= (uint)KeyCode.A && wch <= (uint)KeyCode.Z)
-            {
-                k = (KeyCode)wch | KeyCode.ShiftMask;
-            }
-
-            if (wch == '\n' || wch == '\r')
-            {
-                k = KeyCode.Enter;
-            }
-
-            // Strip the KeyCode.Space flag off if it's set
-            //if (k != KeyCode.Space && k.HasFlag (KeyCode.Space))
-            if (Key.GetIsKeyCodeAtoZ (k) && (k & KeyCode.Space) != 0)
-            {
-                k &= ~KeyCode.Space;
-            }
-
-            if (IsValidInput (k, out k))
-            {
-                OnKeyDown (new (k));
-                OnKeyUp (new (k));
-            }
-        }
-    }
-
-    internal void ProcessWinChange ()
-    {
-        if (!RunningUnitTests && Curses.CheckWinChange ())
-        {
-            ClearContents ();
-            OnSizeChanged (new SizeChangedEventArgs (new (Cols, Rows)));
-        }
-    }
-    static string ConvertToString (ConsoleKeyInfo [] keyInfos)
-    {
-        char [] chars = new char [keyInfos.Length];
-        for (int i = 0; i < keyInfos.Length; i++)
-        {
-            chars [i] = keyInfos [i].KeyChar;
-        }
-        return new string (chars);
-    }
-
-    private void HandleEscSeqResponse (
-        ref int code,
-        ref KeyCode k,
-        ref int wch2,
-        ref Key keyEventArgs,
-        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)
-            {
-                // Give ansi parser a chance to deal with the escape sequence
-                if (cki != null && string.IsNullOrEmpty(_parser.ProcessInput (ConvertToString(cki))))
-                {
-                    // Parser fully consumed all keys meaning keys are processed - job done
-                    return;
-                }
-
-                // Ansi parser could not deal with it either because it is not expecting
-                // the given terminator (e.g. mouse) or did not understand format somehow.
-                // Carry on with the older code for processing curses escape codes
-
-                EscSeqUtils.DecodeEscSeq (
-                                          ref consoleKeyInfo,
-                                          ref ck,
-                                          cki!,
-                                          ref mod,
-                                          out _,
-                                          out _,
-                                          out _,
-                                          out _,
-                                          out bool isKeyMouse,
-                                          out List<MouseFlags> mouseFlags,
-                                          out Point pos,
-                                          out _,
-                                          EscSeqUtils.ProcessMouseEvent
-                                         );
-
-                if (isKeyMouse)
-                {
-                    foreach (MouseFlags mf in mouseFlags)
-                    {
-                        OnMouseEvent (new () { Flags = mf, Position = pos });
-                    }
-
-                    cki = null;
-
-                    if (wch2 == 27)
-                    {
-                        cki = EscSeqUtils.ResizeArray (
-                                                       new ConsoleKeyInfo (
-                                                                           (char)KeyCode.Esc,
-                                                                           0,
-                                                                           false,
-                                                                           false,
-                                                                           false
-                                                                          ),
-                                                       cki
-                                                      );
-                    }
-                }
-                else
-                {
-                    k = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
-                    keyEventArgs = new Key (k);
-                    OnKeyDown (keyEventArgs);
-                }
-            }
-            else
-            {
-                cki = EscSeqUtils.ResizeArray (consoleKeyInfo, cki);
-            }
-        }
-    }
-
-    private void EscSeqUtils_ContinuousButtonPressed (object? sender, MouseEventArgs e)
-    {
-        OnMouseEvent (e);
-    }
-
-    private static KeyCode MapCursesKey (int cursesKey)
-    {
-        switch (cursesKey)
-        {
-            case Curses.KeyF1: return KeyCode.F1;
-            case Curses.KeyF2: return KeyCode.F2;
-            case Curses.KeyF3: return KeyCode.F3;
-            case Curses.KeyF4: return KeyCode.F4;
-            case Curses.KeyF5: return KeyCode.F5;
-            case Curses.KeyF6: return KeyCode.F6;
-            case Curses.KeyF7: return KeyCode.F7;
-            case Curses.KeyF8: return KeyCode.F8;
-            case Curses.KeyF9: return KeyCode.F9;
-            case Curses.KeyF10: return KeyCode.F10;
-            case Curses.KeyF11: return KeyCode.F11;
-            case Curses.KeyF12: return KeyCode.F12;
-            case Curses.KeyUp: return KeyCode.CursorUp;
-            case Curses.KeyDown: return KeyCode.CursorDown;
-            case Curses.KeyLeft: return KeyCode.CursorLeft;
-            case Curses.KeyRight: return KeyCode.CursorRight;
-            case Curses.KeyHome: return KeyCode.Home;
-            case Curses.KeyEnd: return KeyCode.End;
-            case Curses.KeyNPage: return KeyCode.PageDown;
-            case Curses.KeyPPage: return KeyCode.PageUp;
-            case Curses.KeyDeleteChar: return KeyCode.Delete;
-            case Curses.KeyInsertChar: return KeyCode.Insert;
-            case Curses.KeyTab: return KeyCode.Tab;
-            case Curses.KeyBackTab: return KeyCode.Tab | KeyCode.ShiftMask;
-            case Curses.KeyBackspace: return KeyCode.Backspace;
-            case Curses.ShiftKeyUp: return KeyCode.CursorUp | KeyCode.ShiftMask;
-            case Curses.ShiftKeyDown: return KeyCode.CursorDown | KeyCode.ShiftMask;
-            case Curses.ShiftKeyLeft: return KeyCode.CursorLeft | KeyCode.ShiftMask;
-            case Curses.ShiftKeyRight: return KeyCode.CursorRight | KeyCode.ShiftMask;
-            case Curses.ShiftKeyHome: return KeyCode.Home | KeyCode.ShiftMask;
-            case Curses.ShiftKeyEnd: return KeyCode.End | KeyCode.ShiftMask;
-            case Curses.ShiftKeyNPage: return KeyCode.PageDown | KeyCode.ShiftMask;
-            case Curses.ShiftKeyPPage: return KeyCode.PageUp | KeyCode.ShiftMask;
-            case Curses.AltKeyUp: return KeyCode.CursorUp | KeyCode.AltMask;
-            case Curses.AltKeyDown: return KeyCode.CursorDown | KeyCode.AltMask;
-            case Curses.AltKeyLeft: return KeyCode.CursorLeft | KeyCode.AltMask;
-            case Curses.AltKeyRight: return KeyCode.CursorRight | KeyCode.AltMask;
-            case Curses.AltKeyHome: return KeyCode.Home | KeyCode.AltMask;
-            case Curses.AltKeyEnd: return KeyCode.End | KeyCode.AltMask;
-            case Curses.AltKeyNPage: return KeyCode.PageDown | KeyCode.AltMask;
-            case Curses.AltKeyPPage: return KeyCode.PageUp | KeyCode.AltMask;
-            case Curses.CtrlKeyUp: return KeyCode.CursorUp | KeyCode.CtrlMask;
-            case Curses.CtrlKeyDown: return KeyCode.CursorDown | KeyCode.CtrlMask;
-            case Curses.CtrlKeyLeft: return KeyCode.CursorLeft | KeyCode.CtrlMask;
-            case Curses.CtrlKeyRight: return KeyCode.CursorRight | KeyCode.CtrlMask;
-            case Curses.CtrlKeyHome: return KeyCode.Home | KeyCode.CtrlMask;
-            case Curses.CtrlKeyEnd: return KeyCode.End | KeyCode.CtrlMask;
-            case Curses.CtrlKeyNPage: return KeyCode.PageDown | KeyCode.CtrlMask;
-            case Curses.CtrlKeyPPage: return KeyCode.PageUp | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyUp: return KeyCode.CursorUp | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyDown: return KeyCode.CursorDown | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyLeft: return KeyCode.CursorLeft | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyRight: return KeyCode.CursorRight | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyHome: return KeyCode.Home | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyEnd: return KeyCode.End | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyNPage: return KeyCode.PageDown | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftCtrlKeyPPage: return KeyCode.PageUp | KeyCode.ShiftMask | KeyCode.CtrlMask;
-            case Curses.ShiftAltKeyUp: return KeyCode.CursorUp | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyDown: return KeyCode.CursorDown | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyLeft: return KeyCode.CursorLeft | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyRight: return KeyCode.CursorRight | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyNPage: return KeyCode.PageDown | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyPPage: return KeyCode.PageUp | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyHome: return KeyCode.Home | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.ShiftAltKeyEnd: return KeyCode.End | KeyCode.ShiftMask | KeyCode.AltMask;
-            case Curses.AltCtrlKeyNPage: return KeyCode.PageDown | KeyCode.AltMask | KeyCode.CtrlMask;
-            case Curses.AltCtrlKeyPPage: return KeyCode.PageUp | KeyCode.AltMask | KeyCode.CtrlMask;
-            case Curses.AltCtrlKeyHome: return KeyCode.Home | KeyCode.AltMask | KeyCode.CtrlMask;
-            case Curses.AltCtrlKeyEnd: return KeyCode.End | KeyCode.AltMask | KeyCode.CtrlMask;
-            default: return KeyCode.Null;
-        }
-    }
-
-    public override void End ()
-    {
-        EscSeqUtils.ContinuousButtonPressed -= EscSeqUtils_ContinuousButtonPressed;
-
-        StopReportingMouseMoves ();
-        SetCursorVisibility (CursorVisibility.Default);
-
-        if (_mainLoopDriver is { } && _processInputToken != null)
-        {
-            _mainLoopDriver.RemoveWatch (_processInputToken);
-        }
-
-        if (RunningUnitTests)
-        {
-            return;
-        }
-
-        // throws away any typeahead that has been typed by
-        // the user and has not yet been read by the program.
-        Curses.flushinp ();
-
-        Curses.endwin ();
-    }
-
-    #endregion Init/End/MainLoop
-
-    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;
-        //}
-        (int exitCode, string result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true);
-
-        if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL"))
-        {
-            return true;
-        }
-
-        return false;
-    }
-
-    /// <inheritdoc/>
-    public override void WriteRaw (string ansi) { _mainLoopDriver?.WriteRaw (ansi); }
-}

+ 0 - 5
Terminal.Gui/Drivers/CursesDriver/README.md

@@ -1,5 +0,0 @@
-This directory contains a copy of the MonoCurses binding from:
-
-http://github.com/mono/mono-curses
-
-The code has diverged from `mono-curses` a it's been leveraged for `Terminal.Gui`'s Curses driver.

+ 0 - 256
Terminal.Gui/Drivers/CursesDriver/UnixMainLoop.cs

@@ -1,256 +0,0 @@
-#nullable enable
-//
-// mainloop.cs: Linux/Curses MainLoop implementation.
-//
-
-using System.Runtime.InteropServices;
-using IMainLoopDriver = Terminal.Gui.App.IMainLoopDriver;
-using MainLoop = Terminal.Gui.App.MainLoop;
-
-namespace Terminal.Gui.Drivers;
-
-/// <summary>Unix main loop, suitable for using on Posix systems</summary>
-/// <remarks>
-///     In addition to the general functions of the MainLoop, the Unix version can watch file descriptors using the
-///     AddWatch methods.
-/// </remarks>
-internal class UnixMainLoop : IMainLoopDriver
-{
-    /// <summary>Condition on which to wake up from file descriptor activity.  These match the Linux/BSD poll definitions.</summary>
-    [Flags]
-    internal enum Condition : short
-    {
-        /// <summary>There is data to read</summary>
-        PollIn = 1,
-
-        /// <summary>Writing to the specified descriptor will not block</summary>
-        PollOut = 4,
-
-        /// <summary>There is urgent data to read</summary>
-        PollPri = 2,
-
-        /// <summary>Error condition on output</summary>
-        PollErr = 8,
-
-        /// <summary>Hang-up on output</summary>
-        PollHup = 16,
-
-        /// <summary>File descriptor is not open.</summary>
-        PollNval = 32
-    }
-
-    public const int KEY_RESIZE = unchecked((int)0xffffffffffffffff);
-    private static readonly nint _ignore = Marshal.AllocHGlobal (1);
-
-    private readonly CursesDriver _cursesDriver;
-    private readonly Dictionary<int, Watch> _descriptorWatchers = new ();
-    private readonly int [] _wakeUpPipes = new int [2];
-    private MainLoop? _mainLoop;
-    private bool _pollDirty = true;
-    private Pollfd []? _pollMap;
-    private bool _winChanged;
-
-    public UnixMainLoop (IConsoleDriver IConsoleDriver)
-    {
-        ArgumentNullException.ThrowIfNull (IConsoleDriver);
-
-        _cursesDriver = (CursesDriver)IConsoleDriver;
-    }
-
-    void IMainLoopDriver.Wakeup ()
-    {
-        if (!ConsoleDriver.RunningUnitTests)
-        {
-            write (_wakeUpPipes [1], _ignore, 1);
-        }
-    }
-
-    void IMainLoopDriver.Setup (MainLoop mainLoop)
-    {
-        _mainLoop = mainLoop;
-
-        if (ConsoleDriver.RunningUnitTests)
-        {
-            return;
-        }
-
-        try
-        {
-            pipe (_wakeUpPipes);
-
-            AddWatch (
-                      _wakeUpPipes [0],
-                      Condition.PollIn,
-                      _ =>
-                      {
-                          read (_wakeUpPipes [0], _ignore, 1);
-
-                          return true;
-                      }
-                     );
-        }
-        catch (DllNotFoundException e)
-        {
-            throw new NotSupportedException ("libc not found", e);
-        }
-    }
-
-    bool IMainLoopDriver.EventsPending ()
-    {
-        if (ConsoleDriver.RunningUnitTests)
-        {
-            return true;
-        }
-
-        UpdatePollMap ();
-
-        bool checkTimersResult = _mainLoop!.TimedEvents.CheckTimers (out int pollTimeout);
-
-        int n = poll (_pollMap!, (uint)_pollMap!.Length, pollTimeout);
-
-        if (n == KEY_RESIZE)
-        {
-            _winChanged = true;
-        }
-
-        return checkTimersResult || n >= KEY_RESIZE;
-    }
-
-    void IMainLoopDriver.Iteration ()
-    {
-        if (ConsoleDriver.RunningUnitTests)
-        {
-            return;
-        }
-
-        if (_winChanged)
-        {
-            _winChanged = false;
-            _cursesDriver.ProcessInput ();
-
-            // This is needed on the mac. See https://github.com/gui-cs/Terminal.Gui/pull/2922#discussion_r1365992426
-            _cursesDriver.ProcessWinChange ();
-        }
-
-        if (_pollMap is null)
-        {
-            return;
-        }
-
-        foreach (Pollfd p in _pollMap)
-        {
-            if (p.revents == 0)
-            {
-                continue;
-            }
-
-            if (!_descriptorWatchers.TryGetValue (p.fd, out Watch? watch))
-            {
-                continue;
-            }
-
-            if (!watch.Callback (_mainLoop!))
-            {
-                _descriptorWatchers.Remove (p.fd);
-            }
-        }
-    }
-
-    void IMainLoopDriver.TearDown ()
-    {
-        _descriptorWatchers.Clear ();
-
-        _mainLoop = null;
-    }
-
-    /// <summary>Watches a file descriptor for activity.</summary>
-    /// <remarks>
-    ///     When the condition is met, the provided callback is invoked.  If the callback returns false, the watch is
-    ///     automatically removed. The return value is a token that represents this watch, you can use this token to remove the
-    ///     watch by calling RemoveWatch.
-    /// </remarks>
-    internal object AddWatch (int fileDescriptor, Condition condition, Func<MainLoop, bool> callback)
-    {
-        ArgumentNullException.ThrowIfNull (callback);
-
-        var watch = new Watch { Condition = condition, Callback = callback, File = fileDescriptor };
-        _descriptorWatchers [fileDescriptor] = watch;
-        _pollDirty = true;
-
-        return watch;
-    }
-
-    /// <summary>Removes an active watch from the mainloop.</summary>
-    /// <remarks>The token parameter is the value returned from AddWatch</remarks>
-    internal void RemoveWatch (object token)
-    {
-        if (!ConsoleDriver.RunningUnitTests)
-        {
-            if (token is not Watch watch)
-            {
-                return;
-            }
-
-            _descriptorWatchers.Remove (watch.File);
-        }
-    }
-
-    private void UpdatePollMap ()
-    {
-        if (!_pollDirty)
-        {
-            return;
-        }
-
-        _pollDirty = false;
-
-        _pollMap = new Pollfd [_descriptorWatchers.Count];
-        var i = 0;
-
-        foreach (int fd in _descriptorWatchers.Keys)
-        {
-            _pollMap [i].fd = fd;
-            _pollMap [i].events = (short)_descriptorWatchers [fd].Condition;
-            i++;
-        }
-    }
-
-    internal void WriteRaw (string ansiRequest)
-    {
-        // Write to stdout (fd 1)
-        write (STDOUT_FILENO, ansiRequest, ansiRequest.Length);
-    }
-
-    [DllImport ("libc")]
-    private static extern int pipe ([In][Out] int [] pipes);
-
-    [DllImport ("libc")]
-    private static extern int poll ([In] [Out] Pollfd [] ufds, uint nfds, int timeout);
-
-    [DllImport ("libc")]
-    private static extern int read (int fd, nint buf, nint n);
-
-    [DllImport ("libc")]
-    private static extern int write (int fd, nint buf, nint n);
-
-    // File descriptor for stdout
-    private const int STDOUT_FILENO = 1;
-
-    [DllImport ("libc")]
-    private static extern int write (int fd, string buf, int n);
-
-    [StructLayout (LayoutKind.Sequential)]
-    private struct Pollfd
-    {
-        public int fd;
-        public short events;
-        public readonly short revents;
-    }
-
-    private class Watch
-    {
-        public Func<MainLoop, bool>? Callback;
-        public Condition Condition;
-        public int File;
-    }
-}

+ 0 - 95
Terminal.Gui/Drivers/CursesDriver/UnmanagedLibrary.cs

@@ -1,95 +0,0 @@
-// Copyright 2015 gRPC authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//	 http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-
-namespace Unix.Terminal;
-
-/// <summary>
-///     Represents a dynamically loaded unmanaged library in a (partially) platform independent manner. First, the
-///     native library is loaded using dlopen (on Unix systems) or using LoadLibrary (on Windows). dlsym or GetProcAddress
-///     are then used to obtain symbol addresses. <c>Marshal.GetDelegateForFunctionPointer</c> transforms the addresses
-///     into delegates to native methods. See
-///     http://stackoverflow.com/questions/13461989/p-invoke-to-dynamically-loaded-library-on-mono.
-/// </summary>
-internal class UnmanagedLibrary
-{
-    public readonly string LibraryPath;
-    public nint NativeLibraryHandle { get; }
-
-    //
-    // if isFullPath is set to true, the provided array of libraries are full paths
-    // and are tested for the file existing, otherwise the file is merely the name
-    // of the shared library that we pass to dlopen
-    //
-    public UnmanagedLibrary (string [] libraryPathAlternatives, bool isFullPath)
-    {
-        if (isFullPath)
-        {
-            foreach (string path in libraryPathAlternatives)
-            {
-                if (File.Exists (path))
-                {
-                    LibraryPath = path;
-                    break;
-                }
-            }
-
-            if (LibraryPath is null)
-                throw new FileNotFoundException ($"Error loading native library. Not found in any of the possible locations: {string.Join (",", libraryPathAlternatives)}");
-
-            NativeLibraryHandle = NativeLibrary.Load (LibraryPath);
-        }
-        else
-        {
-            foreach (string lib in libraryPathAlternatives)
-            {
-                NativeLibraryHandle = NativeLibrary.Load (lib);
-                if (NativeLibraryHandle != nint.Zero)
-                {
-                    LibraryPath = lib;
-
-                    break;
-                }
-            }
-        }
-
-        if (NativeLibraryHandle == nint.Zero)
-        {
-            throw new IOException ($"Error loading native library \"{string.Join (", ", libraryPathAlternatives)}\"");
-        }
-    }
-
-    /// <summary>Loads symbol in a platform specific way.</summary>
-    /// <param name="symbolName"></param>
-    /// <returns></returns>
-    public nint LoadSymbol (string symbolName)
-    {
-        return NativeLibrary.GetExport(NativeLibraryHandle, symbolName);
-    }
-
-    public T GetNativeMethodDelegate<T> (string methodName)
-        where T : class
-    {
-        nint ptr = LoadSymbol (methodName);
-
-        if (ptr == nint.Zero)
-        {
-            throw new MissingMethodException (string.Format ("The native method \"{0}\" does not exist", methodName));
-        }
-
-        return Marshal.GetDelegateForFunctionPointer<T> (ptr); // non-generic version is obsolete
-    }
-}

+ 0 - 746
Terminal.Gui/Drivers/CursesDriver/binding.cs

@@ -1,746 +0,0 @@
-//
-// TODO:
-// * FindNCurses needs to remove the old probing code
-// * Removal of that proxy code
-// * Need to implement reading pointers with the new API
-// * Can remove the manual Dlopen features
-// * initscr() diagnostics based on DLL can be fixed
-//
-// binding.cs.in: Core binding for curses.
-//
-// This file attempts to call into ncurses without relying on Mono's
-// dllmap, so it will work with .NET Core.  This means that it needs
-// two sets of bindings, one for "ncurses" which works on OSX, and one
-// that works against "libncursesw.so.5" which is what you find on
-// assorted Linux systems.
-//
-// Additionally, I do not want to rely on an external native library
-// which is why all this pain to bind two separate ncurses is here.
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-// Copyright (C) 2007 Novell (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System.Runtime.InteropServices;
-
-namespace Unix.Terminal;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-
-internal partial class Curses
-{
-    // We encode ESC + char (what Alt-char generates) as 0x2000 + char
-    public const int KeyAlt = 0x2000;
-    private static nint curses_handle, curscr_ptr, lines_ptr, cols_ptr;
-
-    // If true, uses the DllImport into "ncurses", otherwise "libncursesw.so.5"
-    //static bool use_naked_driver;
-    private static UnmanagedLibrary curses_library;
-    private static int lines, cols;
-    private static Window main_window;
-    private static NativeMethods methods;
-    private static char [] r = new char [1];
-    private static nint stdscr;
-    public static int ColorPairs => methods.COLOR_PAIRS ();
-
-    public static int Cols
-    {
-        get => cols;
-        internal set =>
-
-            // For unit tests
-            cols = value;
-    }
-
-    public static bool HasColors => methods.has_colors ();
-
-    public static int Lines
-    {
-        get => lines;
-        internal set =>
-
-            // For unit tests
-            lines = value;
-    }
-
-    //
-    // Have to wrap the native addch, as it can not
-    // display unicode characters, we have to use addstr
-    // for that.   but we need addch to render special ACS
-    // characters
-    //
-    public static int addch (int ch)
-    {
-        if (ch < 127 || ch > 0xffff)
-        {
-            return methods.addch (ch);
-        }
-
-        var c = (char)ch;
-
-        return addwstr (new string (c, 1));
-    }
-
-    public static int addstr (string format, params object [] args)
-    {
-        string s = string.Format (format, args);
-
-        return addwstr (s);
-    }
-
-    public static int addwstr (string s) { return methods.addwstr (s); }
-    public static int attroff (int attrs) { return methods.attroff (attrs); }
-
-    //static public int wechochar (IntPtr win, int ch) => methods.wechochar (win, ch);
-    public static int attron (int attrs) { return methods.attron (attrs); }
-    public static int attrset (int attrs) { return methods.attrset (attrs); }
-    public static int cbreak () { return methods.cbreak (); }
-
-    //
-    // Returns true if the window changed since the last invocation, as a
-    // side effect, the Lines and Cols properties are updated
-    //
-    public static bool CheckWinChange ()
-    {
-        int l, c;
-
-        console_sharp_get_dims (out l, out c);
-
-        if (l < 1)
-        {
-            l = 1;
-        }
-
-        if (l != lines || c != cols)
-        {
-            lines = l;
-            cols = c;
-
-            return true;
-        }
-
-        return false;
-    }
-
-    public static int clearok (nint win, bool bf) { return methods.clearok (win, bf); }
-    public static int COLOR_PAIRS () { return methods.COLOR_PAIRS (); }
-    public static int curs_set (int visibility) { return methods.curs_set (visibility); }
-
-    public static string curses_version ()
-    {
-        nint v = methods.curses_version ();
-
-        return $"{Marshal.PtrToStringAnsi (v)}, {curses_library.LibraryPath}";
-    }
-
-    public static int def_prog_mode () { return methods.def_prog_mode (); }
-    public static int def_shell_mode () { return methods.def_shell_mode (); }
-    public static int doupdate () { return methods.doupdate (); }
-    public static int echo () { return methods.echo (); }
-
-    //static public int addch (int ch) => methods.addch (ch);
-    public static int echochar (int ch) { return methods.echochar (ch); }
-
-    //
-    // The proxy methods to call into each version
-    //
-    public static int endwin () { return methods.endwin (); }
-    public static int flushinp () { return methods.flushinp (); }
-    public static int get_wch (out int sequence) { return methods.get_wch (out sequence); }
-    public static int getch () { return methods.getch (); }
-    public static uint getmouse (out MouseEvent ev) { return methods.getmouse (out ev); }
-    public static int halfdelay (int t) { return methods.halfdelay (t); }
-    public static bool has_colors () { return methods.has_colors (); }
-    public static void idcok (nint win, bool bf) { methods.idcok (win, bf); }
-    public static int idlok (nint win, bool bf) { return methods.idlok (win, bf); }
-    public static void immedok (nint win, bool bf) { methods.immedok (win, bf); }
-    public static int init_pair (short pair, short f, short b) { return methods.init_pair (pair, f, b); }
-
-    /// <summary>
-    ///     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.
-    /// </summary>
-    /// <param name="pair"></param>
-    /// <param name="foreground"></param>
-    /// <param name="background"></param>
-    /// <returns></returns>
-    public static int InitColorPair (short pair, short foreground, short background) { return methods.init_pair (pair, foreground, background); }
-
-    public static Window initscr ()
-    {
-        setlocale (LC_ALL, "");
-        FindNCurses ();
-
-        // Prevents the terminal from being locked after exiting.
-        reset_shell_mode ();
-
-        main_window = new Window (methods.initscr ());
-
-        try
-        {
-            console_sharp_get_dims (out lines, out cols);
-        }
-        catch (DllNotFoundException)
-        {
-            endwin ();
-
-            Console.Error.WriteLine (
-                                     "Unable to find the @MONO_CURSES@ native library\n"
-                                     + "this is different than the managed mono-curses.dll\n\n"
-                                     + "Typically you need to install to a LD_LIBRARY_PATH directory\n"
-                                     + "or DYLD_LIBRARY_PATH directory or run /sbin/ldconfig"
-                                    );
-            Environment.Exit (1);
-        }
-
-        //Console.Error.WriteLine ($"using curses {Curses.curses_version ()}");
-
-        return main_window;
-    }
-
-    public static int intrflush (nint win, bool bf) { return methods.intrflush (win, bf); }
-    public static bool is_term_resized (int lines, int columns) { return methods.is_term_resized (lines, columns); }
-
-    public static int IsAlt (int key)
-    {
-        if ((key & KeyAlt) != 0)
-        {
-            return key & ~KeyAlt;
-        }
-
-        return 0;
-    }
-
-    public static bool isendwin () { return methods.isendwin (); }
-    public static int keypad (nint win, bool bf) { return methods.keypad (win, bf); }
-    public static int leaveok (nint win, bool bf) { return methods.leaveok (win, bf); }
-    public static int meta (nint win, bool bf) { return methods.meta (win, bf); }
-    public static int mouseinterval (int interval) { return methods.mouseinterval (interval); }
-
-    public static Event mousemask (Event newmask, out Event oldmask)
-    {
-        nint e;
-        var ret = (Event)methods.mousemask ((nint)newmask, out e);
-        oldmask = (Event)e;
-
-        return ret;
-    }
-
-    public static int move (int line, int col) { return methods.move (line, col); }
-
-    public static int mvaddch (int y, int x, int ch)
-    {
-        if (ch < 127 || ch > 0xffff)
-        {
-            return methods.mvaddch (y, x, ch);
-        }
-
-        var c = (char)ch;
-
-        return mvaddwstr (y, x, new string (c, 1));
-    }
-
-    public static int mvaddwstr (int y, int x, string s) { return methods.mvaddwstr (y, x, s); }
-    public static int mvgetch (int y, int x) { return methods.mvgetch (y, x); }
-    public static int nl () { return methods.nl (); }
-    public static int nocbreak () { return methods.nocbreak (); }
-    public static int noecho () { return methods.noecho (); }
-    public static int nonl () { return methods.nonl (); }
-    public static void noqiflush () { methods.noqiflush (); }
-    public static int noraw () { return methods.noraw (); }
-    public static int notimeout (nint win, bool bf) { return methods.notimeout (win, bf); }
-    public static void qiflush () { methods.qiflush (); }
-    public static int raw () { return methods.raw (); }
-    public static int redrawwin (nint win) { return methods.redrawwin (win); }
-    public static int refresh () { return methods.refresh (); }
-    public static int reset_prog_mode () { return methods.reset_prog_mode (); }
-    public static int reset_shell_mode () { return methods.reset_shell_mode (); }
-    public static int resetty () { return methods.resetty (); }
-    public static int resize_term (int lines, int columns) { return methods.resize_term (lines, columns); }
-    public static int resizeterm (int lines, int columns) { return methods.resizeterm (lines, columns); }
-    public static int savetty () { return methods.savetty (); }
-    public static int scrollok (nint win, bool bf) { return methods.scrollok (win, bf); }
-    public static int set_escdelay (int size) { return methods.set_escdelay (size); }
-
-    [DllImport ("libc")]
-    public static extern int setlocale (int cate, [MarshalAs (UnmanagedType.LPStr)] string locale);
-
-    public static int setscrreg (int top, int bot) { return methods.setscrreg (top, bot); }
-    public static int start_color () { return methods.start_color (); }
-    public static int StartColor () { return methods.start_color (); }
-    public static int timeout (int delay) { return methods.timeout (delay); }
-    public static int typeahead (nint fd) { return methods.typeahead (fd); }
-    public static int ungetch (int ch) { return methods.ungetch (ch); }
-    public static uint ungetmouse (ref MouseEvent ev) { return methods.ungetmouse (ref ev); }
-    public static int use_default_colors () { return methods.use_default_colors (); }
-    public static void use_env (bool f) { methods.use_env (f); }
-
-    // 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 () { return methods.use_default_colors (); }
-    public static int waddch (nint win, int ch) { return methods.waddch (win, ch); }
-    public static int wmove (nint win, int line, int col) { return methods.wmove (win, line, col); }
-
-    //static public int wredrawwin (IntPtr win, int beg_line, int num_lines) => methods.wredrawwin (win, beg_line, num_lines);
-    public static int wnoutrefresh (nint win) { return methods.wnoutrefresh (win); }
-    public static int wrefresh (nint win) { return methods.wrefresh (win); }
-    public static int wsetscrreg (nint win, int top, int bot) { return methods.wsetscrreg (win, top, bot); }
-    public static int wtimeout (nint win, int delay) { return methods.wtimeout (win, delay); }
-    internal static nint console_sharp_get_curscr () { return Marshal.ReadIntPtr (curscr_ptr); }
-
-    internal static void console_sharp_get_dims (out int lines, out int cols)
-    {
-        lines = Marshal.ReadInt32 (lines_ptr);
-        cols = Marshal.ReadInt32 (cols_ptr);
-
-        //int cmd;
-        //if (UnmanagedLibrary.IsMacOSPlatform) {
-        //	cmd = TIOCGWINSZ_MAC;
-        //} else {
-        //	cmd = TIOCGWINSZ;
-        //}
-
-        //if (ioctl (1, cmd, out winsize ws) == 0) {
-        //	lines = ws.ws_row;
-        //	cols = ws.ws_col;
-
-        //	if (lines == Lines && cols == Cols) {
-        //		return;
-        //	}
-
-        //	resizeterm (lines, cols);
-        //} else {
-        //	lines = Lines;
-        //	cols = Cols;
-        //}
-    }
-
-    internal static nint console_sharp_get_stdscr () { return stdscr; }
-
-    internal static nint read_static_ptr (string key)
-    {
-        nint ptr = get_ptr (key);
-
-        return Marshal.ReadIntPtr (ptr);
-    }
-
-    private static void FindNCurses ()
-    {
-        LoadMethods ();
-        curses_handle = methods.UnmanagedLibrary.NativeLibraryHandle;
-
-        stdscr = read_static_ptr ("stdscr");
-        curscr_ptr = get_ptr ("curscr");
-        lines_ptr = get_ptr ("LINES");
-        cols_ptr = get_ptr ("COLS");
-    }
-
-    private static nint get_ptr (string key)
-    {
-        nint ptr = curses_library.LoadSymbol (key);
-
-        if (ptr == nint.Zero)
-        {
-            throw new Exception ("Could not load the key " + key);
-        }
-
-        return ptr;
-    }
-
-    //[DllImport ("libc")]
-    //public extern static int ioctl (int fd, int cmd, out winsize argp);
-
-    private static void LoadMethods ()
-    {
-        string [] libs = OperatingSystem.IsMacOS()
-                             ? ["libncurses.dylib"]
-                             : ["libncursesw.so.6", "libncursesw.so.5"];
-        var attempts = 1;
-
-        while (true)
-        {
-            try
-            {
-                curses_library = new UnmanagedLibrary (libs, false);
-                methods = new NativeMethods (curses_library);
-
-                break;
-            }
-            catch (Exception ex)
-            {
-                if (attempts == 1)
-                {
-                    attempts++;
-
-                    (int exitCode, string result) =
-                        ClipboardProcessRunner.Bash ("cat /etc/os-release", waitForOutput: true);
-
-                    if (exitCode == 0 && result.Contains ("opensuse"))
-                    {
-                        libs [0] = "libncursesw.so.5";
-                    }
-                }
-                else
-                {
-                    throw ex.GetBaseException ();
-                }
-            }
-        }
-    }
-
-    //[StructLayout (LayoutKind.Sequential)]
-    //public struct winsize {
-    //	public ushort ws_row;
-    //	public ushort ws_col;
-    //	public ushort ws_xpixel;   /* unused */
-    //	public ushort ws_ypixel;   /* unused */
-    //};
-
-    [StructLayout (LayoutKind.Sequential)]
-    internal struct MouseEvent
-    {
-        public short ID;
-        public int X, Y, Z;
-        public Event ButtonState;
-    }
-}
-
-#pragma warning disable RCS1102 // Make class static.'
-internal class Delegates
-{
-#pragma warning restore RCS1102 // Make class static.
-#pragma warning disable CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
-    public delegate nint initscr ();
-
-    public delegate int endwin ();
-
-    public delegate bool isendwin ();
-
-    public delegate int cbreak ();
-
-    public delegate int nocbreak ();
-
-    public delegate int echo ();
-
-    public delegate int noecho ();
-
-    public delegate int halfdelay (int t);
-
-    public delegate int raw ();
-
-    public delegate int noraw ();
-
-    public delegate void noqiflush ();
-
-    public delegate void qiflush ();
-
-    public delegate int typeahead (nint fd);
-
-    public delegate int timeout (int delay);
-
-    public delegate int wtimeout (nint win, int delay);
-
-    public delegate int notimeout (nint win, bool bf);
-
-    public delegate int keypad (nint win, bool bf);
-
-    public delegate int meta (nint win, bool bf);
-
-    public delegate int intrflush (nint win, bool bf);
-
-    public delegate int clearok (nint win, bool bf);
-
-    public delegate int idlok (nint win, bool bf);
-
-    public delegate void idcok (nint win, bool bf);
-
-    public delegate void immedok (nint win, bool bf);
-
-    public delegate int leaveok (nint win, bool bf);
-
-    public delegate int wsetscrreg (nint win, int top, int bot);
-
-    public delegate int scrollok (nint win, bool bf);
-
-    public delegate int nl ();
-
-    public delegate int nonl ();
-
-    public delegate int setscrreg (int top, int bot);
-
-    public delegate int refresh ();
-
-    public delegate int doupdate ();
-
-    public delegate int wrefresh (nint win);
-
-    public delegate int redrawwin (nint win);
-
-    //public delegate int wredrawwin (IntPtr win, int beg_line, int num_lines);
-    public delegate int wnoutrefresh (nint win);
-
-    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);
-
-    public delegate int wmove (nint win, int line, int col);
-
-    public delegate int waddch (nint win, int ch);
-
-    public delegate int attron (int attrs);
-
-    public delegate int attroff (int attrs);
-
-    public delegate int attrset (int attrs);
-
-    public delegate int getch ();
-
-    public delegate int get_wch (out int sequence);
-
-    public delegate int ungetch (int ch);
-
-    public delegate int mvgetch (int y, int x);
-
-    public delegate bool has_colors ();
-
-    public delegate int start_color ();
-
-    public delegate int init_pair (short pair, short f, short b);
-
-    public delegate int use_default_colors ();
-
-    public delegate int COLOR_PAIRS ();
-
-    public delegate uint getmouse (out Curses.MouseEvent ev);
-
-    public delegate uint ungetmouse (ref Curses.MouseEvent ev);
-
-    public delegate int mouseinterval (int interval);
-
-    public delegate nint mousemask (nint newmask, out nint oldMask);
-
-    public delegate bool is_term_resized (int lines, int columns);
-
-    public delegate int resize_term (int lines, int columns);
-
-    public delegate int resizeterm (int lines, int columns);
-
-    public delegate void use_env (bool f);
-
-    public delegate int flushinp ();
-
-    public delegate int def_prog_mode ();
-
-    public delegate int def_shell_mode ();
-
-    public delegate int reset_prog_mode ();
-
-    public delegate int reset_shell_mode ();
-
-    public delegate int savetty ();
-
-    public delegate int resetty ();
-
-    public delegate int set_escdelay (int size);
-
-    public delegate nint curses_version ();
-}
-
-internal class NativeMethods
-{
-    public readonly Delegates.addch addch;
-    public readonly Delegates.addwstr addwstr;
-    public readonly Delegates.attroff attroff;
-
-    //public readonly Delegates.wechochar wechochar;
-    public readonly Delegates.attron attron;
-    public readonly Delegates.attrset attrset;
-    public readonly Delegates.cbreak cbreak;
-    public readonly Delegates.clearok clearok;
-    public readonly Delegates.COLOR_PAIRS COLOR_PAIRS;
-    public readonly Delegates.curs_set curs_set;
-    public readonly Delegates.curses_version curses_version;
-    public readonly Delegates.def_prog_mode def_prog_mode;
-    public readonly Delegates.def_shell_mode def_shell_mode;
-    public readonly Delegates.doupdate doupdate;
-    public readonly Delegates.echo echo;
-    public readonly Delegates.echochar echochar;
-    public readonly Delegates.endwin endwin;
-    public readonly Delegates.flushinp flushinp;
-    public readonly Delegates.get_wch get_wch;
-    public readonly Delegates.getch getch;
-    public readonly Delegates.getmouse getmouse;
-    public readonly Delegates.halfdelay halfdelay;
-    public readonly Delegates.has_colors has_colors;
-    public readonly Delegates.idcok idcok;
-    public readonly Delegates.idlok idlok;
-    public readonly Delegates.immedok immedok;
-    public readonly Delegates.init_pair init_pair;
-    public readonly Delegates.initscr initscr;
-    public readonly Delegates.intrflush intrflush;
-    public readonly Delegates.is_term_resized is_term_resized;
-    public readonly Delegates.isendwin isendwin;
-    public readonly Delegates.keypad keypad;
-    public readonly Delegates.leaveok leaveok;
-    public readonly Delegates.meta meta;
-    public readonly Delegates.mouseinterval mouseinterval;
-    public readonly Delegates.mousemask mousemask;
-    public readonly Delegates.move move;
-    public readonly Delegates.mvaddch mvaddch;
-    public readonly Delegates.mvaddwstr mvaddwstr;
-    public readonly Delegates.mvgetch mvgetch;
-    public readonly Delegates.nl nl;
-    public readonly Delegates.nocbreak nocbreak;
-    public readonly Delegates.noecho noecho;
-    public readonly Delegates.nonl nonl;
-    public readonly Delegates.noqiflush noqiflush;
-    public readonly Delegates.noraw noraw;
-    public readonly Delegates.notimeout notimeout;
-    public readonly Delegates.qiflush qiflush;
-    public readonly Delegates.raw raw;
-    public readonly Delegates.redrawwin redrawwin;
-    public readonly Delegates.refresh refresh;
-    public readonly Delegates.reset_prog_mode reset_prog_mode;
-    public readonly Delegates.reset_shell_mode reset_shell_mode;
-    public readonly Delegates.resetty resetty;
-    public readonly Delegates.resize_term resize_term;
-    public readonly Delegates.resizeterm resizeterm;
-    public readonly Delegates.savetty savetty;
-    public readonly Delegates.scrollok scrollok;
-    public readonly Delegates.set_escdelay set_escdelay;
-    public readonly Delegates.setscrreg setscrreg;
-    public readonly Delegates.start_color start_color;
-    public readonly Delegates.timeout timeout;
-    public readonly Delegates.typeahead typeahead;
-    public readonly Delegates.ungetch ungetch;
-    public readonly Delegates.ungetmouse ungetmouse;
-    public readonly Delegates.use_default_colors use_default_colors;
-    public readonly Delegates.use_env use_env;
-    public readonly Delegates.waddch waddch;
-    public readonly Delegates.wmove wmove;
-
-    //public readonly Delegates.wredrawwin wredrawwin;
-    public readonly Delegates.wnoutrefresh wnoutrefresh;
-    public readonly Delegates.wrefresh wrefresh;
-    public readonly Delegates.wsetscrreg wsetscrreg;
-    public readonly Delegates.wtimeout wtimeout;
-    public UnmanagedLibrary UnmanagedLibrary;
-
-    public NativeMethods (UnmanagedLibrary lib)
-    {
-        UnmanagedLibrary = lib;
-        initscr = lib.GetNativeMethodDelegate<Delegates.initscr> ("initscr");
-        endwin = lib.GetNativeMethodDelegate<Delegates.endwin> ("endwin");
-        isendwin = lib.GetNativeMethodDelegate<Delegates.isendwin> ("isendwin");
-        cbreak = lib.GetNativeMethodDelegate<Delegates.cbreak> ("cbreak");
-        nocbreak = lib.GetNativeMethodDelegate<Delegates.nocbreak> ("nocbreak");
-        echo = lib.GetNativeMethodDelegate<Delegates.echo> ("echo");
-        noecho = lib.GetNativeMethodDelegate<Delegates.noecho> ("noecho");
-        halfdelay = lib.GetNativeMethodDelegate<Delegates.halfdelay> ("halfdelay");
-        raw = lib.GetNativeMethodDelegate<Delegates.raw> ("raw");
-        noraw = lib.GetNativeMethodDelegate<Delegates.noraw> ("noraw");
-        noqiflush = lib.GetNativeMethodDelegate<Delegates.noqiflush> ("noqiflush");
-        qiflush = lib.GetNativeMethodDelegate<Delegates.qiflush> ("qiflush");
-        typeahead = lib.GetNativeMethodDelegate<Delegates.typeahead> ("typeahead");
-        timeout = lib.GetNativeMethodDelegate<Delegates.timeout> ("timeout");
-        wtimeout = lib.GetNativeMethodDelegate<Delegates.wtimeout> ("wtimeout");
-        notimeout = lib.GetNativeMethodDelegate<Delegates.notimeout> ("notimeout");
-        keypad = lib.GetNativeMethodDelegate<Delegates.keypad> ("keypad");
-        meta = lib.GetNativeMethodDelegate<Delegates.meta> ("meta");
-        intrflush = lib.GetNativeMethodDelegate<Delegates.intrflush> ("intrflush");
-        clearok = lib.GetNativeMethodDelegate<Delegates.clearok> ("clearok");
-        idlok = lib.GetNativeMethodDelegate<Delegates.idlok> ("idlok");
-        idcok = lib.GetNativeMethodDelegate<Delegates.idcok> ("idcok");
-        immedok = lib.GetNativeMethodDelegate<Delegates.immedok> ("immedok");
-        leaveok = lib.GetNativeMethodDelegate<Delegates.leaveok> ("leaveok");
-        wsetscrreg = lib.GetNativeMethodDelegate<Delegates.wsetscrreg> ("wsetscrreg");
-        scrollok = lib.GetNativeMethodDelegate<Delegates.scrollok> ("scrollok");
-        nl = lib.GetNativeMethodDelegate<Delegates.nl> ("nl");
-        nonl = lib.GetNativeMethodDelegate<Delegates.nonl> ("nonl");
-        setscrreg = lib.GetNativeMethodDelegate<Delegates.setscrreg> ("setscrreg");
-        refresh = lib.GetNativeMethodDelegate<Delegates.refresh> ("refresh");
-        doupdate = lib.GetNativeMethodDelegate<Delegates.doupdate> ("doupdate");
-        wrefresh = lib.GetNativeMethodDelegate<Delegates.wrefresh> ("wrefresh");
-        redrawwin = lib.GetNativeMethodDelegate<Delegates.redrawwin> ("redrawwin");
-
-        //wredrawwin = lib.GetNativeMethodDelegate<Delegates.wredrawwin> ("wredrawwin");
-        wnoutrefresh = lib.GetNativeMethodDelegate<Delegates.wnoutrefresh> ("wnoutrefresh");
-        move = lib.GetNativeMethodDelegate<Delegates.move> ("move");
-        curs_set = lib.GetNativeMethodDelegate<Delegates.curs_set> ("curs_set");
-        addch = lib.GetNativeMethodDelegate<Delegates.addch> ("addch");
-        echochar = lib.GetNativeMethodDelegate<Delegates.echochar> ("echochar");
-        mvaddch = lib.GetNativeMethodDelegate<Delegates.mvaddch> ("mvaddch");
-        addwstr = lib.GetNativeMethodDelegate<Delegates.addwstr> ("addwstr");
-        mvaddwstr = lib.GetNativeMethodDelegate<Delegates.mvaddwstr> ("mvaddwstr");
-        wmove = lib.GetNativeMethodDelegate<Delegates.wmove> ("wmove");
-        waddch = lib.GetNativeMethodDelegate<Delegates.waddch> ("waddch");
-        attron = lib.GetNativeMethodDelegate<Delegates.attron> ("attron");
-        attroff = lib.GetNativeMethodDelegate<Delegates.attroff> ("attroff");
-        attrset = lib.GetNativeMethodDelegate<Delegates.attrset> ("attrset");
-        getch = lib.GetNativeMethodDelegate<Delegates.getch> ("getch");
-        get_wch = lib.GetNativeMethodDelegate<Delegates.get_wch> ("get_wch");
-        ungetch = lib.GetNativeMethodDelegate<Delegates.ungetch> ("ungetch");
-        mvgetch = lib.GetNativeMethodDelegate<Delegates.mvgetch> ("mvgetch");
-        has_colors = lib.GetNativeMethodDelegate<Delegates.has_colors> ("has_colors");
-        start_color = lib.GetNativeMethodDelegate<Delegates.start_color> ("start_color");
-        init_pair = lib.GetNativeMethodDelegate<Delegates.init_pair> ("init_pair");
-        use_default_colors = lib.GetNativeMethodDelegate<Delegates.use_default_colors> ("use_default_colors");
-        COLOR_PAIRS = lib.GetNativeMethodDelegate<Delegates.COLOR_PAIRS> ("COLOR_PAIRS");
-        getmouse = lib.GetNativeMethodDelegate<Delegates.getmouse> ("getmouse");
-        ungetmouse = lib.GetNativeMethodDelegate<Delegates.ungetmouse> ("ungetmouse");
-        mouseinterval = lib.GetNativeMethodDelegate<Delegates.mouseinterval> ("mouseinterval");
-        mousemask = lib.GetNativeMethodDelegate<Delegates.mousemask> ("mousemask");
-        is_term_resized = lib.GetNativeMethodDelegate<Delegates.is_term_resized> ("is_term_resized");
-        resize_term = lib.GetNativeMethodDelegate<Delegates.resize_term> ("resize_term");
-        resizeterm = lib.GetNativeMethodDelegate<Delegates.resizeterm> ("resizeterm");
-        use_env = lib.GetNativeMethodDelegate<Delegates.use_env> ("use_env");
-        flushinp = lib.GetNativeMethodDelegate<Delegates.flushinp> ("flushinp");
-        def_prog_mode = lib.GetNativeMethodDelegate<Delegates.def_prog_mode> ("def_prog_mode");
-        def_shell_mode = lib.GetNativeMethodDelegate<Delegates.def_shell_mode> ("def_shell_mode");
-        reset_prog_mode = lib.GetNativeMethodDelegate<Delegates.reset_prog_mode> ("reset_prog_mode");
-        reset_shell_mode = lib.GetNativeMethodDelegate<Delegates.reset_shell_mode> ("reset_shell_mode");
-        savetty = lib.GetNativeMethodDelegate<Delegates.savetty> ("savetty");
-        resetty = lib.GetNativeMethodDelegate<Delegates.resetty> ("resetty");
-        set_escdelay = lib.GetNativeMethodDelegate<Delegates.set_escdelay> ("set_escdelay");
-        curses_version = lib.GetNativeMethodDelegate<Delegates.curses_version> ("curses_version");
-    }
-}
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
-#pragma warning restore CS8981 // The type name only contains lower-cased ascii characters. Such names may become reserved for the language.

+ 0 - 177
Terminal.Gui/Drivers/CursesDriver/constants.cs

@@ -1,177 +0,0 @@
-/*
- * This file is autogenerated by the attrib.c program, do not edit
- */
-
-//#define XTERM1006
-
-using System.Runtime.InteropServices;
-
-namespace Unix.Terminal;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-internal partial class Curses
-{
-    public const int A_NORMAL = 0x0;
-    public const int A_STANDOUT = 0x10000;
-    public const int A_UNDERLINE = 0x20000;
-    public const int A_REVERSE = 0x40000;
-    public const int A_BLINK = 0x80000;
-    public const int A_DIM = 0x100000;
-    public const int A_BOLD = 0x200000;
-    public const int A_PROTECT = 0x1000000;
-    public const int A_INVIS = 0x800000;
-    public const int ACS_LLCORNER = 0x40006d;
-    public const int ACS_LRCORNER = 0x40006a;
-    public const int ACS_HLINE = 0x400071;
-    public const int ACS_ULCORNER = 0x40006c;
-    public const int ACS_URCORNER = 0x40006b;
-    public const int ACS_VLINE = 0x400078;
-    public const int ACS_LTEE = 0x400074;
-    public const int ACS_RTEE = 0x400075;
-    public const int ACS_BTEE = 0x400076;
-    public const int ACS_TTEE = 0x400077;
-    public const int ACS_PLUS = 0x40006e;
-    public const int ACS_S1 = 0x40006f;
-    public const int ACS_S9 = 0x400073;
-    public const int ACS_DIAMOND = 0x400060;
-    public const int ACS_CKBOARD = 0x400061;
-    public const int ACS_DEGREE = 0x400066;
-    public const int ACS_PLMINUS = 0x400067;
-    public const int ACS_BULLET = 0x40007e;
-    public const int ACS_LARROW = 0x40002c;
-    public const int ACS_RARROW = 0x40002b;
-    public const int ACS_DARROW = 0x40002e;
-    public const int ACS_UARROW = 0x40002d;
-    public const int ACS_BOARD = 0x400068;
-    public const int ACS_LANTERN = 0x400069;
-    public const int ACS_BLOCK = 0x400030;
-    public const int COLOR_BLACK = 0x0;
-    public const int COLOR_RED = 0x1;
-    public const int COLOR_GREEN = 0x2;
-    public const int COLOR_YELLOW = 0x3;
-    public const int COLOR_BLUE = 0x4;
-    public const int COLOR_MAGENTA = 0x5;
-    public const int COLOR_CYAN = 0x6;
-    public const int COLOR_WHITE = 0x7;
-    public const int COLOR_GRAY = 0x8;
-    public const int KEY_CODE_YES = 0x100;
-    public const int ERR = unchecked ((int)0xffffffff);
-    public const int TIOCGWINSZ = 0x5413;
-    public const int TIOCGWINSZ_MAC = 0x40087468;
-    [Flags]
-    internal enum Event : long
-    {
-        Button1Pressed = 0x2,
-        Button1Released = 0x1,
-        Button1Clicked = 0x4,
-        Button1DoubleClicked = 0x8,
-        Button1TripleClicked = 0x10,
-        Button2Pressed = 0x40,
-        Button2Released = 0x20,
-        Button2Clicked = 0x80,
-        Button2DoubleClicked = 0x100,
-        Button2TripleClicked = 0x200,
-        Button3Pressed = 0x800,
-        Button3Released = 0x400,
-        Button3Clicked = 0x1000,
-        Button3DoubleClicked = 0x2000,
-        Button3TripleClicked = 0x4000,
-        ButtonWheeledUp = 0x10000,
-        ButtonWheeledDown = 0x200000,
-        Button4Pressed = 0x80000,
-        Button4Released = 0x40000,
-        Button4Clicked = 0x100000,
-        Button4DoubleClicked = 0x20000,
-        Button4TripleClicked = 0x400000,
-        ButtonShift = 0x4000000,
-        ButtonCtrl = 0x2000000,
-        ButtonAlt = 0x8000000,
-        ReportMousePosition = 0x10000000,
-        AllEvents = 0x7ffffff
-    }
-#if XTERM1006
-    public const int LeftRightUpNPagePPage = unchecked ((int)0x8);
-    public const int DownEnd = unchecked ((int)0x6);
-    public const int Home = unchecked ((int)0x7);
-#else
-    public const int LeftRightUpNPagePPage = 0x0;
-    public const int DownEnd = 0x0;
-    public const int Home = 0x0;
-#endif
-    public const int KeyBackspace = 0x107;
-    public const int KeyUp = 0x103;
-    public const int KeyDown = 0x102;
-    public const int KeyLeft = 0x104;
-    public const int KeyRight = 0x105;
-    public const int KeyNPage = 0x152;
-    public const int KeyPPage = 0x153;
-    public const int KeyHome = 0x106;
-    public const int KeyMouse = 0x199;
-    public const int KeyEnd = 0x168;
-    public const int KeyDeleteChar = 0x14a;
-    public const int KeyInsertChar = 0x14b;
-    public const int KeyTab = 0x009;
-    public const int KeyBackTab = 0x161;
-    public const int KeyF1 = 0x109;
-    public const int KeyF2 = 0x10a;
-    public const int KeyF3 = 0x10b;
-    public const int KeyF4 = 0x10c;
-    public const int KeyF5 = 0x10d;
-    public const int KeyF6 = 0x10e;
-    public const int KeyF7 = 0x10f;
-    public const int KeyF8 = 0x110;
-    public const int KeyF9 = 0x111;
-    public const int KeyF10 = 0x112;
-    public const int KeyF11 = 0x113;
-    public const int KeyF12 = 0x114;
-    public const int KeyResize = 0x19a;
-    public const int ShiftKeyUp = 0x151;
-    public const int ShiftKeyDown = 0x150;
-    public const int ShiftKeyLeft = 0x189;
-    public const int ShiftKeyRight = 0x192;
-    public const int ShiftKeyNPage = 0x18c;
-    public const int ShiftKeyPPage = 0x18e;
-    public const int ShiftKeyHome = 0x187;
-    public const int ShiftKeyEnd = 0x182;
-    public const int AltKeyUp = unchecked (0x234 + LeftRightUpNPagePPage);
-    public const int AltKeyDown = unchecked (0x20b + DownEnd);
-    public const int AltKeyLeft = unchecked (0x21f + LeftRightUpNPagePPage);
-    public const int AltKeyRight = unchecked (0x22e + LeftRightUpNPagePPage);
-    public const int AltKeyNPage = unchecked (0x224 + LeftRightUpNPagePPage);
-    public const int AltKeyPPage = unchecked (0x229 + LeftRightUpNPagePPage);
-    public const int AltKeyHome = unchecked (0x215 + Home);
-    public const int AltKeyEnd = unchecked (0x210 + DownEnd);
-    public const int CtrlKeyUp = unchecked (0x236 + LeftRightUpNPagePPage);
-    public const int CtrlKeyDown = unchecked (0x20d + DownEnd);
-    public const int CtrlKeyLeft = unchecked (0x221 + LeftRightUpNPagePPage);
-    public const int CtrlKeyRight = unchecked (0x230 + LeftRightUpNPagePPage);
-    public const int CtrlKeyNPage = unchecked (0x226 + LeftRightUpNPagePPage);
-    public const int CtrlKeyPPage = unchecked (0x22b + LeftRightUpNPagePPage);
-    public const int CtrlKeyHome = unchecked (0x217 + Home);
-    public const int CtrlKeyEnd = unchecked (0x212 + DownEnd);
-    public const int ShiftCtrlKeyUp = unchecked (0x237 + LeftRightUpNPagePPage);
-    public const int ShiftCtrlKeyDown = unchecked (0x20e + DownEnd);
-    public const int ShiftCtrlKeyLeft = unchecked (0x222 + LeftRightUpNPagePPage);
-    public const int ShiftCtrlKeyRight = unchecked (0x231 + LeftRightUpNPagePPage);
-    public const int ShiftCtrlKeyNPage = unchecked (0x227 + LeftRightUpNPagePPage);
-    public const int ShiftCtrlKeyPPage = unchecked (0x22c + LeftRightUpNPagePPage);
-    public const int ShiftCtrlKeyHome = unchecked (0x218 + Home);
-    public const int ShiftCtrlKeyEnd = unchecked (0x213 + DownEnd);
-    public const int ShiftAltKeyUp = unchecked (0x235 + LeftRightUpNPagePPage);
-    public const int ShiftAltKeyDown = unchecked (0x20c + DownEnd);
-    public const int ShiftAltKeyLeft = unchecked (0x220 + LeftRightUpNPagePPage);
-    public const int ShiftAltKeyRight = unchecked (0x22f + LeftRightUpNPagePPage);
-    public const int ShiftAltKeyNPage = unchecked (0x225 + LeftRightUpNPagePPage);
-    public const int ShiftAltKeyPPage = unchecked (0x22a + LeftRightUpNPagePPage);
-    public const int ShiftAltKeyHome = unchecked (0x216 + Home);
-    public const int ShiftAltKeyEnd = unchecked (0x211 + DownEnd);
-    public const int AltCtrlKeyNPage = unchecked (0x228 + LeftRightUpNPagePPage);
-    public const int AltCtrlKeyPPage = unchecked (0x22d + LeftRightUpNPagePPage);
-    public const int AltCtrlKeyHome = unchecked (0x219 + Home);
-    public const int AltCtrlKeyEnd = unchecked (0x214 + DownEnd);
-
-    // see #949
-    public static int LC_ALL { get; }
-    static Curses () { LC_ALL = RuntimeInformation.IsOSPlatform (OSPlatform.OSX) ? 0 : 6; }
-    public static int ColorPair (int n) { return 0 + n * 256; }
-}
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

+ 0 - 86
Terminal.Gui/Drivers/CursesDriver/handles.cs

@@ -1,86 +0,0 @@
-//
-// handles.cs: OO wrappers for some curses objects
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-// Copyright (C) 2007 Novell (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-namespace Unix.Terminal;
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
-internal partial class Curses
-{
-    internal class Window
-    {
-        public readonly nint Handle;
-
-        static Window ()
-        {
-            initscr ();
-            Standard = new Window (console_sharp_get_stdscr ());
-            Current = new Window (console_sharp_get_curscr ());
-        }
-
-        internal Window (nint handle) { Handle = handle; }
-        public static Window Standard { get; }
-        public static Window Current { get; }
-        public int wtimeout (int delay) { return Curses.wtimeout (Handle, delay); }
-        public int notimeout (bool bf) { return Curses.notimeout (Handle, bf); }
-        public int keypad (bool bf) { return Curses.keypad (Handle, bf); }
-        public int meta (bool bf) { return Curses.meta (Handle, bf); }
-        public int intrflush (bool bf) { return Curses.intrflush (Handle, bf); }
-        public int clearok (bool bf) { return Curses.clearok (Handle, bf); }
-        public int idlok (bool bf) { return Curses.idlok (Handle, bf); }
-        public void idcok (bool bf) { Curses.idcok (Handle, bf); }
-        public void immedok (bool bf) { Curses.immedok (Handle, bf); }
-        public int leaveok (bool bf) { return Curses.leaveok (Handle, bf); }
-        public int setscrreg (int top, int bot) { return wsetscrreg (Handle, top, bot); }
-        public int scrollok (bool bf) { return Curses.scrollok (Handle, bf); }
-        public int wrefresh () { return Curses.wrefresh (Handle); }
-        public int redrawwin () { return Curses.redrawwin (Handle); }
-#if false
-			public int wredrawwin (int beg_line, int num_lines)
-			{
-				return Curses.wredrawwin (Handle, beg_line, num_lines);
-			}
-#endif
-        public int wnoutrefresh () { return Curses.wnoutrefresh (Handle); }
-        public int move (int line, int col) { return wmove (Handle, line, col); }
-        public int addch (char ch) { return waddch (Handle, ch); }
-
-        //public int echochar (char ch)
-        //{
-        //	return Curses.wechochar (Handle, ch);
-        //}
-        public int refresh () { return Curses.wrefresh (Handle); }
-    }
-
-    // Currently unused, to do later
-    internal class Screen
-    {
-        public readonly nint Handle;
-        internal Screen (nint handle) { Handle = handle; }
-    }
-
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
-}

+ 0 - 0
Terminal.Gui/Drivers/V2/INetInput.cs → Terminal.Gui/Drivers/DotNetDriver/INetInput.cs


+ 1 - 1
Terminal.Gui/Drivers/V2/NetComponentFactory.cs → Terminal.Gui/Drivers/DotNetDriver/NetComponentFactory.cs

@@ -4,7 +4,7 @@ using System.Collections.Concurrent;
 namespace Terminal.Gui.Drivers;
 
 /// <summary>
-/// <see cref="IComponentFactory{T}"/> implementation for native csharp console I/O i.e. v2net.
+/// <see cref="IComponentFactory{T}"/> implementation for native csharp console I/O i.e. dotnet.
 /// This factory creates instances of internal classes <see cref="NetInput"/>, <see cref="NetOutput"/> etc.
 /// </summary>
 public class NetComponentFactory : ComponentFactory<ConsoleKeyInfo>

+ 16 - 0
Terminal.Gui/Drivers/V2/NetInput.cs → Terminal.Gui/Drivers/DotNetDriver/NetInput.cs

@@ -70,10 +70,23 @@ public class NetInput : ConsoleInput<ConsoleKeyInfo>, INetInput
         }
     }
 
+    private void FlushConsoleInput ()
+    {
+        if (!ConsoleDriver.RunningUnitTests)
+        {
+            while (Console.KeyAvailable)
+            {
+                Console.ReadKey (intercept: true);
+            }
+        }
+    }
+
     /// <inheritdoc/>
     public override void Dispose ()
     {
         base.Dispose ();
+
+        // Disable mouse events first
         Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
 
         //Disable alternative screen buffer.
@@ -83,5 +96,8 @@ public class NetInput : ConsoleInput<ConsoleKeyInfo>, INetInput
         Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
 
         _adjustConsole?.Cleanup ();
+
+        // Flush any pending input so no stray events appear
+        FlushConsoleInput ();
     }
 }

+ 4 - 1
Terminal.Gui/Drivers/V2/NetInputProcessor.cs → Terminal.Gui/Drivers/DotNetDriver/NetInputProcessor.cs

@@ -20,7 +20,10 @@ public class NetInputProcessor : InputProcessor<ConsoleKeyInfo>
 #pragma warning restore CA2211
 
     /// <inheritdoc/>
-    public NetInputProcessor (ConcurrentQueue<ConsoleKeyInfo> inputBuffer) : base (inputBuffer, new NetKeyConverter ()) { }
+    public NetInputProcessor (ConcurrentQueue<ConsoleKeyInfo> inputBuffer) : base (inputBuffer, new NetKeyConverter ())
+    {
+        DriverName = "dotnet";
+    }
 
     /// <inheritdoc/>
     protected override void Process (ConsoleKeyInfo consoleKeyInfo)

+ 0 - 0
Terminal.Gui/Drivers/V2/NetKeyConverter.cs → Terminal.Gui/Drivers/DotNetDriver/NetKeyConverter.cs


+ 38 - 15
Terminal.Gui/Drivers/V2/NetOutput.cs → Terminal.Gui/Drivers/DotNetDriver/NetOutput.cs

@@ -54,24 +54,31 @@ public class NetOutput : OutputBase, IConsoleOutput
     /// <inheritdoc/>
     protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
     {
-        EscSeqUtils.CSI_AppendForegroundColorRGB (
-                                                  output,
-                                                  attr.Foreground.R,
-                                                  attr.Foreground.G,
-                                                  attr.Foreground.B
-                                                 );
-
-        EscSeqUtils.CSI_AppendBackgroundColorRGB (
-                                                  output,
-                                                  attr.Background.R,
-                                                  attr.Background.G,
-                                                  attr.Background.B
-                                                 );
+        if (Application.Force16Colors)
+        {
+            output.Append (EscSeqUtils.CSI_SetForegroundColor (attr.Foreground.GetAnsiColorCode ()));
+            output.Append (EscSeqUtils.CSI_SetBackgroundColor (attr.Background.GetAnsiColorCode ()));
+        }
+        else
+        {
+            EscSeqUtils.CSI_AppendForegroundColorRGB (
+                                                      output,
+                                                      attr.Foreground.R,
+                                                      attr.Foreground.G,
+                                                      attr.Foreground.B
+                                                     );
+
+            EscSeqUtils.CSI_AppendBackgroundColorRGB (
+                                                      output,
+                                                      attr.Background.R,
+                                                      attr.Background.G,
+                                                      attr.Background.B
+                                                     );
+        }
 
         EscSeqUtils.CSI_AppendTextStyleChange (output, redrawTextStyle, attr.Style);
     }
 
-
     /// <inheritdoc />
     protected override void Write (StringBuilder output)
     {
@@ -116,9 +123,25 @@ public class NetOutput : OutputBase, IConsoleOutput
     }
 
 
+    private EscSeqUtils.DECSCUSR_Style? _currentDecscusrStyle;
+
     /// <inheritdoc cref="IConsoleOutput.SetCursorVisibility"/>
     public override void SetCursorVisibility (CursorVisibility visibility)
     {
-        Console.Out.Write (visibility == CursorVisibility.Default ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
+        if (visibility != CursorVisibility.Invisible)
+        {
+            if (_currentDecscusrStyle is null || _currentDecscusrStyle != (EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF))
+            {
+                _currentDecscusrStyle = (EscSeqUtils.DECSCUSR_Style)(((int)visibility >> 24) & 0xFF);
+
+                Write (EscSeqUtils.CSI_SetCursorStyle ((EscSeqUtils.DECSCUSR_Style)_currentDecscusrStyle));
+            }
+
+            Write (EscSeqUtils.CSI_ShowCursor);
+        }
+        else
+        {
+            Write (EscSeqUtils.CSI_HideCursor);
+        }
     }
 }

+ 0 - 0
Terminal.Gui/Drivers/NetDriver/NetWinVTConsole.cs → Terminal.Gui/Drivers/DotNetDriver/NetWinVTConsole.cs


+ 49 - 0
Terminal.Gui/Drivers/FakeDriver/FakeComponentFactory.cs

@@ -0,0 +1,49 @@
+#nullable enable
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+/// <see cref="IComponentFactory{T}"/> implementation for fake/mock console I/O used in unit tests.
+/// This factory creates instances that simulate console behavior without requiring a real terminal.
+/// </summary>
+public class FakeComponentFactory : ComponentFactory<ConsoleKeyInfo>
+{
+    private readonly ConcurrentQueue<ConsoleKeyInfo>? _predefinedInput;
+    private readonly FakeConsoleOutput? _output;
+
+    /// <summary>
+    /// Creates a new FakeComponentFactory with optional predefined input and output capture.
+    /// </summary>
+    /// <param name="predefinedInput">Optional queue of predefined input events to simulate.</param>
+    /// <param name="output">Optional fake output to capture what would be written to console.</param>
+    public FakeComponentFactory (ConcurrentQueue<ConsoleKeyInfo>? predefinedInput = null, FakeConsoleOutput? output = null)
+    {
+        _predefinedInput = predefinedInput;
+        _output = output;
+    }
+
+    /// <inheritdoc/>
+    public override IConsoleInput<ConsoleKeyInfo> CreateInput ()
+    {
+        return new FakeConsoleInput (_predefinedInput);
+    }
+
+    /// <inheritdoc />
+    public override IConsoleOutput CreateOutput ()
+    {
+        return _output ?? new FakeConsoleOutput ();
+    }
+
+    /// <inheritdoc />
+    public override IInputProcessor CreateInputProcessor (ConcurrentQueue<ConsoleKeyInfo> inputBuffer)
+    {
+        return new NetInputProcessor (inputBuffer);
+    }
+
+    /// <inheritdoc />
+    public override IWindowSizeMonitor CreateWindowSizeMonitor (IConsoleOutput consoleOutput, IOutputBuffer outputBuffer)
+    {
+        return new FakeWindowSizeMonitor(consoleOutput, outputBuffer);
+    }
+}

+ 42 - 0
Terminal.Gui/Drivers/FakeDriver/FakeConsoleInput.cs

@@ -0,0 +1,42 @@
+#nullable enable
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+/// Fake console input for testing that can return predefined input or wait indefinitely.
+/// </summary>
+public class FakeConsoleInput : ConsoleInput<ConsoleKeyInfo>
+{
+    private readonly ConcurrentQueue<ConsoleKeyInfo>? _predefinedInput;
+
+    /// <summary>
+    /// Creates a new FakeConsoleInput with optional predefined input.
+    /// </summary>
+    /// <param name="predefinedInput">Optional queue of predefined input to return.</param>
+    public FakeConsoleInput (ConcurrentQueue<ConsoleKeyInfo>? predefinedInput = null)
+    {
+        _predefinedInput = predefinedInput;
+    }
+
+    /// <inheritdoc/>
+    protected override bool Peek ()
+    {
+        if (_predefinedInput != null && !_predefinedInput.IsEmpty)
+        {
+            return true;
+        }
+
+        // No input available
+        return false;
+    }
+
+    /// <inheritdoc/>
+    protected override IEnumerable<ConsoleKeyInfo> Read ()
+    {
+        if (_predefinedInput != null && _predefinedInput.TryDequeue (out ConsoleKeyInfo key))
+        {
+            yield return key;
+        }
+    }
+}

+ 88 - 0
Terminal.Gui/Drivers/FakeDriver/FakeConsoleOutput.cs

@@ -0,0 +1,88 @@
+#nullable enable
+using System;
+using System.Text;
+
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+/// Fake console output for testing that captures what would be written to the console.
+/// </summary>
+public class FakeConsoleOutput : OutputBase, IConsoleOutput
+{
+    private readonly StringBuilder _output = new ();
+    private int _cursorLeft;
+    private int _cursorTop;
+    private Size _windowSize = new (80, 25);
+
+    /// <summary>
+    /// Gets the captured output as a string.
+    /// </summary>
+    public string Output => _output.ToString ();
+
+    /// <summary>
+    /// Clears the captured output.
+    /// </summary>
+    public void ClearOutput () => _output.Clear ();
+
+    /// <inheritdoc/>
+    public void SetCursorPosition (int col, int row)
+    {
+        SetCursorPositionImpl (col, row);
+    }
+
+    /// <inheritdoc/>
+    protected override bool SetCursorPositionImpl (int col, int row)
+    {
+        _cursorLeft = col;
+        _cursorTop = row;
+        return true;
+    }
+
+    /// <summary>
+    /// Sets the fake window size.
+    /// </summary>
+    public void SetWindowSize (int width, int height)
+    {
+        _windowSize = new Size (width, height);
+    }
+
+    /// <summary>
+    /// Gets the current cursor position.
+    /// </summary>
+    public (int left, int top) GetCursorPosition () => (_cursorLeft, _cursorTop);
+
+    /// <inheritdoc/>
+    public Size GetWindowSize () => _windowSize;
+
+    /// <inheritdoc/>
+    public void Write (ReadOnlySpan<char> text)
+    {
+        _output.Append (text);
+    }
+
+    /// <inheritdoc/>
+    public override void SetCursorVisibility (CursorVisibility visibility)
+    {
+        // Capture but don't act on it in fake output
+    }
+
+    /// <inheritdoc/>
+    public void Dispose ()
+    {
+        // Nothing to dispose
+    }
+
+    /// <inheritdoc/>
+    protected override void AppendOrWriteAttribute (StringBuilder output, Attribute attr, TextStyle redrawTextStyle)
+    {
+        // For testing, we can skip the actual color/style output
+        // or capture it if needed for verification
+    }
+
+    /// <inheritdoc/>
+    protected override void Write (StringBuilder output)
+    {
+        _output.Append (output);
+    }
+
+}

+ 6 - 4
Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs

@@ -48,6 +48,9 @@ public class FakeDriver : ConsoleDriver
 
     public FakeDriver ()
     {
+        // FakeDriver implies UnitTests
+        RunningUnitTests = true;
+
         base.Cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;
         base.Rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT;
 
@@ -70,13 +73,13 @@ public class FakeDriver : ConsoleDriver
             }
             else
             {
-                if (CursesDriver.Is_WSL_Platform ())
+                if (PlatformDetection.IsWSLPlatform ())
                 {
                     Clipboard = new WSLClipboard ();
                 }
                 else
                 {
-                    Clipboard = new CursesClipboard ();
+                    Clipboard = new UnixClipboard ();
                 }
             }
         }
@@ -235,7 +238,7 @@ public class FakeDriver : ConsoleDriver
     #region Color Handling
 
     ///// <remarks>
-    ///// In the FakeDriver, colors are encoded as an int; same as NetDriver
+    ///// In the FakeDriver, colors are encoded as an int; same as DotNetDriver
     ///// However, the foreground color is stored in the most significant 16 bits, 
     ///// and the background color is stored in the least significant 16 bits.
     ///// </remarks>
@@ -243,7 +246,6 @@ public class FakeDriver : ConsoleDriver
     //{
     //	// Encode the colors into the int value.
     //	return new Attribute (
-    //		platformColor: 0,//((((int)foreground.ColorName) & 0xffff) << 16) | (((int)background.ColorName) & 0xffff),
     //		foreground: foreground,
     //		background: background
     //	);

+ 41 - 0
Terminal.Gui/Drivers/FakeDriver/FakeWindowSizeMonitor.cs

@@ -0,0 +1,41 @@
+using Microsoft.Extensions.Logging;
+
+namespace Terminal.Gui.Drivers;
+
+internal class FakeWindowSizeMonitor (IConsoleOutput consoleOut, IOutputBuffer outputBuffer) : IWindowSizeMonitor
+{
+    private Size _lastSize = new (0, 0);
+
+    /// <summary>Invoked when the terminal's size changed. The new size of the terminal is provided.</summary>
+    public event EventHandler<SizeChangedEventArgs> SizeChanging;
+
+    /// <summary>Raises the <see cref="SizeChanging"/> event with the specified size. Used for testing.</summary>
+    /// <param name="newSize">The new size to report.</param>
+    public void RaiseSizeChanging (Size newSize)
+    {
+        SizeChanging?.Invoke (this, new (newSize));
+    }
+
+    /// <inheritdoc/>
+    public bool Poll ()
+    {
+        if (ConsoleDriver.RunningUnitTests)
+        {
+            return false;
+        }
+
+        Size size = consoleOut.GetWindowSize ();
+
+        if (size != _lastSize)
+        {
+            Logging.Logger.LogInformation ($"Console size changes from '{_lastSize}' to {size}");
+            outputBuffer.SetWindowSize (size.Width, size.Height);
+            _lastSize = size;
+            SizeChanging?.Invoke (this, new (size));
+
+            return true;
+        }
+
+        return false;
+    }
+}

+ 1 - 0
Terminal.Gui/Drivers/V2/IComponentFactory.cs → Terminal.Gui/Drivers/IComponentFactory.cs

@@ -1,5 +1,6 @@
 #nullable enable
 using System.Collections.Concurrent;
+using Terminal.Gui.App;
 
 namespace Terminal.Gui.Drivers;
 

+ 3 - 3
Terminal.Gui/Drivers/IConsoleDriver.cs

@@ -4,8 +4,8 @@ namespace Terminal.Gui.Drivers;
 
 /// <summary>Base interface for Terminal.Gui ConsoleDriver implementations.</summary>
 /// <remarks>
-///     There are currently four implementations: - <see cref="CursesDriver"/> (for Unix and Mac) -
-///     <see cref="WindowsDriver"/> - <see cref="NetDriver"/> that uses the .NET Console API - <see cref="FakeConsole"/>
+///     There are currently four implementations: - <see cref="UnixDriver"/> (for Unix and Mac) -
+///     <see cref="WindowsDriver"/> - <see cref="DotNetDriver"/> that uses the .NET Console API - <see cref="FakeConsole"/>
 ///     for unit testing.
 /// </remarks>
 public interface IConsoleDriver
@@ -206,7 +206,7 @@ public interface IConsoleDriver
     event EventHandler<SizeChangedEventArgs>? SizeChanged;
 
     /// <summary>Suspends the application (e.g. on Linux via SIGTSTP) and upon resume, resets the console driver.</summary>
-    /// <remarks>This is only implemented in <see cref="CursesDriver"/>.</remarks>
+    /// <remarks>This is only implemented in <see cref="UnixDriver"/>.</remarks>
     void Suspend ();
 
     /// <summary>

+ 0 - 0
Terminal.Gui/Drivers/V2/IConsoleDriverFacade.cs → Terminal.Gui/Drivers/IConsoleDriverFacade.cs


+ 0 - 0
Terminal.Gui/Drivers/V2/IConsoleInput.cs → Terminal.Gui/Drivers/IConsoleInput.cs


+ 0 - 0
Terminal.Gui/Drivers/V2/IConsoleOutput.cs → Terminal.Gui/Drivers/IConsoleOutput.cs


+ 5 - 0
Terminal.Gui/Drivers/V2/IInputProcessor.cs → Terminal.Gui/Drivers/IInputProcessor.cs

@@ -25,6 +25,11 @@ public interface IInputProcessor
     /// <summary>Event fired when a mouse event occurs.</summary>
     event EventHandler<MouseEventArgs>? MouseEvent;
 
+    /// <summary>
+    /// Gets the name of the driver associated with this input processor.
+    /// </summary>
+    string DriverName { get; init; }
+
     /// <summary>
     ///     Called when a key is pressed down. Fires the <see cref="KeyDown"/> event. This is a precursor to
     ///     <see cref="OnKeyUp"/>.

+ 0 - 0
Terminal.Gui/Drivers/V2/IKeyConverter.cs → Terminal.Gui/Drivers/IKeyConverter.cs


+ 0 - 0
Terminal.Gui/Drivers/V2/IOutputBuffer.cs → Terminal.Gui/Drivers/IOutputBuffer.cs


+ 0 - 0
Terminal.Gui/Drivers/V2/IWindowSizeMonitor.cs → Terminal.Gui/Drivers/IWindowSizeMonitor.cs


+ 3 - 0
Terminal.Gui/Drivers/V2/InputProcessor.cs → Terminal.Gui/Drivers/InputProcessor.cs

@@ -30,6 +30,9 @@ public abstract class InputProcessor<T> : IInputProcessor
     /// </summary>
     public ConcurrentQueue<T> InputBuffer { get; }
 
+    /// <inheritdoc />
+    public string DriverName { get; init; }
+
     /// <inheritdoc/>
     public IAnsiResponseParser GetParser () { return Parser; }
 

+ 0 - 0
Terminal.Gui/Drivers/V2/MouseButtonStateEx.cs → Terminal.Gui/Drivers/MouseButtonStateEx.cs


+ 0 - 0
Terminal.Gui/Drivers/V2/MouseInterpreter.cs → Terminal.Gui/Drivers/MouseInterpreter.cs


+ 0 - 739
Terminal.Gui/Drivers/NetDriver/NetDriver.cs

@@ -1,739 +0,0 @@
-#nullable enable
-//
-// NetDriver.cs: The System.Console-based .NET driver, works on Windows and Unix, but is not particularly efficient.
-//
-
-using System.Runtime.InteropServices;
-using static Terminal.Gui.Drivers.NetEvents;
-
-namespace Terminal.Gui.Drivers;
-
-internal class NetDriver : ConsoleDriver
-{
-
-    public bool IsWinPlatform { get; private set; }
-    public NetWinVTConsole? NetWinConsole { get; private set; }
-
-
-    public override void Suspend ()
-    {
-        if (Environment.OSVersion.Platform != PlatformID.Unix)
-        {
-            return;
-        }
-
-        StopReportingMouseMoves ();
-
-        if (!RunningUnitTests)
-        {
-            Console.ResetColor ();
-            Console.Clear ();
-
-            //Disable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
-
-            //Set cursor key to cursor.
-            Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
-
-            Platform.Suspend ();
-
-            //Enable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
-
-            SetContentsAsDirty ();
-            Refresh ();
-        }
-
-        StartReportingMouseMoves ();
-    }
-
-    public override bool UpdateScreen ()
-    {
-        bool updated = false;
-        if (RunningUnitTests
-            || _winSizeChanging
-            || Console.WindowHeight < 1
-            || Contents?.Length != Rows * Cols
-            || Rows != Console.WindowHeight)
-        {
-            return updated;
-        }
-
-        var top = 0;
-        var left = 0;
-        int rows = Rows;
-        int cols = Cols;
-        var output = new StringBuilder ();
-        Attribute? redrawAttr = null;
-        int lastCol = -1;
-
-        CursorVisibility? savedVisibility = _cachedCursorVisibility;
-        SetCursorVisibility (CursorVisibility.Invisible);
-
-        for (int row = top; row < rows; row++)
-        {
-            if (Console.WindowHeight < 1)
-            {
-                return updated;
-            }
-
-            if (!_dirtyLines! [row])
-            {
-                continue;
-            }
-
-            if (!SetCursorPosition (0, row))
-            {
-                return updated;
-            }
-
-            updated = true;
-            _dirtyLines [row] = false;
-            output.Clear ();
-
-            for (int 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;
-                    }
-
-                    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;
-
-                        if (Force16Colors)
-                        {
-                            output.Append (
-                                           EscSeqUtils.CSI_SetGraphicsRendition (
-                                                                                 MapColors (
-                                                                                            (ConsoleColor)attr.Background.GetClosestNamedColor16 (),
-                                                                                            false
-                                                                                           ),
-                                                                                 MapColors ((ConsoleColor)attr.Foreground.GetClosestNamedColor16 ())
-                                                                                )
-                                          );
-                        }
-                        else
-                        {
-                            output.Append (
-                                           EscSeqUtils.CSI_SetForegroundColorRGB (
-                                                                                  attr.Foreground.R,
-                                                                                  attr.Foreground.G,
-                                                                                  attr.Foreground.B
-                                                                                 )
-                                          );
-
-                            output.Append (
-                                           EscSeqUtils.CSI_SetBackgroundColorRGB (
-                                                                                  attr.Background.R,
-                                                                                  attr.Background.G,
-                                                                                  attr.Background.B
-                                                                                 )
-                                          );
-                        }
-                    }
-
-                    outputWidth++;
-                    Rune rune = Contents [row, col].Rune;
-                    output.Append (rune);
-
-                    if (Contents [row, col].CombiningMarks.Count > 0)
-                    {
-                        // AtlasEngine does not support NON-NORMALIZED combining marks in a way
-                        // compatible with the driver architecture. Any CMs (except in the first col)
-                        // are correctly combined with the base char, but are ALSO treated as 1 column
-                        // width codepoints E.g. `echo "[e`u{0301}`u{0301}]"` will output `[é  ]`.
-                        // 
-                        // For now, we just ignore the list of CMs.
-                        //foreach (var combMark in Contents [row, col].CombiningMarks) {
-                        //	output.Append (combMark);
-                        //}
-                        // WriteToConsole (output, ref lastCol, row, ref outputWidth);
-                    }
-                    else if (rune.IsSurrogatePair () && rune.GetColumns () < 2)
-                    {
-                        WriteToConsole (output, ref lastCol, row, ref outputWidth);
-                        SetCursorPosition (col - 1, row);
-                    }
-
-                    Contents [row, col].IsDirty = false;
-                }
-            }
-
-            if (output.Length > 0)
-            {
-                SetCursorPosition (lastCol, row);
-                Console.Write (output);
-            }
-
-            foreach (var s in Application.Sixel)
-            {
-                if (!string.IsNullOrWhiteSpace (s.SixelData))
-                {
-                    SetCursorPosition (s.ScreenPosition.X, s.ScreenPosition.Y);
-                    Console.Write (s.SixelData);
-                }
-            }
-        }
-
-        SetCursorPosition (0, 0);
-
-        _cachedCursorVisibility = savedVisibility;
-
-        void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int outputWidth)
-        {
-            SetCursorPosition (lastCol, row);
-            Console.Write (output);
-            output.Clear ();
-            lastCol += outputWidth;
-            outputWidth = 0;
-        }
-
-        return updated;
-    }
-    #region Init/End/MainLoop
-
-    // BUGBUG: Fix this nullable issue.
-    /// <inheritdoc />
-    internal override IAnsiResponseParser GetParser () => _mainLoopDriver!._netEvents!.Parser;
-    internal NetMainLoop? _mainLoopDriver;
-
-    /// <inheritdoc />
-    public override MainLoop Init ()
-    {
-        Console.OutputEncoding = Encoding.UTF8;
-
-        PlatformID 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.
-            }
-        }
-
-        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 ();
-            }
-        }
-
-        if (!RunningUnitTests)
-        {
-            Console.TreatControlCAsInput = true;
-
-            Cols = Console.WindowWidth;
-            Rows = Console.WindowHeight;
-
-            //Enable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
-
-            //Set cursor key to application.
-            Console.Out.Write (EscSeqUtils.CSI_HideCursor);
-        }
-        else
-        {
-            // We are being run in an environment that does not support a console
-            // such as a unit test, or a pipe.
-            Cols = 80;
-            Rows = 24;
-        }
-
-        ResizeScreen ();
-        ClearContents ();
-        CurrentAttribute = new (Color.White, Color.Black);
-
-        StartReportingMouseMoves ();
-
-        _mainLoopDriver = new (this);
-        _mainLoopDriver.ProcessInput = ProcessInput;
-
-        return new (_mainLoopDriver);
-    }
-
-    private void ProcessInput (InputResult inputEvent)
-    {
-        switch (inputEvent.EventType)
-        {
-            case EventType.Key:
-                ConsoleKeyInfo consoleKeyInfo = inputEvent.ConsoleKeyInfo;
-
-                //if (consoleKeyInfo.Key == ConsoleKey.Packet) {
-                //	consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
-                //}
-
-                //Debug.WriteLine ($"event: {inputEvent}");
-
-                KeyCode map = EscSeqUtils.MapKey (consoleKeyInfo);
-
-                if (map == KeyCode.Null)
-                {
-                    break;
-                }
-
-                if (IsValidInput (map, out map))
-                {
-                    OnKeyDown (new (map));
-                    OnKeyUp (new (map));
-                }
-
-                break;
-            case EventType.Mouse:
-                MouseEventArgs me = ToDriverMouse (inputEvent.MouseEvent);
-                //Debug.WriteLine ($"NetDriver: ({me.X},{me.Y}) - {me.Flags}");
-                OnMouseEvent (me);
-
-                break;
-            case 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;
-                OnSizeChanged (new (new (Cols, Rows)));
-
-                break;
-            case EventType.RequestResponse:
-                break;
-            case EventType.WindowPosition:
-                break;
-            default:
-                throw new ArgumentOutOfRangeException ();
-        }
-    }
-    public override void End ()
-    {
-        StopReportingMouseMoves ();
-
-        if (!RunningUnitTests)
-        {
-            Console.ResetColor ();
-
-            //Disable alternative screen buffer.
-            Console.Out.Write (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
-
-            //Set cursor key to cursor.
-            Console.Out.Write (EscSeqUtils.CSI_ShowCursor);
-            Console.Out.Close ();
-
-            // Reset the console to its original state
-            // after sending the escape sequences to restore
-            // alternative buffer and cursor visibility.
-            NetWinConsole?.Cleanup ();
-        }
-    }
-
-    #endregion Init/End/MainLoop
-
-    
-
-
-    #region Color Handling
-
-    public override bool SupportsTrueColor => Environment.OSVersion.Platform == PlatformID.Unix
-                                              || (IsWinPlatform && Environment.OSVersion.Version.Build >= 14931);
-
-    private const int COLOR_BLACK = 30;
-    private const int COLOR_BLUE = 34;
-    private const int COLOR_BRIGHT_BLACK = 90;
-    private const int COLOR_BRIGHT_BLUE = 94;
-    private const int COLOR_BRIGHT_CYAN = 96;
-    private const int COLOR_BRIGHT_GREEN = 92;
-    private const int COLOR_BRIGHT_MAGENTA = 95;
-    private const int COLOR_BRIGHT_RED = 91;
-    private const int COLOR_BRIGHT_WHITE = 97;
-    private const int COLOR_BRIGHT_YELLOW = 93;
-    private const int COLOR_CYAN = 36;
-    private const int COLOR_GREEN = 32;
-    private const int COLOR_MAGENTA = 35;
-    private const int COLOR_RED = 31;
-    private const int COLOR_WHITE = 37;
-    private const int COLOR_YELLOW = 33;
-
-    //// Cache the list of ConsoleColor values.
-    //[UnconditionalSuppressMessage (
-    //                                  "AOT",
-    //                                  "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.",
-    //                                  Justification = "<Pending>")]
-    //private static readonly HashSet<int> ConsoleColorValues = new (
-    //                                                               Enum.GetValues (typeof (ConsoleColor))
-    //                                                                   .OfType<ConsoleColor> ()
-    //                                                                   .Select (c => (int)c)
-    //                                                              );
-
-    // Dictionary for mapping ConsoleColor values to the values used by System.Net.Console.
-    private static readonly Dictionary<ConsoleColor, int> _colorMap = new ()
-    {
-        { 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.
-    private int MapColors (ConsoleColor color, bool isForeground = true)
-    {
-        return _colorMap.TryGetValue (color, out int colorValue) ? colorValue + (isForeground ? 0 : 10) : 0;
-    }
-
-    #endregion
-
-    #region Cursor Handling
-
-    private 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;
-            }
-        }
-
-        // + 1 is needed because non-Windows is based on 1 instead of 0 and
-        // Console.CursorTop/CursorLeft isn't reliable.
-        Console.Out.Write (EscSeqUtils.CSI_SetCursorPosition (row + 1, col + 1));
-
-        return true;
-    }
-
-    private CursorVisibility? _cachedCursorVisibility;
-
-    public override void UpdateCursor ()
-    {
-        EnsureCursorVisibility ();
-
-        if (Col >= 0 && Col < Cols && Row >= 0 && Row <= Rows)
-        {
-            SetCursorPosition (Col, Row);
-            SetWindowPosition (0, Row);
-        }
-    }
-
-    public override bool GetCursorVisibility (out CursorVisibility visibility)
-    {
-        visibility = _cachedCursorVisibility ?? CursorVisibility.Default;
-
-        return visibility == CursorVisibility.Default;
-    }
-
-    public override bool SetCursorVisibility (CursorVisibility visibility)
-    {
-        _cachedCursorVisibility = visibility;
-
-        Console.Out.Write (visibility == CursorVisibility.Default ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
-
-        return visibility == CursorVisibility.Default;
-    }
-
-    private void EnsureCursorVisibility ()
-    {
-        if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows))
-        {
-            GetCursorVisibility (out CursorVisibility cursorVisibility);
-            _cachedCursorVisibility = cursorVisibility;
-            SetCursorVisibility (CursorVisibility.Invisible);
-
-            return;
-        }
-
-        SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default);
-    }
-
-    #endregion
-
-    #region Mouse Handling
-
-    public void StartReportingMouseMoves ()
-    {
-        if (!RunningUnitTests)
-        {
-            Console.Out.Write (EscSeqUtils.CSI_EnableMouseEvents);
-        }
-    }
-
-    public void StopReportingMouseMoves ()
-    {
-        if (!RunningUnitTests)
-        {
-            Console.Out.Write (EscSeqUtils.CSI_DisableMouseEvents);
-        }
-    }
-
-    private MouseEventArgs ToDriverMouse (MouseEvent me)
-    {
-        //System.Diagnostics.Debug.WriteLine ($"X: {me.Position.X}; Y: {me.Position.Y}; ButtonState: {me.ButtonState}");
-
-        MouseFlags mouseFlag = 0;
-
-        if ((me.ButtonState & MouseButtonState.Button1Pressed) != 0)
-        {
-            mouseFlag |= MouseFlags.Button1Pressed;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button1Released) != 0)
-        {
-            mouseFlag |= MouseFlags.Button1Released;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button1Clicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button1Clicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button1DoubleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button1DoubleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button1TripleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button1TripleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button2Pressed) != 0)
-        {
-            mouseFlag |= MouseFlags.Button2Pressed;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button2Released) != 0)
-        {
-            mouseFlag |= MouseFlags.Button2Released;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button2Clicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button2Clicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button2DoubleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button2DoubleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button2TripleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button2TripleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button3Pressed) != 0)
-        {
-            mouseFlag |= MouseFlags.Button3Pressed;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button3Released) != 0)
-        {
-            mouseFlag |= MouseFlags.Button3Released;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button3Clicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button3Clicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button3DoubleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button3DoubleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button3TripleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button3TripleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonWheeledUp) != 0)
-        {
-            mouseFlag |= MouseFlags.WheeledUp;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonWheeledDown) != 0)
-        {
-            mouseFlag |= MouseFlags.WheeledDown;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonWheeledLeft) != 0)
-        {
-            mouseFlag |= MouseFlags.WheeledLeft;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonWheeledRight) != 0)
-        {
-            mouseFlag |= MouseFlags.WheeledRight;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button4Pressed) != 0)
-        {
-            mouseFlag |= MouseFlags.Button4Pressed;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button4Released) != 0)
-        {
-            mouseFlag |= MouseFlags.Button4Released;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button4Clicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button4Clicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button4DoubleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button4DoubleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.Button4TripleClicked) != 0)
-        {
-            mouseFlag |= MouseFlags.Button4TripleClicked;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ReportMousePosition) != 0)
-        {
-            mouseFlag |= MouseFlags.ReportMousePosition;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonShift) != 0)
-        {
-            mouseFlag |= MouseFlags.ButtonShift;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonCtrl) != 0)
-        {
-            mouseFlag |= MouseFlags.ButtonCtrl;
-        }
-
-        if ((me.ButtonState & MouseButtonState.ButtonAlt) != 0)
-        {
-            mouseFlag |= MouseFlags.ButtonAlt;
-        }
-
-        return new() { Position = me.Position, Flags = mouseFlag };
-    }
-
-    #endregion Mouse Handling
-
-    #region Keyboard Handling
-
-    //private ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
-    //{
-    //    if (consoleKeyInfo.Key != ConsoleKey.Packet)
-    //    {
-    //        return consoleKeyInfo;
-    //    }
-
-    //    ConsoleModifiers mod = consoleKeyInfo.Modifiers;
-    //    bool shift = (mod & ConsoleModifiers.Shift) != 0;
-    //    bool alt = (mod & ConsoleModifiers.Alt) != 0;
-    //    bool control = (mod & ConsoleModifiers.Control) != 0;
-
-    //    ConsoleKeyInfo cKeyInfo = DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo);
-
-    //    return new (cKeyInfo.KeyChar, cKeyInfo.Key, shift, alt, control);
-    //}
-
-    #endregion Keyboard Handling
-
-    #region Low-Level DotNet tuff
-
-    /// <inheritdoc/>
-    public override void WriteRaw (string ansi)
-    {
-        Console.Out.Write (ansi);
-        Console.Out.Flush ();
-    }
-
-    private volatile bool _winSizeChanging;
-
-    private void SetWindowPosition (int col, int row)
-    {
-        if (!RunningUnitTests)
-        {
-            Top = Console.WindowTop;
-            Left = Console.WindowLeft;
-        }
-        else
-        {
-            Top = row;
-            Left = col;
-        }
-    }
-
-    public virtual void ResizeScreen ()
-    {
-        // CONCURRENCY: Unsynchronized access to Clip is not safe.
-        Clip = new (Screen);
-    }
-
-    #endregion Low-Level DotNet tuff
-}

+ 0 - 618
Terminal.Gui/Drivers/NetDriver/NetEvents.cs

@@ -1,618 +0,0 @@
-#nullable enable
-using System.Collections.Concurrent;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Terminal.Gui.Drivers;
-
-internal class NetEvents : IDisposable
-{
-    private CancellationTokenSource? _netEventsDisposed = new CancellationTokenSource ();
-
-    //CancellationTokenSource _waitForStartCancellationTokenSource;
-    private readonly ManualResetEventSlim _winChange = new (false);
-    private readonly BlockingCollection<InputResult?> _inputQueue = new (new ConcurrentQueue<InputResult?> ());
-    private readonly IConsoleDriver _consoleDriver;
-
-    public AnsiResponseParser<ConsoleKeyInfo> Parser { get; private set; } = new ();
-
-    public NetEvents (IConsoleDriver consoleDriver)
-    {
-        _consoleDriver = consoleDriver ?? throw new ArgumentNullException (nameof (consoleDriver));
-
-        if (ConsoleDriver.RunningUnitTests)
-        {
-            return;
-        }
-
-        Task.Run (() =>
-        {
-            try
-            {
-                ProcessInputQueue ();
-            }
-            catch (OperationCanceledException)
-            { }
-        }, _netEventsDisposed.Token);
-
-        Task.Run (() =>
-        {
-            try
-            {
-                CheckWindowSizeChange ();
-            }
-            catch (OperationCanceledException)
-            { }
-        }, _netEventsDisposed.Token);
-
-        Parser.UnexpectedResponseHandler = ProcessRequestResponse;
-    }
-
-
-    public InputResult? DequeueInput ()
-    {
-        while (_netEventsDisposed is { Token.IsCancellationRequested: false })
-        {
-            _winChange.Set ();
-
-            try
-            {
-                if (_inputQueue.TryTake (out var item, -1, _netEventsDisposed.Token))
-                {
-                    return item;
-                }
-            }
-            catch (OperationCanceledException)
-            {
-                return null;
-            }
-
-        }
-
-        return null;
-    }
-
-    private ConsoleKeyInfo ReadConsoleKeyInfo (bool intercept = true)
-    {
-        // if there is a key available, return it without waiting
-        //  (or dispatching work to the thread queue)
-        if (Console.KeyAvailable)
-        {
-            return Console.ReadKey (intercept);
-        }
-
-        while (!_netEventsDisposed!.IsCancellationRequested)
-        {
-            Task.Delay (100, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token);
-
-            foreach (var k in ShouldReleaseParserHeldKeys ())
-            {
-                ProcessMapConsoleKeyInfo (k);
-            }
-
-            if (Console.KeyAvailable)
-            {
-                return Console.ReadKey (intercept);
-            }
-        }
-
-        _netEventsDisposed.Token.ThrowIfCancellationRequested ();
-
-        return default (ConsoleKeyInfo);
-    }
-
-    public IEnumerable<ConsoleKeyInfo> ShouldReleaseParserHeldKeys ()
-    {
-        if (Parser.State == AnsiResponseParserState.ExpectingEscapeSequence &&
-            DateTime.Now - Parser.StateChangedAt > ((NetDriver)_consoleDriver).EscTimeout)
-        {
-            return Parser.Release ().Select (o => o.Item2);
-        }
-
-        return [];
-    }
-
-    private void ProcessInputQueue ()
-    {
-        while (_netEventsDisposed is { IsCancellationRequested: false })
-        {
-            if (_inputQueue.Count == 0)
-            {
-                while (_netEventsDisposed is { IsCancellationRequested: false })
-                {
-                    ConsoleKeyInfo consoleKeyInfo;
-
-                    consoleKeyInfo = ReadConsoleKeyInfo ();
-
-                    // Parse
-                    foreach (var k in Parser.ProcessInput (Tuple.Create (consoleKeyInfo.KeyChar, consoleKeyInfo)))
-                    {
-                        ProcessMapConsoleKeyInfo (k.Item2);
-                    }
-                }
-            }
-        }
-    }
-
-    void ProcessMapConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
-    {
-        _inputQueue.Add (
-                             new InputResult
-                             {
-                                 EventType = EventType.Key, ConsoleKeyInfo = EscSeqUtils.MapConsoleKeyInfo (consoleKeyInfo)
-                             }
-                            );
-    }
-
-    private void CheckWindowSizeChange ()
-    {
-        void RequestWindowSize ()
-        {
-            while (_netEventsDisposed is { IsCancellationRequested: false })
-            {
-                // Wait for a while then check if screen has changed sizes
-                Task.Delay (500, _netEventsDisposed.Token).Wait (_netEventsDisposed.Token);
-
-                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 (EnqueueWindowSizeEvent (
-                                            Math.Max (Console.WindowHeight, 0),
-                                            Math.Max (Console.WindowWidth, 0),
-                                            buffHeight,
-                                            buffWidth
-                                           ))
-                {
-                    return;
-                }
-            }
-
-            _netEventsDisposed.Token.ThrowIfCancellationRequested ();
-        }
-
-        while (!_netEventsDisposed!.IsCancellationRequested)
-        {
-            try
-            {
-                _winChange.Wait (_netEventsDisposed.Token);
-                _winChange.Reset ();
-
-                RequestWindowSize ();
-            }
-            catch (OperationCanceledException)
-            {
-                return;
-            }
-        }
-    }
-
-    /// <summary>Enqueue a window size event if the window size has changed.</summary>
-    /// <param name="winHeight"></param>
-    /// <param name="winWidth"></param>
-    /// <param name="buffHeight"></param>
-    /// <param name="buffWidth"></param>
-    /// <returns></returns>
-    private bool EnqueueWindowSizeEvent (int winHeight, int winWidth, int buffHeight, int buffWidth)
-    {
-        if (winWidth == _consoleDriver.Cols && winHeight == _consoleDriver.Rows)
-        {
-            return false;
-        }
-
-        int w = Math.Max (winWidth, 0);
-        int h = Math.Max (winHeight, 0);
-
-        _inputQueue.Add (
-                             new InputResult
-                             {
-                                 EventType = EventType.WindowSize, WindowSizeEvent = new WindowSizeEvent { Size = new (w, h) }
-                             }
-                            );
-
-        return true;
-    }
-
-    private bool ProcessRequestResponse (IEnumerable<Tuple<char, ConsoleKeyInfo>> obj)
-    {
-        // Added for signature compatibility with existing method, not sure what they are even for.
-        ConsoleKeyInfo newConsoleKeyInfo = default;
-        ConsoleKey key = default;
-        ConsoleModifiers mod = default;
-
-        ProcessRequestResponse (ref newConsoleKeyInfo, ref key, obj.Select (v => v.Item2).ToArray (), ref mod);
-
-        // Handled
-        return true;
-    }
-
-    // Process a CSI sequence received by the driver (key pressed, mouse event, or request/response event)
-    private void ProcessRequestResponse (
-        ref ConsoleKeyInfo newConsoleKeyInfo,
-        ref ConsoleKey key,
-        ConsoleKeyInfo [] cki,
-        ref ConsoleModifiers mod
-    )
-    {
-
-        // isMouse is true if it's CSI<, false otherwise
-        EscSeqUtils.DecodeEscSeq (
-                                  ref newConsoleKeyInfo,
-                                  ref key,
-                                  cki,
-                                  ref mod,
-                                  out string c1Control,
-                                  out string code,
-                                  out string [] values,
-                                  out string terminating,
-                                  out bool isMouse,
-                                  out List<MouseFlags> mouseFlags,
-                                  out Point pos,
-                                  out bool isReq,
-                                  (f, p) => HandleMouseEvent (MapMouseFlags (f), p)
-                                 );
-
-        if (isMouse)
-        {
-            foreach (MouseFlags mf in mouseFlags)
-            {
-                HandleMouseEvent (MapMouseFlags (mf), pos);
-            }
-
-            return;
-        }
-
-        if (isReq)
-        {
-            HandleRequestResponseEvent (c1Control, code, values, terminating);
-
-            return;
-        }
-
-        HandleKeyboardEvent (newConsoleKeyInfo);
-    }
-
-    [UnconditionalSuppressMessage ("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
-    private MouseButtonState MapMouseFlags (MouseFlags mouseFlags)
-    {
-        MouseButtonState mbs = default;
-
-        foreach (object 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;
-    }
-
-    private Point _lastCursorPosition;
-
-    private 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:
-                var point = new Point { X = int.Parse (values [1]) - 1, Y = int.Parse (values [0]) - 1 };
-
-                if (_lastCursorPosition.Y != point.Y)
-                {
-                    _lastCursorPosition = point;
-                    var eventType = EventType.WindowPosition;
-                    var winPositionEv = new WindowPositionEvent { CursorPosition = point };
-
-                    _inputQueue.Add (
-                                         new InputResult { EventType = eventType, WindowPositionEvent = winPositionEv }
-                                        );
-                }
-
-                break;
-
-            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;
-        }
-    }
-
-    private void EnqueueRequestResponseEvent (string c1Control, string code, string [] values, string terminating)
-    {
-        var eventType = EventType.RequestResponse;
-        var requestRespEv = new RequestResponseEvent { ResultTuple = (c1Control, code, values, terminating) };
-
-        _inputQueue.Add (
-                             new InputResult { EventType = eventType, RequestResponseEvent = requestRespEv }
-                            );
-    }
-
-    private void HandleMouseEvent (MouseButtonState buttonState, Point pos)
-    {
-        var mouseEvent = new MouseEvent { Position = pos, ButtonState = buttonState };
-
-        _inputQueue.Add (
-                             new InputResult { EventType = EventType.Mouse, MouseEvent = mouseEvent }
-                            );
-    }
-
-    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;
-
-        public readonly override string ToString ()
-        {
-            return (EventType switch
-                    {
-                        EventType.Key => ToString (ConsoleKeyInfo),
-                        EventType.Mouse => MouseEvent.ToString (),
-
-                        //EventType.WindowSize => WindowSize.ToString (),
-                        //EventType.RequestResponse => RequestResponse.ToString (),
-                        _ => "Unknown event type: " + EventType
-                    })!;
-        }
-
-        /// <summary>Prints a ConsoleKeyInfoEx structure</summary>
-        /// <param name="cki"></param>
-        /// <returns></returns>
-        public readonly string ToString (ConsoleKeyInfo cki)
-        {
-            var ke = new Key ((KeyCode)cki.KeyChar);
-            var sb = new StringBuilder ();
-            sb.Append ($"Key: {(KeyCode)cki.Key} ({cki.Key})");
-            sb.Append ((cki.Modifiers & ConsoleModifiers.Shift) != 0 ? " | Shift" : string.Empty);
-            sb.Append ((cki.Modifiers & ConsoleModifiers.Control) != 0 ? " | Control" : string.Empty);
-            sb.Append ((cki.Modifiers & ConsoleModifiers.Alt) != 0 ? " | Alt" : string.Empty);
-            sb.Append ($", KeyChar: {ke.AsRune.MakePrintable ()} ({(uint)cki.KeyChar}) ");
-            string s = sb.ToString ().TrimEnd (',').TrimEnd (' ');
-
-            return $"[ConsoleKeyInfo({s})]";
-        }
-    }
-
-    private void HandleKeyboardEvent (ConsoleKeyInfo cki)
-    {
-        var inputResult = new InputResult { EventType = EventType.Key, ConsoleKeyInfo = cki };
-
-        _inputQueue.Add (inputResult);
-    }
-
-    public void Dispose ()
-    {
-        _netEventsDisposed?.Cancel ();
-        _netEventsDisposed?.Dispose ();
-        _netEventsDisposed = null;
-
-        try
-        {
-            // throws away any typeahead that has been typed by
-            // the user and has not yet been read by the program.
-            while (Console.KeyAvailable)
-            {
-                Console.ReadKey (true);
-            }
-        }
-        catch (InvalidOperationException)
-        {
-            // Ignore - Console input has already been closed
-        }
-    }
-}

+ 0 - 167
Terminal.Gui/Drivers/NetDriver/NetMainLoop.cs

@@ -1,167 +0,0 @@
-#nullable enable
-
-using System.Collections.Concurrent;
-using IMainLoopDriver = Terminal.Gui.App.IMainLoopDriver;
-using MainLoop = Terminal.Gui.App.MainLoop;
-
-namespace Terminal.Gui.Drivers;
-
-/// <summary>
-///     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.
-/// </summary>
-/// <remarks>This implementation is used for NetDriver.</remarks>
-internal class NetMainLoop : IMainLoopDriver
-{
-    internal NetEvents? _netEvents;
-
-    /// <summary>Invoked when a Key is pressed.</summary>
-    internal Action<NetEvents.InputResult>? ProcessInput;
-
-    private readonly ManualResetEventSlim _eventReady = new (false);
-    private readonly CancellationTokenSource _eventReadyTokenSource = new ();
-    private readonly CancellationTokenSource _inputHandlerTokenSource = new ();
-    private readonly ManualResetEventSlim _waitForProbe = new (false);
-    private readonly ConcurrentQueue<NetEvents.InputResult> _resultQueue = new ();
-    private MainLoop? _mainLoop;
-
-    /// <summary>Initializes the class with the console driver.</summary>
-    /// <remarks>Passing a IConsoleDriver is provided to capture windows resizing.</remarks>
-    /// <param name="consoleDriver">The console driver used by this Net main loop.</param>
-    /// <exception cref="ArgumentNullException"></exception>
-    public NetMainLoop (IConsoleDriver consoleDriver)
-    {
-        ArgumentNullException.ThrowIfNull (consoleDriver);
-
-        _netEvents = new (consoleDriver);
-    }
-
-    void IMainLoopDriver.Setup (MainLoop mainLoop)
-    {
-        _mainLoop = mainLoop;
-
-        if (!ConsoleDriver.RunningUnitTests)
-        {
-            Task.Run (NetInputHandler, _inputHandlerTokenSource.Token);
-        }
-    }
-
-    void IMainLoopDriver.Wakeup () { _eventReady.Set (); }
-
-    bool IMainLoopDriver.EventsPending ()
-    {
-        if (ConsoleDriver.RunningUnitTests)
-        {
-            return true;
-        }
-
-        _waitForProbe.Set ();
-
-        if (_resultQueue.Count > 0 || _mainLoop!.TimedEvents.CheckTimers (out int waitTimeout))
-        {
-            return true;
-        }
-
-        try
-        {
-            if (!_eventReadyTokenSource.IsCancellationRequested)
-            {
-                // Note: ManualResetEventSlim.Wait will wait indefinitely if the timeout is -1. The timeout is -1 when there
-                // are no timers, but there IS an idle handler waiting.
-                _eventReady.Wait (waitTimeout, _eventReadyTokenSource.Token);
-            }
-        }
-        catch (OperationCanceledException)
-        {
-            return true;
-        }
-        finally
-        {
-            _eventReady.Reset ();
-        }
-
-        _eventReadyTokenSource.Token.ThrowIfCancellationRequested ();
-
-        if (!_eventReadyTokenSource.IsCancellationRequested)
-        {
-            return _resultQueue.Count > 0 || _mainLoop.TimedEvents.CheckTimers (out _);
-        }
-
-        // If cancellation was requested then always return true
-        return true;
-    }
-
-    void IMainLoopDriver.Iteration ()
-    {
-        while (!ConsoleDriver.RunningUnitTests && _resultQueue.TryDequeue (out NetEvents.InputResult inputRecords))
-        {
-            ProcessInput?.Invoke (inputRecords);
-        }
-    }
-
-    void IMainLoopDriver.TearDown ()
-    {
-        _inputHandlerTokenSource.Cancel ();
-        _inputHandlerTokenSource.Dispose ();
-        _eventReadyTokenSource.Cancel ();
-        _eventReadyTokenSource.Dispose ();
-
-        _eventReady.Dispose ();
-        _waitForProbe.Dispose ();
-
-        _resultQueue.Clear ();
-        _netEvents?.Dispose ();
-        _netEvents = null;
-
-        _mainLoop = null;
-    }
-
-    private void NetInputHandler ()
-    {
-        while (_mainLoop is { })
-        {
-            try
-            {
-                if (!_inputHandlerTokenSource.IsCancellationRequested)
-                {
-                    try
-                    {
-                        _waitForProbe.Wait (_inputHandlerTokenSource.Token);
-                    }
-                    catch (Exception ex)
-                    {
-                        if (ex is OperationCanceledException or ObjectDisposedException)
-                        {
-                            return;
-                        }
-
-                        throw;
-                    }
-
-                    _waitForProbe.Reset ();
-                }
-
-                ProcessInputQueue ();
-            }
-            catch (OperationCanceledException)
-            {
-                return;
-            }
-        }
-    }
-
-    private void ProcessInputQueue ()
-    {
-        if (_resultQueue.Count == 0)
-        {
-            NetEvents.InputResult? result = _netEvents!.DequeueInput ();
-
-            if (result.HasValue)
-            {
-                _resultQueue.Enqueue (result.Value);
-
-                _eventReady.Set ();
-            }
-        }
-    }
-}

+ 10 - 10
Terminal.Gui/Drivers/V2/OutputBase.cs → Terminal.Gui/Drivers/OutputBase.cs

@@ -24,12 +24,12 @@ public abstract class OutputBase
             return;
         }
 
-        if (Console.WindowHeight < 1
-            || buffer.Contents.Length != buffer.Rows * buffer.Cols
-            || buffer.Rows != Console.WindowHeight)
-        {
-            //     return;
-        }
+        //if (Console.WindowHeight < 1
+        //    || buffer.Contents.Length != buffer.Rows * buffer.Cols
+        //    || buffer.Rows != Console.WindowHeight)
+        //{
+        //    //     return;
+        //}
 
         var top = 0;
         var left = 0;
@@ -47,10 +47,10 @@ public abstract class OutputBase
 
         for (int row = top; row < rows; row++)
         {
-            if (Console.WindowHeight < 1)
-            {
-                return;
-            }
+            //if (Console.WindowHeight < 1)
+            //{
+            //    return;
+            //}
 
             if (!SetCursorPositionImpl (0, row))
             {

+ 2 - 19
Terminal.Gui/Drivers/V2/OutputBuffer.cs → Terminal.Gui/Drivers/OutputBuffer.cs

@@ -17,7 +17,6 @@ public class OutputBuffer : IOutputBuffer
     /// </summary>
     public Cell [,] Contents { get; set; } = new Cell[0, 0];
 
-    private Attribute _currentAttribute;
     private int _cols;
     private int _rows;
 
@@ -25,23 +24,7 @@ public class OutputBuffer : IOutputBuffer
     ///     The <see cref="Attribute"/> that will be used for the next <see cref="AddRune(Rune)"/> or <see cref="AddStr"/>
     ///     call.
     /// </summary>
-    public Attribute CurrentAttribute
-    {
-        get => _currentAttribute;
-        set
-        {
-            // TODO: This makes IConsoleDriver dependent on Application, which is not ideal. Once Attribute.PlatformColor is removed, this can be fixed.
-            if (Application.Driver is { })
-            {
-                // TODO: Update this when attributes can include TextStyle in the constructor
-                _currentAttribute = new (value.Foreground, value.Background, value.Style);
-
-                return;
-            }
-
-            _currentAttribute = value;
-        }
-    }
+    public Attribute CurrentAttribute { get; set; }
 
     /// <summary>The leftmost column in the terminal.</summary>
     public virtual int Left { get; set; } = 0;
@@ -141,7 +124,7 @@ public class OutputBuffer : IOutputBuffer
             return;
         }
 
-        Clip ??= new Region (Screen);
+        Clip ??= new (Screen);
 
         Rectangle clipRect = Clip!.GetBounds ();
 

+ 0 - 0
Terminal.Gui/Drivers/CursesDriver/Platform.cs → Terminal.Gui/Drivers/Platform.cs


+ 26 - 0
Terminal.Gui/Drivers/PlatformDetection.cs

@@ -0,0 +1,26 @@
+using System.Runtime.InteropServices;
+
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+/// Helper class for detecting platform-specific features.
+/// </summary>
+internal static class PlatformDetection
+{
+    /// <summary>
+    /// Determines if the current platform is WSL (Windows Subsystem for Linux).
+    /// </summary>
+    /// <returns>True if running on WSL, false otherwise.</returns>
+    public static bool IsWSLPlatform ()
+    {
+        // xclip does not work on WSL, so we need to use the Windows clipboard via Powershell
+        (int exitCode, string result) = ClipboardProcessRunner.Bash ("uname -a", waitForOutput: true);
+
+        if (exitCode == 0 && result.Contains ("microsoft") && result.Contains ("WSL"))
+        {
+            return true;
+        }
+
+        return false;
+    }
+}

+ 3 - 0
Terminal.Gui/Drivers/UnixDriver/IUnixInput.cs

@@ -0,0 +1,3 @@
+namespace Terminal.Gui.Drivers;
+
+internal interface IUnixInput : IConsoleInput<char>;

+ 3 - 31
Terminal.Gui/Drivers/CursesDriver/ClipboardImpl.cs → Terminal.Gui/Drivers/UnixDriver/UnixClipboard.cs

@@ -1,14 +1,13 @@
 using System.Runtime.InteropServices;
-using Unix.Terminal;
 
 namespace Terminal.Gui.Drivers;
 
-/// <summary>A clipboard implementation for Linux. This implementation uses the xclip command to access the clipboard.</summary>
+/// <summary>A clipboard implementation for Unix that uses the xclip command to access the clipboard.</summary>
 /// <remarks>If xclip is not installed, this implementation will not work.</remarks>
-internal class CursesClipboard : ClipboardBase
+internal class UnixClipboard : ClipboardBase
 {
     private string _xclipPath = string.Empty;
-    public CursesClipboard () { IsSupported = CheckSupport (); }
+    public UnixClipboard () { IsSupported = CheckSupport (); }
     public override bool IsSupported { get; }
 
     protected override string GetClipboardDataImpl ()
@@ -23,12 +22,6 @@ internal class CursesClipboard : ClipboardBase
 
             if (exitCode == 0)
             {
-                if (Application.Driver is CursesDriver)
-                {
-                    Curses.raw ();
-                    Curses.noecho ();
-                }
-
                 return File.ReadAllText (tempFileName);
             }
         }
@@ -51,12 +44,6 @@ internal class CursesClipboard : ClipboardBase
         try
         {
             (int exitCode, _) = ClipboardProcessRunner.Bash ($"{_xclipPath} {xclipargs}", text);
-
-            if (exitCode == 0 && Application.Driver is CursesDriver)
-            {
-                Curses.raw ();
-                Curses.noecho ();
-            }
         }
         catch (Exception e)
         {
@@ -207,12 +194,6 @@ internal class WSLClipboard : ClipboardBase
 
         if (exitCode == 0)
         {
-            if (Application.Driver is CursesDriver)
-            {
-                Curses.raw ();
-                Curses.noecho ();
-            }
-
             if (output.EndsWith ("\r\n"))
             {
                 output = output.Substring (0, output.Length - 2);
@@ -235,15 +216,6 @@ internal class WSLClipboard : ClipboardBase
                                                                         _powershellPath,
                                                                         $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""
                                                                        );
-
-        if (exitCode == 0)
-        {
-            if (Application.Driver is CursesDriver)
-            {
-                Curses.raw ();
-                Curses.noecho ();
-            }
-        }
     }
 
     private bool CheckSupport ()

+ 29 - 0
Terminal.Gui/Drivers/UnixDriver/UnixComponentFactory.cs

@@ -0,0 +1,29 @@
+#nullable enable
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+/// <see cref="IComponentFactory{T}"/> implementation for native unix console I/O.
+/// This factory creates instances of internal classes <see cref="UnixInput"/>, <see cref="UnixOutput"/> etc.
+/// </summary>
+public class UnixComponentFactory : ComponentFactory<char>
+{
+    /// <inheritdoc />
+    public override IConsoleInput<char> CreateInput ()
+    {
+        return new UnixInput ();
+    }
+
+    /// <inheritdoc />
+    public override IInputProcessor CreateInputProcessor (ConcurrentQueue<char> inputBuffer)
+    {
+        return new UnixInputProcessor (inputBuffer);
+    }
+
+    /// <inheritdoc />
+    public override IConsoleOutput CreateOutput ()
+    {
+        return new UnixOutput ();
+    }
+}

+ 266 - 0
Terminal.Gui/Drivers/UnixDriver/UnixInput.cs

@@ -0,0 +1,266 @@
+using System.Runtime.InteropServices;
+using Microsoft.Extensions.Logging;
+
+namespace Terminal.Gui.Drivers;
+
+internal class UnixInput : ConsoleInput<char>, IUnixInput
+{
+    private const int STDIN_FILENO = 0;
+
+    [StructLayout (LayoutKind.Sequential)]
+    private struct Termios
+    {
+        public uint c_iflag;
+        public uint c_oflag;
+        public uint c_cflag;
+        public uint c_lflag;
+
+        [MarshalAs (UnmanagedType.ByValArray, SizeConst = 32)]
+        public byte [] c_cc;
+
+        public uint c_ispeed;
+        public uint c_ospeed;
+    }
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern int tcgetattr (int fd, out Termios termios);
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern int tcsetattr (int fd, int optional_actions, ref Termios termios);
+
+    // try cfmakeraw (glibc and macOS usually export it)
+    [DllImport ("libc", EntryPoint = "cfmakeraw", SetLastError = false)]
+    private static extern void cfmakeraw_ref (ref Termios termios);
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern nint strerror (int err);
+
+    private const int TCSANOW = 0;
+
+    private const ulong BRKINT = 0x00000002;
+    private const ulong ICRNL = 0x00000100;
+    private const ulong INPCK = 0x00000010;
+    private const ulong ISTRIP = 0x00000020;
+    private const ulong IXON = 0x00000400;
+
+    private const ulong OPOST = 0x00000001;
+
+    private const ulong ECHO = 0x00000008;
+    private const ulong ICANON = 0x00000100;
+    private const ulong IEXTEN = 0x00008000;
+    private const ulong ISIG = 0x00000001;
+
+    private const ulong CS8 = 0x00000030;
+
+    private Termios _original;
+
+    [StructLayout (LayoutKind.Sequential)]
+    private struct Pollfd
+    {
+        public int fd;
+        public short events;
+        public readonly short revents; // readonly signals "don't touch this in managed code"
+    }
+
+    /// <summary>Condition on which to wake up from file descriptor activity.  These match the Linux/BSD poll definitions.</summary>
+    [Flags]
+    private enum Condition : short
+    {
+        /// <summary>There is data to read</summary>
+        PollIn = 1,
+
+        /// <summary>There is urgent data to read</summary>
+        PollPri = 2,
+
+        /// <summary>Writing to the specified descriptor will not block</summary>
+        PollOut = 4,
+
+        /// <summary>Error condition on output</summary>
+        PollErr = 8,
+
+        /// <summary>Hang-up on output</summary>
+        PollHup = 16,
+
+        /// <summary>File descriptor is not open.</summary>
+        PollNval = 32
+    }
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern int poll ([In][Out] Pollfd [] ufds, uint nfds, int timeout);
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern int read (int fd, byte [] buf, int count);
+
+    // File descriptor for stdout
+    private const int STDOUT_FILENO = 1;
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern int write (int fd, byte [] buf, int count);
+
+    [DllImport ("libc", SetLastError = true)]
+    private static extern int tcflush (int fd, int queueSelector);
+
+    private const int TCIFLUSH = 0;  // flush data received but not read
+
+    private Pollfd [] _pollMap;
+
+    public UnixInput ()
+    {
+        Logging.Logger.LogInformation ($"Creating {nameof (UnixInput)}");
+
+        if (ConsoleDriver.RunningUnitTests)
+        {
+            return;
+        }
+
+        _pollMap = new Pollfd [1];
+        _pollMap [0].fd = STDIN_FILENO; // stdin
+        _pollMap [0].events = (short)Condition.PollIn;
+
+        EnableRawModeAndTreatControlCAsInput ();
+
+        //Enable alternative screen buffer.
+        WriteRaw (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll);
+
+        //Set cursor key to application.
+        WriteRaw (EscSeqUtils.CSI_HideCursor);
+
+        WriteRaw (EscSeqUtils.CSI_EnableMouseEvents);
+    }
+
+    private void EnableRawModeAndTreatControlCAsInput ()
+    {
+        if (tcgetattr (STDIN_FILENO, out _original) != 0)
+        {
+            var e = Marshal.GetLastWin32Error ();
+            throw new InvalidOperationException ($"tcgetattr failed errno={e} ({StrError (e)})");
+        }
+
+        var raw = _original;
+
+        // Prefer cfmakeraw if available
+        try
+        {
+            cfmakeraw_ref (ref raw);
+        }
+        catch (EntryPointNotFoundException)
+        {
+            // fallback: roughly cfmakeraw equivalent
+            raw.c_iflag &= ~((uint)BRKINT | (uint)ICRNL | (uint)INPCK | (uint)ISTRIP | (uint)IXON);
+            raw.c_oflag &= ~(uint)OPOST;
+            raw.c_cflag |= (uint)CS8;
+            raw.c_lflag &= ~((uint)ECHO | (uint)ICANON | (uint)IEXTEN | (uint)ISIG);
+        }
+
+        if (tcsetattr (STDIN_FILENO, TCSANOW, ref raw) != 0)
+        {
+            var e = Marshal.GetLastWin32Error ();
+            throw new InvalidOperationException ($"tcsetattr failed errno={e} ({StrError (e)})");
+        }
+    }
+
+    private string StrError (int err)
+    {
+        var p = strerror (err);
+        return p == nint.Zero ? $"errno={err}" : Marshal.PtrToStringAnsi (p) ?? $"errno={err}";
+    }
+
+    /// <inheritdoc />
+    protected override bool Peek ()
+    {
+        try
+        {
+            if (ConsoleDriver.RunningUnitTests)
+            {
+                return false;
+            }
+
+            int n = poll (_pollMap!, (uint)_pollMap!.Length, 0);
+
+            if (n != 0)
+            {
+                return true;
+            }
+
+            return false;
+        }
+        catch (Exception ex)
+        {
+            // Optionally log the exception
+            Logging.Logger.LogError ($"Error in Peek: {ex.Message}");
+
+            return false;
+        }
+    }
+    private void WriteRaw (string text)
+    {
+        if (!ConsoleDriver.RunningUnitTests)
+        {
+            byte [] utf8 = Encoding.UTF8.GetBytes (text);
+            // Write to stdout (fd 1)
+            write (STDOUT_FILENO, utf8, utf8.Length);
+        }
+    }
+
+    /// <inheritdoc/>
+    protected override IEnumerable<char> Read ()
+    {
+        while (poll (_pollMap!, (uint)_pollMap!.Length, 0) != 0)
+        {
+            // Check if stdin has data
+            if ((_pollMap [0].revents & (int)Condition.PollIn) != 0)
+            {
+                var buf = new byte [256];
+                int bytesRead = read (0, buf, buf.Length); // Read from stdin
+                string input = Encoding.UTF8.GetString (buf, 0, bytesRead);
+
+                foreach (char ch in input)
+                {
+                    yield return ch;
+                }
+            }
+        }
+    }
+
+    private void FlushConsoleInput ()
+    {
+        if (!ConsoleDriver.RunningUnitTests)
+        {
+            var fds = new Pollfd [1];
+            fds [0].fd = STDIN_FILENO;
+            fds [0].events = (short)Condition.PollIn;
+            var buf = new byte [256];
+            while (poll (fds, 1, 0) > 0)
+            {
+                read (STDIN_FILENO, buf, buf.Length);
+            }
+        }
+    }
+
+    /// <inheritdoc />
+    public override void Dispose ()
+    {
+        base.Dispose ();
+
+        if (!ConsoleDriver.RunningUnitTests)
+        {
+            // Disable mouse events first
+            WriteRaw (EscSeqUtils.CSI_DisableMouseEvents);
+
+            // Drain any pending input already queued by the terminal
+            FlushConsoleInput ();
+
+            // Flush kernel input buffer
+            tcflush (STDIN_FILENO, TCIFLUSH);
+
+            //Disable alternative screen buffer.
+            WriteRaw (EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll);
+
+            //Set cursor key to cursor.
+            WriteRaw (EscSeqUtils.CSI_ShowCursor);
+
+            // Restore terminal to original state
+            tcsetattr (STDIN_FILENO, TCSANOW, ref _original);
+        }
+    }
+}

+ 38 - 0
Terminal.Gui/Drivers/UnixDriver/UnixInputProcessor.cs

@@ -0,0 +1,38 @@
+using System.Collections.Concurrent;
+
+namespace Terminal.Gui.Drivers;
+
+/// <summary>
+///     Input processor for <see cref="UnixInput"/>, deals in <see cref="char"/> stream.
+/// </summary>
+internal class UnixInputProcessor : InputProcessor<char>
+{
+    /// <inheritdoc />
+    public UnixInputProcessor (ConcurrentQueue<char> inputBuffer) : base (inputBuffer, new UnixKeyConverter ())
+    {
+        DriverName = "Unix";
+    }
+
+    /// <inheritdoc />
+    protected override void Process (char input)
+    {
+        foreach (Tuple<char, char> released in Parser.ProcessInput (Tuple.Create (input, input)))
+        {
+            ProcessAfterParsing (released.Item2);
+        }
+
+    }
+
+    /// <inheritdoc />
+    protected override void ProcessAfterParsing (char input)
+    {
+        var key = KeyConverter.ToKey (input);
+
+        // If the key is not valid, we don't want to raise any events.
+        if (IsValidInput (key, out key))
+        {
+            OnKeyDown (key);
+            OnKeyUp (key);
+        }
+    }
+}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません