using System.Collections.Concurrent; namespace Terminal.Gui.App; /// /// Implementation of core methods using the modern /// main loop architecture with component factories for different platforms. /// public partial class ApplicationImpl : IApplication { /// /// INTERNAL: Creates a new instance of the Application backend and subscribes to Application configuration property /// events. /// internal ApplicationImpl () { // Subscribe to Application static property change events Application.Force16ColorsChanged += OnForce16ColorsChanged; Application.ForceDriverChanged += OnForceDriverChanged; } /// /// INTERNAL: Creates a new instance of the Application backend. /// /// internal ApplicationImpl (IComponentFactory componentFactory) : this () { _componentFactory = componentFactory; } private string? _driverName; #region Clipboard /// public IClipboard? Clipboard => Driver?.Clipboard; #endregion Clipboard /// public new string ToString () => Driver?.ToString () ?? string.Empty; #region Singleton /// /// Lock object for synchronizing access to ModelUsage and _instance. /// private static readonly object _modelUsageLock = new (); /// /// Tracks which application model has been used in this process. /// public static ApplicationModelUsage ModelUsage { get; private set; } = ApplicationModelUsage.None; /// /// Error message for when trying to use modern model after legacy static model. /// internal const string ERROR_MODERN_AFTER_LEGACY = "Cannot use modern instance-based model (Application.Create) after using legacy static Application model (Application.Init/ApplicationImpl.Instance). " + "Use only one model per process."; /// /// Error message for when trying to use legacy static model after modern model. /// internal const string ERROR_LEGACY_AFTER_MODERN = "Cannot use legacy static Application model (Application.Init/ApplicationImpl.Instance) after using modern instance-based model (Application.Create). " + "Use only one model per process."; /// /// Configures the singleton instance of to use the specified backend implementation. /// /// public static void SetInstance (IApplication? app) { lock (_modelUsageLock) { ModelUsage = ApplicationModelUsage.LegacyStatic; _instance = app; } } // Private static readonly Lazy instance of Application private static IApplication? _instance; /// /// Gets the currently configured backend implementation of gateway methods. /// public static IApplication Instance { get { //Debug.Fail ("ApplicationImpl.Instance accessed - parallelizable tests should not use legacy static Application model"); // Thread-safe: Use lock to make check-and-create atomic lock (_modelUsageLock) { // If an instance already exists, return it without fence checking // This allows for cleanup/reset operations if (_instance is { }) { return _instance; } // Check if the instance-based model has already been used if (ModelUsage == ApplicationModelUsage.InstanceBased) { throw new InvalidOperationException (ERROR_LEGACY_AFTER_MODERN); } // Mark the usage and create the instance ModelUsage = ApplicationModelUsage.LegacyStatic; return _instance = new ApplicationImpl (); } } } /// /// INTERNAL: Marks that the instance-based model has been used. Called by Application.Create(). /// internal static void MarkInstanceBasedModelUsed () { lock (_modelUsageLock) { // Check if the legacy static model has already been initialized if (ModelUsage == ApplicationModelUsage.LegacyStatic && _instance?.Initialized == true) { throw new InvalidOperationException (ERROR_MODERN_AFTER_LEGACY); } ModelUsage = ApplicationModelUsage.InstanceBased; } } /// /// INTERNAL: Resets the model usage tracking. Only for testing purposes. /// internal static void ResetModelUsageTracking () { lock (_modelUsageLock) { ModelUsage = ApplicationModelUsage.None; _instance = null; } } /// /// INTERNAL: Resets state without going through the fence-checked Instance property. /// Used by Application.ResetState() to allow cleanup regardless of which model was used. /// internal static void ResetStateStatic (bool ignoreDisposed = false) { // If an instance exists, reset it _instance?.ResetState (ignoreDisposed); // Reset Application static properties to their defaults // This ensures tests start with clean state Application.ForceDriver = string.Empty; Application.Force16Colors = false; Application.IsMouseDisabled = false; Application.QuitKey = Key.Esc; Application.ArrangeKey = Key.F5.WithCtrl; Application.NextTabGroupKey = Key.F6; Application.NextTabKey = Key.Tab; Application.PrevTabGroupKey = Key.F6.WithShift; Application.PrevTabKey = Key.Tab.WithShift; // Always reset the model tracking to allow tests to use either model after reset ResetModelUsageTracking (); } #endregion Singleton #region Input private IMouse? _mouse; /// /// Handles mouse event state and processing. /// public IMouse Mouse { get { _mouse ??= new MouseImpl { App = this }; return _mouse; } set => _mouse = value ?? throw new ArgumentNullException (nameof (value)); } private IKeyboard? _keyboard; /// /// Handles keyboard input and key bindings at the Application level /// public IKeyboard Keyboard { get { _keyboard ??= new KeyboardImpl { App = this }; return _keyboard; } set => _keyboard = value ?? throw new ArgumentNullException (nameof (value)); } #endregion Input #region View Management private ApplicationPopover? _popover; /// public ApplicationPopover? Popover { get { _popover ??= new () { App = this }; return _popover; } set => _popover = value; } private ApplicationNavigation? _navigation; /// public ApplicationNavigation? Navigation { get { _navigation ??= new () { App = this }; return _navigation; } set => _navigation = value ?? throw new ArgumentNullException (nameof (value)); } private Toplevel? _topRunnable; /// public Toplevel? TopRunnable { get => _topRunnable; set { _topRunnable = value; if (_topRunnable is { }) { _topRunnable.App = this; } } } /// public ConcurrentStack SessionStack { get; } = new (); /// public Toplevel? CachedSessionTokenToplevel { get; set; } /// public ConcurrentStack? RunnableSessionStack { get; } = new (); /// public IRunnable? FrameworkOwnedRunnable { get; set; } #endregion View Management }