Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #3692++ - Rearchitects drivers #3837

Draft
wants to merge 95 commits into
base: v2_develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
efbdfbe
Add simple loop input classes for Net and Win32
tznind Nov 18, 2024
8000c8d
Add scaffolding
tznind Nov 18, 2024
b0fe7a4
Adjust layout
tznind Nov 18, 2024
43418b5
Add Drivers2 test project and make everything public. Also add output
tznind Nov 19, 2024
77febd8
Launch with "win"
tznind Nov 19, 2024
6a535b4
Merge branch 'v2_develop' into v2-drivers
tig Nov 19, 2024
e24d06f
Deal with strange thread requirements of Win32 apis
tznind Nov 20, 2024
d267a0b
Merge branch 'v2-drivers' of https://github.com/tznind/gui.cs into v2…
tznind Nov 20, 2024
4cd73c7
WIP output buffer
tznind Nov 21, 2024
eb61350
Merge branch 'v2_develop' into v2-drivers
tznind Nov 22, 2024
0655f0d
WIP try to exercise new buffer for hello world
tznind Nov 22, 2024
15c9c07
Merge branch 'v2-drivers' of https://github.com/tznind/gui.cs into v2…
tznind Nov 22, 2024
aaef87d
Hello world appeared!
tznind Nov 22, 2024
ea3468e
Rainbows!
tznind Nov 22, 2024
d98a841
suppress suspicious check
tznind Nov 22, 2024
584a2ab
Add props and random render for fun
tznind Nov 22, 2024
c6b9c28
Starting on input processing
tznind Nov 22, 2024
6124deb
WIP : mouse support
tznind Nov 23, 2024
9f6e23a
Fix not reseting state after releasing mouse events
tznind Nov 23, 2024
24c2a5a
Refactor InputProcessor to be abstract with typed inheritors
tznind Nov 23, 2024
4c63297
WindowsOutput and InputProcessor working ish
tznind Nov 23, 2024
485be29
Add window size support
tznind Nov 23, 2024
d081b4d
Fix WindowsInputProcessor to handle mouse properly
tznind Nov 23, 2024
be8c339
Merge branch 'v2_develop' into v2-drivers
tznind Nov 23, 2024
a066f23
Merge branch 'v2_develop' into v2-drivers
tznind Nov 24, 2024
5e202b0
Add IConsoleDriver
tznind Nov 24, 2024
ee335bd
WIP: trying to implement IConsoleDriver facade
tznind Nov 24, 2024
90b5836
WIP Implement more of facade
tznind Nov 24, 2024
68f39a8
Building with new facacde
tznind Nov 25, 2024
e46b2a5
Tests passing
tznind Nov 25, 2024
02d1a7c
Set Application.Driver to facade when building main loop via coordinator
tznind Nov 25, 2024
d0ae141
Start working towards integrating View/Application with v2
tznind Nov 25, 2024
f1f9c95
Add key up and mouse handler
tznind Nov 25, 2024
e7b40a3
Update class diagram to show scheduler and facade
tznind Nov 25, 2024
04c467e
WIP mouse state machine - commented out for now
tznind Nov 25, 2024
12edf58
Merge branch 'v2_develop' into v2-drivers
tznind Nov 26, 2024
231270d
Click event getting raised
tznind Nov 26, 2024
27165e9
Merge branch 'v2-drivers' of https://github.com/tznind/gui.cs into v2…
tznind Nov 26, 2024
e9b8264
Working on hit tracking
tznind Nov 26, 2024
0fd0f15
Ability to switch focus
tznind Nov 26, 2024
7dead87
Update class diagram
tznind Nov 26, 2024
1214a96
merge ansi-parser into branch
tznind Nov 28, 2024
f280048
fix for 0 based seq
tznind Nov 28, 2024
59a7479
Update class diagram
tznind Nov 28, 2024
889e426
Update class diagram
tznind Nov 28, 2024
d91a87f
Merge branch 'ansi-parser' into v2-drivers
tznind Dec 3, 2024
e2fd4fe
More conservative version of IApplication
tznind Dec 3, 2024
cd65e0d
Add ChangeInstance method
tznind Dec 3, 2024
0592f5f
Merge branch 'iapplication-2' into v2-drivers
tznind Dec 3, 2024
b504e0f
WIP: how to create a v2 application
tznind Dec 3, 2024
280f288
WIP net driver working for Top (but not subviews)
tznind Dec 6, 2024
3785171
Merge branch 'v2_develop' into v2-drivers
tznind Dec 6, 2024
d418847
Add RequestStop to IApplication
tznind Dec 6, 2024
4fe25e3
Add IsLegacy field to IApplication so we can turn off janky code in a…
tznind Dec 7, 2024
3a09c2f
WIP trying to get exit to work
tznind Dec 7, 2024
9744041
Update class diagram
tznind Dec 7, 2024
ede6b43
Merge branch 'v2_develop' into v2-drivers
tznind Dec 7, 2024
2360ed8
WIP can exit with 2x Esc (bug) and can open scenario
tznind Dec 7, 2024
9007caa
Merge branch 'v2-drivers' of https://github.com/tznind/gui.cs into v2…
tznind Dec 7, 2024
d8ac1c4
Can switch between apps but have lost keybindings somehow
tznind Dec 7, 2024
f2497ef
Add esc timeout into InputProcessor
tznind Dec 7, 2024
8405ef1
Fix event wiring
tznind Dec 7, 2024
d0dd43a
Move Idles/Timeouts into their own self contained class TimedEvents b…
tznind Dec 7, 2024
634693a
WIP try to resolve the fact that timeouts/locks are deadlock city
tznind Dec 7, 2024
00990ba
Revert "WIP try to resolve the fact that timeouts/locks are deadlock …
tznind Dec 8, 2024
4482c21
Fix reverted TimedEvents to operate the same as before - we can refac…
tznind Dec 8, 2024
3ea02ca
V2 TimedEvents support
tznind Dec 8, 2024
2650e36
Add timed events to class diagram
tznind Dec 8, 2024
be7d8df
WIP: Trying to get windows driver to work
tznind Dec 8, 2024
cea3e77
WIP WindowsConsole / WindowsOutput
tznind Dec 8, 2024
9a72628
Make input infinite thread in WindowsConsole constructor optional
tznind Dec 9, 2024
25968c1
Remove dependency on WindowsConsole
tznind Dec 11, 2024
ac58ed1
Fledgling mouse support for windows input
tznind Dec 11, 2024
da533f1
Merge branch 'v2_develop' into v2-drivers
tznind Dec 11, 2024
4c667f3
Fix for new names
tznind Dec 11, 2024
650a075
Use LayoutAndDraw on iteration
tznind Dec 14, 2024
a1dcb08
How we would like draw loop to be
tznind Dec 14, 2024
6d4f6c0
Menu working
tznind Dec 14, 2024
adceea8
Revert removal of Suspend
tznind Dec 14, 2024
128b219
Revert changes to PositionCursor
tznind Dec 14, 2024
32aa24b
Revert lots of little changes to accessibility etc in main codebase
tznind Dec 14, 2024
e832d61
Remove debug only reference
tznind Dec 14, 2024
293fe4e
Do not deadlock in Stop if invoked
tznind Dec 14, 2024
f0a5e04
Support specify v2net or v2win
tznind Dec 14, 2024
13de24b
Update launch settings for new v2 options
tznind Dec 14, 2024
124ab9a
Move v2 driver bootstrap logic into Application and out of UICatalog
tznind Dec 15, 2024
74dc894
Add IWindowSizeMonitor
tznind Dec 15, 2024
c31d11d
Make sleep wait in input loop conditional
tznind Dec 15, 2024
c01f974
Create first test for Application2
tznind Dec 15, 2024
55fb132
Add shutdown call
tznind Dec 15, 2024
8216597
Allow DI of input/output
tznind Dec 15, 2024
ba09cd2
Fix wrong lock in TimedEvents
tznind Dec 15, 2024
52a0dec
tests for AnsiResponseParser HandleMouse
tznind Dec 16, 2024
e0f6643
Switch to established method EscSeqUtils.MapKey
tznind Dec 16, 2024
efd6b97
ConsoleInput test
tznind Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions Terminal.Gui/Application/Application.Initialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Terminal.Gui.ConsoleDrivers.V2;

