|
3 | 3 | --********************************************************************/
|
4 | 4 |
|
5 | 5 | using System;
|
| 6 | +using System.Collections.Generic; |
6 | 7 | using System.ComponentModel;
|
7 | 8 | using System.Runtime.InteropServices;
|
8 | 9 | using Microsoft.PowerShell;
|
| 10 | +using Microsoft.PowerShell.Internal; |
9 | 11 | using Microsoft.Win32.SafeHandles;
|
10 | 12 |
|
11 | 13 | static class PlatformWindows
|
@@ -92,12 +94,14 @@ private static bool OnBreak(ConsoleBreakSignal signal)
|
92 | 94 | }
|
93 | 95 |
|
94 | 96 | private static PSConsoleReadLine _singleton;
|
95 |
| - internal static void OneTimeInit(PSConsoleReadLine singleton) |
| 97 | + internal static IConsole OneTimeInit(PSConsoleReadLine singleton) |
96 | 98 | {
|
97 | 99 | _singleton = singleton;
|
98 | 100 | var breakHandlerGcHandle = GCHandle.Alloc(new BreakHandler(OnBreak));
|
99 | 101 | SetConsoleCtrlHandler((BreakHandler)breakHandlerGcHandle.Target, true);
|
100 | 102 | _enableVtOutput = SetConsoleOutputVirtualTerminalProcessing();
|
| 103 | + |
| 104 | + return _enableVtOutput ? new VirtualTerminal() : new LegacyWin32Console(); |
101 | 105 | }
|
102 | 106 |
|
103 | 107 | // Input modes
|
@@ -359,4 +363,166 @@ public static bool IsConsoleApiAvailable(bool input, bool output)
|
359 | 363 | }
|
360 | 364 | return true;
|
361 | 365 | }
|
| 366 | + |
| 367 | + internal class LegacyWin32Console : VirtualTerminal |
| 368 | + { |
| 369 | + private static ConsoleColor InitialFG = Console.ForegroundColor; |
| 370 | + private static ConsoleColor InitialBG = Console.BackgroundColor; |
| 371 | + |
| 372 | + private static readonly Dictionary<string, Action> EscapeSequenceActions = new Dictionary<string, Action> { |
| 373 | + {"\x1b[30;47m", () => { |
| 374 | + Console.ForegroundColor = ConsoleColor.Black; |
| 375 | + Console.BackgroundColor = ConsoleColor.Gray; } }, |
| 376 | + {"\x1b[40m", () => Console.BackgroundColor = ConsoleColor.Black}, |
| 377 | + {"\x1b[44m", () => Console.BackgroundColor = ConsoleColor.DarkBlue }, |
| 378 | + {"\x1b[42m", () => Console.BackgroundColor = ConsoleColor.DarkGreen}, |
| 379 | + {"\x1b[46m", () => Console.BackgroundColor = ConsoleColor.DarkCyan}, |
| 380 | + {"\x1b[41m", () => Console.BackgroundColor = ConsoleColor.DarkRed}, |
| 381 | + {"\x1b[45m", () => Console.BackgroundColor = ConsoleColor.DarkMagenta}, |
| 382 | + {"\x1b[43m", () => Console.BackgroundColor = ConsoleColor.DarkYellow}, |
| 383 | + {"\x1b[47m", () => Console.BackgroundColor = ConsoleColor.Gray}, |
| 384 | + {"\x1b[100m", () => Console.BackgroundColor = ConsoleColor.DarkGray}, |
| 385 | + {"\x1b[104m", () => Console.BackgroundColor = ConsoleColor.Blue}, |
| 386 | + {"\x1b[102m", () => Console.BackgroundColor = ConsoleColor.Green}, |
| 387 | + {"\x1b[106m", () => Console.BackgroundColor = ConsoleColor.Cyan}, |
| 388 | + {"\x1b[101m", () => Console.BackgroundColor = ConsoleColor.Red}, |
| 389 | + {"\x1b[105m", () => Console.BackgroundColor = ConsoleColor.Magenta}, |
| 390 | + {"\x1b[103m", () => Console.BackgroundColor = ConsoleColor.Yellow}, |
| 391 | + {"\x1b[107m", () => Console.BackgroundColor = ConsoleColor.White}, |
| 392 | + {"\x1b[30m", () => Console.ForegroundColor = ConsoleColor.Black}, |
| 393 | + {"\x1b[34m", () => Console.ForegroundColor = ConsoleColor.DarkBlue}, |
| 394 | + {"\x1b[32m", () => Console.ForegroundColor = ConsoleColor.DarkGreen}, |
| 395 | + {"\x1b[36m", () => Console.ForegroundColor = ConsoleColor.DarkCyan}, |
| 396 | + {"\x1b[31m", () => Console.ForegroundColor = ConsoleColor.DarkRed}, |
| 397 | + {"\x1b[35m", () => Console.ForegroundColor = ConsoleColor.DarkMagenta}, |
| 398 | + {"\x1b[33m", () => Console.ForegroundColor = ConsoleColor.DarkYellow}, |
| 399 | + {"\x1b[37m", () => Console.ForegroundColor = ConsoleColor.Gray}, |
| 400 | + {"\x1b[90m", () => Console.ForegroundColor = ConsoleColor.DarkGray}, |
| 401 | + {"\x1b[94m", () => Console.ForegroundColor = ConsoleColor.Blue}, |
| 402 | + {"\x1b[92m", () => Console.ForegroundColor = ConsoleColor.Green}, |
| 403 | + {"\x1b[96m", () => Console.ForegroundColor = ConsoleColor.Cyan}, |
| 404 | + {"\x1b[91m", () => Console.ForegroundColor = ConsoleColor.Red}, |
| 405 | + {"\x1b[95m", () => Console.ForegroundColor = ConsoleColor.Magenta}, |
| 406 | + {"\x1b[93m", () => Console.ForegroundColor = ConsoleColor.Yellow}, |
| 407 | + {"\x1b[97m", () => Console.ForegroundColor = ConsoleColor.White}, |
| 408 | + {"\x1b[0m", () => { |
| 409 | + Console.ForegroundColor = InitialFG; |
| 410 | + Console.BackgroundColor = InitialBG; |
| 411 | + }} |
| 412 | + }; |
| 413 | + |
| 414 | + private void WriteHelper(string s, bool line) |
| 415 | + { |
| 416 | + var from = 0; |
| 417 | + for (int i = 0; i < s.Length; i++) |
| 418 | + { |
| 419 | + if (s[i] == '\x1b') |
| 420 | + { |
| 421 | + // Escape sequence - limited support here. |
| 422 | + var endSequence = s.IndexOf("m", i, StringComparison.Ordinal); |
| 423 | + if (endSequence > 0) |
| 424 | + { |
| 425 | + var escapeSequence = s.Substring(i, endSequence - i + 1); |
| 426 | + if (EscapeSequenceActions.TryGetValue(escapeSequence, out var action)) |
| 427 | + { |
| 428 | + Console.Write(s.Substring(from, i - from)); |
| 429 | + action(); |
| 430 | + i = endSequence; |
| 431 | + from = i + 1; |
| 432 | + } |
| 433 | + } |
| 434 | + } |
| 435 | + } |
| 436 | + |
| 437 | + var tailSegment = s.Substring(from); |
| 438 | + if (line) Console.WriteLine(tailSegment); |
| 439 | + else Console.Write(tailSegment); |
| 440 | + } |
| 441 | + |
| 442 | + public override void Write(string s) |
| 443 | + { |
| 444 | + WriteHelper(s, false); |
| 445 | + } |
| 446 | + |
| 447 | + public override void WriteLine(string s) |
| 448 | + { |
| 449 | + WriteHelper(s, true); |
| 450 | + } |
| 451 | + |
| 452 | + public struct SMALL_RECT |
| 453 | + { |
| 454 | + public short Left; |
| 455 | + public short Top; |
| 456 | + public short Right; |
| 457 | + public short Bottom; |
| 458 | + } |
| 459 | + |
| 460 | + internal struct COORD |
| 461 | + { |
| 462 | + public short X; |
| 463 | + public short Y; |
| 464 | + } |
| 465 | + |
| 466 | + public struct CHAR_INFO |
| 467 | + { |
| 468 | + public ushort UnicodeChar; |
| 469 | + public ushort Attributes; |
| 470 | + public CHAR_INFO(char c, ConsoleColor foreground, ConsoleColor background) |
| 471 | + { |
| 472 | + UnicodeChar = c; |
| 473 | + Attributes = (ushort)(((int)background << 4) | (int)foreground); |
| 474 | + } |
| 475 | + } |
| 476 | + |
| 477 | + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
| 478 | + public static extern bool ScrollConsoleScreenBuffer(IntPtr hConsoleOutput, |
| 479 | + ref SMALL_RECT lpScrollRectangle, |
| 480 | + IntPtr lpClipRectangle, |
| 481 | + COORD dwDestinationOrigin, |
| 482 | + ref CHAR_INFO lpFill); |
| 483 | + |
| 484 | + public override void ScrollBuffer(int lines) |
| 485 | + { |
| 486 | + var handle = GetStdHandle((uint) StandardHandleId.Output); |
| 487 | + var scrollRectangle = new SMALL_RECT |
| 488 | + { |
| 489 | + Top = (short) lines, |
| 490 | + Left = 0, |
| 491 | + Bottom = (short)(Console.BufferHeight - 1), |
| 492 | + Right = (short)Console.BufferWidth |
| 493 | + }; |
| 494 | + var destinationOrigin = new COORD {X = 0, Y = 0}; |
| 495 | + var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor); |
| 496 | + ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar); |
| 497 | + } |
| 498 | + |
| 499 | + public override int CursorSize |
| 500 | + { |
| 501 | + get => IsConsoleApiAvailable(input: false, output: true) ? Console.CursorSize : _unixCursorSize; |
| 502 | + set |
| 503 | + { |
| 504 | + if (IsConsoleApiAvailable(input: false, output: true)) |
| 505 | + { |
| 506 | + Console.CursorSize = value; |
| 507 | + } |
| 508 | + else |
| 509 | + { |
| 510 | + // I'm not sure the cursor is even visible, at any rate, no escape sequences supported. |
| 511 | + _unixCursorSize = value; |
| 512 | + } |
| 513 | + } |
| 514 | + } |
| 515 | + |
| 516 | + public override void BlankRestOfLine() |
| 517 | + { |
| 518 | + // This shouldn't scroll, but I'm lazy and don't feel like using a P/Invoke. |
| 519 | + var x = CursorLeft; |
| 520 | + var y = CursorTop; |
| 521 | + |
| 522 | + for (int i = 0; i < BufferWidth - x; i++) Console.Write(' '); |
| 523 | + if (CursorTop != y+1) y -= 1; |
| 524 | + |
| 525 | + SetCursorPosition(x, y); |
| 526 | + } |
| 527 | + } |
362 | 528 | }
|
0 commit comments