From ee4565066097992ebb36d5b1397c92d517e315a7 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 31 May 2017 10:26:41 -0700 Subject: [PATCH] Enable PSReadline to work on NanoServer (#3815) NanoServer is missing some console APIs that PSReadline uses which causes PSReadline to not be usable on NanoServer docker images. For the missing font/display APIs, the fix is to assume all characters use 1 cell which is not always correct for some languages. For the missing keymap APIs - there is no workaround in this change and a small number of key bindings don't work, there is an open issue to track those. Fixes #3463 --- .../ConsoleKeyChordConverter.cs | 6 +-- .../ConsoleLib.cs | 47 +++++++++++++++---- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs index 64de462c6..6abaa815e 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleKeyChordConverter.cs @@ -1,4 +1,4 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -231,7 +231,6 @@ namespace Microsoft.PowerShell return valid; } -#if UNIX // this is borrowed from the CoreFX internal System.IO.StdInReader class // https://github.com/dotnet/corefx/blob/5b2ae6aa485773cd5569f56f446698633c9ad945/src/System.Console/src/System/IO/StdInReader.cs#L222 private static ConsoleKey GetKeyFromCharValue(char x, out bool isShift, out bool isCtrl) @@ -303,7 +302,8 @@ namespace Microsoft.PowerShell return default(ConsoleKey); } -#else + +#if !UNIX internal static char GetCharFromConsoleKey(ConsoleKey key, ConsoleModifiers modifiers) { // default for unprintables and unhandled diff --git a/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs index 01090bae3..98b9eb665 100644 --- a/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs +++ b/src/Microsoft.PowerShell.PSReadLine/ConsoleLib.cs @@ -1,4 +1,4 @@ -/********************************************************************++ +/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ @@ -370,6 +370,23 @@ namespace Microsoft.PowerShell.Internal internal static class ConsoleKeyInfoExtension { +#if !UNIX + private static bool _toUnicodeApiAvailable = true; + + static ConsoleKeyInfoExtension() + { + try + { + var chars = new char[2]; + NativeMethods.ToUnicode(13, 28, null, chars, chars.Length, 0); + } + catch (System.EntryPointNotFoundException) + { + _toUnicodeApiAvailable = false; // api not available on NanoServer + } + } +#endif + public static string ToGestureString(this ConsoleKeyInfo key) { var mods = key.Modifiers; @@ -386,13 +403,16 @@ namespace Microsoft.PowerShell.Internal sb.Append("Alt"); } -#if UNIX char c = key.KeyChar; -#else - // Windows cannot use KeyChar as some chords (like Ctrl+[) show up as control characters. - char c = ConsoleKeyChordConverter.GetCharFromConsoleKey(key.Key, - (mods & ConsoleModifiers.Shift) != 0 ? ConsoleModifiers.Shift : 0); +#if !UNIX + if (_toUnicodeApiAvailable) + { + // Windows cannot use KeyChar as some chords (like Ctrl+[) show up as control characters. + c = ConsoleKeyChordConverter.GetCharFromConsoleKey(key.Key, + (mods & ConsoleModifiers.Shift) != 0 ? ConsoleModifiers.Shift : 0); + } #endif + if (char.IsControl(c) || char.IsWhiteSpace(c)) { if (key.Modifiers.HasFlag(ConsoleModifiers.Shift)) @@ -426,6 +446,7 @@ namespace Microsoft.PowerShell.Internal private bool _istmInitialized = false; private TEXTMETRIC _tm = new TEXTMETRIC(); private bool _trueTypeInUse = false; + private static bool _getCurrentConsoleFontExApiAvailable = true; private readonly Lazy _outputHandle = new Lazy(() => { @@ -691,7 +712,18 @@ namespace Microsoft.PowerShell.Internal CONSOLE_FONT_INFO_EX fontInfo = new CONSOLE_FONT_INFO_EX(); fontInfo.cbSize = Marshal.SizeOf(fontInfo); - bool result = NativeMethods.GetCurrentConsoleFontEx(consoleHandle.DangerousGetHandle(), false, ref fontInfo); + bool result = true; + if (_getCurrentConsoleFontExApiAvailable) + { + try + { + result = NativeMethods.GetCurrentConsoleFontEx(consoleHandle.DangerousGetHandle(), false, ref fontInfo); + } + catch (System.EntryPointNotFoundException) + { + _getCurrentConsoleFontExApiAvailable = false; // api not available on NanoServer + } + } if (result == false) { @@ -865,7 +897,6 @@ namespace Microsoft.PowerShell.Internal CONSOLE_FONT_INFO_EX fontInfo = ConhostConsole.GetConsoleFontInfo(consoleHandle); int fontType = fontInfo.FontFamily & NativeMethods.FontTypeMask; _trueTypeInUse = (fontType & NativeMethods.TrueTypeFont) == NativeMethods.TrueTypeFont; - } public void EndRender()