spectreconsole / spectre.console

A .NET library that makes it easier to create beautiful console applications.
https://spectreconsole.net
MIT License
9.1k stars 465 forks source link

Failing To Render On Windows Terminal #703

Open sangeethnandakumar opened 2 years ago

sangeethnandakumar commented 2 years ago

Ways To Reproduce:

  1. Setup Windows Terminal (https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701#activetab=pivot:overviewtab) with Visual Studio.
  2. Create a simple progress view

using Spectre.Console;

AnsiConsole.Progress()
    .Start(ctx =>
    {
        // Define tasks
        var task1 = ctx.AddTask("[green]Reticulating splines[/]");
        var task2 = ctx.AddTask("[green]Folding space[/]");

        while (!ctx.IsFinished)
        {
            task1.Increment(1.5);
            task2.Increment(0.5);
            Thread.Sleep(100);
        }
    });

Console.ReadKey();
  1. Run the program. This will print and updates an active progress bar image

  2. Now adjust the width of Windows Terminal while progress bar is running. The render fails and messes up the console. image

  3. There is no going back after this. Once console got messy, Even if we resize the console back to normal or maximized stage, The screen won't update to normal way


Please upvote :+1: this issue if you are interested in it.

mgood7123 commented 2 years ago

this works fine in Windows Console

do note, that trying to update text WHILE it is resizing is often UB in all terminals

after the console has been resized, the Row and Width info are unreliable and you cannot update test unless you restore to original or greater window size

for example if you know you need to move up 5 rows to get to the start, and to resize to 2 width, then moving up 5 rows will not get you to start, corrupting your output

same for columns and width

also occurs when resizing smaller than what is being updated in terms of height

such is very difficult to fix and standardize

sangeethnandakumar commented 1 year ago

That is true. When terminals are resizing it's challenging to keep up with size. But can we do something like this, Just an idea only not sure if it comes with challenges

  1. Listen to Window Resize events from running terminal
  2. When a resize event is triggered, Immediately store the current state of renderer
  3. Hault until window resize done
  4. Repaint the renderer state with updated window size

Anyways, Big thanks for this awesome library.

FrankRay78 commented 1 year ago

Is there a cross-platform / cross-terminal way to hook into window resize events?

sangeethnandakumar commented 11 months ago

How about this goes with cross-platform & cross-terminal?

Demo

image

Code

 static void Main(string[] args)
 {
     // Store the initial terminal size
     int initialWidth = Console.WindowWidth;
     int initialHeight = Console.WindowHeight;

     // Register for the terminal resize event
     Console.CancelKeyPress += (sender, eventArgs) =>
     {
         // Check if the event was a terminal resize (e.g., Ctrl+C)
         if (eventArgs.SpecialKey == ConsoleSpecialKey.ControlC)
         {
             // Handle the terminal resize event here
             Console.WriteLine("Terminal resized");
         }
     };

     // Continuously monitor and print the terminal size
     while (true)
     {
         // Check the current terminal size
         int currentWidth = Console.WindowWidth;
         int currentHeight = Console.WindowHeight;

         // Print the current size
         Console.WriteLine($"Terminal Size: {currentWidth} columns x {currentHeight} rows");

         // Sleep briefly before checking again
         Thread.Sleep(1000); // Adjust the sleep interval as needed
     }
 }
sangeethnandakumar commented 11 months ago

Other approaches include:


Mono Posix Approach

Otherwise for Unix-Like systems (like Linux or MacOS). We can also rely on this Mono Posix NuGet -

Mono.Posix is a library that provides access to Unix-specific APIs from C# or .NET applications. It allows you to interact with Unix-like operating systems (such as Linux and macOS) and access functionality not typically available in cross-platform .NET code.

Repo: https://github.com/mono/mono.posix

Couldn't check all the aspects of the below code fully. Only did a basic test on a VM running Ubuntu:

using System;
using Mono.Unix;
using Mono.Unix.Native;

class Program
{
    static void Main(string[] args)
    {
        // Register for the terminal resize signal
        UnixSignal[] signals = new UnixSignal[] {
            new UnixSignal(Signum.SIGWINCH)
        };

        Console.WriteLine("Listening for terminal resize events. Press Ctrl+C to exit.");

        while (true)
        {
            // Wait for a signal (including terminal resize)
            int index = UnixSignal.WaitAny(signals);

            // Check if the signal is due to a terminal resize
            if (signals[index].Signum == Signum.SIGWINCH)
            {
                Console.WriteLine("Terminal resized");
            }
        }
    }
}
FrankRay78 commented 9 months ago

It's an area I'm quite interested in, and thanks for the code contribution above @sangeethnandakumar. However, for me, at this time, it's unlikely I'll do anything more than simply watch this issue from the sidelines.