Skip to content

Commit

Permalink
Implement Console.SetWindowSize() for linux (#75824)
Browse files Browse the repository at this point in the history
Co-authored-by: Stephen Toub <stoub@microsoft.com>
Co-authored-by: Michał Petryka <35800402+MichalPetryka@users.noreply.github.com>
Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
Co-authored-by: Dan Moseley <danmose@microsoft.com>
  • Loading branch information
5 people committed Nov 8, 2022
1 parent 86611ab commit 32c7147
Show file tree
Hide file tree
Showing 15 changed files with 257 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ internal struct WinSize

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetWindowSize", SetLastError = true)]
internal static partial int GetWindowSize(out WinSize winSize);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetWindowSize", SetLastError = true)]
internal static partial int SetWindowSize(in WinSize winSize);
}
}
17 changes: 14 additions & 3 deletions src/libraries/System.Console/ref/System.Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,18 @@ public static partial class Console
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
public static bool TreatControlCAsInput { get { throw null; } set { } }
public static int WindowHeight { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
public static int WindowHeight { get { throw null; } set { } }
public static int WindowLeft { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } }
public static int WindowTop { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } }
public static int WindowWidth { [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios"), System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
public static int WindowWidth { get { throw null; } set { } }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
Expand Down Expand Up @@ -147,7 +155,10 @@ public static void SetIn(System.IO.TextReader newIn) { }
public static void SetOut(System.IO.TextWriter newOut) { }
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
public static void SetWindowPosition(int left, int top) { }
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("android")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")]
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")]
public static void SetWindowSize(int width, int height) { }
public static void Write(bool value) { }
public static void Write(char value) { }
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/System.Console/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
<data name="InvalidOperation_ConsoleReadKeyOnFile" xml:space="preserve">
<value>Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.</value>
</data>
<data name="InvalidOperation_SetWindowSize" xml:space="preserve">
<value>Cannot set window size when console output has been redirected.</value>
</data>
<data name="PersistedFiles_NoHomeDirectory" xml:space="preserve">
<value>The home directory of the current user could not be determined.</value>
</data>
Expand Down
68 changes: 55 additions & 13 deletions src/libraries/System.Console/src/System/Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,26 +392,50 @@ public static int WindowTop
set { ConsolePal.WindowTop = value; }
}

[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
public static int WindowWidth
{
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
get { return ConsolePal.WindowWidth; }
[SupportedOSPlatform("windows")]
set { ConsolePal.WindowWidth = value; }
set
{
if (Console.IsOutputRedirected)
{
throw new IOException(SR.InvalidOperation_SetWindowSize);
}

if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_NeedPosNum);
}

ConsolePal.WindowWidth = value;
}
}

[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
public static int WindowHeight
{
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
get { return ConsolePal.WindowHeight; }
[SupportedOSPlatform("windows")]
set { ConsolePal.WindowHeight = value; }
set
{
if (Console.IsOutputRedirected)
{
throw new IOException(SR.InvalidOperation_SetWindowSize);
}

if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_NeedPosNum);
}

ConsolePal.WindowHeight = value;
}
}

[SupportedOSPlatform("windows")]
Expand All @@ -420,9 +444,27 @@ public static void SetWindowPosition(int left, int top)
ConsolePal.SetWindowPosition(left, top);
}

[SupportedOSPlatform("windows")]
[UnsupportedOSPlatform("android")]
[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
public static void SetWindowSize(int width, int height)
{
if (Console.IsOutputRedirected)
{
throw new IOException(SR.InvalidOperation_SetWindowSize);
}

if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width), width, SR.ArgumentOutOfRange_NeedPosNum);
}

if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), height, SR.ArgumentOutOfRange_NeedPosNum);
}

ConsolePal.SetWindowSize(width, height);
}

Expand Down
23 changes: 20 additions & 3 deletions src/libraries/System.Console/src/System/ConsolePal.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ public static int WindowWidth
GetWindowSize(out int width, out _);
return width;
}
set { throw new PlatformNotSupportedException(); }
set => SetWindowSize(value, WindowHeight);
}

