From 88cd4bbca1750e9a33545e52c04be624376b838d Mon Sep 17 00:00:00 2001
From: Takashi Yano <takashi.yano@nifty.ne.jp>
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 <sys/param.h>
 #include <sys/cygwin.h>
 #include <cygwin/kd.h>
+#include <unistd.h>
 #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 <ALT> key always generates
-	   META, but the right <ALT> 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 <ALT> (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 <ALT> key always generates
+	 META, but the right <ALT> 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 <ALT> (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

