mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 17:57:57 +01:00
177 lines
7.2 KiB
C#
177 lines
7.2 KiB
C#
#nullable enable
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace Terminal.Gui.App;
|
|
|
|
public partial class ApplicationImpl
|
|
{
|
|
/// <inheritdoc/>
|
|
public IDriver? Driver { get; set; }
|
|
|
|
/// <inheritdoc/>
|
|
public bool Force16Colors { get; set; }
|
|
|
|
/// <inheritdoc/>
|
|
public string ForceDriver { get; set; } = string.Empty;
|
|
|
|
/// <inheritdoc/>
|
|
public List<SixelToRender> Sixel { get; } = new ();
|
|
|
|
/// <summary>
|
|
/// Creates the appropriate <see cref="IDriver"/> based on platform and driverName.
|
|
/// </summary>
|
|
/// <param name="driverName"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="Exception"></exception>
|
|
/// <exception cref="InvalidOperationException"></exception>
|
|
private void CreateDriver (string? 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 ());
|
|
_driverName = "fake";
|
|
}
|
|
else if (factoryIsWindows || (!factoryIsDotNet && !factoryIsUnix && nameIsWindows))
|
|
{
|
|
Coordinator = CreateSubcomponents (() => new WindowsComponentFactory ());
|
|
_driverName = "windows";
|
|
}
|
|
else if (factoryIsDotNet || (!factoryIsWindows && !factoryIsUnix && nameIsDotNet))
|
|
{
|
|
Coordinator = CreateSubcomponents (() => new NetComponentFactory ());
|
|
_driverName = "dotnet";
|
|
}
|
|
else if (factoryIsUnix || (!factoryIsWindows && !factoryIsDotNet && nameIsUnix))
|
|
{
|
|
Coordinator = CreateSubcomponents (() => new UnixComponentFactory ());
|
|
_driverName = "unix";
|
|
}
|
|
else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
|
|
{
|
|
Coordinator = CreateSubcomponents (() => new WindowsComponentFactory ());
|
|
_driverName = "windows";
|
|
}
|
|
else if (p == PlatformID.Unix)
|
|
{
|
|
Coordinator = CreateSubcomponents (() => new UnixComponentFactory ());
|
|
_driverName = "unix";
|
|
}
|
|
else
|
|
{
|
|
Logging.Information($"Falling back to dotnet driver.");
|
|
Coordinator = CreateSubcomponents (() => new NetComponentFactory ());
|
|
_driverName = "dotnet";
|
|
}
|
|
|
|
Logging.Trace ($"Created Subcomponents: {Coordinator}");
|
|
|
|
Coordinator.StartInputTaskAsync ().Wait ();
|
|
|
|
if (Driver == null)
|
|
{
|
|
throw new ("Driver was null even after booting MainLoopCoordinator");
|
|
}
|
|
}
|
|
|
|
private readonly IComponentFactory? _componentFactory;
|
|
|
|
/// <summary>
|
|
/// INTERNAL: Gets or sets the main loop coordinator that orchestrates the application's event processing,
|
|
/// input handling, and rendering pipeline.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The <see cref="IMainLoopCoordinator"/> is the central component responsible for:
|
|
/// <list type="bullet">
|
|
/// <item>Managing the platform-specific input thread that reads from the console</item>
|
|
/// <item>Coordinating the main application loop via <see cref="IMainLoopCoordinator.RunIteration"/></item>
|
|
/// <item>Processing queued input events and translating them to Terminal.Gui events</item>
|
|
/// <item>Managing the <see cref="ApplicationMainLoop{TInputRecord}"/> that handles rendering</item>
|
|
/// <item>Executing scheduled timeouts and callbacks via <see cref="ITimedEvents"/></item>
|
|
/// </list>
|
|
/// </para>
|
|
/// <para>
|
|
/// The coordinator is created in <see cref="CreateDriver"/> based on the selected driver
|
|
/// (Windows, Unix, .NET, or Fake) and is started by calling
|
|
/// <see cref="IMainLoopCoordinator.StartInputTaskAsync"/>.
|
|
/// </para>
|
|
/// </remarks>
|
|
internal IMainLoopCoordinator? Coordinator { get; private set; }
|
|
|
|
/// <summary>
|
|
/// INTERNAL: Creates a <see cref="MainLoopCoordinator{TInputRecord}"/> with the appropriate component factory
|
|
/// for the specified input record type.
|
|
/// </summary>
|
|
/// <typeparam name="TInputRecord">
|
|
/// Platform-specific input type: <see cref="ConsoleKeyInfo"/> (.NET/Fake),
|
|
/// <see cref="WindowsConsole.InputRecord"/> (Windows), or <see cref="char"/> (Unix).
|
|
/// </typeparam>
|
|
/// <param name="fallbackFactory">
|
|
/// Factory function to create the component factory if <see cref="_componentFactory"/>
|
|
/// is not of type <see cref="IComponentFactory{TInputRecord}"/>.
|
|
/// </param>
|
|
/// <returns>
|
|
/// A <see cref="MainLoopCoordinator{TInputRecord}"/> configured with the input queue,
|
|
/// main loop, timed events, and selected component factory.
|
|
/// </returns>
|
|
private IMainLoopCoordinator CreateSubcomponents<TInputRecord> (Func<IComponentFactory<TInputRecord>> fallbackFactory) where TInputRecord : struct
|
|
{
|
|
ConcurrentQueue<TInputRecord> inputQueue = new ();
|
|
ApplicationMainLoop<TInputRecord> loop = new ();
|
|
|
|
IComponentFactory<TInputRecord> cf;
|
|
|
|
if (_componentFactory is IComponentFactory<TInputRecord> typedFactory)
|
|
{
|
|
cf = typedFactory;
|
|
}
|
|
else
|
|
{
|
|
cf = fallbackFactory ();
|
|
}
|
|
|
|
return new MainLoopCoordinator<TInputRecord> (_timedEvents, inputQueue, loop, cf);
|
|
}
|
|
|
|
internal void SubscribeDriverEvents ()
|
|
{
|
|
ArgumentNullException.ThrowIfNull (Driver);
|
|
|
|
Driver.SizeChanged += Driver_SizeChanged;
|
|
Driver.KeyDown += Driver_KeyDown;
|
|
Driver.KeyUp += Driver_KeyUp;
|
|
Driver.MouseEvent += Driver_MouseEvent;
|
|
}
|
|
|
|
internal void UnsubscribeDriverEvents ()
|
|
{
|
|
ArgumentNullException.ThrowIfNull (Driver);
|
|
|
|
Driver.SizeChanged -= Driver_SizeChanged;
|
|
Driver.KeyDown -= Driver_KeyDown;
|
|
Driver.KeyUp -= Driver_KeyUp;
|
|
Driver.MouseEvent -= Driver_MouseEvent;
|
|
}
|
|
|
|
private void Driver_KeyDown (object? sender, Key e) { Keyboard?.RaiseKeyDownEvent (e); }
|
|
|
|
private void Driver_KeyUp (object? sender, Key e) { Keyboard?.RaiseKeyUpEvent (e); }
|
|
|
|
private void Driver_MouseEvent (object? sender, MouseEventArgs e) { Mouse?.RaiseMouseEvent (e); }
|
|
}
|