public static int WindowHeight
Expand All @@ -356,7 +356,7 @@ public static int WindowHeight
GetWindowSize(out _, out int height);
return height;
}
set { throw new PlatformNotSupportedException(); }
set => SetWindowSize(WindowWidth, value);
}

private static void GetWindowSize(out int width, out int height)
Expand Down Expand Up @@ -392,7 +392,24 @@ public static void SetWindowPosition(int left, int top)

public static void SetWindowSize(int width, int height)
{
throw new PlatformNotSupportedException();
lock (Console.Out)
{
Interop.Sys.WinSize winsize = default;
winsize.Row = (ushort)height;
winsize.Col = (ushort)width;
if (Interop.Sys.SetWindowSize(in winsize) == 0)
{
s_windowWidth = winsize.Col;
s_windowHeight = winsize.Row;
}
else
{
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
throw errorInfo.Error == Interop.Error.ENOTSUP ?
new PlatformNotSupportedException() :
Interop.GetIOException(errorInfo);
}
}
}

public static bool CursorVisible
Expand Down
5 changes: 0 additions & 5 deletions src/libraries/System.Console/src/System/ConsolePal.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -948,11 +948,6 @@ public static unsafe void SetWindowPosition(int left, int top)

public static unsafe void SetWindowSize(int width, int height)
{
if (width <= 0)
throw new ArgumentOutOfRangeException(nameof(width), width, SR.ArgumentOutOfRange_NeedPosNum);
if (height <= 0)
throw new ArgumentOutOfRangeException(nameof(height), height, SR.ArgumentOutOfRange_NeedPosNum);

// Get the position of the current console window
Interop.Kernel32.CONSOLE_SCREEN_BUFFER_INFO csbi = GetBufferInfo();

Expand Down
33 changes: 33 additions & 0 deletions src/libraries/System.Console/tests/ManualTests/ManualTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Xunit;

namespace System
Expand Down Expand Up @@ -321,6 +323,37 @@ public static void EncodingTest()
AssertUserExpectedResults("Pi and Sigma or question marks");
}

[ConditionalFact(nameof(ManualTestsEnabled))]
[SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on Browser, iOS, MacCatalyst, or tvOS.")]
public static void ResizeTest()
{
bool wasResized = false;

using (ManualResetEvent manualResetEvent = new(false))
using (PosixSignalRegistration.Create(PosixSignal.SIGWINCH,
ctx =>
{
wasResized = true;
Assert.Equal(PosixSignal.SIGWINCH, ctx.Signal);
manualResetEvent.Set();
}))
{
int widthBefore = Console.WindowWidth;
int heightBefore = Console.WindowHeight;

Assert.False(wasResized);

Console.SetWindowSize(widthBefore / 2, heightBefore / 2);

Assert.True(manualResetEvent.WaitOne(TimeSpan.FromMilliseconds(50)));
Assert.True(wasResized);
Assert.Equal(widthBefore / 2, Console.WindowWidth );
Assert.Equal(heightBefore / 2, Console.WindowHeight );

Console.SetWindowSize(widthBefore, heightBefore);
}
}

private static void AssertUserExpectedResults(string expected)
{
Console.Write($"Did you see {expected}? [y/n] ");
Expand Down
24 changes: 12 additions & 12 deletions src/libraries/System.Console/tests/WindowAndCursorProps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static void SetBufferSize_Unix_ThrowsPlatformNotSupportedException()
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)]
[PlatformSpecific((TestPlatforms.Windows) | (TestPlatforms.AnyUnix & ~TestPlatforms.Browser & ~TestPlatforms.iOS & ~TestPlatforms.MacCatalyst & ~TestPlatforms.tvOS))]
[InlineData(0)]
[InlineData(-1)]
public static void WindowWidth_SetInvalid_ThrowsArgumentOutOfRangeException(int value)
Expand All @@ -71,14 +71,14 @@ public static void WindowWidth_GetUnix_Success()
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser)] // Expected behavior specific to Unix
[PlatformSpecific(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS)] // Expected behavior specific to Unix
public static void WindowWidth_SetUnix_ThrowsPlatformNotSupportedException()
{
Assert.Throws<PlatformNotSupportedException>(() => Console.WindowWidth = 100);
}

