Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Console.SetWindowSize() for linux #75824

Merged
merged 12 commits into from
Nov 8, 2022
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);
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_NeedPosNum);
HJLeee marked this conversation as resolved.
Show resolved Hide resolved
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

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
{
HJLeee marked this conversation as resolved.
Show resolved Hide resolved
if (Console.IsOutputRedirected)
{
throw new IOException(SR.InvalidOperation_SetWindowSize);
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_NeedPosNum);
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

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)
HJLeee marked this conversation as resolved.
Show resolved Hide resolved
{
throw new IOException(SR.InvalidOperation_SetWindowSize);
danmoseley marked this conversation as resolved.
Show resolved Hide resolved
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width), width, SR.ArgumentOutOfRange_NeedPosNum);
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height), height, SR.ArgumentOutOfRange_NeedPosNum);
}
HJLeee marked this conversation as resolved.
Show resolved Hide resolved

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
HJLeee marked this conversation as resolved.
Show resolved Hide resolved
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()
HJLeee marked this conversation as resolved.
Show resolved Hide resolved
{
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
HJLeee marked this conversation as resolved.
Show resolved Hide resolved
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)]
adamsitnik marked this conversation as resolved.
Show resolved Hide resolved
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