Skip to content

Instantly share code, notes, and snippets.

@SeeminglyScience
Last active January 31, 2018 23:52
Show Gist options
  • Save SeeminglyScience/18a38347625c47c11017908d4dc5874d to your computer and use it in GitHub Desktop.
Save SeeminglyScience/18a38347625c47c11017908d4dc5874d to your computer and use it in GitHub Desktop.
Unsuccessful proof of concept for fixing Unix Console.ReadKey without native binaries.
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