| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- #nullable enable
- using System.Runtime.InteropServices;
- using Microsoft.Extensions.Logging;
- using static Terminal.Gui.Drivers.WindowsConsole;
- namespace Terminal.Gui.Drivers;
- internal class WindowsInput : ConsoleInput<InputRecord>, IWindowsInput
- {
- private readonly nint _inputHandle;
- [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
- public static extern bool ReadConsoleInput (
- nint hConsoleInput,
- nint lpBuffer,
- uint nLength,
- out uint lpNumberOfEventsRead
- );
- [DllImport ("kernel32.dll", EntryPoint = "PeekConsoleInputW", CharSet = CharSet.Unicode)]
- public static extern bool PeekConsoleInput (
- nint hConsoleInput,
- nint lpBuffer,
- uint nLength,
- out uint lpNumberOfEventsRead
- );
- [DllImport ("kernel32.dll", SetLastError = true)]
- private static extern nint GetStdHandle (int nStdHandle);
- [DllImport ("kernel32.dll")]
- private static extern bool GetConsoleMode (nint hConsoleHandle, out uint lpMode);
- [DllImport ("kernel32.dll")]
- private static extern bool SetConsoleMode (nint hConsoleHandle, uint dwMode);
- private readonly uint _originalConsoleMode;
- [DllImport ("kernel32.dll", SetLastError = true)]
- private static extern bool FlushConsoleInputBuffer (nint hConsoleInput);
- public WindowsInput ()
- {
- Logging.Logger.LogInformation ($"Creating {nameof (WindowsInput)}");
- if (ConsoleDriver.RunningUnitTests)
- {
- return;
- }
- _inputHandle = GetStdHandle (STD_INPUT_HANDLE);
- GetConsoleMode (_inputHandle, out uint v);
- _originalConsoleMode = v;
- uint newConsoleMode = _originalConsoleMode;
- newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
- newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
- newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
- SetConsoleMode (_inputHandle, newConsoleMode);
- }
- protected override bool Peek ()
- {
- if (ConsoleDriver.RunningUnitTests)
- {
- return false;
- }
- const int BUFFER_SIZE = 1; // We only need to check if there's at least one event
- nint pRecord = Marshal.AllocHGlobal (Marshal.SizeOf<InputRecord> () * BUFFER_SIZE);
- try
- {
- // Use PeekConsoleInput to inspect the input buffer without removing events
- if (PeekConsoleInput (_inputHandle, pRecord, BUFFER_SIZE, out uint numberOfEventsRead))
- {
- // Return true if there's at least one event in the buffer
- return numberOfEventsRead > 0;
- }
- else
- {
- // Handle the failure of PeekConsoleInput
- throw new InvalidOperationException ("Failed to peek console input.");
- }
- }
- catch (Exception ex)
- {
- // Optionally log the exception
- Console.WriteLine (@$"Error in Peek: {ex.Message}");
- return false;
- }
- finally
- {
- // Free the allocated memory
- Marshal.FreeHGlobal (pRecord);
- }
- }
- protected override IEnumerable<InputRecord> Read ()
- {
- const int BUFFER_SIZE = 1;
- nint pRecord = Marshal.AllocHGlobal (Marshal.SizeOf<InputRecord> () * BUFFER_SIZE);
- try
- {
- ReadConsoleInput (
- _inputHandle,
- pRecord,
- BUFFER_SIZE,
- out uint numberEventsRead);
- return numberEventsRead == 0
- ? []
- : new [] { Marshal.PtrToStructure<InputRecord> (pRecord) };
- }
- catch (Exception)
- {
- return [];
- }
- finally
- {
- Marshal.FreeHGlobal (pRecord);
- }
- }
- public override void Dispose ()
- {
- if (ConsoleDriver.RunningUnitTests)
- {
- return;
- }
- if (!FlushConsoleInputBuffer (_inputHandle))
- {
- throw new ApplicationException ($"Failed to flush input buffer, error code: {Marshal.GetLastWin32Error ()}.");
- }
- SetConsoleMode (_inputHandle, _originalConsoleMode);
- }
- }
|