namespace Terminal.Gui;

Expand Down Expand Up @@ -37,7 +38,15 @@ public static partial class Application // Initialization (Init/Shutdown)
/// </param>
[RequiresUnreferencedCode ("AOT")]
[RequiresDynamicCode ("AOT")]
public static void Init (IConsoleDriver? driver = null, string? driverName = null) { InternalInit (driver, driverName); }
public static void Init (IConsoleDriver? driver = null, string? driverName = null)
{
if (driverName?.StartsWith ("v2") ?? false)
{
ApplicationImpl.ChangeInstance (new ApplicationV2 ());
}

ApplicationImpl.Instance.Init (driver, driverName);
}

internal static int MainThreadId { get; set; } = -1;

Expand Down Expand Up @@ -226,20 +235,7 @@ internal static void UnsubscribeDriverEvents ()
/// up (Disposed)
/// and terminal settings are restored.
/// </remarks>
public static void Shutdown ()
{
// TODO: Throw an exception if Init hasn't been called.

bool wasInitialized = Initialized;
ResetState ();
PrintJsonErrors ();

if (wasInitialized)
{
bool init = Initialized;
InitializedChanged?.Invoke (null, new (in init));
}
}
public static void Shutdown () => ApplicationImpl.Instance.Shutdown ();

/// <summary>
/// Gets whether the application has been initialized with <see cref="Init"/> and not yet shutdown with <see cref="Shutdown"/>.
Expand All @@ -258,4 +254,12 @@ public static void Shutdown ()
/// Intended to support unit tests that need to know when the application has been initialized.
/// </remarks>
public static event EventHandler<EventArgs<bool>>? InitializedChanged;

