From a02bb0aabd39e4528e536258a139583c4685c73f Mon Sep 17 00:00:00 2001 From: Reece Date: Wed, 18 May 2022 19:14:24 +0100 Subject: [PATCH] [*] Early internal non-canonical TTY api --- Source/Console/ConsoleStd/ConsoleStd.cpp | 281 +++++++++++++++++++- Source/Console/ConsoleStd/ConsoleStd.hpp | 34 +++ Source/Console/ConsoleTTY/ConsoleTTY.NT.cpp | 3 +- 3 files changed, 303 insertions(+), 15 deletions(-) diff --git a/Source/Console/ConsoleStd/ConsoleStd.cpp b/Source/Console/ConsoleStd/ConsoleStd.cpp index 262c8331..f26b277d 100755 --- a/Source/Console/ConsoleStd/ConsoleStd.cpp +++ b/Source/Console/ConsoleStd/ConsoleStd.cpp @@ -15,6 +15,8 @@ #include #include +#include + #if defined(AURORA_IS_MODERNNT_DERIVED) || defined(AURORA_IS_POSIX_DERIVED) #if defined(AURORA_IS_MODERNNT_DERIVED) @@ -77,6 +79,7 @@ namespace Aurora::Console::ConsoleStd using StreamHandle_t = HANDLE; static StreamHandle_t gWin32Thread = INVALID_HANDLE_VALUE; + static DWORD gCanonicalBackup {}; #elif defined(IO_POSIX_STREAMS) #define DEFAULT_HANDLE_VAL 0xFFFFFFFF using StreamHandle_t = int; @@ -95,16 +98,21 @@ namespace Aurora::Console::ConsoleStd static StreamHandle_t gInputStream = DEFAULT_HANDLE_VAL; static StreamHandle_t gOutputStream = DEFAULT_HANDLE_VAL; static AuSPtr gLoopSource; - + static bool gCanonicalEnabled {}; + + static AuList gCanonicalBuffer; + //static AuThreadPrimitives::MutexUnique_t gRingLock = AuThreadPrimitives::MutexUnique(); static AuThreadPrimitives::SpinLock gRingLock;// = AuThreadPrimitives::MutexUnique(); #if defined(AURORA_IS_MODERNNT_DERIVED) + void ProcessCanonical(); + static DWORD WINAPI StdInWin32Thread(void*) { HANDLE a[2] = {gInputStream, gTerminateConsole}; - while (true) + while (!gCanonicalEnabled) { WaitForMultipleObjectsEx(2, a, false, 25, 0); @@ -118,8 +126,262 @@ namespace Aurora::Console::ConsoleStd return 1; } - #endif + void ProcessCanonical(HANDLE h) + { + INPUT_RECORD records[4096]; + DWORD dwRecords; + + void *data = &gLineEncodedBuffer[gEncodedIndex]; + auto length = kLineBufferMax - gEncodedIndex; + + if (!IS_STREAM_HANDLE_VALID(h)) + { + h = gInputStream; + } + + if (length == 0) + { + return; + } + + if (!GetNumberOfConsoleInputEvents(h, + &dwRecords)) + { + return; + } + + dwRecords = AuMin(dwRecords, DWORD(AuArraySize(records))); + if (!dwRecords) + { + return; + } + + if (!ReadConsoleInputW(h, + records, + dwRecords, + &dwRecords)) + { + return; + } + + AU_LOCK_GUARD(gRingLock); + + gCanonicalBuffer.reserve(4096); + + for (int i = 0; i < dwRecords; i++) + { + int z; + bool dBreak = false; + CanonicalInput canInput; + canInput.type = ECanonicalInput::eEnumInvalid; + canInput.scrollDeltaY = 0; + + auto &record = records[i]; + AuString key; + + switch (record.EventType) + { + case KEY_EVENT: + + if (!record.Event.KeyEvent.bKeyDown) + { + dBreak = true; + break; + } + + if (record.Event.KeyEvent.wVirtualKeyCode <= VK_HELP && + record.Event.KeyEvent.wVirtualKeyCode != VK_SPACE) + { + switch (record.Event.KeyEvent.wVirtualKeyCode) + { + case VK_UP: + canInput.type = ECanonicalInput::eArrowUp; + break; + case VK_DOWN: + canInput.type = ECanonicalInput::eArrowDown; + break; + case VK_LEFT: + canInput.type = ECanonicalInput::eArrowLeft; + break; + case VK_RIGHT: + canInput.type = ECanonicalInput::eArrowRight; + break; + case VK_HOME: + canInput.type = ECanonicalInput::eHome; + break; + case VK_BACK: + canInput.type = ECanonicalInput::eBackspace; + break; + case VK_PRIOR: + canInput.type = ECanonicalInput::ePageUp; + break; + case VK_NEXT: + canInput.type = ECanonicalInput::ePageDown; + break; + case VK_RETURN: + canInput.type = ECanonicalInput::eEnter; + break; + default: + dBreak = true; + } + + } + else + { + canInput.type = ECanonicalInput::eInput; + + key = AuLocale::ConvertFromWChar(&record.Event.KeyEvent.uChar.UnicodeChar, 1); + + if (key.empty()) + { + return; + } + + for (z = 0; z < record.Event.KeyEvent.wRepeatCount; z++) + { + canInput.keyUp += key; + } + } + + break; + case MOUSE_EVENT: + + if (record.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED) + { + canInput.type = ECanonicalInput::eScroll; + canInput.scrollDeltaY = AuStaticCast(AuBitsToHigher((AuUInt32)record.Event.MouseEvent.dwButtonState)); + + if (canInput.scrollDeltaY > 1) + canInput.scrollDeltaY = 1; + else if (canInput.scrollDeltaY < -1) + canInput.scrollDeltaY = -1; + } + else + { + dBreak = true; + } + + + break; + + default: + dBreak = true; + break; + }; + + if (dBreak) continue; + + if (!AuTryInsert(gCanonicalBuffer, canInput)) + { + return; + } + } + } + + void ProcessCanonical() + { + ProcessCanonical(INVALID_HANDLE_VALUE); + } + + void SignalKillNT() + { + if (IS_STREAM_HANDLE_VALID(gWin32Thread)) + { + CancelSynchronousIo(gWin32Thread); + } + + if (IS_STREAM_HANDLE_VALID(gTerminateConsole)) + { + SetEvent(gTerminateConsole); + } + + if (WaitForSingleObject(gWin32Thread, 200) != WAIT_OBJECT_0) + { + TerminateThread(gWin32Thread, 0); + } + + AuWin32CloseHandle(gWin32Thread); + } + + bool EnterCanMode() + { + DWORD mode; + + if (!GetConsoleMode(gInputStream, &mode)) + { + return false; + } + + gCanonicalBackup = mode; + + + mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_QUICK_EDIT_MODE); + mode |= ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT | ENABLE_EXTENDED_FLAGS; + + SetConsoleMode(gInputStream, mode); + + gCanonicalEnabled = true; + + SignalKillNT(); + + INPUT_RECORD niceWorkMicrosoft[2] {}; + niceWorkMicrosoft[0].EventType = KEY_EVENT; + niceWorkMicrosoft[0].Event.KeyEvent.bKeyDown = TRUE; + niceWorkMicrosoft[0].Event.KeyEvent.dwControlKeyState = 0; + niceWorkMicrosoft[0].Event.KeyEvent.wRepeatCount = 1; + niceWorkMicrosoft[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + niceWorkMicrosoft[0].Event.KeyEvent.uChar.UnicodeChar = '\r'; + niceWorkMicrosoft[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC); + + niceWorkMicrosoft[1].EventType = KEY_EVENT; + niceWorkMicrosoft[1].Event.KeyEvent.bKeyDown = FALSE; + niceWorkMicrosoft[1].Event.KeyEvent.dwControlKeyState = 0; + niceWorkMicrosoft[1].Event.KeyEvent.wRepeatCount = 1; + niceWorkMicrosoft[1].Event.KeyEvent.wVirtualKeyCode = VK_RETURN; + niceWorkMicrosoft[1].Event.KeyEvent.uChar.UnicodeChar = '\r'; + niceWorkMicrosoft[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC); + + DWORD idc; + WriteConsoleInputW(gInputStream, niceWorkMicrosoft, 2, &idc); + + return true; + } + + void LeaveCanMode() + { + if (!AuExchange(gCanonicalEnabled, false)) + { + return; + } + + SetConsoleMode(gInputStream, gCanonicalBackup); + + gWin32Thread = CreateThread(nullptr, 0, StdInWin32Thread, nullptr, 0, nullptr); + } + +#else + + bool EnterCanMode() + { + } + + void LeaveCanMode() + { + } + + + void ProcessCanonical() + { + + } +#endif + + AuList DebufferCanonicalInput() + { + AU_LOCK_GUARD(gRingLock); + return AuExchange(gCanonicalBuffer, {}); + } bool ConsoleHasDataLoopSource::IsSignaled() { @@ -625,7 +887,6 @@ namespace Aurora::Console::ConsoleStd if (gRuntimeConfig.console.enableStdPassthrough && gRuntimeConfig.console.enableStdIn) { - if (gLoopSource) { gLoopSource->Set(); @@ -675,16 +936,10 @@ namespace Aurora::Console::ConsoleStd #if defined(AURORA_IS_MODERNNT_DERIVED) - if (IS_STREAM_HANDLE_VALID(gWin32Thread)) - { - CancelSynchronousIo(gWin32Thread); - if (WaitForSingleObject(gWin32Thread, 200) != WAIT_OBJECT_0) - { - TerminateThread(gWin32Thread, 0); - } - } + gCanonicalEnabled = false; + + SignalKillNT(); - AuWin32CloseHandle(gWin32Thread); AuWin32CloseHandle(gTerminateConsole); // Note: CloseHandle in the middle of a ReadFile blocks diff --git a/Source/Console/ConsoleStd/ConsoleStd.hpp b/Source/Console/ConsoleStd/ConsoleStd.hpp index d0d64e6b..684e26d8 100644 --- a/Source/Console/ConsoleStd/ConsoleStd.hpp +++ b/Source/Console/ConsoleStd/ConsoleStd.hpp @@ -28,4 +28,38 @@ namespace Aurora::Console::ConsoleStd AuUInt32 WriteStdOut(const void *data, AuUInt32 length); void WriteStdOut(AuUInt8 level, const ConsoleMessage &msg); + + AUE_DEFINE(ECanonicalInput, + ( + eInput, + eArrowUp, + eArrowDown, + eArrowRight, + eArrowLeft, + eHome, + eBackspace, + ePageUp, + ePageDown, + eScroll, + eEnter + )); + + struct CanonicalInput + { + ECanonicalInput type; + AuString keyUp; + int scrollDeltaY; + }; + + + bool EnterCanMode(); + void LeaveCanMode(); + + AuList DebufferCanonicalInput(); + + void ProcessCanonical(); + +#if defined(AURORA_IS_MODERNNT_DERIVED) + void ProcessCanonical(HANDLE h); +#endif } \ No newline at end of file diff --git a/Source/Console/ConsoleTTY/ConsoleTTY.NT.cpp b/Source/Console/ConsoleTTY/ConsoleTTY.NT.cpp index fcbea7c2..2a5c1883 100644 --- a/Source/Console/ConsoleTTY/ConsoleTTY.NT.cpp +++ b/Source/Console/ConsoleTTY/ConsoleTTY.NT.cpp @@ -423,7 +423,7 @@ namespace Aurora::Console::ConsoleTTY SetConsoleWindowInfo(gConsoles[0].h, true, &screen); SetConsoleWindowInfo(gConsoles[1].h, true, &screen); - return false; + return true; } if (((res.first == curConsole.width && @@ -523,7 +523,6 @@ namespace Aurora::Console::ConsoleTTY if (!IdkMan()) { gIsRecording = false; - gRecordedActions.clear(); return false; }