From 6b4aabd79bf347bc9068603c9556dee39bb78ce5 Mon Sep 17 00:00:00 2001
From: dedouwe26 <63008025+dedouwe26@users.noreply.github.com>
Date: Thu, 21 Nov 2024 16:42:28 +0100
Subject: [PATCH] Revert "quick"
This reverts commit ab19baae8ea82dc462480f9239a52fb494b26a78.
---
Terminal/Backend/WindowsBackend.cs | 283 ------------------
Terminal/Terminal.cs | 4 +-
.../TerminalWindow.cs} | 133 ++++----
Terminal/Window/WinTerminalWindow.cs | 266 ++++++++++++++++
Terminal/Window/Window.cs | 13 -
5 files changed, 334 insertions(+), 365 deletions(-)
delete mode 100644 Terminal/Backend/WindowsBackend.cs
rename Terminal/{Backend/TerminalBackend.cs => Window/TerminalWindow.cs} (67%)
create mode 100644 Terminal/Window/WinTerminalWindow.cs
delete mode 100644 Terminal/Window/Window.cs
diff --git a/Terminal/Backend/WindowsBackend.cs b/Terminal/Backend/WindowsBackend.cs
deleted file mode 100644
index 834ef8c..0000000
--- a/Terminal/Backend/WindowsBackend.cs
+++ /dev/null
@@ -1,283 +0,0 @@
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-using System.Text;
-using Microsoft.Win32.SafeHandles;
-
-namespace OxDED.Terminal.Backend;
-
-internal static class Utility {
- internal static Stream GetStream(WinAPI.StandardType type) {
- SafeFileHandle fileHandle = new(WinAPI.GetStdHandle((int)type), false);
-
- if (fileHandle.IsInvalid) {
- fileHandle.SetHandleAsInvalid();
- return Stream.Null;
- }
-
- FileStream stream = new(fileHandle, type != WinAPI.StandardType.Input ? FileAccess.Write : FileAccess.Read);
-
- return stream;
- }
- internal static WinAPI.CONSOLE_SCREEN_BUFFER_INFO GetBufferInfo(WindowsBackend backend) {
- if (backend.outHandle == WinAPI.INVALID_HANDLE_VALUE) {
- throw new Win32Exception("Invalid standard console output handle.");
- }
-
- bool succeeded = WinAPI.GetConsoleScreenBufferInfo(backend.outHandle, out WinAPI.CONSOLE_SCREEN_BUFFER_INFO csbi);
- if (!succeeded) {
- succeeded = WinAPI.GetConsoleScreenBufferInfo(backend.errHandle, out csbi);
- if (!succeeded)
- succeeded = WinAPI.GetConsoleScreenBufferInfo(backend.inHandle, out csbi);
-
- if (!succeeded) {
- int errorCode = Marshal.GetLastWin32Error();
- throw new Win32Exception(errorCode, "Tried to get the console screen buffer info.");
- }
- }
-
- return csbi;
- }
-}
-
-internal static partial class WinAPI {
- internal enum StandardType : int {
- Input = -10,
- Output = -11,
- Error = -12
- }
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- internal struct KEY_EVENT_RECORD {
- [MarshalAs(UnmanagedType.Bool)]
- internal bool bKeyDown;
- internal ushort wRepeatCount;
- internal ushort wVirtualKeyCode;
- internal ushort wVirtualScanCode;
- private ushort _uChar;
- internal uint dwControlKeyState;
- internal readonly char UChar => (char)_uChar;
- }
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- internal struct INPUT_RECORD {
- internal ushort EventType;
- internal KEY_EVENT_RECORD keyEvent;
- }
- [StructLayout(LayoutKind.Sequential)]
- internal struct COORD {
- internal short X;
- internal short Y;
- }
- [StructLayout(LayoutKind.Sequential)]
- internal struct SMALL_RECT {
- internal short Left;
- internal short Top;
- internal short Right;
- internal short Bottom;
- }
- [StructLayout(LayoutKind.Sequential)]
- internal struct CONSOLE_SCREEN_BUFFER_INFO {
- internal COORD dwSize;
- internal COORD dwCursorPosition;
- internal short wAttributes;
- internal SMALL_RECT srWindow;
- internal COORD dwMaximumWindowSize;
- }
- internal const string KERNEL = "kernel32.dll";
- internal const nint INVALID_HANDLE_VALUE = -1;
-
- [LibraryImport(KERNEL, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool AllocConsole();
-
- [LibraryImport(KERNEL, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool FreeConsole();
-
- [LibraryImport(KERNEL)]
- internal static partial nint GetConsoleWindow();
- [LibraryImport(KERNEL, SetLastError = true)]
- internal static partial nint GetStdHandle(int nStdHandle);
-
- [LibraryImport(KERNEL, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool CloseHandle(nint handle);
-
- [LibraryImport(KERNEL, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool SetStdHandle(int nStdHandle, nint hConsoleOutput);
-
- [DllImport(KERNEL, CharSet = CharSet.Auto, BestFitMapping = false, SetLastError = true)]
- internal static extern SafeFileHandle CreateFile(string fileName, uint desiredAccess, int shareMode, nint securityAttributes, int creationDisposition, int flagsAndAttributes, nint templateFile);
-
- [DllImport(KERNEL, SetLastError = true, BestFitMapping = true, CharSet = CharSet.Unicode)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool SetConsoleTitle(string title);
-
- [DllImport(KERNEL, SetLastError = true, BestFitMapping = true, CharSet = CharSet.Unicode)]
- internal static extern uint GetConsoleTitle(out string lpConsoleTitle, uint nSize);
-
- [DllImport(KERNEL, CharSet=CharSet.Auto, SetLastError=true)]
- internal static extern bool ReadConsoleInput(nint hConsoleInput, out INPUT_RECORD buffer, int numInputRecords_UseOne, out int numEventsRead);
-
- [LibraryImport(KERNEL, SetLastError=true)]
- internal static partial uint GetConsoleOutputCP();
-
- [LibraryImport(KERNEL, SetLastError=true)]
- internal static partial uint GetConsoleCP();
-
- [LibraryImport(KERNEL, SetLastError=true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool SetConsoleOutputCP(uint codePage);
-
- [LibraryImport(KERNEL, SetLastError=true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool SetConsoleCP(uint codePage);
-
- [LibraryImport(KERNEL, SetLastError =true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static partial bool GetConsoleScreenBufferInfo(nint hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
-}
-
-///
-/// A wrapper for on Windows.
-///
-public class WindowsBackend : TerminalBackend {
- ///
- /// Creates a new Windows terminal window.
- ///
- ///
- public WindowsBackend() {
- outEnc = Encoding.UTF8;
- inEnc = Encoding.UTF8;
- outHandle = WinAPI.GetStdHandle((int)WinAPI.StandardType.Output);
- inHandle = WinAPI.GetStdHandle((int)WinAPI.StandardType.Input);
- errHandle = WinAPI.GetStdHandle((int)WinAPI.StandardType.Error);
- TextWriter outStream = TextWriter.Synchronized(new StreamWriter(Utility.GetStream(WinAPI.StandardType.Output), outEnc, 256, true));
- TextReader inStream = TextReader.Synchronized(new StreamReader(Utility.GetStream(WinAPI.StandardType.Input), inEnc, false, 256, true));
- TextWriter errStream = TextWriter.Synchronized(new StreamWriter(Utility.GetStream(WinAPI.StandardType.Error), outEnc, 256, true));
- }
- private TextWriter outStream;
- private Encoding outEnc;
- private TextReader inStream;
- private Encoding inEnc;
- private TextWriter errStream;
-
- internal nint outHandle;
- internal nint inHandle;
- internal nint errHandle;
-
- ///
- public override (int Width, int Height) Size {
- get {
- WinAPI.CONSOLE_SCREEN_BUFFER_INFO csbi = Utility.GetBufferInfo(this);
- return (csbi.srWindow.Right - csbi.srWindow.Left + 1, csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
- } set {
- throw new NotImplementedException();
- }
- }
-
- ///
- public override Encoding InputEncoding { get => inEnc;
- set {
- if (inEnc == value) return;
-
- if (!WinAPI.SetConsoleCP((uint)value.CodePage)) {
- throw new Win32Exception("Failed to set console output code page.");
- }
- inEnc = value;
- }
- }
- ///
- public override Encoding OutputEncoding { get => outEnc;
- set {
- if (outEnc == value) return;
-
- outStream.Flush();
- errStream.Flush();
-
- if (!WinAPI.SetConsoleOutputCP((uint)value.CodePage)) {
- throw new Win32Exception("Failed to set console output code page.");
- }
- outEnc = value;
- }
- }
- ///
- public override Encoding ErrorEncoding { get => outEnc; set => OutputEncoding = value; }
-
- ///
- public override TextReader StandardInput => inStream;
-
- ///
- public override TextWriter StandardOutput => outStream;
-
- ///
- public override TextWriter StandardError => errStream;
-
- ///
- /// An event for when a key is released.
- ///
- public event KeyPressCallback? OnKeyRelease;
-
- // ///
- // ///
- // /// Gets the first 300 chars of the title.
- // public override string Title {
- // get {
- // _ = WinAPI.GetConsoleTitle(out string title, 300);
- // return title;
- // }
- // set {
- // if (!WinAPI.SetConsoleTitle(value)) {
- // throw new Win32Exception("Failed to set the title: "+Marshal.GetLastWin32Error());
- // }
- // }
- // }
-
- ///
- ///
- public override void Dispose() {
-
- }
-
- ///
- ///
- protected override void ListenForKeysMethod() {
- while (listenForKeys) {
- if (!WinAPI.ReadConsoleInput(consoleIn, out WinAPI.INPUT_RECORD ev, 1, out int eventsRead)) {
- throw new Win32Exception("Failed to read console inputs: "+Marshal.GetLastWin32Error());
- }
-
- bool isKeyDown = ev.EventType == 0x0001 && ev.keyEvent.bKeyDown != false;
- char ch = ev.keyEvent.UChar;
- ushort keyCode = ev.keyEvent.wVirtualKeyCode;
-
- if (!isKeyDown) {
- if (keyCode != 0x12)
- continue;
- }
- if (ch == 0) {
- if ((keyCode >= 0x10 && keyCode <= 0x12) || keyCode == 0x14 || keyCode == 0x90 || keyCode == 0x91)
- continue;
- }
- ControlKeyState state = (ControlKeyState)ev.keyEvent.dwControlKeyState;
- bool shift = (state & ControlKeyState.ShiftPressed) != 0;
- bool alt = (state & (ControlKeyState.LeftAltPressed | ControlKeyState.RightAltPressed)) != 0;
- bool control = (state & (ControlKeyState.LeftCtrlPressed | ControlKeyState.RightCtrlPressed)) != 0;
- if (isKeyDown) {
- KeyPress((ConsoleKey)keyCode, ch, alt, shift, control);
- } else {
- OnKeyRelease?.Invoke((ConsoleKey)keyCode, ch, alt, shift, control);
- }
- }
- }
- ///
- public override (int x, int y) GetCursorPosition() {
- throw new NotImplementedException();
- }
- ///
- ///
- public override void WaitForKeyPress() {
- if (!WinAPI.ReadConsoleInput(consoleIn, out _, 1, out _)) {
- throw new Win32Exception("Failed to read console inputs: "+Marshal.GetLastWin32Error());
- }
- }
-}
\ No newline at end of file
diff --git a/Terminal/Terminal.cs b/Terminal/Terminal.cs
index 27e71e7..1eb0c21 100644
--- a/Terminal/Terminal.cs
+++ b/Terminal/Terminal.cs
@@ -83,9 +83,9 @@ public static bool ListenForKeys {set {
/// The name of the window
///
///
- public static TerminalWindow CreateBackend(string title) {
+ public static TerminalWindow CreateWindow(string title) {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
- return new WindowsBackend(title);
+ return new WinTerminalWindow(title);
} else {
throw new PlatformNotSupportedException("No window implementation for your platform.");
}
diff --git a/Terminal/Backend/TerminalBackend.cs b/Terminal/Window/TerminalWindow.cs
similarity index 67%
rename from Terminal/Backend/TerminalBackend.cs
rename to Terminal/Window/TerminalWindow.cs
index 382592b..eb8c257 100644
--- a/Terminal/Backend/TerminalBackend.cs
+++ b/Terminal/Window/TerminalWindow.cs
@@ -1,71 +1,54 @@
using System.Text;
-namespace OxDED.Terminal.Backend;
+namespace OxDED.Terminal.Window;
///
-/// Represents an interface of common methods of a terminal.
+/// Represents a terminal window that can be used.
///
-public interface ITerminalBackend : IDisposable {
+public abstract class TerminalWindow : IDisposable {
///
- /// The data stream for reading from the terminal.
+ /// Sets default values.
///
- public TextReader StandardInput { get; }
+ protected TerminalWindow() {
+ IsDisposed = true;
+ }
+
///
- /// The data stream for writing to the terminal.
+ /// The title of the Terminal window.
///
- public TextWriter StandardOutput { get; }
+ public abstract string Title { get; set;}
///
- /// The data stream for writing errors to the terminal.
+ /// The out (to terminal) stream.
///
- public TextWriter StandardError { get; }
+ public abstract TextWriter Out {get;}
///
- /// The encoding used for the stream (default: UTF-8).
+ /// The in (from terminal) stream.
///
- public Encoding InputEncoding { get; set; }
+ public abstract TextReader In {get;}
///
- /// The encoding used for the stream (default: UTF-8).
+ /// The error (to terminal) stream.
///
- public Encoding OutputEncoding { get; set; }
+ public abstract TextWriter Error {get;}
///
- /// The encoding used for the stream (default: UTF-8).
+ /// Hides or shows terminal cursor.
///
- public Encoding ErrorEncoding { get; set; }
+ public abstract bool HideCursor {get; set;}
///
- /// The width and the height (in characters) of the terminal.
+ /// The width (in characters) of the terminal.
///
- public (int Width, int Height) Size { get; set; }
-
-}
-
-///
-/// Represents an interface of common methods of a terminal.
-///
-public abstract class TerminalBackend : ITerminalBackend {
- ///
- public abstract TextReader StandardInput { get; }
-
- ///
- public abstract TextWriter StandardOutput { get; }
-
- ///
- public abstract TextWriter StandardError { get; }
-
- ///
- public abstract Encoding InputEncoding { get; set; }
-
- ///
- public abstract Encoding OutputEncoding { get; set; }
-
- ///
- public abstract Encoding ErrorEncoding { get; set; }
-
- ///
- public virtual (int Width, int Height) Size { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
-
- ///
- public abstract void Dispose();
-
-
+ public abstract int Width {get;}
+ ///
+ /// The height (in characters) of the terminal.
+ ///
+ public abstract int Height {get;}
+ ///
+ /// The encoding used for the in stream (default: UTF-8).
+ ///
+ public abstract Encoding InEncoding {get; set;}
+ ///
+ /// The encoding used for the error and out streams (default: UTF-8).
+ ///
+ public abstract Encoding OutEncoding {get; set;}
///
/// Writes something () to the terminal, with a style.
///
@@ -73,7 +56,7 @@ public abstract class TerminalBackend : ITerminalBackend {
/// The thing to write to the terminal.
/// The text decoration to use.
public virtual void Write(T? text, Style? style = null) {
- StandardOutput.Write((style ?? new Style()).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
+ Out.Write((style ?? new Style()).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
}
///
/// Writes something () to the terminal, with a style.
@@ -82,7 +65,7 @@ public virtual void Write(T? text, Style? style = null) {
/// The thing to write to the terminal.
/// The text decoration to use.
public virtual void WriteLine(T? text, Style? style = null) {
- StandardOutput.WriteLine((style ?? new Style()).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
+ Out.WriteLine((style ?? new Style()).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
}
///
/// Writes something () to the error stream, with a style.
@@ -91,7 +74,7 @@ public virtual void WriteLine(T? text, Style? style = null) {
/// The text to write to the error output stream.
/// The style to use (default: with red foreground).
public virtual void WriteErrorLine(T? text, Style? style = null) {
- StandardError.WriteLine((style ?? new Style {ForegroundColor = Colors.Red}).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
+ Error.WriteLine((style ?? new Style {ForegroundColor = Colors.Red}).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
}
///
/// Writes something () to the error stream, with a style.
@@ -100,7 +83,7 @@ public virtual void WriteErrorLine(T? text, Style? style = null) {
/// The text to write to the error output stream.
/// The style to use (default: with red foreground).
public virtual void WriteError(T? text, Style? style = null) {
- StandardError.Write((style ?? new Style {ForegroundColor = Colors.Red}).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
+ Error.Write((style ?? new Style {ForegroundColor = Colors.Red}).ToANSI()+text?.ToString()+ANSI.Styles.ResetAll);
}
///
/// Sets the cursor to that position.
@@ -108,11 +91,9 @@ public virtual void WriteError(T? text, Style? style = null) {
/// The position.
///
public virtual void Goto((int x, int y) pos) {
- try {
- if (pos.x >= Size.Width || pos.x < 0) { throw new ArgumentOutOfRangeException(nameof(pos), "pos x is higher than the width or is lower than 0."); }
- if (pos.y >= Size.Height || pos.y < 0) { throw new ArgumentOutOfRangeException(nameof(pos), "pos y is higher than the height or is lower than 0."); }
- } catch (NotImplementedException) { }
- StandardOutput.Write(ANSI.MoveCursor(pos.x, pos.y));
+ if (pos.x >= Width || pos.x < 0) { throw new ArgumentOutOfRangeException(nameof(pos), "pos x is higher than the width or is lower than 0."); }
+ if (pos.y >= Height || pos.y < 0) { throw new ArgumentOutOfRangeException(nameof(pos), "pos y is higher than the height or is lower than 0."); }
+ Out.Write(ANSI.MoveCursor(pos.x, pos.y));
}
///
/// Gets the cursor position.
@@ -122,7 +103,7 @@ public virtual void Goto((int x, int y) pos) {
///
/// Sets the something () at a , with a .
///
- /// The type of what to write.
+ ///
/// The thing to set at to the terminal.
/// The position to set at.
/// The text decoration to use.
@@ -134,7 +115,7 @@ public virtual void Set(T? text, (int x, int y) pos, Style? style = null) {
///
/// Sets the something in the error stream () at a , with a .
///
- /// The type of what to write.
+ ///
/// The thing to set at to the terminal.
/// The position to set at.
/// The text decoration to use.
@@ -147,14 +128,14 @@ public virtual void SetError(T? text, (int x, int y) pos, Style? style = null
///
/// The character that has been read (-1 if everything has been read).
public virtual int Read() {
- return StandardInput.Read();
+ return In.Read();
}
///
/// Reads a line from the input stream.
///
/// The line that has been read (null if everything has been read).
public virtual string? ReadLine() {
- return StandardInput.ReadLine();
+ return In.ReadLine();
}
///
/// Waits until a key is pressed.
@@ -181,7 +162,7 @@ protected void KeyPress(ConsoleKey key, char keyChar, bool alt, bool shift, bool
///
public virtual void Clear() {
Goto((0,0));
- StandardOutput.Write(ANSI.EraseScreenFromCursor);
+ Out.Write(ANSI.EraseScreenFromCursor);
}
///
/// Clears screen from the position to end of the screen.
@@ -189,7 +170,7 @@ public virtual void Clear() {
/// The start position.
public virtual void ClearFrom((int x, int y) pos) {
Goto(pos);
- StandardOutput.Write(ANSI.EraseLineFromCursor);
+ Out.Write(ANSI.EraseLineFromCursor);
}
///
/// Clears (deletes) a line.
@@ -197,7 +178,7 @@ public virtual void ClearFrom((int x, int y) pos) {
/// The y-axis of the line.
public virtual void ClearLine(int line) {
Goto((0, line));
- StandardOutput.Write(ANSI.EraseLine);
+ Out.Write(ANSI.EraseLine);
}
///
/// Clears the line from the position to the end of the line.
@@ -205,7 +186,7 @@ public virtual void ClearLine(int line) {
/// The start position.
public virtual void ClearLineFrom((int x, int y) pos) {
Goto(pos);
- StandardOutput.Write(ANSI.EraseLineFromCursor);
+ Out.Write(ANSI.EraseLineFromCursor);
}
///
/// The thread that is running .
@@ -229,9 +210,27 @@ public virtual bool ListenForKeys {set {
} get {
return listenForKeys;
}}
-
///
/// Method in new thread that should call when a key is pressed.
///
protected abstract void ListenForKeysMethod();
+
+ ///
+ /// If it already is disposed.
+ ///
+ public bool IsDisposed {get; protected set;}
+ ///
+ public virtual void Dispose() {
+ if (IsDisposed) { return; }
+ IsDisposed = true;
+
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Disposes the window.
+ ///
+ ~TerminalWindow() {
+ Dispose();
+ }
}
\ No newline at end of file
diff --git a/Terminal/Window/WinTerminalWindow.cs b/Terminal/Window/WinTerminalWindow.cs
new file mode 100644
index 0000000..52bf4d1
--- /dev/null
+++ b/Terminal/Window/WinTerminalWindow.cs
@@ -0,0 +1,266 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+namespace OxDED.Terminal.Window;
+
+internal static class Utils {
+
+ internal static Stream GetStream(nint handle) {
+ SafeFileHandle fileHandle = new(handle, false);
+ FileStream stream = new(fileHandle, FileAccess.ReadWrite);
+ return stream;
+ }
+}
+
+internal static partial class WinAPI {
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct KEY_EVENT_RECORD {
+ [MarshalAs(UnmanagedType.Bool)]
+ internal bool bKeyDown;
+ internal ushort wRepeatCount;
+ internal ushort wVirtualKeyCode;
+ internal ushort wVirtualScanCode;
+ private ushort _uChar;
+ internal uint dwControlKeyState;
+ internal readonly char uChar => (char)_uChar;
+ }
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct INPUT_RECORD {
+ internal ushort EventType;
+ internal KEY_EVENT_RECORD keyEvent;
+ }
+ internal const int STD_OUTPUT_HANDLE = -11;
+ internal const int STD_INPUT_HANDLE = -10;
+ internal const int STD_ERROR_HANDLE = -12;
+
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool AllocConsole();
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool FreeConsole();
+ [LibraryImport("kernel32.dll")]
+ internal static partial nint GetConsoleWindow();
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ internal static partial nint GetStdHandle(int nStdHandle);
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool CloseHandle(nint handle);
+ [LibraryImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static partial bool SetStdHandle(int nStdHandle, nint hConsoleOutput);
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern nint CreateFile(string fileName, uint desiredAccess, int shareMode, nint securityAttributes, int creationDisposition, int flagsAndAttributes, nint templateFile);
+ [DllImport("kernel32.dll", SetLastError = true, BestFitMapping = true, CharSet = CharSet.Unicode)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool SetConsoleTitle(string title);
+ [DllImport("kernel32.dll", SetLastError = true, BestFitMapping = true, CharSet = CharSet.Auto)]
+ internal static extern uint GetConsoleTitle([MarshalAs(UnmanagedType.LPTStr)]out string lpConsoleTitle, uint nSize);
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool ReadConsoleInput(nint hConsoleInput, out INPUT_RECORD lpBuffer, int nLength, out int numEventsRead);
+}
+
+///
+/// A wrapper for on Windows.
+///
+public class WinTerminalWindow : TerminalWindow {
+ private const string ConsoleIn = "CONIN$";
+ private const string ConsoleOut = "CONOUT$";
+ private const string ConsoleError = ConsoleOut;
+
+ private nint consoleOut;
+ private nint consoleIn;
+ private nint consoleErr;
+ ///
+ /// Creates a new Windows terminal window.
+ ///
+ /// The name of the window.
+ ///
+ public WinTerminalWindow(string title) {
+ nint stdOut = WinAPI.GetStdHandle(WinAPI.STD_OUTPUT_HANDLE);
+ nint stdIn = WinAPI.GetStdHandle(WinAPI.STD_INPUT_HANDLE);
+ nint stdErr = WinAPI.GetStdHandle(WinAPI.STD_ERROR_HANDLE);
+
+ if (WinAPI.GetConsoleWindow() == nint.Zero) {
+ if (!WinAPI.AllocConsole()) {
+ throw new Win32Exception("Failed to allocate a console: "+Marshal.GetLastWin32Error());
+ }
+ }
+
+ consoleOut = WinAPI.CreateFile(ConsoleOut, 0x80000000 | 0x40000000, 2, nint.Zero, 3, 0, nint.Zero);
+ consoleIn = WinAPI.CreateFile(ConsoleIn, 0x80000000 | 0x40000000, 1, nint.Zero, 3, 0, nint.Zero);
+ consoleErr = WinAPI.CreateFile(ConsoleError, 0x80000000 | 0x40000000, 2, nint.Zero, 3, 0, nint.Zero);
+
+ if (!WinAPI.SetStdHandle(WinAPI.STD_OUTPUT_HANDLE, consoleOut)) {
+ throw new Win32Exception("Failed to set the handle for the console out stream: "+Marshal.GetLastWin32Error());
+ }
+
+ if (!WinAPI.SetStdHandle(WinAPI.STD_INPUT_HANDLE, consoleIn)) {
+ throw new Win32Exception("Failed to set the handle for the console in stream: "+Marshal.GetLastWin32Error());
+ }
+
+ if (!WinAPI.SetStdHandle(WinAPI.STD_ERROR_HANDLE, consoleErr)) {
+ throw new Win32Exception("Failed to set the handle for the console error stream: "+Marshal.GetLastWin32Error());
+ }
+
+ if (!WinAPI.CloseHandle(stdOut)) {
+ throw new Win32Exception("Failed to close the handle of stdOut: "+Marshal.GetLastWin32Error());
+ }
+ if (!WinAPI.CloseHandle(stdIn)) {
+ throw new Win32Exception("Failed to close the handle of stdIn: "+Marshal.GetLastWin32Error());
+ }
+ if (!WinAPI.CloseHandle(stdErr)) {
+ throw new Win32Exception("Failed to close the handle of stdErr: "+Marshal.GetLastWin32Error());
+ }
+
+ outStream = new StreamWriter(Utils.GetStream(consoleOut), Encoding.UTF8);
+ inStream = new StreamReader(Utils.GetStream(consoleIn), Encoding.UTF8);
+ errStream = new StreamWriter(Utils.GetStream(consoleErr), Encoding.UTF8);
+
+ Title = title;
+ }
+ private StreamWriter outStream;
+ ///
+ public override TextWriter Out { get => outStream;}
+ private StreamReader inStream;
+ ///
+ public override TextReader In { get => inStream;}
+ private StreamWriter errStream;
+ ///
+ public override TextWriter Error { get => errStream;}
+
+ ///
+ public override int Width => Console.WindowWidth;
+
+ ///
+ public override int Height => Console.WindowHeight;
+ ///
+ public override Encoding InEncoding { get => inStream.CurrentEncoding; set { inStream = new StreamReader(Utils.GetStream(consoleIn), value); } }
+ ///
+ public override Encoding OutEncoding { get => outStream.Encoding; set { outStream = new StreamWriter(Utils.GetStream(consoleOut), value); errStream = new StreamWriter(Utils.GetStream(consoleErr), value); } }
+ ///
+ /// An event for when a key is released.
+ ///
+ public event KeyPressCallback? OnKeyRelease;
+
+ ///
+ ///
+ /// Gets the first 300 chars of the title.
+ public override string Title {
+ get {
+ _ = WinAPI.GetConsoleTitle(out string title, 300);
+ return title;
+ }
+ set {
+ if (!WinAPI.SetConsoleTitle(value)) {
+ throw new Win32Exception("Failed to set the title: "+Marshal.GetLastWin32Error());
+ }
+ }
+ }
+
+ private bool isCursorHidden = false;
+
+ ///
+ public override bool HideCursor {
+ set {
+ if (value) {
+ outStream.Write(ANSI.CursorInvisible);
+ } else {
+ outStream.Write(ANSI.CursorVisible);
+ }
+ isCursorHidden = value;
+ } get {
+ return isCursorHidden;
+ }
+ }
+
+ ///
+ ///
+ public override void Dispose() {
+ if (IsDisposed) { return; }
+
+ if (!WinAPI.CloseHandle(consoleOut)) {
+ throw new Win32Exception("Failed to close console out: "+Marshal.GetLastWin32Error());
+ }
+ consoleOut = nint.Zero;
+ if (!WinAPI.CloseHandle(consoleIn)) {
+ throw new Win32Exception("Failed to close console in: "+Marshal.GetLastWin32Error());
+ }
+ consoleIn = nint.Zero;
+ if (!WinAPI.CloseHandle(consoleErr)) {
+ throw new Win32Exception("Failed to close console err: "+Marshal.GetLastWin32Error());
+ }
+ consoleErr = nint.Zero;
+
+ if (!WinAPI.FreeConsole()) {
+ throw new Win32Exception("Failed to free the console window: "+Marshal.GetLastWin32Error());
+ }
+ Console.SetError(new StreamWriter(Console.OpenStandardError()));
+ Console.SetIn(new StreamReader(Console.OpenStandardInput()));
+ Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
+
+ IsDisposed = true;
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ ///
+ protected override void ListenForKeysMethod() {
+ while (listenForKeys) {
+ if (!WinAPI.ReadConsoleInput(consoleIn, out WinAPI.INPUT_RECORD ev, 1, out int eventsRead)) {
+ throw new Win32Exception("Failed to read console inputs: "+Marshal.GetLastWin32Error());
+ }
+
+ bool isKeyDown = ev.EventType == 0x0001 && ev.keyEvent.bKeyDown != false;
+ char ch = ev.keyEvent.uChar;
+ ushort keyCode = ev.keyEvent.wVirtualKeyCode;
+
+ if (!isKeyDown) {
+ if (keyCode != 0x12)
+ continue;
+ }
+ if (ch == 0) {
+ if ((keyCode >= 0x10 && keyCode <= 0x12) || keyCode == 0x14 || keyCode == 0x90 || keyCode == 0x91)
+ continue;
+ }
+ ControlKeyState state = (ControlKeyState)ev.keyEvent.dwControlKeyState;
+ bool shift = (state & ControlKeyState.ShiftPressed) != 0;
+ bool alt = (state & (ControlKeyState.LeftAltPressed | ControlKeyState.RightAltPressed)) != 0;
+ bool control = (state & (ControlKeyState.LeftCtrlPressed | ControlKeyState.RightCtrlPressed)) != 0;
+ if (isKeyDown) {
+ KeyPress((ConsoleKey)keyCode, ch, alt, shift, control);
+ } else {
+ OnKeyRelease?.Invoke((ConsoleKey)keyCode, ch, alt, shift, control);
+ }
+ }
+ }
+ ///
+ public override (int x, int y) GetCursorPosition() {
+ throw new NotImplementedException();
+ }
+ ///
+ ///
+ public override void WaitForKeyPress() {
+ if (!WinAPI.ReadConsoleInput(consoleIn, out _, 1, out _)) {
+ throw new Win32Exception("Failed to read console inputs: "+Marshal.GetLastWin32Error());
+ }
+ }
+
+ [Flags]
+ internal enum ControlKeyState {
+ RightAltPressed = 0x0001,
+ LeftAltPressed = 0x0002,
+ RightCtrlPressed = 0x0004,
+ LeftCtrlPressed = 0x0008,
+ ShiftPressed = 0x0010,
+ NumLockOn = 0x0020,
+ ScrollLockOn = 0x0040,
+ CapsLockOn = 0x0080,
+ EnhancedKey = 0x0100
+ }
+}
\ No newline at end of file
diff --git a/Terminal/Window/Window.cs b/Terminal/Window/Window.cs
deleted file mode 100644
index 08130ec..0000000
--- a/Terminal/Window/Window.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using OxDED.Terminal.Backend;
-
-namespace OxDED.Terminal.Window;
-
-///
-/// Represents a terminal window.
-///
-public interface IWindow : ITerminalBackend {
- ///
- /// The title of the terminal window.
- ///
- public string Title { get; set; }
-}
\ No newline at end of file