From 5783a9081dd2716349ac5893b1093660b56f0a7c Mon Sep 17 00:00:00 2001 From: Evgeny Gorbovoy Date: Tue, 21 Nov 2023 06:41:41 +0100 Subject: [PATCH 01/11] pause of IO --- src/Consolonia.Core/ApplicationStartup.cs | 2 +- .../Infrastructure/ConsoleWindow.cs | 28 +- .../Infrastructure/ConsoloniaLifetime.cs | 41 +++ .../Infrastructure/DefaultNetConsole.cs | 23 +- .../Infrastructure/IConsole.cs | 3 + .../InputLessDefaultNetConsole.cs | 24 +- .../GalleryViews/GalleryAnimatedLines.axaml | 246 +++++++++--------- .../GalleryAnimatedLines.axaml.cs | 14 + src/Consolonia.GuiCS/binding.cs | 3 + .../CursesConsole.cs | 12 +- .../WindowsConsole.cs | 67 ++++- .../Consolonia.TestsCore/UnitTestConsole.cs | 11 + 12 files changed, 332 insertions(+), 142 deletions(-) create mode 100644 src/Consolonia.Core/Infrastructure/ConsoloniaLifetime.cs diff --git a/src/Consolonia.Core/ApplicationStartup.cs b/src/Consolonia.Core/ApplicationStartup.cs index 824f446f..41fcb8a4 100644 --- a/src/Consolonia.Core/ApplicationStartup.cs +++ b/src/Consolonia.Core/ApplicationStartup.cs @@ -68,7 +68,7 @@ public static ClassicDesktopStyleApplicationLifetime BuildLifetime(IConsol private static ClassicDesktopStyleApplicationLifetime CreateLifetime(T builder, string[] args) where T : AppBuilderBase, new() { - var lifetime = new ClassicDesktopStyleApplicationLifetime + var lifetime = new ConsoloniaLifetime { Args = args, ShutdownMode = ShutdownMode.OnMainWindowClose diff --git a/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs b/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs index 1900798c..2d4d43af 100644 --- a/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs +++ b/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs @@ -15,7 +15,7 @@ namespace Consolonia.Core.Infrastructure { internal class ConsoleWindow : IWindowImpl { - [NotNull] private readonly IConsole _console; + [NotNull] internal readonly IConsole Console; private readonly IKeyboardDevice _myKeyboardDevice; internal readonly List InvalidatedRects = new(50); private IInputRoot _inputRoot; @@ -24,21 +24,21 @@ public ConsoleWindow() { _myKeyboardDevice = AvaloniaLocator.Current.GetService(); MouseDevice = AvaloniaLocator.Current.GetService(); - _console = AvaloniaLocator.Current.GetService() ?? throw new NotImplementedException(); - _console.Resized += OnConsoleOnResized; - _console.KeyEvent += ConsoleOnKeyEvent; - _console.MouseEvent += ConsoleOnMouseEvent; - _console.FocusEvent += ConsoleOnFocusEvent; + Console = AvaloniaLocator.Current.GetService() ?? throw new NotImplementedException(); + Console.Resized += OnConsoleOnResized; + Console.KeyEvent += ConsoleOnKeyEvent; + Console.MouseEvent += ConsoleOnMouseEvent; + Console.FocusEvent += ConsoleOnFocusEvent; } public void Dispose() { Closed?.Invoke(); - _console.Resized -= OnConsoleOnResized; - _console.KeyEvent -= ConsoleOnKeyEvent; - _console.MouseEvent -= ConsoleOnMouseEvent; - _console.FocusEvent -= ConsoleOnFocusEvent; - _console.Dispose(); + Console.Resized -= OnConsoleOnResized; + Console.KeyEvent -= ConsoleOnKeyEvent; + Console.MouseEvent -= ConsoleOnMouseEvent; + Console.FocusEvent -= ConsoleOnFocusEvent; + Console.Dispose(); } public IRenderer CreateRenderer(IRenderRoot root) @@ -114,7 +114,7 @@ public Size ClientSize { get { - PixelBufferSize pixelBufferSize = _console.Size; + PixelBufferSize pixelBufferSize = Console.Size; return new Size(pixelBufferSize.Width, pixelBufferSize.Height); } } @@ -181,7 +181,7 @@ public void SetTopmost(bool value) public void SetTitle(string title) { - _console.SetTitle(title); + Console.SetTitle(title); } public void SetParent(IWindowImpl parent) @@ -316,7 +316,7 @@ private void OnConsoleOnResized() { Dispatcher.UIThread.Post(() => { - PixelBufferSize pixelBufferSize = _console.Size; + PixelBufferSize pixelBufferSize = Console.Size; var size = new Size(pixelBufferSize.Width, pixelBufferSize.Height); Resized(size, PlatformResizeReason.Unspecified); //todo; Invalidate(new Rect(size)); diff --git a/src/Consolonia.Core/Infrastructure/ConsoloniaLifetime.cs b/src/Consolonia.Core/Infrastructure/ConsoloniaLifetime.cs new file mode 100644 index 00000000..b7289609 --- /dev/null +++ b/src/Consolonia.Core/Infrastructure/ConsoloniaLifetime.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; + +namespace Consolonia.Core.Infrastructure; + +public class ConsoloniaLifetime : ClassicDesktopStyleApplicationLifetime +{ + /// + /// returned task indicates that console is successfully paused + /// + public Task DisconnectFromConsoleAsync(CancellationToken cancellationToken) + { + var taskToWaitFor = new TaskCompletionSource(); + cancellationToken.Register(() => taskToWaitFor.SetResult()); + + var mainWindowPlatformImpl = (ConsoleWindow)MainWindow.PlatformImpl; + IConsole console = mainWindowPlatformImpl.Console; + + Task pauseTask = taskToWaitFor.Task; + + console.PauseIO(pauseTask); + + pauseTask.ContinueWith(_ => + { + mainWindowPlatformImpl.Console.ClearOutput(); + + Dispatcher.UIThread.Post(() => + { + MainWindow.InvalidateVisual(); + }); + }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); + + return Dispatcher.UIThread.InvokeAsync(() => + { + + }); + } +} \ No newline at end of file diff --git a/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs index ba9f245d..47cf878a 100644 --- a/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/DefaultNetConsole.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Threading; +using System.Threading.Tasks; using Avalonia.Input; using Consolonia.Core.InternalHelpers; @@ -49,13 +51,32 @@ protected override void Dispose(bool disposing) RaiseFocusEvent(false); } + public override void PauseIO(Task task) + { + base.PauseIO(task); + + TextReader defaultIn = Console.In; + Console.SetIn(new StringReader(string.Empty)); + Console.SetIn(defaultIn); + } + private void StartInputReading() { ThreadPool.QueueUserWorkItem(_ => { while (!Disposed) { - ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(true); + PauseTask?.Wait(); + + ConsoleKeyInfo consoleKeyInfo; + try + { + consoleKeyInfo = Console.ReadKey(true); + } + catch (InvalidOperationException) + { + continue; + } Key key = ConvertToKey(consoleKeyInfo.Key); diff --git a/src/Consolonia.Core/Infrastructure/IConsole.cs b/src/Consolonia.Core/Infrastructure/IConsole.cs index beb16511..48a477e5 100644 --- a/src/Consolonia.Core/Infrastructure/IConsole.cs +++ b/src/Consolonia.Core/Infrastructure/IConsole.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Avalonia; using Avalonia.Input; using Avalonia.Input.Raw; @@ -27,5 +28,7 @@ void Print(PixelBufferCoordinate bufferPoint, ConsoleColor backgroundColor, Cons event Action MouseEvent; event Action FocusEvent; + void PauseIO(Task task); + void ClearOutput(); } } \ No newline at end of file diff --git a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs index 91a4a4d0..702b3d90 100644 --- a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs @@ -20,7 +20,7 @@ public class InputLessDefaultNetConsole : IConsole protected InputLessDefaultNetConsole() { Console.CursorVisible = false; - ActualizeTheSize(); + ActualizeSize(); } protected bool Disposed { get; private set; } @@ -67,6 +67,7 @@ public PixelBufferCoordinate GetCaretPosition() public void Print(PixelBufferCoordinate bufferPoint, ConsoleColor backgroundColor, ConsoleColor foregroundColor, string str) { + PauseTask?.Wait(); SetCaretPosition(bufferPoint); if (_headBackground != backgroundColor) @@ -102,6 +103,14 @@ public void Print(PixelBufferCoordinate bufferPoint, ConsoleColor backgroundColo public event Action KeyEvent; public event Action MouseEvent; public event Action FocusEvent; + + public virtual void PauseIO(Task task) + { + task.ContinueWith(_ => { PauseTask = null; }, TaskScheduler.Default); + PauseTask = task; + } + + protected Task PauseTask { get; private set; } public void Dispose() { @@ -113,17 +122,23 @@ public void Dispose() protected bool CheckActualizeTheSize() { if (Size.Width == Console.WindowWidth && Size.Height == Console.WindowHeight) return false; - ActualizeTheSize(); + ActualizeSize(); return true; } - protected void ActualizeTheSize() + protected void ActualizeSize() { - Console.Clear(); Size = new PixelBufferSize((ushort)Console.WindowWidth, (ushort)Console.WindowHeight); Resized?.Invoke(); } + public void ClearOutput() + { + // this is hack, but somehow it does not work when just calling ActualizeSize with same size + Size = new PixelBufferSize(1, 1); + Resized?.Invoke(); + } + protected void RaiseMouseEvent(RawPointerEventType eventType, Point point, Vector? wheelDelta, RawInputModifiers modifiers) { @@ -151,6 +166,7 @@ protected void StartSizeCheckTimerAsync(uint slowInterval = 1500) { while (!Disposed) { + PauseTask?.Wait(); int timeout = (int)(CheckActualizeTheSize() ? 1 : slowInterval); await Task.Delay(timeout).ConfigureAwait(false); } diff --git a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryAnimatedLines.axaml b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryAnimatedLines.axaml index 0256ee82..b537afbb 100644 --- a/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryAnimatedLines.axaml +++ b/src/Consolonia.Gallery/Gallery/GalleryViews/GalleryAnimatedLines.axaml @@ -7,129 +7,135 @@ d:DesignWidth="800" d:DesignHeight="450" x:Class="Consolonia.Gallery.Gallery.GalleryViews.GalleryAnimatedLines"> - - - - - - + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + +