From 88cd4bbca1750e9a33545e52c04be624376b838d Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Sat, 30 Mar 2019 16:17:20 +0900 Subject: [PATCH 2/3] Cygwin: console: rework implementation. - Revise console code to utilize xterm emulation of Windows 10. This enables 24 bit color support. - Add fake 256 color support for legacy console. Now, the nearest color from 16 system colors is used. - Fix behaviour of select() of console in canonical mode. Previously, select() returned by only one key even in canonical mode. - Make the console I/O functions thread-safe. --- winsup/cygwin/fhandler.h | 36 +- winsup/cygwin/fhandler_console.cc | 1087 +++++++++++++++++++---------- winsup/cygwin/select.cc | 90 +-- 3 files changed, 779 insertions(+), 434 deletions(-) diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index b336eb63a..0477888d7 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1698,6 +1698,12 @@ class fhandler_serial: public fhandler_base } }; +#define acquire_input_mutex(ms) \ + __acquire_input_mutex (__PRETTY_FUNCTION__, __LINE__, ms) + +#define release_input_mutex() \ + __release_input_mutex (__PRETTY_FUNCTION__, __LINE__) + #define acquire_output_mutex(ms) \ __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms) @@ -1778,6 +1784,8 @@ enum ansi_intensity #define eattitle 7 #define gotparen 8 #define gotrparen 9 +#define eatpalette 10 +#define endpalette 11 #define MAXARGS 10 enum cltype @@ -1791,6 +1799,9 @@ enum cltype class dev_console { + bool cap24bit_color; /* 24bit-color capability */ + pid_t owner; + WORD default_color, underline_color, dim_color; /* Used to determine if an input keystroke should be modified with META. */ @@ -1880,10 +1891,20 @@ public: tty_min tty_min_state; dev_console con; }; + bool input_ready; + enum input_states + { + input_error = -1, + input_processing = 0, + input_ok = 1, + input_signalled = 2, + input_winch = 3 + }; private: static const unsigned MAX_WRITE_CHARS; static console_state *shared_console_info; static bool invisible_console; + HANDLE input_mutex, output_mutex; /* Used when we encounter a truncated multi-byte sequence. The lead bytes are stored here and revisited in the next write call. */ @@ -1953,8 +1974,11 @@ private: bool focus_aware () {return shared_console_info->con.use_focus;} bool get_cons_readahead_valid () { - return shared_console_info->con.cons_rapoi != NULL && + acquire_input_mutex (INFINITE); + bool ret = shared_console_info->con.cons_rapoi != NULL && *shared_console_info->con.cons_rapoi; + release_input_mutex (); + return ret; } select_record *select_read (select_stuff *); @@ -1965,7 +1989,7 @@ private: void fixup_after_fork (HANDLE) {fixup_after_fork_exec (false);} void set_close_on_exec (bool val); void set_input_state (); - void send_winch_maybe (); + bool send_winch_maybe (); void setup (); bool set_unit (); static bool need_invisible (); @@ -1988,6 +2012,14 @@ private: copyto (fh); return fh; } + + input_states process_input_message (); + void setup_io_mutex (void); + DWORD __acquire_input_mutex (const char *fn, int ln, DWORD ms); + void __release_input_mutex (const char *fn, int ln); + DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms); + void __release_output_mutex (const char *fn, int ln); + friend tty_min * tty_list::get_cttyp (); }; diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc index 281c2005c..f7b50d0a8 100644 --- a/winsup/cygwin/fhandler_console.cc +++ b/winsup/cygwin/fhandler_console.cc @@ -15,6 +15,7 @@ details. */ #include #include #include +#include #include "cygerrno.h" #include "security.h" #include "path.h" @@ -32,6 +33,16 @@ details. */ #include "child_info.h" #include "cygwait.h" +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */ +#ifndef DISABLE_NEWLINE_AUTO_RETURN +#define DISABLE_NEWLINE_AUTO_RETURN 0x0008 +#endif /* DISABLE_NEWLINE_AUTO_RETURN */ +#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT +#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 +#endif /* ENABLE_VIRTUAL_TERMINAL_INPUT */ + /* Don't make this bigger than NT_MAX_PATH as long as the temporary buffer is allocated using tmp_pathbuf!!! */ #define CONVERT_LIMIT NT_MAX_PATH @@ -148,7 +159,11 @@ fhandler_console::set_unit () if (created) shared_console_info->tty_min_state.setntty (DEV_CONS_MAJOR, console_unit (me)); devset = (fh_devices) shared_console_info->tty_min_state.getntty (); + if (created) + con.owner = getpid (); } + if (!created && shared_console_info && kill (con.owner, 0) == -1) + con.owner = getpid (); dev ().parse (devset); if (devset != FH_ERROR) @@ -167,33 +182,44 @@ void fhandler_console::setup () { if (set_unit ()) - { - con.scroll_region.Bottom = -1; - con.dwLastCursorPosition.X = -1; - con.dwLastCursorPosition.Y = -1; - con.dwLastMousePosition.X = -1; - con.dwLastMousePosition.Y = -1; - con.dwLastButtonState = 0; /* none pressed */ - con.last_button_code = 3; /* released */ - con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE; - con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; - con.meta_mask = LEFT_ALT_PRESSED; - /* Set the mask that determines if an input keystroke is modified by - META. We set this based on the keyboard layout language loaded - for the current thread. The left key always generates - META, but the right key only generates META if we are using - an English keyboard because many "international" keyboards - replace common shell symbols ('[', '{', etc.) with accented - language-specific characters (umlaut, accent grave, etc.). On - these keyboards right (called AltGr) is used to produce the - shell symbols and should not be interpreted as META. */ - if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH) - con.meta_mask |= RIGHT_ALT_PRESSED; - con.set_default_attr (); - con.backspace_keycode = CERASE; - con.cons_rapoi = NULL; - shared_console_info->tty_min_state.is_console = true; - } + { + con.scroll_region.Bottom = -1; + con.dwLastCursorPosition.X = -1; + con.dwLastCursorPosition.Y = -1; + con.dwLastMousePosition.X = -1; + con.dwLastMousePosition.Y = -1; + con.dwLastButtonState = 0; /* none pressed */ + con.last_button_code = 3; /* released */ + con.underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE; + con.dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + con.meta_mask = LEFT_ALT_PRESSED; + /* Set the mask that determines if an input keystroke is modified by + META. We set this based on the keyboard layout language loaded + for the current thread. The left key always generates + META, but the right key only generates META if we are using + an English keyboard because many "international" keyboards + replace common shell symbols ('[', '{', etc.) with accented + language-specific characters (umlaut, accent grave, etc.). On + these keyboards right (called AltGr) is used to produce the + shell symbols and should not be interpreted as META. */ + if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH) + con.meta_mask |= RIGHT_ALT_PRESSED; + con.set_default_attr (); + con.backspace_keycode = CERASE; + con.cons_rapoi = NULL; + shared_console_info->tty_min_state.is_console = true; + /* Check if 24bit color is available */ + DWORD dwVersion = GetVersion (); + dwVersion = (LOBYTE (LOWORD (dwVersion)) << 24) + | (HIBYTE (LOWORD (dwVersion)) << 16) | HIWORD (dwVersion); + if (dwVersion >= ((10 << 24) | (0 << 16) | 14931)) + { + con.cap24bit_color = true; + /* If system has 24bit color capability, + use xterm compatible mode. */ + setenv ("TERM", "xterm-256color", 1); + } + } } /* Return the tty structure associated with a given tty number. If the @@ -211,6 +237,45 @@ tty_list::get_cttyp () return NULL; } +void +fhandler_console::setup_io_mutex (void) +{ + char buf[MAX_PATH]; + DWORD res; + + res = WAIT_FAILED; + if (!input_mutex || WAIT_FAILED == (res = acquire_input_mutex (0))) + { + shared_name (buf, "cygcons.input.mutex", get_minor ()); + input_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf); + if (!input_mutex) + input_mutex = CreateMutex (&sec_none, FALSE, buf); + if (!input_mutex) + { + __seterrno (); + return; + } + } + if (res == WAIT_OBJECT_0) + release_input_mutex (); + + res = WAIT_FAILED; + if (!output_mutex || WAIT_FAILED == (res = acquire_output_mutex (0))) + { + shared_name (buf, "cygcons.output.mutex", get_minor ()); + output_mutex = OpenMutex (MAXIMUM_ALLOWED, TRUE, buf); + if (!output_mutex) + output_mutex = CreateMutex (&sec_none, FALSE, buf); + if (!output_mutex) + { + __seterrno (); + return; + } + } + if (res == WAIT_OBJECT_0) + release_output_mutex (); +} + inline DWORD dev_console::con_to_str (char *d, int dlen, WCHAR w) { @@ -251,7 +316,7 @@ fhandler_console::set_cursor_maybe () } } -void +bool fhandler_console::send_winch_maybe () { SHORT y = con.dwWinSize.Y; @@ -263,7 +328,9 @@ fhandler_console::send_winch_maybe () con.scroll_region.Top = 0; con.scroll_region.Bottom = -1; get_ttyp ()->kill_pgrp (SIGWINCH); + return true; } + return false; } /* Check whether a mouse event is to be reported as an escape sequence */ @@ -296,36 +363,17 @@ fhandler_console::mouse_aware (MOUSE_EVENT_RECORD& mouse_event) void __reg3 fhandler_console::read (void *pv, size_t& buflen) { + termios_printf ("read(%p,%d)", pv, buflen); + push_process_state process_state (PID_TTYIN); - HANDLE h = get_handle (); + int copied_chars = 0; -#define buf ((char *) pv) + DWORD timeout = is_nonblocking () ? 0 : INFINITE; - int ch; set_input_state (); - /* Check console read-ahead buffer filled from terminal requests */ - if (con.cons_rapoi && *con.cons_rapoi) - { - *buf = *con.cons_rapoi++; - buflen = 1; - return; - } - - int copied_chars = get_readahead_into_buffer (buf, buflen); - - if (copied_chars) - { - buflen = copied_chars; - return; - } - - DWORD timeout = is_nonblocking () ? 0 : INFINITE; - char tmp[60]; - - termios ti = get_ttyp ()->ti; - for (;;) + while (!input_ready && !get_cons_readahead_valid ()) { int bgres; if ((bgres = bg_check (SIGTTIN)) <= bg_eof) @@ -335,7 +383,7 @@ fhandler_console::read (void *pv, size_t& buflen) } set_cursor_maybe (); /* to make cursor appear on the screen immediately */ - switch (cygwait (h, timeout)) + switch (cygwait (get_handle (), timeout)) { case WAIT_OBJECT_0: break; @@ -353,366 +401,414 @@ fhandler_console::read (void *pv, size_t& buflen) goto err; } - DWORD nread; - INPUT_RECORD input_rec; - const char *toadd = NULL; +#define buf ((char *) pv) - if (!ReadConsoleInputW (h, &input_rec, 1, &nread)) + int ret; + acquire_input_mutex (INFINITE); + ret = process_input_message (); + release_input_mutex (); + switch (ret) { - syscall_printf ("ReadConsoleInput failed, %E"); - goto err; /* seems to be failure */ + case input_error: + goto err; + case input_processing: + continue; + case input_ok: /* input ready */ + break; + case input_signalled: /* signalled */ + goto sig_exit; + case input_winch: + continue; + default: + /* Should not come here */ + goto err; } + } - const WCHAR &unicode_char = input_rec.Event.KeyEvent.uChar.UnicodeChar; - const DWORD &ctrl_key_state = input_rec.Event.KeyEvent.dwControlKeyState; + /* Check console read-ahead buffer filled from terminal requests */ + acquire_input_mutex (INFINITE); + while (con.cons_rapoi && *con.cons_rapoi && buflen) + { + buf[copied_chars++] = *con.cons_rapoi++; + buflen --; + } - /* check the event that occurred */ - switch (input_rec.EventType) - { - case KEY_EVENT: + copied_chars += + get_readahead_into_buffer (buf + copied_chars, buflen); - con.nModifiers = 0; + if (!ralen) + input_ready = false; + release_input_mutex (); + +#undef buf + + buflen = copied_chars; + return; + +err: + __seterrno (); + buflen = (size_t) -1; + return; + +sig_exit: + set_sig_errno (EINTR); + buflen = (size_t) -1; +} + +fhandler_console::input_states +fhandler_console::process_input_message (void) +{ + char tmp[60]; + + if (!shared_console_info) + return input_error; + + termios *ti = &(get_ttyp ()->ti); + + DWORD nread; + INPUT_RECORD input_rec; + const char *toadd = NULL; + + if (!ReadConsoleInputW (get_handle (), &input_rec, 1, &nread)) + { + termios_printf ("ReadConsoleInput 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) + { + 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); - continue; - } + /* 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; + } #endif - if (con.raw_win32_keyboard_mode) + 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); + 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) { - __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); - break; + if (con.metabit) + c |= 0x80; + else + tmp[nread++] = '\e'; } - - /* Ignore key up events, except for Alt+Numpad events. */ - if (!input_rec.Event.KeyEvent.bKeyDown && - !is_alt_numpad_event (&input_rec)) - continue; - /* 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)) - 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.Event.KeyEvent.wVirtualKeyCode == VK_BACK) + tmp[nread++] = c; + tmp[nread] = 0; + toadd = tmp; + } + /* Allow Ctrl-Space to emit ^@ */ + else if (input_rec.Event.KeyEvent.wVirtualKeyCode + == (con.cap24bit_color ? '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) { - 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; + con.nModifiers = 0; + return input_processing; } - /* Allow Ctrl-Space to emit ^@ */ - else if (input_rec.Event.KeyEvent.wVirtualKeyCode == 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)) + 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) { - toadd = get_nonascii_key (input_rec, tmp); - if (!toadd) - { - con.nModifiers = 0; - continue; - } - nread = strlen (toadd); + /* 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 { - 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) + 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) { - /* 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; + syscall_printf ("mouse: double-click -> click"); + mouse_event.dwEventFlags = 0; } - else if (con.metabit) + + /* 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) { - tmp[1] |= 0x80; - toadd = tmp + 1; + if (mouse_event.dwButtonState & 0xFF800000) + { + b = 0x41; + strcpy (sz, "wheel down"); + } + else + { + b = 0x40; + strcpy (sz, "wheel up"); + } } else { - tmp[0] = '\033'; - tmp[1] = cyg_tolower (tmp[1]); - toadd = tmp; - nread++; - con.nModifiers &= ~4; + /* 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; + } } - } - 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) - { - 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 - { - /* 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'; + /* Remember mouse position */ + con.dwLastMousePosition.X = con.dwMousePosition.X; + con.dwLastMousePosition.Y = con.dwMousePosition.Y; - con.last_button_code = b; + /* 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; - 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; + /* Indicate the modifiers */ + b |= con.nModifiers; - case FOCUS_EVENT: - if (con.use_focus) - { - if (input_rec.Event.FocusEvent.bSetFocus) - __small_sprintf (tmp, "\033[I"); + /* 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 - __small_sprintf (tmp, "\033[O"); + { + 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; - nread = 3; } - break; - - case WINDOW_BUFFER_SIZE_EVENT: - send_winch_maybe (); - /* fall through */ - default: - continue; } + break; - if (toadd) + case FOCUS_EVENT: + if (con.use_focus) { - line_edit_status res = line_edit (toadd, nread, ti); - if (res == line_edit_signalled) - goto sig_exit; - else if (res == line_edit_input_done) - break; - } - } + if (input_rec.Event.FocusEvent.bSetFocus) + __small_sprintf (tmp, "\033[I"); + else + __small_sprintf (tmp, "\033[O"); - while (buflen) - if ((ch = get_readahead ()) < 0) + toadd = tmp; + nread = 3; + } break; - else - { - buf[copied_chars++] = (unsigned char)(ch & 0xff); - buflen--; - } -#undef buf - buflen = copied_chars; - return; - -err: - __seterrno (); - buflen = (size_t) -1; - return; + case WINDOW_BUFFER_SIZE_EVENT: + if (send_winch_maybe ()) + return input_winch; + /* fall through */ + default: + return input_processing; + } -sig_exit: - set_sig_errno (EINTR); - buflen = (size_t) -1; + 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; } void @@ -844,6 +940,8 @@ fhandler_console::open (int flags, mode_t) } set_output_handle (h); + setup_io_mutex (); + if (con.fillin (get_output_handle ())) { con.current_win32_attr = con.b.wAttributes; @@ -855,10 +953,23 @@ fhandler_console::open (int flags, mode_t) get_ttyp ()->rstcons (false); set_open_status (); + if (getpid () == con.owner && con.cap24bit_color) + { + DWORD dwMode; + /* Enable xterm compatible mode in output */ + GetConsoleMode (get_output_handle (), &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode (get_output_handle (), dwMode); + /* Enable xterm compatible mode in input */ + GetConsoleMode (get_handle (), &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT; + SetConsoleMode (get_handle (), dwMode); + } + DWORD cflags; if (GetConsoleMode (get_handle (), &cflags)) - SetConsoleMode (get_handle (), - ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags); + SetConsoleMode (get_handle (), ENABLE_WINDOW_INPUT + | (con.cap24bit_color ? 0 : ENABLE_MOUSE_INPUT) | cflags); debug_printf ("opened conin$ %p, conout$ %p", get_handle (), get_output_handle ()); @@ -878,6 +989,26 @@ fhandler_console::open_setup (int flags) int fhandler_console::close () { + debug_printf ("closing: %p, %p", get_handle (), get_output_handle ()); + + CloseHandle (input_mutex); + input_mutex = NULL; + CloseHandle (output_mutex); + output_mutex = NULL; + + if (shared_console_info && getpid () == con.owner && con.cap24bit_color) + { + DWORD dwMode; + /* Disable xterm compatible mode in input */ + GetConsoleMode (get_handle (), &dwMode); + dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT; + SetConsoleMode (get_handle (), dwMode); + /* Disable xterm compatible mode in output */ + GetConsoleMode (get_output_handle (), &dwMode); + dwMode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode (get_output_handle (), dwMode); + } + CloseHandle (get_handle ()); CloseHandle (get_output_handle ()); if (!have_execed) @@ -891,6 +1022,7 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) int res = fhandler_termios::ioctl (cmd, arg); if (res <= 0) return res; + acquire_output_mutex (INFINITE); switch (cmd) { case TIOCGWINSZ: @@ -906,20 +1038,25 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) syscall_printf ("WINSZ: (row=%d,col=%d)", ((struct winsize *) arg)->ws_row, ((struct winsize *) arg)->ws_col); + release_output_mutex (); return 0; } else { syscall_printf ("WINSZ failed"); __seterrno (); + release_output_mutex (); return -1; } + release_output_mutex (); return 0; case TIOCSWINSZ: bg_check (SIGTTOU); + release_output_mutex (); return 0; case KDGKBMETA: *(int *) arg = (con.metabit) ? K_METABIT : K_ESCPREFIX; + release_output_mutex (); return 0; case KDSKBMETA: if ((intptr_t) arg == K_METABIT) @@ -929,16 +1066,20 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) else { set_errno (EINVAL); + release_output_mutex (); return -1; } + release_output_mutex (); return 0; case TIOCLINUX: if (*(unsigned char *) arg == 6) { *(unsigned char *) arg = (unsigned char) con.nModifiers; + release_output_mutex (); return 0; } set_errno (EINVAL); + release_output_mutex (); return -1; case FIONREAD: { @@ -951,17 +1092,20 @@ fhandler_console::ioctl (unsigned int cmd, void *arg) if (!PeekConsoleInputW (get_handle (), inp, INREC_SIZE, &n)) { set_errno (EINVAL); + release_output_mutex (); return -1; } while (n-- > 0) if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown) ++ret; *(int *) arg = ret; + release_output_mutex (); return 0; } break; } + release_output_mutex (); return fhandler_base::ioctl (cmd, arg); } @@ -987,6 +1131,13 @@ fhandler_console::output_tcsetattr (int, struct termios const *t) /* All the output bits we can ignore */ DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + /* If system has 24bit color capability, use xterm compatible mode. */ + if (con.cap24bit_color) + { + flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!(t->c_oflag & OPOST) || !(t->c_oflag & ONLCR)) + flags |= DISABLE_NEWLINE_AUTO_RETURN; + } int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1; if (res) @@ -1043,7 +1194,10 @@ fhandler_console::input_tcsetattr (int, struct termios const *t) flags |= ENABLE_PROCESSED_INPUT; } - flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT; + flags |= ENABLE_WINDOW_INPUT | (con.cap24bit_color ? 0 : ENABLE_MOUSE_INPUT); + /* if system has 24bit color capability, use xterm compatible mode. */ + if (con.cap24bit_color) + flags |= ENABLE_VIRTUAL_TERMINAL_INPUT; int res; if (flags == oflags) @@ -1110,7 +1264,8 @@ fhandler_console::tcgetattr (struct termios *t) } fhandler_console::fhandler_console (fh_devices unit) : - fhandler_termios () + fhandler_termios (), input_ready (false), + input_mutex (NULL), output_mutex (NULL) { if (unit > 0) dev ().parse (unit); @@ -1602,11 +1757,32 @@ static const char base_chars[256] = /*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR, /*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR }; +static const char table256[256] = +{ + 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15, + 0, 1, 1, 1, 9, 9, 2, 3, 3, 3, 3, 9, 2, 3, 3, 3, + 3,11, 2, 3, 3, 3,11,11,10, 3, 3,11,11,11,10,10, + 11,11,11,11, 4, 5, 5, 5, 5, 9, 6, 8, 8, 8, 8, 9, + 6, 8, 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, + 7,11,10,10, 7, 7,11,11, 4, 5, 5, 5, 5,13, 6, 8, + 8, 8, 8, 7, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7, + 6, 8, 7, 7, 7, 7,14, 7, 7, 7, 7, 7, 4, 5, 5, 5, + 13,13, 6, 8, 8, 8, 7, 7, 6, 8, 8, 7, 7, 7, 6, 8, + 7, 7, 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15, + 12, 5, 5,13,13,13, 6, 8, 8, 7, 7,13, 6, 8, 7, 7, + 7, 7,14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14, + 7, 7,15,15,12,12,13,13,13,13,12,12, 7, 7,13,13, + 14, 7, 7, 7, 7, 7,14, 7, 7, 7, 7,15,14,14, 7, 7, + 15,15,14,14, 7,15,15,15, 0, 0, 0, 0, 0, 0, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7,15,15 +}; + void fhandler_console::char_command (char c) { int x, y, n; char buf[40]; + int r, g, b; switch (c) { @@ -1678,6 +1854,40 @@ fhandler_console::char_command (char c) case 37: /* WHITE foreg */ con.fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; break; + case 38: + if (con.nargs < 1) + /* Sequence error (abort) */ + break; + switch (con.args[1]) + { + case 2: + if (con.nargs != 4) + /* Sequence error (abort) */ + break; + r = con.args[2]; + g = con.args[3]; + b = con.args[4]; + r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40; + g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40; + b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40; + con.fg = table256[16 + r*36 + g*6 + b]; + break; + case 5: + if (con.nargs != 2) + /* Sequence error (abort) */ + break; + { + int idx = con.args[2]; + if (idx < 0) + idx = 0; + if (idx > 255) + idx = 255; + con.fg = table256[idx]; + } + break; + } + i += con.nargs; + break; case 39: con.fg = con.default_color & FOREGROUND_ATTR_MASK; break; @@ -1705,6 +1915,40 @@ fhandler_console::char_command (char c) case 47: /* WHITE background */ con.bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED; break; + case 48: + if (con.nargs < 1) + /* Sequence error (abort) */ + break; + switch (con.args[1]) + { + case 2: + if (con.nargs != 4) + /* Sequence error (abort) */ + break; + r = con.args[2]; + g = con.args[3]; + b = con.args[4]; + r = r < (95 + 1) / 2 ? 0 : r > 255 ? 5 : (r - 55 + 20) / 40; + g = g < (95 + 1) / 2 ? 0 : g > 255 ? 5 : (g - 55 + 20) / 40; + b = b < (95 + 1) / 2 ? 0 : b > 255 ? 5 : (b - 55 + 20) / 40; + con.bg = table256[16 + r*36 + g*6 + b] << 4; + break; + case 5: + if (con.nargs != 2) + /* Sequence error (abort) */ + break; + { + int idx = con.args[2]; + if (idx < 0) + idx = 0; + if (idx > 255) + idx = 255; + con.bg = table256[idx] << 4; + } + break; + } + i += con.nargs; + break; case 49: con.bg = con.default_color & BACKGROUND_ATTR_MASK; break; @@ -1938,9 +2182,12 @@ fhandler_console::char_command (char c) fhandler_console object associated with standard input. So puts_readahead does not work. Use a common console read-ahead buffer instead. */ + acquire_input_mutex (INFINITE); con.cons_rapoi = NULL; strcpy (con.cons_rabuf, buf); con.cons_rapoi = con.cons_rabuf; + release_input_mutex (); + PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0); break; case 'n': switch (con.args[0]) @@ -1950,9 +2197,12 @@ fhandler_console::char_command (char c) y -= con.b.srWindow.Top; /* x -= con.b.srWindow.Left; // not available yet */ __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1); + acquire_input_mutex (INFINITE); con.cons_rapoi = NULL; strcpy (con.cons_rabuf, buf); con.cons_rapoi = con.cons_rabuf; + release_input_mutex (); + PostMessageW (GetConsoleWindow (), WM_SETFOCUS, 0, 0); break; default: goto bad_escape; @@ -2143,10 +2393,12 @@ fhandler_console::write_normal (const unsigned char *src, /* Loop over src buffer as long as we have just simple characters. Stop as soon as we reach the conversion limit, or if we encounter a control character or a truncated or invalid mutibyte sequence. */ + /* If system has 24bit color capability, just write all control + sequences to console since xterm compatible mode is enabled. */ memset (&ps, 0, sizeof ps); while (found < end && found - src < CONVERT_LIMIT - && base_chars[*found] == NOR) + && (con.cap24bit_color || base_chars[*found] == NOR) ) { switch (ret = f_mbtowc (_REENT, NULL, (const char *) found, end - found, &ps)) @@ -2295,6 +2547,7 @@ fhandler_console::write (const void *vsrc, size_t len) debug_printf ("%p, %ld", vsrc, len); + acquire_output_mutex (INFINITE); while (src < end) { paranoid_printf ("char %0c state is %d", *src, con.state); @@ -2303,7 +2556,10 @@ fhandler_console::write (const void *vsrc, size_t len) case normal: src = write_normal (src, end); if (!src) /* write_normal failed */ - return -1; + { + release_output_mutex (); + return -1; + } break; case gotesc: if (*src == '[') /* CSI Control Sequence Introducer */ @@ -2394,6 +2650,8 @@ fhandler_console::write (const void *vsrc, size_t len) con.rarg = con.rarg * 10 + (*src - '0'); else if (*src == ';' && (con.rarg == 2 || con.rarg == 0)) con.state = gettitle; + else if (*src == ';' && (con.rarg == 4 || con.rarg == 104)) + con.state = eatpalette; else con.state = eattitle; src++; @@ -2416,6 +2674,21 @@ fhandler_console::write (const void *vsrc, size_t len) src++; break; } + case eatpalette: + if (*src == '\033') + con.state = endpalette; + else if (*src == '\a') + con.state = normal; + src++; + break; + case endpalette: + if (*src == '\\') + con.state = normal; + else + /* Sequence error (abort) */ + con.state = normal; + src++; + break; case gotsquare: if (*src == ';') { @@ -2455,6 +2728,7 @@ fhandler_console::write (const void *vsrc, size_t len) break; } } + release_output_mutex (); syscall_printf ("%ld = fhandler_console::write(...)", len); @@ -2582,6 +2856,7 @@ void fhandler_console::fixup_after_fork_exec (bool execing) { set_unit (); + setup_io_mutex (); } // #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS) @@ -2764,3 +3039,57 @@ fhandler_console::need_invisible () debug_printf ("invisible_console %d", invisible_console); return b; } + +DWORD +fhandler_console::__acquire_input_mutex (const char *fn, int ln, DWORD ms) +{ +#ifdef DEBUGGING + strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get input_mutex", ln); +#endif + DWORD res = WaitForSingleObject (input_mutex, ms); + if (res != WAIT_OBJECT_0) + strace.prntf (_STRACE_TERMIOS, fn, + "(%d): Failed to acquire input_mutex %08x", + ln, GetLastError ()); +#ifdef DEBUGGING + else + strace.prntf (_STRACE_TERMIOS, fn, "(%d): got input_mutex", ln); +#endif + return res; +} + +void +fhandler_console::__release_input_mutex (const char *fn, int ln) +{ + ReleaseMutex (input_mutex); +#ifdef DEBUGGING + strace.prntf (_STRACE_TERMIOS, fn, "(%d): release input_mutex", ln); +#endif +} + +DWORD +fhandler_console::__acquire_output_mutex (const char *fn, int ln, DWORD ms) +{ +#ifdef DEBUGGING + strace.prntf (_STRACE_TERMIOS, fn, "(%d): trying to get output_mutex", ln); +#endif + DWORD res = WaitForSingleObject (output_mutex, ms); + if (res != WAIT_OBJECT_0) + strace.prntf (_STRACE_TERMIOS, fn, + "(%d): Failed to acquire output_mutex %08x", + ln, GetLastError ()); +#ifdef DEBUGGING + else + strace.prntf (_STRACE_TERMIOS, fn, "(%d): got output_mutex", ln); +#endif + return res; +} + +void +fhandler_console::__release_output_mutex (const char *fn, int ln) +{ + ReleaseMutex (output_mutex); +#ifdef DEBUGGING + strace.prntf (_STRACE_TERMIOS, fn, "(%d): release output_mutex", ln); +#endif +} diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 9b18e8f80..85242ec06 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -202,7 +202,9 @@ select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, right value >= 0, matching the number of bits set in the fds records. if ret is 0, continue to loop. */ ret = sel.poll (readfds, writefds, exceptfds); - if (!ret) + if (ret < 0) + wait_state = select_stuff::select_signalled; + else if (!ret) wait_state = select_stuff::select_set_zero; } /* Always clean up everything here. If we're looping then build it @@ -479,6 +481,7 @@ was_timeout: events like mouse movements. The verify function will detect these situations. If it returns false, then this wakeup was a false alarm and we should go back to waiting. */ + int ret = 0; while ((s = s->next)) if (s->saw_error ()) { @@ -488,8 +491,13 @@ was_timeout: } else if ((((wait_ret >= m && s->windows_handle) || s->h == w4[wait_ret])) - && s->verify (s, readfds, writefds, exceptfds)) + && (ret = s->verify (s, readfds, writefds, exceptfds)) > 0) res = select_ok; + else if (ret < 0) + { + res = select_signalled; + goto out; + } select_printf ("res after verify %d", res); break; @@ -539,8 +547,12 @@ select_stuff::poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds) int n = 0; select_record *s = &start; while ((s = s->next)) - n += (!s->peek || s->peek (s, true)) ? - set_bits (s, readfds, writefds, exceptfds) : 0; + { + int ret = s->peek ? s->peek (s, true) : 1; + if (ret < 0) + return -1; + n += (ret > 0) ? set_bits (s, readfds, writefds, exceptfds) : 0; + } return n; } @@ -1010,16 +1022,10 @@ peek_console (select_record *me, bool) return me->write_ready; if (fh->get_cons_readahead_valid ()) - { - select_printf ("cons_readahead"); - return me->read_ready = true; - } + return me->read_ready = true; - if (fh->get_readahead_valid ()) - { - select_printf ("readahead"); - return me->read_ready = true; - } + if (fh->input_ready) + return me->read_ready = true; if (me->read_ready) { @@ -1030,46 +1036,25 @@ peek_console (select_record *me, bool) INPUT_RECORD irec; DWORD events_read; HANDLE h; - char tmpbuf[17]; set_handle_or_return_if_not_open (h, me); - for (;;) - if (fh->bg_check (SIGTTIN, true) <= bg_eof) - return me->read_ready = true; - else if (!PeekConsoleInputW (h, &irec, 1, &events_read) || !events_read) - break; - else - { - fh->send_winch_maybe (); - if (irec.EventType == KEY_EVENT) - { - if (irec.Event.KeyEvent.bKeyDown) - { - /* Ignore Alt+Numpad keys. They are eventually handled in the - key-up case below. */ - if (is_alt_numpad_key (&irec)) - ; - /* Handle normal input. */ - else if (irec.Event.KeyEvent.uChar.UnicodeChar - || fhandler_console::get_nonascii_key (irec, tmpbuf)) - return me->read_ready = true; - } - /* Ignore key up events, except for Alt+Numpad events. */ - else if (is_alt_numpad_event (&irec)) - return me->read_ready = true; - } - else - { - if (irec.EventType == MOUSE_EVENT - && fh->mouse_aware (irec.Event.MouseEvent)) - return me->read_ready = true; - if (irec.EventType == FOCUS_EVENT && fh->focus_aware ()) - return me->read_ready = true; - } - - /* Read and discard the event */ - ReadConsoleInputW (h, &irec, 1, &events_read); - } + while (!fh->input_ready && !fh->get_cons_readahead_valid ()) + { + if (fh->bg_check (SIGTTIN, true) <= bg_eof) + return me->read_ready = true; + else if (!PeekConsoleInputW (h, &irec, 1, &events_read) || !events_read) + break; + fh->acquire_input_mutex (INFINITE); + if (fhandler_console::input_winch == fh->process_input_message ()) + { + set_sig_errno (EINTR); + fh->release_input_mutex (); + return -1; + } + fh->release_input_mutex (); + } + if (fh->input_ready || fh->get_cons_readahead_valid ()) + return me->read_ready = true; return me->write_ready; } @@ -1081,7 +1066,6 @@ verify_console (select_record *me, fd_set *rfds, fd_set *wfds, return peek_console (me, true); } - select_record * fhandler_console::select_read (select_stuff *ss) { @@ -1096,7 +1080,7 @@ fhandler_console::select_read (select_stuff *ss) s->peek = peek_console; s->h = get_handle (); s->read_selected = true; - s->read_ready = get_readahead_valid (); + s->read_ready = input_ready || get_cons_readahead_valid (); return s; } -- 2.17.0