/// <summary>
/// Raises the <see cref="InitializedChanged"/> event.
/// </summary>
internal static void OnInitializedChanged (object sender, EventArgs<bool> e)
{
Application.InitializedChanged?.Invoke (sender,e);
}
}
2 changes: 0 additions & 2 deletions Terminal.Gui/Application/Application.Keyboard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ internal static void AddKeyBindings ()
return true;
}
);

AddCommand (
Command.Suspend,
static () =>
Expand All @@ -187,7 +186,6 @@ internal static void AddKeyBindings ()
return true;
}
);

AddCommand (
Command.NextTabStop,
static () => Navigation?.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop));
Expand Down
135 changes: 10 additions & 125 deletions Terminal.Gui/Application/Application.Run.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,8 @@ internal static bool PositionCursor ()
/// <returns>The created <see cref="Toplevel"/> object. The caller is responsible for disposing this object.</returns>
[RequiresUnreferencedCode ("AOT")]
[RequiresDynamicCode ("AOT")]
public static Toplevel Run (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null) { return Run<Toplevel> (errorHandler, driver); }
public static Toplevel Run (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null) =>
ApplicationImpl.Instance.Run (errorHandler, driver);

/// <summary>
/// Runs the application by creating a <see cref="Toplevel"/>-derived object of type <c>T</c> and calling
Expand All @@ -331,20 +332,7 @@ internal static bool PositionCursor ()
[RequiresUnreferencedCode ("AOT")]
[RequiresDynamicCode ("AOT")]
public static T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
where T : Toplevel, new()
{
if (!Initialized)
{
// Init() has NOT been called.
InternalInit (driver, null, true);
}

var top = new T ();

Run (top, errorHandler);

return top;
}
where T : Toplevel, new() => ApplicationImpl.Instance.Run<T> (errorHandler, driver);

