1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-10 20:42:21 +02:00
Beef/BeefLibs/corlib/src/Console.bf
2025-01-14 09:48:08 -05:00

462 lines
12 KiB
Beef

using System.Text;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace System
{
public static class Console
{
public enum CancelKind
{
CtrlC,
CtrlBreak
}
public struct CancelInfo
{
public static Event<delegate void (CancelKind cancelKind, ref bool terminate)> sOnCancel ~ _.Dispose();
public static bool sCancelEventRegistered;
}
static Encoding sInputEncoding;
static Encoding sOutputEncoding;
static Encoding InputEncoding
{
get
{
return sInputEncoding ?? Encoding.ASCII;
}
set
{
sInputEncoding = value;
}
}
static Encoding OutputEncoding
{
get
{
return sOutputEncoding ?? Encoding.ASCII;
}
set
{
SetupOutStringEx();
sOutputEncoding = value;
}
}
static ConsoleColor sForegroundColor = .White;
static ConsoleColor sBackgroundColor = .Black;
static readonly ConsoleColor sOriginalForegroundColor = sForegroundColor;
static readonly ConsoleColor sOriginalBackgroundColor = sBackgroundColor;
public static ConsoleColor ForegroundColor
{
get { return sForegroundColor; }
set { sForegroundColor = value; SetColors(); }
}
public static ConsoleColor BackgroundColor
{
get { return sBackgroundColor; }
set { sBackgroundColor = value; SetColors(); }
}
const uint32 STD_INPUT_HANDLE = (uint32)-10;
const uint32 STD_OUTPUT_HANDLE = (uint32)-11;
const uint32 STD_ERROR_HANDLE = (uint32)-12;
[CRepr]
struct CONSOLE_SCREEN_BUFFER_INFO
{
public uint16[2] mSize;
public uint16[2] mCursorPosition;
public uint16 mAttributes;
public uint16[4] mWindow;
public uint16[2] mMaximumWindowSize;
}
[CRepr]
struct COORD : this(int16 X, int16 Y)
{
}
static void SetupOutStringEx()
{
OutString = => OutString_Ex;
}
static function void(StringView str) OutString = => OutString_Simple;
#if !BF_RUNTIME_DISABLE && !BF_PLATFORM_WASM
private static extern void PutChars(char8* c, int32 len);
#else
[CLink]
private static extern int32 putchar(char8 c);
[LinkName("Console_PutChars")]
private static void PutChars(char8* c, int32 len)
{
for (int i < len)
putchar(c[i]);
}
#endif
public static extern void ReopenHandles();
static void OutString_Simple(StringView str)
{
PutChars(str.Ptr, (.)str.Length);
}
static void OutString_Ex(StringView str)
{
Out.Write(str).IgnoreError();
}
public static ref Event<delegate void (CancelKind cancelKind, ref bool terminate)> OnCancel
{
get
{
if (!CancelInfo.sCancelEventRegistered)
{
CancelInfo.sCancelEventRegistered = true;
#if BF_PLATFORM_WINDOWS
SetConsoleCtrlHandler(=> ConsoleCtrlHandler, true);
#endif
}
return ref CancelInfo.sOnCancel;
}
}
#if BF_PLATFORM_WINDOWS
[CallingConvention(.Stdcall)]
public static Windows.IntBool ConsoleCtrlHandler(int32 ctrlType)
{
bool terminate = true;
if ((ctrlType == 0) || (ctrlType == 1))
CancelInfo.sOnCancel((.)ctrlType, ref terminate);
return terminate ? false : true;
}
//SetConsoleOutputCP set to CP_UTF8
const uint32 ENABLE_LINE_INPUT = 0x0002;
const uint32 ENABLE_ECHO_INPUT = 0x0004;
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool GetConsoleMode(Windows.Handle hConsoleHandle, out uint32 mode);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool SetConsoleMode(Windows.Handle hConsoleHandle, uint32 mode);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool SetConsoleTextAttribute(Windows.Handle hConsoleOutput, uint16 wAttributes);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool GetConsoleScreenBufferInfo(Windows.Handle hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.Handle GetStdHandle(uint32 nStdHandle);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool SetConsoleOutputCP(uint32 wCodePageID);
[CallingConvention(.Stdcall)]
function Windows.IntBool ConsoleCtrlHandler(int32 ctrlType);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool SetConsoleCtrlHandler(ConsoleCtrlHandler handler, Windows.IntBool addHandler);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool FillConsoleOutputCharacterW(Windows.Handle hConsoleOutput, char16 cCharacter, uint32 nLength, COORD dwWriteCoord, uint32* lpNumberOfCharsWritten);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool FillConsoleOutputAttribute(Windows.Handle hConsoleOutput, uint16 wAttribute, uint32 nLength, COORD dwWriteCoord, uint32* lpNumberOfAttrsWritten);
[CLink, CallingConvention(.Stdcall)]
static extern Windows.IntBool SetConsoleCursorPosition(Windows.Handle hConsoleOutput, COORD dwCursorPosition);
public static this()
{
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo = .();
if (GetConsoleScreenBufferInfo(handle, out consoleInfo) != 0)
{
sOriginalForegroundColor.ConsoleTextAttribute = (uint8)(consoleInfo.mAttributes & 0xF);
sOriginalBackgroundColor.ConsoleTextAttribute = (uint8)(consoleInfo.mAttributes >> 4);
}
SetConsoleOutputCP(/*CP_UTF8*/65001);
}
public static int32 CursorTop
{
public get
{
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo = .();
GetConsoleScreenBufferInfo(handle,out consoleInfo);
return consoleInfo.mCursorPosition[1]; //1 = y position
}
public set
{
//This has to be done afaik to ensure x stays the same
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo = .();
GetConsoleScreenBufferInfo(handle,out consoleInfo);
SetConsoleCursorPosition(handle, COORD((.)consoleInfo.mCursorPosition[0], (.)value));
}
}
public static int32 CursorLeft
{
public get
{
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo = .();
GetConsoleScreenBufferInfo(handle,out consoleInfo);
return consoleInfo.mCursorPosition[0]; //1 = y position
}
public set
{
//This has to be done afaik to ensure x stays the same
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO consoleInfo = .();
GetConsoleScreenBufferInfo(handle,out consoleInfo);
SetConsoleCursorPosition(handle, COORD((.)value,(.)consoleInfo.mCursorPosition[1]));
}
}
#endif
static StreamWriter OpenStreamWriter(Platform.BfpFileStdKind stdKind, ref StreamWriter outStreamWriter)
{
if (outStreamWriter == null)
{
Stream stream;
#if BF_TEST_BUILD
stream = new Test.TestStream();
#else
FileStream fileStream = new .();
stream = fileStream;
if (fileStream.OpenStd(stdKind) case .Err)
{
DeleteAndNullify!(fileStream);
stream = new NullStream();
}
#endif
StreamWriter newStreamWriter = new StreamWriter(stream, OutputEncoding ?? Encoding.ASCII, 4096, true);
newStreamWriter.AutoFlush = true;
let prevValue = Interlocked.CompareExchange(ref outStreamWriter, null, newStreamWriter);
if (prevValue != null)
{
// This was already set - race condition
delete newStreamWriter;
return prevValue;
}
return newStreamWriter;
}
return outStreamWriter;
}
static StreamReader OpenStreamReader(Platform.BfpFileStdKind stdKind, ref StreamReader outStreamReader)
{
if (outStreamReader == null)
{
FileStream fileStream = new .();
Stream stream = fileStream;
if (fileStream.OpenStd(stdKind) case .Ok)
{
#if BF_PLATFORM_WINDOWS
GetConsoleMode((.)fileStream.Handle, var consoleMode);
consoleMode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
SetConsoleMode((.)fileStream.Handle, consoleMode);
#endif
}
else
{
DeleteAndNullify!(fileStream);
stream = new NullStream();
}
StreamReader newStreamReader = new StreamReader(stream, InputEncoding ?? Encoding.ASCII, false, 4096, true);
let prevValue = Interlocked.CompareExchange(ref outStreamReader, null, newStreamReader);
if (prevValue != null)
{
// This was already set - race condition
delete newStreamReader;
return prevValue;
}
return newStreamReader;
}
return outStreamReader;
}
public static volatile StreamWriter mOut ~ delete _;
public static StreamWriter Out
{
get
{
return OpenStreamWriter(.Out, ref mOut);
}
}
public static volatile StreamWriter mError ~ delete _;
public static StreamWriter Error
{
get
{
return OpenStreamWriter(.Error, ref mError);
}
}
public static volatile StreamReader mIn ~ delete _;
public static StreamReader In
{
get
{
return OpenStreamReader(.In, ref mIn);
}
}
public static bool KeyAvailable => In.CanReadNow;
public static Result<char8> Read() => In.Read();
public static Result<void> ReadLine(String strBuffer)
{
#if BF_PLATFORM_WINDOWS
var fs = In.BaseStream as FileStream;
GetConsoleMode((.)fs.Handle, var consoleMode);
SetConsoleMode((.)fs.Handle, consoleMode | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
#endif
var result = In.ReadLine(strBuffer);
#if BF_PLATFORM_WINDOWS
SetConsoleMode((.)fs.Handle, consoleMode);
#endif
return result;
}
public static Task<String> ReadLineAsync() => In.ReadLineAsync();
public static Result<void> ReadToEnd(String outText) => In.ReadToEnd(outText);
public static void Write(StringView line)
{
OutString(line);
}
public static void Write(StringView fmt, params Object[] args)
{
String str = scope String(256);
str.AppendF(fmt, params args);
Write(str);
}
public static void Write(Object obj)
{
String str = scope String(256);
if (obj == null)
str.Append("null");
else
obj.ToString(str);
Write(str);
}
public static void WriteLine()
{
OutString("\n");
}
public static void WriteLine(StringView line)
{
OutString(line);
OutString("\n");
}
public static void WriteLine(StringView fmt, params Object[] args)
{
String str = scope String(256);
str.AppendF(fmt, params args);
WriteLine(str);
}
public static void WriteLine(Object obj)
{
String str = scope String(256);
if (obj == null)
str.Append("null");
else
obj.ToString(str);
WriteLine(str);
}
public static void ResetColor()
{
sForegroundColor = sOriginalForegroundColor;
sBackgroundColor = sOriginalBackgroundColor;
SetColors();
#if !BF_PLATFORM_WINDOWS
Write("\x1B[0m");
#endif
}
static void SetColors()
{
#if BF_PLATFORM_WINDOWS
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
let fgColor = ForegroundColor.ConsoleTextAttribute;
let bgColor = BackgroundColor.ConsoleTextAttribute;
SetConsoleTextAttribute(handle, bgColor * 16 + fgColor);
#else
Write("\x1B[{}m", ForegroundColor.AnsiCode);
Write("\x1B[{}m", BackgroundColor.AnsiCode + 10);
#endif
}
public static void Clear()
{
#if BF_PLATFORM_WINDOWS
Windows.Handle hStdOut;
CONSOLE_SCREEN_BUFFER_INFO csbi;
uint32 count;
uint32 cellCount;
COORD homeCoords = .(0, 0);
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
if (hStdOut == .InvalidHandle)
return;
/* Get the number of cells in the current buffer */
if (!GetConsoleScreenBufferInfo( hStdOut, out csbi ))
return;
cellCount = csbi.mSize[0] * csbi.mSize[1];
/* Fill the entire buffer with spaces */
if (!FillConsoleOutputCharacterW(
hStdOut,
' ',
cellCount,
homeCoords,
&count
)) return;
/* Fill the entire buffer with the current colors and attributes */
if (!FillConsoleOutputAttribute(
hStdOut,
csbi.mAttributes,
cellCount,
homeCoords,
&count
)) return;
/* Move the cursor home */
SetConsoleCursorPosition( hStdOut, homeCoords );
#else
Write("\x1B[H\x1B[J");
#endif
}
}
}