[Theory]
[PlatformSpecific(TestPlatforms.Windows)] // Expected behavior specific to Windows
[PlatformSpecific((TestPlatforms.Windows) | (TestPlatforms.AnyUnix & ~TestPlatforms.Browser & ~TestPlatforms.iOS & ~TestPlatforms.MacCatalyst & ~TestPlatforms.tvOS))]
[InlineData(0)]
[InlineData(-1)]
public static void WindowHeight_SetInvalid_ThrowsArgumentOutOfRangeException(int value)
Expand All @@ -102,13 +102,6 @@ public static void WindowHeight_GetUnix_Success()
Helpers.RunInRedirectedOutput((data) => Console.WriteLine(Console.WindowHeight));
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)] // Expected behavior specific to Unix
public static void WindowHeight_SetUnix_ThrowsPlatformNotSupportedException()
{
Assert.Throws<PlatformNotSupportedException>(() => Console.WindowHeight = 100);
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser & ~TestPlatforms.iOS & ~TestPlatforms.MacCatalyst & ~TestPlatforms.tvOS)] // Expected behavior specific to Unix
public static void LargestWindowWidth_UnixGet_ReturnsExpected()
Expand All @@ -117,6 +110,13 @@ public static void LargestWindowWidth_UnixGet_ReturnsExpected()
Helpers.RunInRedirectedOutput((data) => Assert.Equal(Console.WindowWidth, Console.LargestWindowWidth));
}

[Fact]
[PlatformSpecific(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS)] // Expected behavior specific to Unix
public static void WindowHeight_SetUnix_ThrowsPlatformNotSupportedException()
{
Assert.Throws<PlatformNotSupportedException>(() => Console.WindowHeight = 100);
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.Browser & ~TestPlatforms.iOS & ~TestPlatforms.MacCatalyst & ~TestPlatforms.tvOS)] // Expected behavior specific to Unix
public static void LargestWindowHeight_UnixGet_ReturnsExpected()
Expand Down Expand Up @@ -559,7 +559,7 @@ public void SetWindowPosition_Unix_ThrowsPlatformNotSupportedException()
Assert.Throws<PlatformNotSupportedException>(() => Console.SetWindowPosition(50, 50));
}

[PlatformSpecific(TestPlatforms.Windows)]
[PlatformSpecific((TestPlatforms.Windows) | (TestPlatforms.AnyUnix & ~TestPlatforms.Browser & ~TestPlatforms.iOS & ~TestPlatforms.MacCatalyst & ~TestPlatforms.tvOS))]
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoNorServerCore))]
public void SetWindowSize_GetWindowSize_ReturnsExpected()
{
Expand Down Expand Up @@ -587,7 +587,7 @@ public void SetWindowSize_GetWindowSize_ReturnsExpected()
}

[Fact]
[PlatformSpecific(TestPlatforms.AnyUnix)]
[PlatformSpecific(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS)]
public void SetWindowSize_Unix_ThrowsPlatformNotSupportedException()
{
Assert.Throws<PlatformNotSupportedException>(() => Console.SetWindowSize(50, 50));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ public ArgumentOutOfRangeException(string? message, Exception? innerException)
HResult = HResults.COR_E_ARGUMENTOUTOFRANGE;
}

// We will not use this in the classlibs, but we'll provide it for
// anyone that's really interested so they don't have to stick a bunch
// of printf's in their code.
public ArgumentOutOfRangeException(string? paramName, object? actualValue, string? message)
: base(message, paramName)
{
Expand Down Expand Up @@ -87,9 +84,6 @@ public override string Message
}

// Gets the value of the argument that caused the exception.
// Note - we don't set this anywhere in the class libraries in
// version 1, but it might come in handy for other developers who
// want to avoid sticking printf's in their code.
public virtual object? ActualValue => _actualValue;
}
}
Loading

0 comments on commit 32c7147

Please sign in to comment.