/// <summary>Runs the Application using the provided <see cref="Toplevel"/> view.</summary>
/// <remarks>
Expand Down Expand Up @@ -385,84 +373,15 @@ public static T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriv
/// rethrows when null).
/// </param>
public static void Run (Toplevel view, Func<Exception, bool>? errorHandler = null)
{
ArgumentNullException.ThrowIfNull (view);

if (Initialized)
{
if (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."
);
}
}
else
{
// Init() has NOT been called.
throw new InvalidOperationException (
"Init() has not been called. Only Run() or Run<T>() can be used without calling Init()."
);
}

var resume = true;

while (resume)
{
#if !DEBUG
try
{
#endif
resume = false;
RunState runState = Begin (view);

// If EndAfterFirstIteration is true then the user must dispose of the runToken
// by using NotifyStopRunState event.
RunLoop (runState);

if (runState.Toplevel is null)
{
#if DEBUG_IDISPOSABLE
Debug.Assert (TopLevels.Count == 0);
#endif
runState.Dispose ();

return;
}

if (!EndAfterFirstIteration)
{
End (runState);
}
#if !DEBUG
}
catch (Exception error)
{
if (errorHandler is null)
{
throw;
}

resume = errorHandler (error);
}
#endif
}
}
=> ApplicationImpl.Instance.Run (view, errorHandler);

/// <summary>Adds a timeout to the application.</summary>
/// <remarks>
/// When time specified passes, the callback will be invoked. If the callback returns true, the timeout will be
/// reset, repeating the invocation. If it returns false, the timeout will stop and be removed. The returned value is a
/// token that can be used to stop the timeout by calling <see cref="RemoveTimeout(object)"/>.
/// </remarks>
public static object? AddTimeout (TimeSpan time, Func<bool> callback)
{
return MainLoop?.AddTimeout (time, callback) ?? null;
}
public static object? AddTimeout (TimeSpan time, Func<bool> callback) => ApplicationImpl.Instance.AddTimeout (time, callback);

/// <summary>Removes a previously scheduled timeout</summary>
/// <remarks>The token parameter is the value returned by <see cref="AddTimeout"/>.</remarks>
Expand All @@ -474,21 +393,11 @@ public static void Run (Toplevel view, Func<Exception, bool>? errorHandler = nul
/// This method also returns
/// <c>false</c>
/// if the timeout is not found.
public static bool RemoveTimeout (object token) { return MainLoop?.RemoveTimeout (token) ?? false; }
public static bool RemoveTimeout (object token) => ApplicationImpl.Instance.RemoveTimeout (token);

/// <summary>Runs <paramref name="action"/> on the thread that is processing events</summary>
/// <param name="action">the action to be invoked on the main processing thread.</param>
public static void Invoke (Action action)
{
MainLoop?.AddIdle (
() =>
{
action ();

return false;
}
);
}
public static void Invoke (Action action) => ApplicationImpl.Instance.Invoke (action);

// TODO: Determine if this is really needed. The only code that calls WakeUp I can find
// is ProgressBarStyles, and it's not clear it needs to.
Expand Down Expand Up @@ -517,8 +426,7 @@ public static void LayoutAndDraw (bool forceDraw = false)

View.SetClipToScreen ();
View.Draw (TopLevels, neededLayout || forceDraw);
View.SetClipToScreen ();

View.SetClipToScreen ();
Driver?.Refresh ();
}

