Introduce TTYConsole for UNIX

This commit is contained in:
Jason Shirk (POWERSHELL)
2016-07-07 12:07:52 -07:00
parent 8e234322c4
commit e79c62f9ec
5 changed files with 238 additions and 101 deletions
+219 -46
View File
@@ -317,7 +317,7 @@ namespace Microsoft.PowerShell.Internal
public CHAR_INFO(char c, ConsoleColor foreground, ConsoleColor background)
{
UnicodeChar = c;
Attributes = (ushort)(((int)background << 4) | (int)foreground);
Attributes = (ushort)((((int)background << 4) & 0xf) | ((int)foreground & 0xf));
}
[ExcludeFromCodeCoverage]
@@ -330,7 +330,7 @@ namespace Microsoft.PowerShell.Internal
[ExcludeFromCodeCoverage]
public ConsoleColor BackgroundColor
{
get { return (ConsoleColor)((Attributes & 0xf0) >> 4); }
get { return (ConsoleColor)((Attributes & 0x00f0) >> 4); }
set { Attributes = (ushort)((Attributes & 0xff0f) | (((int)value & 0xf) << 4)); }
}
@@ -426,6 +426,7 @@ namespace Microsoft.PowerShell.Internal
}
}
#if !LINUX
internal class ConhostConsole : IConsole
{
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
@@ -482,18 +483,42 @@ namespace Microsoft.PowerShell.Internal
return new SafeFileHandle(handle, true);
});
public uint GetConsoleInputMode()
public object GetConsoleInputMode()
{
uint mode;
var handle = _inputHandle.Value.DangerousGetHandle();
uint result;
NativeMethods.GetConsoleMode(handle, out result);
return result;
NativeMethods.GetConsoleMode(handle, out mode);
return mode;
}
public void SetConsoleInputMode(uint mode)
public void SetConsoleInputMode(object modeObj)
{
var handle = _inputHandle.Value.DangerousGetHandle();
NativeMethods.SetConsoleMode(handle, mode);
if (modeObj is uint)
{
// Clear a couple flags so we can actually receive certain keys:
// ENABLE_PROCESSED_INPUT - enables Ctrl+C
// ENABLE_LINE_INPUT - enables Ctrl+S
// Also clear a couple flags so we don't mask the input that we ignore:
// ENABLE_MOUSE_INPUT - mouse events
// ENABLE_WINDOW_INPUT - window resize events
var mode = (uint)modeObj &
~(NativeMethods.ENABLE_PROCESSED_INPUT |
NativeMethods.ENABLE_LINE_INPUT |
NativeMethods.ENABLE_WINDOW_INPUT |
NativeMethods.ENABLE_MOUSE_INPUT);
var handle = _inputHandle.Value.DangerousGetHandle();
NativeMethods.SetConsoleMode(handle, mode);
}
}
public void RestoreConsoleInputMode(object modeObj)
{
if (modeObj is uint)
{
var handle = _inputHandle.Value.DangerousGetHandle();
NativeMethods.SetConsoleMode(handle, (uint)modeObj);
}
}
public ConsoleKeyInfo ReadKey()
@@ -601,24 +626,7 @@ namespace Microsoft.PowerShell.Internal
ScrollBuffer(scrollCount);
top -= scrollCount;
}
#if LINUX
ConsoleColor foregroundColor = Console.ForegroundColor;
ConsoleColor backgroundColor = Console.BackgroundColor;
Console.SetCursorPosition(0, (top>=0) ? top : 0);
for (int i = 0; i < buffer.Length; ++i)
{
// TODO: use escape sequences for better perf
Console.ForegroundColor = buffer[i].ForegroundColor;
Console.BackgroundColor = buffer[i].BackgroundColor;
Console.Write((char)buffer[i].UnicodeChar);
}
Console.BackgroundColor = backgroundColor;
Console.ForegroundColor = foregroundColor;
#else
var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
var bufferSize = new COORD
{
@@ -643,18 +651,10 @@ namespace Microsoft.PowerShell.Internal
{
Console.CursorTop = bottom;
}
#endif
}
public void ScrollBuffer(int lines)
{
#if LINUX
for (int i=0; i<lines; ++i)
{
Console.SetCursorPosition(Console.BufferWidth, Console.BufferHeight - 1);
Console.WriteLine();
}
#else
var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
var scrollRectangle = new SMALL_RECT
@@ -667,20 +667,11 @@ namespace Microsoft.PowerShell.Internal
var destinationOrigin = new COORD {X = 0, Y = 0};
var fillChar = new CHAR_INFO(' ', Console.ForegroundColor, Console.BackgroundColor);
NativeMethods.ScrollConsoleScreenBuffer(handle, ref scrollRectangle, IntPtr.Zero, destinationOrigin, ref fillChar);
#endif
}
public CHAR_INFO[] ReadBufferLines(int top, int count)
{
var result = new CHAR_INFO[BufferWidth * count];
#if LINUX
for (int i=0; i<BufferWidth*count; ++i)
{
result[i].UnicodeChar = ' ';
result[i].ForegroundColor = Console.ForegroundColor;
result[i].BackgroundColor = Console.BackgroundColor;
}
#else
var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output);
var readBufferSize = new COORD {
@@ -696,7 +687,7 @@ namespace Microsoft.PowerShell.Internal
};
NativeMethods.ReadConsoleOutput(handle, result,
readBufferSize, readBufferCoord, ref readRegion);
#endif
return result;
}
@@ -891,14 +882,196 @@ namespace Microsoft.PowerShell.Internal
NativeMethods.ReleaseDC(_hwnd, _hDC);
}
}
}
#if LINUX // TODO: this is not correct, PSReadline never clears the screen, it should only scroll
#else
internal class TTYConsole : IConsole
{
public object GetConsoleInputMode()
{
return Console.TreatControlCAsInput;
}
public void SetConsoleInputMode(object modeObj)
{
Console.TreatControlCAsInput = true;
}
public void RestoreConsoleInputMode(object modeObj)
{
if (modeObj is bool)
{
Console.TreatControlCAsInput = (bool)modeObj;
}
}
public ConsoleKeyInfo ReadKey()
{
return Console.ReadKey(true);
}
public bool KeyAvailable
{
get { return Console.KeyAvailable; }
}
public int CursorLeft
{
get { return Console.CursorLeft; }
set { Console.CursorLeft = value; }
}
public int CursorTop
{
get { return Console.CursorTop; }
set { Console.CursorTop = value; }
}
public int CursorSize
{
get { return Console.CursorSize; }
set { Console.CursorSize = value; }
}
public int BufferWidth
{
get { return Console.BufferWidth; }
set { Console.BufferWidth = value; }
}
public int BufferHeight
{
get { return Console.BufferHeight; }
set { Console.BufferHeight = value; }
}
public int WindowWidth
{
get { return Console.WindowWidth; }
set { Console.WindowWidth = value; }
}
public int WindowHeight
{
get { return Console.WindowHeight; }
set { Console.WindowHeight = value; }
}
public int WindowTop
{
get { return Console.WindowTop; }
set { Console.WindowTop = value; }
}
public ConsoleColor BackgroundColor
{
get { return Console.BackgroundColor; }
set { Console.BackgroundColor = value; }
}
public ConsoleColor ForegroundColor
{
get { return Console.ForegroundColor; }
set { Console.ForegroundColor = value; }
}
public void SetWindowPosition(int left, int top)
{
Console.SetWindowPosition(left, top);
}
public void SetCursorPosition(int left, int top)
{
Console.SetCursorPosition(left, top);
}
public void Write(string value)
{
Console.Write(value);
}
public void WriteLine(string value)
{
Console.WriteLine(value);
}
public void WriteBufferLines(CHAR_INFO[] buffer, ref int top)
{
WriteBufferLines(buffer, ref top, true);
}
public void WriteBufferLines(CHAR_INFO[] buffer, ref int top, bool ensureBottomLineVisible)
{
int bufferWidth = Console.BufferWidth;
int bufferLineCount = buffer.Length / bufferWidth;
if ((top + bufferLineCount) > Console.BufferHeight)
{
var scrollCount = (top + bufferLineCount) - Console.BufferHeight;
ScrollBuffer(scrollCount);
top -= scrollCount;
}
ConsoleColor foregroundColor = Console.ForegroundColor;
ConsoleColor backgroundColor = Console.BackgroundColor;
Console.SetCursorPosition(0, (top>=0) ? top : 0);
for (int i = 0; i < buffer.Length; ++i)
{
// TODO: use escape sequences for better perf
Console.ForegroundColor = buffer[i].ForegroundColor;
Console.BackgroundColor = buffer[i].BackgroundColor;
Console.Write((char)buffer[i].UnicodeChar);
}
Console.BackgroundColor = backgroundColor;
Console.ForegroundColor = foregroundColor;
}
public void ScrollBuffer(int lines)
{
for (int i=0; i<lines; ++i)
{
Console.SetCursorPosition(Console.BufferWidth, Console.BufferHeight - 1);
Console.WriteLine();
}
}
public CHAR_INFO[] ReadBufferLines(int top, int count)
{
var result = new CHAR_INFO[BufferWidth * count];
for (int i=0; i<BufferWidth*count; ++i)
{
result[i].UnicodeChar = ' ';
result[i].ForegroundColor = Console.ForegroundColor;
result[i].BackgroundColor = Console.BackgroundColor;
}
return result;
}
public int LengthInBufferCells(char c)
{
return 1;
}
public void StartRender()
{
}
public void EndRender()
{
}
// TODO: this is not correct, PSReadline never clears the screen, it should only scroll
public void Clear()
{
Console.Clear();
}
#endif
}
#endif
#if CORECLR // TODO: remove if CORECLR adds this attribute back
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]