From fd4da73a5a00cffe106f035be842dabea7c024b7 Mon Sep 17 00:00:00 2001 From: Konstantin-Glukhov Date: Fri, 30 Jun 2023 02:58:12 +0900 Subject: [PATCH] Unify MS Windows console events into one buffer --- os.c | 15 +--- screen.c | 233 ++++++++++++++++++++----------------------------------- 2 files changed, 89 insertions(+), 159 deletions(-) diff --git a/os.c b/os.c index 35eb1533..b9097c41 100644 --- a/os.c +++ b/os.c @@ -244,18 +244,11 @@ public int iread(int fd, unsigned char *buf, unsigned int len) } #else #if MSDOS_COMPILER==WIN32C - if (win32_kbhit()) + if (win32_kbhit() && WIN32getch() == intr_char) { - int c; - - c = WIN32getch(); - if (c == intr_char) - { - sigs |= S_INTERRUPT; - reading = 0; - return (READ_INTR); - } - WIN32ungetch(c); + sigs |= S_INTERRUPT; + reading = 0; + return (READ_INTR); } #endif #endif diff --git a/screen.c b/screen.c index f90ba764..de3d6428 100644 --- a/screen.c +++ b/screen.c @@ -140,20 +140,17 @@ extern int sc_height; // UTF16 and UTF8 buffer sizes #define UTF8_MAX_LENGTH 4 #define UTF16_MAX_LENGTH 2 -struct keyRecord +#define KEY_BUFFER_MAX_LENGTH 6 +struct consoleBuffer { - int scan; - char ascii; - char utf8[UTF8_MAX_LENGTH]; - int utf8_nbytes; - int utf8_current_byte; -} currentKey; + char buf[KEY_BUFFER_MAX_LENGTH]; + int buf_nbytes; + int buf_current_byte; +} console; +// Scan codes must be returned as 2-byte sequence: with '\0' 1st byte, scancode 2nd byte +#define setScancode(scancode) (console.buf_nbytes = 2, console.buf[0] = 0, console.buf[1] = scancode) -static int keyCount = 0; static WORD curr_attr; -static char x11mousebuf[] = "[M???"; /* Mouse report, after ESC */ -static int x11mousePos, x11mouseCount; -static int win_unget_pending = FALSE; static int win_unget_data; static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ @@ -2807,53 +2804,39 @@ public int win32_kbhit(void) char16_t utf16[UTF16_MAX_LENGTH]; int utf16_nwords; - if (keyCount > 0 || win_unget_pending) - return (TRUE); - - currentKey.ascii = 0; - currentKey.scan = 0; - - if (x11mouseCount > 0) - { - currentKey.ascii = x11mousebuf[x11mousePos++]; - --x11mouseCount; - keyCount = 1; - return (TRUE); - } - - // Read Console Input Loop + // Read Console Input Loop and fill console.buf with either mouse or key event info + console.buf_current_byte = 0; while(1) { PeekConsoleInputW(tty, &ip, 1, &read); if (read == 0) return (FALSE); ReadConsoleInputW(tty, &ip, 1, &read); /* generate an X11 mouse sequence from the mouse event */ - if (mousecap && ip.EventType == MOUSE_EVENT && - ip.Event.MouseEvent.dwEventFlags != MOUSE_MOVED) - { - x11mousebuf[3] = X11MOUSE_OFFSET + ip.Event.MouseEvent.dwMousePosition.X + 1; - x11mousebuf[4] = X11MOUSE_OFFSET + ip.Event.MouseEvent.dwMousePosition.Y + 1; + if (mousecap && ip.EventType == MOUSE_EVENT && ip.Event.MouseEvent.dwEventFlags != MOUSE_MOVED) { + console.buf_nbytes = KEY_BUFFER_MAX_LENGTH; + console.buf[0] = ESC; + console.buf[1] = '['; + console.buf[2] = 'M'; + console.buf[3] = '?'; + console.buf[4] = X11MOUSE_OFFSET + ip.Event.MouseEvent.dwMousePosition.X + 1; + console.buf[5] = X11MOUSE_OFFSET + ip.Event.MouseEvent.dwMousePosition.Y + 1; switch (ip.Event.MouseEvent.dwEventFlags) { case 0: /* press or release */ if (ip.Event.MouseEvent.dwButtonState == 0) - x11mousebuf[2] = X11MOUSE_OFFSET + X11MOUSE_BUTTON_REL; + console.buf[3] = X11MOUSE_OFFSET + X11MOUSE_BUTTON_REL; else if (ip.Event.MouseEvent.dwButtonState & (FROM_LEFT_3RD_BUTTON_PRESSED | FROM_LEFT_4TH_BUTTON_PRESSED)) - continue; + continue; // if we get another mouse event everything will be overwritten, should return TRUE instead? else - x11mousebuf[2] = X11MOUSE_OFFSET + X11MOUSE_BUTTON1 + ((int)ip.Event.MouseEvent.dwButtonState << 1); + console.buf[3] = X11MOUSE_OFFSET + X11MOUSE_BUTTON1 + ((int)ip.Event.MouseEvent.dwButtonState << 1); break; case MOUSE_WHEELED: - x11mousebuf[2] = X11MOUSE_OFFSET + (((int)ip.Event.MouseEvent.dwButtonState < 0) ? X11MOUSE_WHEEL_DOWN : X11MOUSE_WHEEL_UP); + console.buf[3] = X11MOUSE_OFFSET + (((int)ip.Event.MouseEvent.dwButtonState < 0) ? X11MOUSE_WHEEL_DOWN : X11MOUSE_WHEEL_UP); break; default: - continue; + continue; // if we get another mouse event everything will be overwritten, should return TRUE instead? } - x11mousePos = 0; - x11mouseCount = 5; - currentKey.ascii = ESC; - keyCount = 1; - return (TRUE); + return TRUE; } /* * Wait for a real key-down event, but ignore @@ -2871,136 +2854,91 @@ public int win32_kbhit(void) (ip.Event.KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_CTRL_PRESSED)) == (RIGHT_ALT_PRESSED|LEFT_CTRL_PRESSED) ) ) || + ip.Event.KeyEvent.wVirtualKeyCode == VK_NUMLOCK || + ip.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL || ip.Event.KeyEvent.wVirtualKeyCode == VK_KANJI || ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU ) continue; - // If we got here, we got a real charachter code - // If ASCII character, return it without coversion - if ( ip.Event.KeyEvent.uChar.UnicodeChar <= 0x7F ) { - currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; - // UTF-16 character - } else { - // If HS (high surrogate), save it to the first UTF-16 word and wait for LS - if (0xD800 <= ip.Event.KeyEvent.uChar.UnicodeChar && ip.Event.KeyEvent.uChar.UnicodeChar <= 0xDBFF) { - utf16[0] = ip.Event.KeyEvent.uChar.UnicodeChar; - continue; - // If LS (low surrogate), save it to the second UTF-16 word - } else if(0xDC00 <= ip.Event.KeyEvent.uChar.UnicodeChar && ip.Event.KeyEvent.uChar.UnicodeChar <= 0xDFFF) { - utf16[1] = ip.Event.KeyEvent.uChar.UnicodeChar; // found LS - utf16_nwords = 2; - // If BMP (Basic Multilingual Plane), save it to the first UTF-16 word - } else { - utf16[0] = ip.Event.KeyEvent.uChar.UnicodeChar; - utf16_nwords = 1; + if (ip.Event.KeyEvent.uChar.UnicodeChar) { // If character is available return it as UTF-8 multi-byte string + // If Alt-E is pressed return it's scancode + if (ip.Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) { + switch (ip.Event.KeyEvent.wVirtualScanCode) + { + case PCK_ALT_E: + setScancode(PCK_ALT_E); + return TRUE; + } } - // Convert UTF-16 to UTF-8 - currentKey.utf8_nbytes = WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_nwords, currentKey.utf8, UTF8_MAX_LENGTH, NULL, NULL); - if (currentKey.utf8_nbytes) { - currentKey.ascii = currentKey.utf8[0]; - currentKey.utf8_current_byte = 1; + if ( ip.Event.KeyEvent.uChar.UnicodeChar <= 0x7F ) { + // If ASCII character, save it to the 1st byte of UTF-8 string and return + console.buf[0] = ip.Event.KeyEvent.uChar.AsciiChar; + console.buf_nbytes = 1; } else { - currentKey.utf8_current_byte = 0; - return FALSE; - } - } - - // TODO: Document what the following block does - currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; - keyCount = ip.Event.KeyEvent.wRepeatCount; - - if (ip.Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) { - switch (currentKey.scan) - { - case PCK_ALT_E: /* letter 'E' */ - currentKey.ascii = 0; - break; - } - } else if (ip.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { - switch (currentKey.scan) - { - case PCK_RIGHT: /* right arrow */ - currentKey.scan = PCK_CTL_RIGHT; - break; - case PCK_LEFT: /* left arrow */ - currentKey.scan = PCK_CTL_LEFT; - break; - case PCK_DELETE: /* delete */ - currentKey.scan = PCK_CTL_DELETE; - break; - } - } else if (ip.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) - { - switch (currentKey.scan) - { - case PCK_SHIFT_TAB: /* tab */ - currentKey.ascii = 0; - break; + // If Unicode character, coververt to UTF-8 multi-byte string + if (0xD800 <= ip.Event.KeyEvent.uChar.UnicodeChar && ip.Event.KeyEvent.uChar.UnicodeChar <= 0xDBFF) { + // If HS (high surrogate), save it to the 1st word of UTF-16 buffer and wait for LS + utf16[0] = ip.Event.KeyEvent.uChar.UnicodeChar; + continue; + } else if(0xDC00 <= ip.Event.KeyEvent.uChar.UnicodeChar && ip.Event.KeyEvent.uChar.UnicodeChar <= 0xDFFF) { + // If LS (low surrogate), save it to the 2nd word of UTF-16 buffer + utf16[1] = ip.Event.KeyEvent.uChar.UnicodeChar; // found LS + utf16_nwords = 2; + } else { + // If BMP (Basic Multilingual Plane), save it to the 1st word of UTF-16 buffer + utf16[0] = ip.Event.KeyEvent.uChar.UnicodeChar; + utf16_nwords = 1; + } + // Convert UTF-16 to UTF-8 + console.buf_nbytes = WideCharToMultiByte(CP_UTF8, 0, utf16, utf16_nwords, console.buf, UTF8_MAX_LENGTH, NULL, NULL); + if (!console.buf_nbytes) + return FALSE; } + } else { // Otherwise return a key scancode + if (ip.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { + // If CTRL is pressed + switch (ip.Event.KeyEvent.wVirtualScanCode) + { + case PCK_RIGHT: /* right arrow */ + setScancode(PCK_CTL_RIGHT); + break; + case PCK_LEFT: /* left arrow */ + setScancode(PCK_CTL_LEFT); + break; + case PCK_DELETE: /* delete */ + setScancode(PCK_CTL_DELETE); + break; + } + } else + setScancode(ip.Event.KeyEvent.wVirtualScanCode & 0x00FF); } return TRUE; } } /* - * Read a character from the keyboard. + * Read a character from the console. * * Known issues: * - WIN32getch API should be int like libc (with unsigned char values or -1). - * - The unicode code below can return 0 - incorrectly indicating scan code. - * - UTF16-LE surrogate pairs don't work (and return 0). - * - If win32_kbhit returns true then WIN32getch should never block, but it - * will block till the next keypress if it's numlock/capslock scan code. */ public char WIN32getch(void) { - static int pending_scancode = 0; - - if (win_unget_pending) - { - win_unget_pending = FALSE; - return (char) win_unget_data; - } + // Return the rest of the buffer + if (console.buf_current_byte < console.buf_nbytes) + return console.buf[console.buf_current_byte++]; - // If extended key, return its scan code - if (pending_scancode) { - pending_scancode = 0; - return ((char)(currentKey.scan & 0x00FF)); + // Wait for the mouse or keyboard event + while (!win32_kbhit()) { + Sleep(20); + if (ABORT_SIGS()) + return ('\003'); } - // If UTF-8 byte string, return it one byte at a time - if (currentKey.utf8_current_byte < currentKey.utf8_nbytes) - return currentKey.utf8[currentKey.utf8_current_byte++]; - - do { - while (!win32_kbhit()) - { - Sleep(20); - if (ABORT_SIGS()) - return ('\003'); - } - keyCount--; - /* - * On PC's, the extended keys return a 2 byte sequence beginning - * with '00', so if the ascii code is 00, the next byte will be - * the lsb of the scan code. - */ - pending_scancode = (currentKey.ascii == 0x00); - } while (pending_scancode && - (currentKey.scan == PCK_CAPS_LOCK || currentKey.scan == PCK_NUM_LOCK)); - - return currentKey.ascii; -} - -/* - * Make the next call to WIN32getch return ch without changing the queue state. - */ -public void WIN32ungetch(int ch) -{ - win_unget_pending = TRUE; - win_unget_data = ch; + // Return first byte of the buffer + return console.buf[console.buf_current_byte++]; } #endif @@ -3038,4 +2976,3 @@ public void WIN32textout(char *text, int len) #endif } #endif -