From 991511fbda9a7bb3b50c96e31741ad9f180753c0 Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Sat, 14 Sep 2019 04:34:39 +0900 Subject: [PATCH] Cygwin: console: Fix read() in non-canonical mode. - In non-canonical mode, cygwin console returned only one character even if several keys are typed before read() called. This patch fixes this behaviour. --- winsup/cygwin/fhandler_console.cc | 664 ++++++++++++++++-------------- 1 file changed, 344 insertions(+), 320 deletions(-) diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc index 778279f99..709b8255d 100644 --- a/winsup/cygwin/fhandler_console.cc +++ b/winsup/cygwin/fhandler_console.cc @@ -499,354 +499,378 @@ fhandler_console::process_input_message (void) termios *ti = &(get_ttyp ()->ti); - DWORD nread; - INPUT_RECORD input_rec; - const char *toadd = NULL; + /* Per MSDN, max size of buffer required is below 64K. */ +#define INREC_SIZE (65536 / sizeof (INPUT_RECORD)) - if (!ReadConsoleInputW (get_handle (), &input_rec, 1, &nread)) + fhandler_console::input_states stat = input_processing; + DWORD total_read, i; + INPUT_RECORD input_rec[INREC_SIZE]; + + if (!PeekConsoleInputW (get_handle (), input_rec, INREC_SIZE, &total_read)) { - termios_printf ("ReadConsoleInput failed, %E"); + termios_printf ("PeekConsoleInput failed, %E"); return input_error; } - const WCHAR &unicode_char = input_rec.Event.KeyEvent.uChar.UnicodeChar; - const DWORD &ctrl_key_state = input_rec.Event.KeyEvent.dwControlKeyState; - - /* check the event that occurred */ - switch (input_rec.EventType) + for (i = 0; i < total_read; i ++) { - case KEY_EVENT: + DWORD nread = 1; + const char *toadd = NULL; - con.nModifiers = 0; + const WCHAR &unicode_char = + input_rec[i].Event.KeyEvent.uChar.UnicodeChar; + const DWORD &ctrl_key_state = + input_rec[i].Event.KeyEvent.dwControlKeyState; + + /* check the event that occurred */ + switch (input_rec[i].EventType) + { + case KEY_EVENT: + + con.nModifiers = 0; #ifdef DEBUGGING - /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */ - if (input_rec.Event.KeyEvent.bKeyDown - && input_rec.Event.KeyEvent.wVirtualKeyCode == VK_SCROLL - && (ctrl_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) - == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) - { - set_raw_win32_keyboard_mode (!con.raw_win32_keyboard_mode); - return input_processing; - } + /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */ + if (input_rec[i].Event.KeyEvent.bKeyDown + && input_rec[i].Event.KeyEvent.wVirtualKeyCode == VK_SCROLL + && (ctrl_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) + == (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) + { + set_raw_win32_keyboard_mode (!con.raw_win32_keyboard_mode); + continue; + } #endif - if (con.raw_win32_keyboard_mode) - { - __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK", - input_rec.Event.KeyEvent.bKeyDown, - input_rec.Event.KeyEvent.wRepeatCount, - input_rec.Event.KeyEvent.wVirtualKeyCode, - input_rec.Event.KeyEvent.wVirtualScanCode, - input_rec.Event.KeyEvent.uChar.UnicodeChar, - input_rec.Event.KeyEvent.dwControlKeyState); - toadd = tmp; - nread = strlen (toadd); + if (con.raw_win32_keyboard_mode) + { + __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK", + input_rec[i].Event.KeyEvent.bKeyDown, + input_rec[i].Event.KeyEvent.wRepeatCount, + input_rec[i].Event.KeyEvent.wVirtualKeyCode, + input_rec[i].Event.KeyEvent.wVirtualScanCode, + input_rec[i].Event.KeyEvent.uChar.UnicodeChar, + input_rec[i].Event.KeyEvent.dwControlKeyState); + toadd = tmp; + nread = strlen (toadd); + break; + } + + /* Ignore key up events, except for Alt+Numpad events. */ + if (!input_rec[i].Event.KeyEvent.bKeyDown && + !is_alt_numpad_event (&input_rec[i])) + continue; + /* Ignore Alt+Numpad keys. They are eventually handled below after + releasing the Alt key. */ + if (input_rec[i].Event.KeyEvent.bKeyDown + && is_alt_numpad_key (&input_rec[i])) + continue; + + if (ctrl_key_state & SHIFT_PRESSED) + con.nModifiers |= 1; + if (ctrl_key_state & RIGHT_ALT_PRESSED) + con.nModifiers |= 2; + if (ctrl_key_state & CTRL_PRESSED) + con.nModifiers |= 4; + if (ctrl_key_state & LEFT_ALT_PRESSED) + con.nModifiers |= 8; + + /* Allow Backspace to emit ^? and escape sequences. */ + if (input_rec[i].Event.KeyEvent.wVirtualKeyCode == VK_BACK) + { + char c = con.backspace_keycode; + nread = 0; + if (ctrl_key_state & ALT_PRESSED) + { + if (con.metabit) + c |= 0x80; + else + tmp[nread++] = '\e'; + } + tmp[nread++] = c; + tmp[nread] = 0; + toadd = tmp; + } + /* Allow Ctrl-Space to emit ^@ */ + else if (input_rec[i].Event.KeyEvent.wVirtualKeyCode + == (wincap.has_con_24bit_colors () ? '2' : VK_SPACE) + && (ctrl_key_state & CTRL_PRESSED) + && !(ctrl_key_state & ALT_PRESSED)) + toadd = ""; + else if (unicode_char == 0 + /* arrow/function keys */ + || (input_rec[i].Event.KeyEvent.dwControlKeyState + & ENHANCED_KEY)) + { + toadd = get_nonascii_key (input_rec[i], tmp); + if (!toadd) + { + con.nModifiers = 0; + continue; + } + nread = strlen (toadd); + } + else + { + nread = con.con_to_str (tmp + 1, 59, unicode_char); + /* Determine if the keystroke is modified by META. The tricky + part is to distinguish whether the right Alt key should be + recognized as Alt, or as AltGr. */ + bool meta = + /* Alt but not AltGr (= left ctrl + right alt)? */ + (ctrl_key_state & ALT_PRESSED) != 0 + && ((ctrl_key_state & CTRL_PRESSED) == 0 + /* but also allow Alt-AltGr: */ + || (ctrl_key_state & ALT_PRESSED) == ALT_PRESSED + || (unicode_char <= 0x1f || unicode_char == 0x7f)); + if (!meta) + { + /* Determine if the character is in the current multibyte + charset. The test is easy. If the multibyte sequence + is > 1 and the first byte is ASCII CAN, the character + has been translated into the ASCII CAN + UTF-8 replacement + sequence. If so, just ignore the keypress. + FIXME: Is there a better solution? */ + if (nread > 1 && tmp[1] == 0x18) + beep (); + else + toadd = tmp + 1; + } + else if (con.metabit) + { + tmp[1] |= 0x80; + toadd = tmp + 1; + } + else + { + tmp[0] = '\033'; + tmp[1] = cyg_tolower (tmp[1]); + toadd = tmp; + nread++; + con.nModifiers &= ~4; + } + } break; - } - /* Ignore key up events, except for Alt+Numpad events. */ - if (!input_rec.Event.KeyEvent.bKeyDown && - !is_alt_numpad_event (&input_rec)) - return input_processing; - /* Ignore Alt+Numpad keys. They are eventually handled below after - releasing the Alt key. */ - if (input_rec.Event.KeyEvent.bKeyDown - && is_alt_numpad_key (&input_rec)) - return input_processing; - - if (ctrl_key_state & SHIFT_PRESSED) - con.nModifiers |= 1; - if (ctrl_key_state & RIGHT_ALT_PRESSED) - con.nModifiers |= 2; - if (ctrl_key_state & CTRL_PRESSED) - con.nModifiers |= 4; - if (ctrl_key_state & LEFT_ALT_PRESSED) - con.nModifiers |= 8; - - /* Allow Backspace to emit ^? and escape sequences. */ - if (input_rec.Event.KeyEvent.wVirtualKeyCode == VK_BACK) - { - char c = con.backspace_keycode; - nread = 0; - if (ctrl_key_state & ALT_PRESSED) + case MOUSE_EVENT: + send_winch_maybe (); { - if (con.metabit) - c |= 0x80; - else - tmp[nread++] = '\e'; - } - tmp[nread++] = c; - tmp[nread] = 0; - toadd = tmp; - } - /* Allow Ctrl-Space to emit ^@ */ - else if (input_rec.Event.KeyEvent.wVirtualKeyCode - == (wincap.has_con_24bit_colors () ? '2' : VK_SPACE) - && (ctrl_key_state & CTRL_PRESSED) - && !(ctrl_key_state & ALT_PRESSED)) - toadd = ""; - else if (unicode_char == 0 - /* arrow/function keys */ - || (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY)) - { - toadd = get_nonascii_key (input_rec, tmp); - if (!toadd) - { - con.nModifiers = 0; - return input_processing; - } - nread = strlen (toadd); - } - else - { - nread = con.con_to_str (tmp + 1, 59, unicode_char); - /* Determine if the keystroke is modified by META. The tricky - part is to distinguish whether the right Alt key should be - recognized as Alt, or as AltGr. */ - bool meta = - /* Alt but not AltGr (= left ctrl + right alt)? */ - (ctrl_key_state & ALT_PRESSED) != 0 - && ((ctrl_key_state & CTRL_PRESSED) == 0 - /* but also allow Alt-AltGr: */ - || (ctrl_key_state & ALT_PRESSED) == ALT_PRESSED - || (unicode_char <= 0x1f || unicode_char == 0x7f)); - if (!meta) - { - /* Determine if the character is in the current multibyte - charset. The test is easy. If the multibyte sequence - is > 1 and the first byte is ASCII CAN, the character - has been translated into the ASCII CAN + UTF-8 replacement - sequence. If so, just ignore the keypress. - FIXME: Is there a better solution? */ - if (nread > 1 && tmp[1] == 0x18) - beep (); - else - toadd = tmp + 1; - } - else if (con.metabit) - { - tmp[1] |= 0x80; - toadd = tmp + 1; - } - else - { - tmp[0] = '\033'; - tmp[1] = cyg_tolower (tmp[1]); - toadd = tmp; - nread++; - con.nModifiers &= ~4; - } - } - break; - - case MOUSE_EVENT: - send_winch_maybe (); - { - MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent; - /* As a unique guard for mouse report generation, - call mouse_aware() which is common with select(), so the result - of select() and the actual read() will be consistent on the - issue of whether input (i.e. a mouse escape sequence) will - be available or not */ - if (mouse_aware (mouse_event)) - { - /* Note: Reported mouse position was already retrieved by - mouse_aware() and adjusted by window scroll buffer offset */ - - /* Treat the double-click event like a regular button press */ - if (mouse_event.dwEventFlags == DOUBLE_CLICK) + MOUSE_EVENT_RECORD& mouse_event = input_rec[i].Event.MouseEvent; + /* As a unique guard for mouse report generation, + call mouse_aware() which is common with select(), so the result + of select() and the actual read() will be consistent on the + issue of whether input (i.e. a mouse escape sequence) will + be available or not */ + if (mouse_aware (mouse_event)) { - syscall_printf ("mouse: double-click -> click"); - mouse_event.dwEventFlags = 0; - } + /* Note: Reported mouse position was already retrieved by + mouse_aware() and adjusted by window scroll buffer offset */ - /* This code assumes Windows never reports multiple button - events at the same time. */ - int b = 0; - char sz[32]; - char mode6_term = 'M'; - - if (mouse_event.dwEventFlags == MOUSE_WHEELED) - { - if (mouse_event.dwButtonState & 0xFF800000) + /* Treat the double-click event like a regular button press */ + if (mouse_event.dwEventFlags == DOUBLE_CLICK) { - b = 0x41; - strcpy (sz, "wheel down"); + syscall_printf ("mouse: double-click -> click"); + mouse_event.dwEventFlags = 0; + } + + /* This code assumes Windows never reports multiple button + events at the same time. */ + int b = 0; + char sz[32]; + char mode6_term = 'M'; + + if (mouse_event.dwEventFlags == MOUSE_WHEELED) + { + if (mouse_event.dwButtonState & 0xFF800000) + { + b = 0x41; + strcpy (sz, "wheel down"); + } + else + { + b = 0x40; + strcpy (sz, "wheel up"); + } } else { - b = 0x40; - strcpy (sz, "wheel up"); + /* Ignore unimportant mouse buttons */ + mouse_event.dwButtonState &= 0x7; + + if (mouse_event.dwEventFlags == MOUSE_MOVED) + { + b = con.last_button_code; + } + else if (mouse_event.dwButtonState < con.dwLastButtonState + && !con.ext_mouse_mode6) + { + b = 3; + strcpy (sz, "btn up"); + } + else if ((mouse_event.dwButtonState & 1) + != (con.dwLastButtonState & 1)) + { + b = 0; + strcpy (sz, "btn1 down"); + } + else if ((mouse_event.dwButtonState & 2) + != (con.dwLastButtonState & 2)) + { + b = 2; + strcpy (sz, "btn2 down"); + } + else if ((mouse_event.dwButtonState & 4) + != (con.dwLastButtonState & 4)) + { + b = 1; + strcpy (sz, "btn3 down"); + } + + if (con.ext_mouse_mode6 /* distinguish release */ + && mouse_event.dwButtonState < con.dwLastButtonState) + mode6_term = 'm'; + + con.last_button_code = b; + + if (mouse_event.dwEventFlags == MOUSE_MOVED) + { + b += 32; + strcpy (sz, "move"); + } + else + { + /* Remember the modified button state */ + con.dwLastButtonState = mouse_event.dwButtonState; + } } + + /* Remember mouse position */ + con.dwLastMousePosition.X = con.dwMousePosition.X; + con.dwLastMousePosition.Y = con.dwMousePosition.Y; + + /* Remember the modifiers */ + con.nModifiers = 0; + if (mouse_event.dwControlKeyState & SHIFT_PRESSED) + con.nModifiers |= 0x4; + if (mouse_event.dwControlKeyState & ALT_PRESSED) + con.nModifiers |= 0x8; + if (mouse_event.dwControlKeyState & CTRL_PRESSED) + con.nModifiers |= 0x10; + + /* Indicate the modifiers */ + b |= con.nModifiers; + + /* We can now create the code. */ + if (con.ext_mouse_mode6) + { + __small_sprintf (tmp, "\033[<%d;%d;%d%c", b, + con.dwMousePosition.X + 1, + con.dwMousePosition.Y + 1, + mode6_term); + nread = strlen (tmp); + } + else if (con.ext_mouse_mode15) + { + __small_sprintf (tmp, "\033[%d;%d;%dM", b + 32, + con.dwMousePosition.X + 1, + con.dwMousePosition.Y + 1); + nread = strlen (tmp); + } + else if (con.ext_mouse_mode5) + { + unsigned int xcode = con.dwMousePosition.X + ' ' + 1; + unsigned int ycode = con.dwMousePosition.Y + ' ' + 1; + + __small_sprintf (tmp, "\033[M%c", b + ' '); + nread = 4; + /* the neat nested encoding function of mintty + does not compile in g++, so let's unfold it: */ + if (xcode < 0x80) + tmp [nread++] = xcode; + else if (xcode < 0x800) + { + tmp [nread++] = 0xC0 + (xcode >> 6); + tmp [nread++] = 0x80 + (xcode & 0x3F); + } + else + tmp [nread++] = 0; + if (ycode < 0x80) + tmp [nread++] = ycode; + else if (ycode < 0x800) + { + tmp [nread++] = 0xC0 + (ycode >> 6); + tmp [nread++] = 0x80 + (ycode & 0x3F); + } + else + tmp [nread++] = 0; + } + else + { + unsigned int xcode = con.dwMousePosition.X + ' ' + 1; + unsigned int ycode = con.dwMousePosition.Y + ' ' + 1; + if (xcode >= 256) + xcode = 0; + if (ycode >= 256) + ycode = 0; + __small_sprintf (tmp, "\033[M%c%c%c", b + ' ', + xcode, ycode); + nread = 6; /* tmp may contain NUL bytes */ + } + syscall_printf ("mouse: %s at (%d,%d)", sz, + con.dwMousePosition.X, + con.dwMousePosition.Y); + + toadd = tmp; } + } + break; + + case FOCUS_EVENT: + if (con.use_focus) + { + if (input_rec[i].Event.FocusEvent.bSetFocus) + __small_sprintf (tmp, "\033[I"); else - { - /* Ignore unimportant mouse buttons */ - mouse_event.dwButtonState &= 0x7; - - if (mouse_event.dwEventFlags == MOUSE_MOVED) - { - b = con.last_button_code; - } - else if (mouse_event.dwButtonState < con.dwLastButtonState - && !con.ext_mouse_mode6) - { - b = 3; - strcpy (sz, "btn up"); - } - else if ((mouse_event.dwButtonState & 1) - != (con.dwLastButtonState & 1)) - { - b = 0; - strcpy (sz, "btn1 down"); - } - else if ((mouse_event.dwButtonState & 2) - != (con.dwLastButtonState & 2)) - { - b = 2; - strcpy (sz, "btn2 down"); - } - else if ((mouse_event.dwButtonState & 4) - != (con.dwLastButtonState & 4)) - { - b = 1; - strcpy (sz, "btn3 down"); - } - - if (con.ext_mouse_mode6 /* distinguish release */ - && mouse_event.dwButtonState < con.dwLastButtonState) - mode6_term = 'm'; - - con.last_button_code = b; - - if (mouse_event.dwEventFlags == MOUSE_MOVED) - { - b += 32; - strcpy (sz, "move"); - } - else - { - /* Remember the modified button state */ - con.dwLastButtonState = mouse_event.dwButtonState; - } - } - - /* Remember mouse position */ - con.dwLastMousePosition.X = con.dwMousePosition.X; - con.dwLastMousePosition.Y = con.dwMousePosition.Y; - - /* Remember the modifiers */ - con.nModifiers = 0; - if (mouse_event.dwControlKeyState & SHIFT_PRESSED) - con.nModifiers |= 0x4; - if (mouse_event.dwControlKeyState & ALT_PRESSED) - con.nModifiers |= 0x8; - if (mouse_event.dwControlKeyState & CTRL_PRESSED) - con.nModifiers |= 0x10; - - /* Indicate the modifiers */ - b |= con.nModifiers; - - /* We can now create the code. */ - if (con.ext_mouse_mode6) - { - __small_sprintf (tmp, "\033[<%d;%d;%d%c", b, - con.dwMousePosition.X + 1, - con.dwMousePosition.Y + 1, - mode6_term); - nread = strlen (tmp); - } - else if (con.ext_mouse_mode15) - { - __small_sprintf (tmp, "\033[%d;%d;%dM", b + 32, - con.dwMousePosition.X + 1, - con.dwMousePosition.Y + 1); - nread = strlen (tmp); - } - else if (con.ext_mouse_mode5) - { - unsigned int xcode = con.dwMousePosition.X + ' ' + 1; - unsigned int ycode = con.dwMousePosition.Y + ' ' + 1; - - __small_sprintf (tmp, "\033[M%c", b + ' '); - nread = 4; - /* the neat nested encoding function of mintty - does not compile in g++, so let's unfold it: */ - if (xcode < 0x80) - tmp [nread++] = xcode; - else if (xcode < 0x800) - { - tmp [nread++] = 0xC0 + (xcode >> 6); - tmp [nread++] = 0x80 + (xcode & 0x3F); - } - else - tmp [nread++] = 0; - if (ycode < 0x80) - tmp [nread++] = ycode; - else if (ycode < 0x800) - { - tmp [nread++] = 0xC0 + (ycode >> 6); - tmp [nread++] = 0x80 + (ycode & 0x3F); - } - else - tmp [nread++] = 0; - } - else - { - unsigned int xcode = con.dwMousePosition.X + ' ' + 1; - unsigned int ycode = con.dwMousePosition.Y + ' ' + 1; - if (xcode >= 256) - xcode = 0; - if (ycode >= 256) - ycode = 0; - __small_sprintf (tmp, "\033[M%c%c%c", b + ' ', - xcode, ycode); - nread = 6; /* tmp may contain NUL bytes */ - } - syscall_printf ("mouse: %s at (%d,%d)", sz, - con.dwMousePosition.X, - con.dwMousePosition.Y); + __small_sprintf (tmp, "\033[O"); toadd = tmp; + nread = 3; + } + break; + + case WINDOW_BUFFER_SIZE_EVENT: + if (send_winch_maybe ()) + { + stat = input_winch; + goto out; + } + /* fall through */ + default: + continue; + } + + if (toadd) + { + ssize_t ret; + line_edit_status res = line_edit (toadd, nread, *ti, &ret); + if (res == line_edit_signalled) + { + stat = input_signalled; + goto out; + } + else if (res == line_edit_input_done) + { + input_ready = true; + stat = input_ok; + if (ti->c_lflag & ICANON) + goto out; } } - break; - - case FOCUS_EVENT: - if (con.use_focus) - { - if (input_rec.Event.FocusEvent.bSetFocus) - __small_sprintf (tmp, "\033[I"); - else - __small_sprintf (tmp, "\033[O"); - - toadd = tmp; - nread = 3; - } - break; - - case WINDOW_BUFFER_SIZE_EVENT: - if (send_winch_maybe ()) - return input_winch; - /* fall through */ - default: - return input_processing; } - - if (toadd) - { - ssize_t ret; - line_edit_status res = line_edit (toadd, nread, *ti, &ret); - if (res == line_edit_signalled) - return input_signalled; - else if (res == line_edit_input_done) - { - input_ready = true; - return input_ok; - } - } - return input_processing; +out: + /* Discard processed recored. */ + DWORD dummy; + ReadConsoleInputW (get_handle (), input_rec, min (total_read, i+1), &dummy); + return stat; } void