[*] Early internal non-canonical TTY api
This commit is contained in:
parent
6fb3f3a9e5
commit
a02bb0aabd
@ -15,6 +15,8 @@
|
|||||||
#include <Source/Loop/LSHandle.hpp>
|
#include <Source/Loop/LSHandle.hpp>
|
||||||
#include <Source/Loop/LSEvent.hpp>
|
#include <Source/Loop/LSEvent.hpp>
|
||||||
|
|
||||||
|
#include <Source/Console/ConsoleTTY/ConsoleTTY.hpp>
|
||||||
|
|
||||||
#if defined(AURORA_IS_MODERNNT_DERIVED) || defined(AURORA_IS_POSIX_DERIVED)
|
#if defined(AURORA_IS_MODERNNT_DERIVED) || defined(AURORA_IS_POSIX_DERIVED)
|
||||||
|
|
||||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
@ -77,6 +79,7 @@ namespace Aurora::Console::ConsoleStd
|
|||||||
using StreamHandle_t = HANDLE;
|
using StreamHandle_t = HANDLE;
|
||||||
|
|
||||||
static StreamHandle_t gWin32Thread = INVALID_HANDLE_VALUE;
|
static StreamHandle_t gWin32Thread = INVALID_HANDLE_VALUE;
|
||||||
|
static DWORD gCanonicalBackup {};
|
||||||
#elif defined(IO_POSIX_STREAMS)
|
#elif defined(IO_POSIX_STREAMS)
|
||||||
#define DEFAULT_HANDLE_VAL 0xFFFFFFFF
|
#define DEFAULT_HANDLE_VAL 0xFFFFFFFF
|
||||||
using StreamHandle_t = int;
|
using StreamHandle_t = int;
|
||||||
@ -95,16 +98,21 @@ namespace Aurora::Console::ConsoleStd
|
|||||||
static StreamHandle_t gInputStream = DEFAULT_HANDLE_VAL;
|
static StreamHandle_t gInputStream = DEFAULT_HANDLE_VAL;
|
||||||
static StreamHandle_t gOutputStream = DEFAULT_HANDLE_VAL;
|
static StreamHandle_t gOutputStream = DEFAULT_HANDLE_VAL;
|
||||||
static AuSPtr<ConsoleHasDataLoopSource> gLoopSource;
|
static AuSPtr<ConsoleHasDataLoopSource> gLoopSource;
|
||||||
|
static bool gCanonicalEnabled {};
|
||||||
|
|
||||||
|
static AuList<CanonicalInput> gCanonicalBuffer;
|
||||||
|
|
||||||
//static AuThreadPrimitives::MutexUnique_t gRingLock = AuThreadPrimitives::MutexUnique();
|
//static AuThreadPrimitives::MutexUnique_t gRingLock = AuThreadPrimitives::MutexUnique();
|
||||||
static AuThreadPrimitives::SpinLock gRingLock;// = AuThreadPrimitives::MutexUnique();
|
static AuThreadPrimitives::SpinLock gRingLock;// = AuThreadPrimitives::MutexUnique();
|
||||||
|
|
||||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
|
void ProcessCanonical();
|
||||||
|
|
||||||
static DWORD WINAPI StdInWin32Thread(void*)
|
static DWORD WINAPI StdInWin32Thread(void*)
|
||||||
{
|
{
|
||||||
HANDLE a[2] = {gInputStream, gTerminateConsole};
|
HANDLE a[2] = {gInputStream, gTerminateConsole};
|
||||||
|
|
||||||
while (true)
|
while (!gCanonicalEnabled)
|
||||||
{
|
{
|
||||||
WaitForMultipleObjectsEx(2, a, false, 25, 0);
|
WaitForMultipleObjectsEx(2, a, false, 25, 0);
|
||||||
|
|
||||||
@ -118,8 +126,262 @@ namespace Aurora::Console::ConsoleStd
|
|||||||
|
|
||||||
return 1;
|
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<AuInt16>(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<CanonicalInput> DebufferCanonicalInput()
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(gRingLock);
|
||||||
|
return AuExchange(gCanonicalBuffer, {});
|
||||||
|
}
|
||||||
|
|
||||||
bool ConsoleHasDataLoopSource::IsSignaled()
|
bool ConsoleHasDataLoopSource::IsSignaled()
|
||||||
{
|
{
|
||||||
@ -625,7 +887,6 @@ namespace Aurora::Console::ConsoleStd
|
|||||||
|
|
||||||
if (gRuntimeConfig.console.enableStdPassthrough && gRuntimeConfig.console.enableStdIn)
|
if (gRuntimeConfig.console.enableStdPassthrough && gRuntimeConfig.console.enableStdIn)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (gLoopSource)
|
if (gLoopSource)
|
||||||
{
|
{
|
||||||
gLoopSource->Set();
|
gLoopSource->Set();
|
||||||
@ -675,16 +936,10 @@ namespace Aurora::Console::ConsoleStd
|
|||||||
|
|
||||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
|
|
||||||
if (IS_STREAM_HANDLE_VALID(gWin32Thread))
|
gCanonicalEnabled = false;
|
||||||
{
|
|
||||||
CancelSynchronousIo(gWin32Thread);
|
SignalKillNT();
|
||||||
if (WaitForSingleObject(gWin32Thread, 200) != WAIT_OBJECT_0)
|
|
||||||
{
|
|
||||||
TerminateThread(gWin32Thread, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AuWin32CloseHandle(gWin32Thread);
|
|
||||||
AuWin32CloseHandle(gTerminateConsole);
|
AuWin32CloseHandle(gTerminateConsole);
|
||||||
|
|
||||||
// Note: CloseHandle in the middle of a ReadFile blocks
|
// Note: CloseHandle in the middle of a ReadFile blocks
|
||||||
|
@ -28,4 +28,38 @@ namespace Aurora::Console::ConsoleStd
|
|||||||
AuUInt32 WriteStdOut(const void *data, AuUInt32 length);
|
AuUInt32 WriteStdOut(const void *data, AuUInt32 length);
|
||||||
|
|
||||||
void WriteStdOut(AuUInt8 level, const ConsoleMessage &msg);
|
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<CanonicalInput> DebufferCanonicalInput();
|
||||||
|
|
||||||
|
void ProcessCanonical();
|
||||||
|
|
||||||
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||||
|
void ProcessCanonical(HANDLE h);
|
||||||
|
#endif
|
||||||
}
|
}
|
@ -423,7 +423,7 @@ namespace Aurora::Console::ConsoleTTY
|
|||||||
SetConsoleWindowInfo(gConsoles[0].h, true, &screen);
|
SetConsoleWindowInfo(gConsoles[0].h, true, &screen);
|
||||||
SetConsoleWindowInfo(gConsoles[1].h, true, &screen);
|
SetConsoleWindowInfo(gConsoles[1].h, true, &screen);
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((res.first == curConsole.width &&
|
if (((res.first == curConsole.width &&
|
||||||
@ -523,7 +523,6 @@ namespace Aurora::Console::ConsoleTTY
|
|||||||
if (!IdkMan())
|
if (!IdkMan())
|
||||||
{
|
{
|
||||||
gIsRecording = false;
|
gIsRecording = false;
|
||||||
gRecordedActions.clear();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user