Expand Down Expand Up @@ -612,31 +520,8 @@ public static bool RunIteration (ref RunState state, bool firstIteration = false
/// property on the currently running <see cref="Toplevel"/> to false.
/// </para>
/// </remarks>
public static void RequestStop (Toplevel? top = null)
{
if (top is null)
{
top = Top;
}

if (!top!.Running)
{
return;
}

var ev = new ToplevelClosingEventArgs (top);
top.OnClosing (ev);

if (ev.Cancel)
{
return;
}

top.Running = false;
OnNotifyStopRunState (top);
}

private static void OnNotifyStopRunState (Toplevel top)
public static void RequestStop (Toplevel? top = null) => ApplicationImpl.Instance.RequestStop (top);
internal static void OnNotifyStopRunState (Toplevel top)
{
if (EndAfterFirstIteration)
{
Expand Down
91 changes: 91 additions & 0 deletions Terminal.Gui/Application/Application.cd
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<ClassDiagram MajorVersion="1" MinorVersion="1">
<Class Name="Terminal.Gui.Application">
<Position X="2.25" Y="1.5" Width="1.5" />
<TypeIdentifier>
<HashCode>hEI4FAgAqARIspQfBQo0gTGiACNL0AICESJKoggBSg8=</HashCode>
<FileName>Application\Application.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.ApplicationNavigation" Collapsed="true">
<Position X="13.75" Y="1.75" Width="2" />
<TypeIdentifier>
<HashCode>AABAAAAAAABCAAAAAAAAAAAAAAAAIgIAAAAAAAAAAAA=</HashCode>
<FileName>Application\ApplicationNavigation.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.IterationEventArgs" Collapsed="true">
<Position X="16" Y="2" Width="2" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Application\IterationEventArgs.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.MainLoop" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="10.25" Y="2.75" Width="1.5" />
<TypeIdentifier>
<HashCode>CAAAIAAAASAAAQAQAAAAAIBADQAAEAAYIgIIwAAAAAI=</HashCode>
<FileName>Application\MainLoop.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="Terminal.Gui.MainLoopSyncContext" Collapsed="true">
<Position X="12" Y="2.75" Width="2" />
<TypeIdentifier>
<HashCode>AAAAAgAAAAAAAAAAAEAAAAAACAAAAAAAAAAAAAAAAAA=</HashCode>
<FileName>Application\MainLoopSyncContext.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.RunState" Collapsed="true" BaseTypeListCollapsed="true">
<Position X="14.25" Y="3" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAACACAgAAAAAAAAAAAAAAAAACQAAAAAAAAAA=</HashCode>
<FileName>Application\RunState.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" Collapsed="true" />
</Class>
<Class Name="Terminal.Gui.RunStateEventArgs" Collapsed="true">
<Position X="16" Y="3" Width="2" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA=</HashCode>
<FileName>Application\RunStateEventArgs.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.Timeout" Collapsed="true">
<Position X="10.25" Y="3.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAQAA=</HashCode>
<FileName>Application\Timeout.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.TimeoutEventArgs" Collapsed="true">
<Position X="12" Y="3.75" Width="2" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAACAIAAAAAAAAAAAA=</HashCode>
<FileName>Application\TimeoutEventArgs.cs</FileName>
</TypeIdentifier>
</Class>
<Class Name="Terminal.Gui.ApplicationImpl" BaseTypeListCollapsed="true">
<Position X="5.75" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAQAACAACAAAI=</HashCode>
<FileName>Application\ApplicationImpl.cs</FileName>
</TypeIdentifier>
<Lollipop Position="0.2" />
</Class>
<Interface Name="Terminal.Gui.IMainLoopDriver" Collapsed="true">
<Position X="12" Y="5" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAACAAAAAQAAAAABAAAAAAAEAAAAAAAAAAAAAA=</HashCode>
<FileName>Application\MainLoop.cs</FileName>
</TypeIdentifier>
</Interface>
<Interface Name="Terminal.Gui.IApplication">
<Position X="4" Y="1.75" Width="1.5" />
<TypeIdentifier>
<HashCode>AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAACAAAAAAI=</HashCode>
<FileName>Application\IApplication.cs</FileName>
</TypeIdentifier>
</Interface>
<Font Name="Segoe UI" Size="9" />
</ClassDiagram>
3 changes: 3 additions & 0 deletions Terminal.Gui/Application/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace Terminal.Gui;
/// <remarks></remarks>
public static partial class Application
{
internal static bool IsLegacy => ApplicationImpl.Instance.IsLegacy;

/// <summary>Gets all cultures supported by the application without the invariant language.</summary>
public static List<CultureInfo>? SupportedCultures { get; private set; }

Expand Down Expand Up @@ -225,4 +227,5 @@ internal static void ResetState (bool ignoreDisposed = false)
}

// Only return true if the Current has changed.
public static void AddIdle (Func<bool> func) => ApplicationImpl.Instance.AddIdle (func);
}
Loading
Loading