Skip to content

Commit 1d8dd4a

Browse files
SteveL-MSFTlzybkr
authored andcommitted
Fix unintentional font change in Windows console (#771)
conhost (Windows only) could change the font unexpectedly if the current font is a raster font and the code page changes to UTF8. PSReadLine was switching to UTF8 output encoding to address some display issues with CJK, This change skips the UTF8 output encoding change if it would cause conhost to change the font.
1 parent 57bc3a6 commit 1d8dd4a

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

PSReadLine/PlatformWindows.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,41 @@ private static bool OnBreak(ConsoleBreakSignal signal)
9393
return false;
9494
}
9595

96+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
97+
private struct CONSOLE_FONT_INFO_EX
98+
{
99+
internal int cbSize;
100+
internal int nFont;
101+
internal short FontWidth;
102+
internal short FontHeight;
103+
internal FontFamily FontFamily;
104+
internal uint FontWeight;
105+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
106+
internal string FontFace;
107+
}
108+
109+
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
110+
private static extern bool GetCurrentConsoleFontEx(IntPtr consoleOutput, bool bMaximumWindow, ref CONSOLE_FONT_INFO_EX consoleFontInfo);
111+
112+
[Flags()]
113+
internal enum FontFamily : uint
114+
{
115+
TMPF_FIXED_PITCH = 0x01
116+
}
117+
118+
internal static bool IsUsingRasterFont()
119+
{
120+
CONSOLE_FONT_INFO_EX fontInfo = new CONSOLE_FONT_INFO_EX();
121+
fontInfo.cbSize = Marshal.SizeOf(fontInfo);
122+
var handle = _outputHandle.Value.DangerousGetHandle();
123+
bool result = GetCurrentConsoleFontEx(handle, false, ref fontInfo);
124+
// If this bit is set the font is a variable pitch font.
125+
// If this bit is clear the font is a fixed pitch font.
126+
// Note very carefully that those meanings are the opposite of what the constant name implies.
127+
return !fontInfo.FontFamily.HasFlag(FontFamily.TMPF_FIXED_PITCH);
128+
}
129+
130+
96131
private static PSConsoleReadLine _singleton;
97132
internal static IConsole OneTimeInit(PSConsoleReadLine singleton)
98133
{
@@ -333,13 +368,18 @@ private static bool SetConsoleOutputVirtualTerminalProcessing()
333368
&& SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
334369
}
335370

371+
internal static bool IsConsoleInput()
372+
{
373+
var handle = GetStdHandle((uint)StandardHandleId.Input);
374+
return GetFileType(handle) == FILE_TYPE_CHAR;
375+
}
376+
336377
private static bool IsHandleRedirected(bool stdin)
337378
{
338379
var handle = GetStdHandle((uint)(stdin ? StandardHandleId.Input : StandardHandleId.Output));
339380

340381
// If handle is not to a character device, we must be redirected:
341-
int fileType = GetFileType(handle);
342-
if ((fileType & FILE_TYPE_CHAR) != FILE_TYPE_CHAR)
382+
if (GetFileType(handle) != FILE_TYPE_CHAR)
343383
return true;
344384

345385
// Char device - if GetConsoleMode succeeds, we are NOT redirected.

PSReadLine/ReadLine.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods
4646
private IConsole _console;
4747
private ICharMap _charMap;
4848
private Encoding _initialOutputEncoding;
49-
49+
private bool _skipOutputEncodingChange;
5050
private EngineIntrinsics _engineIntrinsics;
5151
private Thread _readKeyThread;
5252
private AutoResetEvent _readKeyWaitHandle;
@@ -539,7 +539,10 @@ T CallPossibleExternalApplication<T>(Func<T> func)
539539
}
540540
finally
541541
{
542-
_console.OutputEncoding = Encoding.UTF8;
542+
if (!_skipOutputEncodingChange)
543+
{
544+
_console.OutputEncoding = Encoding.UTF8;
545+
}
543546
}
544547
}
545548

@@ -655,7 +658,17 @@ private void Initialize(Runspace runspace, EngineIntrinsics engineIntrinsics)
655658
_statusIsErrorMessage = false;
656659

657660
_initialOutputEncoding = _console.OutputEncoding;
658-
_console.OutputEncoding = Encoding.UTF8;
661+
662+
// Don't change the OutputEncoding if already UTF8, no console, or using raster font on Windows
663+
_skipOutputEncodingChange = _initialOutputEncoding == Encoding.UTF8
664+
|| (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
665+
&& PlatformWindows.IsConsoleInput()
666+
&& PlatformWindows.IsUsingRasterFont());
667+
668+
if (!_skipOutputEncodingChange) {
669+
_console.OutputEncoding = Encoding.UTF8;
670+
}
671+
659672
_lastRenderTime = Stopwatch.StartNew();
660673

661674
_killCommandCount = 0;

0 commit comments

Comments
 (0)