Last active
January 31, 2018 23:52
-
-
Save SeeminglyScience/18a38347625c47c11017908d4dc5874d to your computer and use it in GitHub Desktop.
Unsuccessful proof of concept for fixing Unix Console.ReadKey without native binaries.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.InteropServices; | |
namespace Microsoft.PowerShell.Internal | |
{ | |
internal class PlatformUnix | |
{ | |
// termios | |
private const int STDIN_FILENO = 0; // File descriptor for Standard Input. | |
private const int NCCS = 64; // Size of the array c_cc for control characters. | |
// Control Characters (c_cc) | |
private const uint VTIME = 5; // Timeout in deciseconds. | |
private const uint VMIN = 6; // Minimum number of characters to expect. | |
// Local Modes (c_lflag) | |
private const uint ICANON = 0000002; // Canonical input (erase and kill processing). | |
private const uint ECHO = 0000010; // Enable echo. | |
private const uint IEXTEN = 0100000; // Enable extended input character processing. | |
// Input Modes (c_iflag) | |
private const uint IXON = 0002000; // Enable start/stop output control. | |
private const uint IXOFF = 0010000; // Enable start/stop input control. | |
// Attribute Selection (tcsetattr) | |
private const int TCSANOW = 0; // Change attributes immediately. | |
// Poll Events | |
private const ushort POLLIN = 0x0001; // Standard Input is ready. | |
internal static ConsoleKeyInfo ReadKey() | |
{ | |
BlockUntilKeyAvailable(); | |
return Console.ReadKey(intercept: true); | |
} | |
[DllImport("libc")] | |
private static extern uint tcgetattr(int fd, IntPtr termios_p); | |
[DllImport("libc")] | |
private static extern uint tcsetattr(int fd, int optional_actions, IntPtr termios_p); | |
[DllImport("libc")] | |
private static extern uint poll(ref pollfd fds, uint nfds, int timeout); | |
private static void BlockUntilKeyAvailable() | |
{ | |
pollfd fds; | |
fds.fd = STDIN_FILENO; | |
fds.events = POLLIN; | |
fds.revents = 0; | |
termios newTerminal = GetTerminal(); | |
termios previousTerminal = newTerminal; | |
newTerminal.c_iflag &= ~(IXON | IXOFF); | |
newTerminal.c_lflag &= ~(ECHO | ICANON | IEXTEN); | |
newTerminal.c_cc[VMIN] = 1; | |
newTerminal.c_cc[VTIME] = 0; | |
SetTerminal(newTerminal); | |
try | |
{ | |
poll(ref fds, 1, -1); | |
} | |
finally | |
{ | |
SetTerminal(previousTerminal); | |
} | |
} | |
private static termios GetTerminal() | |
{ | |
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(termios))); | |
try | |
{ | |
if (tcgetattr(STDIN_FILENO, ptr) >= 0) | |
{ | |
return Marshal.PtrToStructure<termios>(ptr); | |
} | |
return default(termios); | |
} | |
finally | |
{ | |
Marshal.FreeHGlobal(ptr); | |
} | |
} | |
private static bool SetTerminal(termios termios) | |
{ | |
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(termios))); | |
try | |
{ | |
Marshal.StructureToPtr<termios>(termios, ptr, fDeleteOld: false); | |
if (tcsetattr(STDIN_FILENO, TCSANOW, ptr) <= 0) | |
{ | |
return true; | |
} | |
return false; | |
} | |
finally | |
{ | |
Marshal.FreeHGlobal(ptr); | |
} | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
private struct termios | |
{ | |
// Input Mode Flags | |
internal uint c_iflag; | |
// Output Mode Flags | |
internal uint c_oflag; | |
// Control Mode Flags | |
internal uint c_cflag; | |
// Local Mode Flags | |
internal uint c_lflag; | |
internal byte c_line; | |
/// Control Characters | |
[MarshalAs(UnmanagedType.ByValArray, SizeConst=NCCS)] | |
internal byte[] c_cc; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
private struct pollfd | |
{ | |
internal uint fd; | |
internal ushort events; | |
internal ushort revents; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment