- Pseudo console support introduced by commit
  169d65a5774acc76ce3f3feeedcbae7405aa9b57 has some bugs which
  cause mismatch between state variables and real pseudo console
  state regarding console attaching and r/w pipe switching. This
  patch fixes this issue by redesigning the state management.
---
 winsup/cygwin/dtable.cc           |  15 +-
 winsup/cygwin/fhandler.h          |   6 +-
 winsup/cygwin/fhandler_console.cc |   6 +-
 winsup/cygwin/fhandler_tty.cc     | 415 ++++++++++++++++--------------
 winsup/cygwin/fork.cc             |  24 +-
 winsup/cygwin/spawn.cc            |  65 +++--
 6 files changed, 289 insertions(+), 242 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index ba5d16206..6266f1bc2 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -150,8 +150,11 @@ dtable::stdio_init ()
   bool need_fixup_handle = false;
   fhandler_pty_slave *ptys = NULL;
   bool is_pty[3] = {false, false, false};
-  for (int fd = 0; fd < 3; fd ++)
+  bool already_attached = false;
+  int chk_order[] = {1, 0, 2};
+  for (int i = 0; i < 3; i ++)
     {
+      int fd = chk_order[i];
       fhandler_base *fh = cygheap->fdtab[fd];
       if (fh && fh->get_major () == DEV_PTYS_MAJOR)
        {
@@ -161,14 +164,18 @@ dtable::stdio_init ()
              is_pty[fd] = true;
              bool attached = !!fhandler_console::get_console_process_id
                (ptys->getHelperProcessId (), true);
-             if (!attached)
+             already_attached = already_attached || attached;
+             if (!already_attached)
                {
                  /* Not attached to pseudo console in fork() or spawn()
                     by some reason. This happens if the executable is
                     a windows GUI binary, such as mintty. */
                  FreeConsole ();
-                 AttachConsole (ptys->getHelperProcessId ());
-                 need_fixup_handle = true;
+                 if (AttachConsole (ptys->getHelperProcessId ()))
+                   {
+                     need_fixup_handle = true;
+                     already_attached = true;
+                   }
                }
              ptys->reset_switch_to_pcon ();
            }
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index c75e40c0a..af2b948cb 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2106,19 +2106,22 @@ class fhandler_pty_common: public fhandler_termios
  protected:
   BOOL process_opost_output (HANDLE h,
                             const void *ptr, ssize_t& len, bool is_echo);
-  bool check_switch_to_pcon (void);
 };
 
 class fhandler_pty_slave: public fhandler_pty_common
 {
   HANDLE inuse;                        // used to indicate that a tty is in use
   HANDLE output_handle_cyg, io_handle_cyg;
+  DWORD pidRestore;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
   int fch_set_sd (security_descriptor &sd, bool chown);
   void fch_close_handles ();
 
+  bool try_reattach_pcon ();
+  void restore_reattach_pcon ();
+
  public:
   /* Constructor */
   fhandler_pty_slave (int);
@@ -2172,7 +2175,6 @@ class fhandler_pty_slave: public fhandler_pty_common
   void set_switch_to_pcon (void);
   void reset_switch_to_pcon (void);
   void push_to_pcon_screenbuffer (const char *ptr, size_t len);
-  bool has_master_opened (void);
   void mask_switch_to_pcon (bool mask)
   {
     get_ttyp ()->mask_switch_to_pcon = mask;
diff --git a/winsup/cygwin/fhandler_console.cc 
b/winsup/cygwin/fhandler_console.cc
index 997c50d23..ae7f66b80 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -3138,14 +3138,14 @@ fhandler_console::get_console_process_id (DWORD pid, 
bool match)
   DWORD tmp;
   int num = GetConsoleProcessList (&tmp, 1);
   DWORD *list = (DWORD *)
-             HeapAlloc (GetProcessHeap (), 0, num * sizeof (DWORD));
-  num = GetConsoleProcessList (list, num);
+             HeapAlloc (GetProcessHeap (), 0, (num + 16) * sizeof (DWORD));
+  num = GetConsoleProcessList (list, num + 16);
   tmp = 0;
   for (int i=0; i<num; i++)
     if ((match && list[i] == pid) || (!match && list[i] != pid))
       {
        tmp = list[i];
-       //break;
+       break;
       }
   HeapFree (GetProcessHeap (), 0, list);
   return tmp;
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index dd5ab528a..0fc9a1895 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -71,7 +71,7 @@ struct pipe_reply {
   DWORD error;
 };
 
-static bool pcon_attached[NTTYS];
+static int pcon_attached_to = -1;
 static bool isHybrid;
 
 #if USE_API_HOOK
@@ -129,7 +129,6 @@ set_switch_to_pcon (void)
        fhandler_base *fh = cfd;
        fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
        ptys->set_switch_to_pcon ();
-       return;
       }
 }
 
@@ -381,25 +380,6 @@ fhandler_pty_common::__release_output_mutex (const char 
*fn, int ln)
 #endif
 }
 
-static bool switch_to_pcon_prev;
-
-bool
-fhandler_pty_common::check_switch_to_pcon (void)
-{
-  bool switch_to_pcon_now = get_ttyp ()->switch_to_pcon;
-  if (!isHybrid && !switch_to_pcon_prev && switch_to_pcon_now)
-    {
-      Sleep (40);
-      /* Check again */
-      switch_to_pcon_now = get_ttyp ()->switch_to_pcon;
-      if (switch_to_pcon_now)
-       switch_to_pcon_prev = true;
-    }
-  else
-    switch_to_pcon_prev = switch_to_pcon_now;
-  return switch_to_pcon_prev;
-}
-
 /* Process pty input. */
 
 void
@@ -595,7 +575,7 @@ out:
 
 fhandler_pty_slave::fhandler_pty_slave (int unit)
   : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL),
-  io_handle_cyg (NULL)
+  io_handle_cyg (NULL), pidRestore (0)
 {
   if (unit >= 0)
     dev ().parse (DEV_PTYS_MAJOR, unit);
@@ -604,32 +584,34 @@ fhandler_pty_slave::fhandler_pty_slave (int unit)
 fhandler_pty_slave::~fhandler_pty_slave ()
 {
   if (!get_ttyp ())
-    {
-      /* Why it comes here? */
-      init_console_handler (false);
-      FreeConsole ();
-      pcon_attached[get_minor ()] = false;
-    }
-  else if (getPseudoConsole ())
+    /* Why comes here? Who clears _tc? */
+    return;
+  mask_switch_to_pcon (false);
+  if (getPseudoConsole ())
     {
       int used = 0;
+      int attached = 0;
       cygheap_fdenum cfd (false);
       while (cfd.next () >= 0)
-       if (cfd->get_major () == DEV_PTYS_MAJOR &&
-           cfd->get_minor () == get_minor ())
-         used ++;
-
-      /* Call FreeConsole() if no pty slave on this pty is
-        opened and the process is attached to the pseudo
-        console corresponding to this pty. This is needed
-        to make GNU screen and tmux work in Windows 10 1903. */
-      if (used == 0 &&
-         fhandler_console::get_console_process_id (getHelperProcessId (),
-                                                   true))
+       {
+         if (cfd->get_major () == DEV_PTYS_MAJOR ||
+             cfd->get_major () == DEV_CONS_MAJOR)
+           used ++;
+         if (cfd->get_major () == DEV_PTYS_MAJOR &&
+             cfd->get_minor () == pcon_attached_to)
+           attached ++;
+       }
+
+      /* Call FreeConsole() if no tty is opened and the process
+        is attached to console corresponding to tty. This is
+        needed to make GNU screen and tmux work in Windows 10
+        1903. */
+      if (attached == 0)
+       pcon_attached_to = -1;
+      if (used == 0)
        {
          init_console_handler (false);
          FreeConsole ();
-         pcon_attached[get_minor ()] = false;
        }
     }
 }
@@ -813,7 +795,38 @@ fhandler_pty_slave::open (int flags, mode_t)
   set_output_handle (to_master_local);
   set_output_handle_cyg (to_master_cyg_local);
 
-  fhandler_console::need_invisible ();
+  if (!getPseudoConsole () || ALWAYS_USE_PCON)
+    {
+      fhandler_console::need_invisible ();
+      pcon_attached_to = -1;
+    }
+#if 0
+  else if (!fhandler_console::get_console_process_id
+                                    (GetCurrentProcessId (), true))
+    {
+      /* Not attached to any console */
+      if (AttachConsole (getHelperProcessId ()))
+       {
+         pcon_attached_to = get_minor ();
+         init_console_handler (true);
+       }
+      else
+       {
+         fhandler_console::need_invisible ();
+         pcon_attached_to = -1;
+       }
+    }
+#endif
+  else if (fhandler_console::get_console_process_id
+                                    (getHelperProcessId (), true))
+    /* Attached to pcon of this pty */
+    {
+      pcon_attached_to = get_minor ();
+      init_console_handler (true);
+    }
+  else if (pcon_attached_to < 0)
+    fhandler_console::need_invisible ();
+
   set_open_status ();
   return 1;
 
@@ -855,26 +868,6 @@ fhandler_pty_slave::cleanup ()
 int
 fhandler_pty_slave::close ()
 {
-#if 0
-  if (getPseudoConsole ())
-    {
-      INPUT_RECORD inp[128];
-      DWORD n;
-      PeekFunc =
-       PeekConsoleInputA_Orig ? PeekConsoleInputA_Orig : PeekConsoleInput;
-      PeekFunc (get_handle (), inp, 128, &n);
-      bool pipe_empty = true;
-      while (n-- > 0)
-       if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown)
-         pipe_empty = false;
-      if (pipe_empty)
-       {
-         /* Flush input buffer */
-         size_t len = UINT_MAX;
-         read (NULL, len);
-       }
-    }
-#endif
   termios_printf ("closing last open %s handle", ttyname ());
   if (inuse && !CloseHandle (inuse))
     termios_printf ("CloseHandle (inuse), %E");
@@ -886,12 +879,13 @@ fhandler_pty_slave::close ()
   if (!ForceCloseHandle (get_handle_cyg ()))
     termios_printf ("CloseHandle (get_handle_cyg ()<%p>), %E",
        get_handle_cyg ());
-  if ((unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ()))
+  if (!getPseudoConsole () &&
+      (unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ()))
     fhandler_console::free_console (); /* assumes that we are the last pty 
closer */
   fhandler_pty_common::close ();
   if (!ForceCloseHandle (output_mutex))
     termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
-  if (pcon_attached[get_minor ()])
+  if (pcon_attached_to == get_minor ())
     get_ttyp ()->num_pcon_attached_slaves --;
   return 0;
 }
@@ -936,14 +930,53 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+bool
+fhandler_pty_slave::try_reattach_pcon (void)
+{
+  pidRestore = 0;
+
+  /* Do not detach from the console because re-attaching will
+     fail if helper process is running as service account. */
+  if (pcon_attached_to >= 0 &&
+      cygwin_shared->tty[pcon_attached_to]->attach_pcon_in_fork)
+    return false;
+
+  pidRestore =
+    fhandler_console::get_console_process_id (GetCurrentProcessId (),
+                                             false);
+  /* If pidRestore is not set, give up. */
+  if (!pidRestore)
+    return false;
+
+  FreeConsole ();
+  if (!AttachConsole (getHelperProcessId ()))
+    {
+      system_printf ("pty%d: AttachConsole(helper=%d) failed. 0x%08lx",
+                    get_minor (), getHelperProcessId (), GetLastError ());
+      return false;
+    }
+  return true;
+}
+
 void
-fhandler_pty_slave::set_switch_to_pcon (void)
+fhandler_pty_slave::restore_reattach_pcon (void)
 {
-  if (!pcon_attached[get_minor ()])
+  if (pidRestore)
     {
-      isHybrid = false;
-      return;
+      FreeConsole ();
+      if (!AttachConsole (pidRestore))
+       {
+         system_printf ("pty%d: AttachConsole(restore=%d) failed. 0x%08lx",
+                        get_minor (), pidRestore, GetLastError ());
+         pcon_attached_to = -1;
+       }
     }
+  pidRestore = 0;
+}
+
+void
+fhandler_pty_slave::set_switch_to_pcon (void)
+{
   if (!isHybrid)
     {
       reset_switch_to_pcon ();
@@ -951,6 +984,16 @@ fhandler_pty_slave::set_switch_to_pcon (void)
     }
   if (!get_ttyp ()->switch_to_pcon)
     {
+      pidRestore = 0;
+      if (pcon_attached_to != get_minor ())
+       if (!try_reattach_pcon ())
+         goto skip_console_setting;
+      FlushConsoleInputBuffer (get_handle ());
+      DWORD mode;
+      GetConsoleMode (get_handle (), &mode);
+      SetConsoleMode (get_handle (), mode | ENABLE_ECHO_INPUT);
+skip_console_setting:
+      restore_reattach_pcon ();
       Sleep (20);
       if (get_ttyp ()->pcon_pid == 0 ||
          kill (get_ttyp ()->pcon_pid, 0) != 0)
@@ -980,7 +1023,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
       DWORD mode;
       GetConsoleMode (get_handle (), &mode);
       SetConsoleMode (get_handle (), mode & ~ENABLE_ECHO_INPUT);
-      Sleep (60); /* Wait for pty_master_fwd_thread() */
+      Sleep (20); /* Wait for pty_master_fwd_thread() */
     }
   get_ttyp ()->pcon_pid = 0;
   get_ttyp ()->switch_to_pcon = false;
@@ -989,43 +1032,31 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
 void
 fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len)
 {
-  DWORD pidRestore = 0;
-  if (!fhandler_console::get_console_process_id (getHelperProcessId (), true))
-    if (pcon_attached[get_minor ()])
-      {
-       Sleep (20);
-       /* Check again */
-       if (!fhandler_console::get_console_process_id
-                                   (getHelperProcessId (), true))
-         {
-           system_printf ("pty%d: pcon_attach mismatch?????? (%p)",
-                          get_minor (), this);
-           //pcon_attached[get_minor ()] = false;
-           return;
-         }
-      }
-  /* If not attached pseudo console yet, try to attach temporally. */
-  if (!pcon_attached[get_minor ()])
+  bool attached =
+    !!fhandler_console::get_console_process_id (getHelperProcessId (), true);
+  if (!attached && pcon_attached_to == get_minor ())
     {
-      if (has_master_opened ())
-       return;
-
-      pidRestore =
-       fhandler_console::get_console_process_id (GetCurrentProcessId (),
-                                                 false);
-      /* If pidRestore is not set, give up to push. */
-      if (!pidRestore)
-       return;
-
-      FreeConsole ();
-      if (!AttachConsole (getHelperProcessId ()))
+      for (DWORD t0 = GetTickCount (); GetTickCount () - t0 < 100; )
+       {
+         Sleep (1);
+         attached = fhandler_console::get_console_process_id
+                                     (getHelperProcessId (), true);
+         if (attached)
+           break;
+       }
+      if (!attached)
        {
-         system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx",
-                        get_minor (), getHelperProcessId (),
-                        this, GetLastError ());
-         goto detach;
+         system_printf ("pty%d: pcon_attach_to mismatch??????", get_minor ());
+         return;
        }
     }
+
+  /* If not attached to this pseudo console, try to attach temporarily. */
+  pidRestore = 0;
+  if (pcon_attached_to != get_minor ())
+    if (!try_reattach_pcon ())
+      goto detach;
+
   char *buf;
   size_t nlen;
   DWORD origCP;
@@ -1067,7 +1098,7 @@ fhandler_pty_slave::push_to_pcon_screenbuffer (const char 
*ptr, size_t len)
     }
   if (!nlen) /* Nothing to be synchronized */
     goto cleanup;
-  if (check_switch_to_pcon ())
+  if (get_ttyp ()->switch_to_pcon)
     goto cleanup;
   /* Remove ESC sequence which returns results to console
      input buffer. Without this, cursor position report
@@ -1122,27 +1153,7 @@ cleanup:
   SetConsoleOutputCP (origCP);
   HeapFree (GetProcessHeap (), 0, buf);
 detach:
-  if (!pcon_attached[get_minor ()])
-    {
-      FreeConsole ();
-      if (!AttachConsole (pidRestore))
-       {
-         system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx",
-                        get_minor (), pidRestore, this, GetLastError ());
-         pcon_attached[get_minor ()] = false;
-       }
-    }
-}
-
-bool
-fhandler_pty_slave::has_master_opened (void)
-{
-  cygheap_fdenum cfd (false);
-  while (cfd.next () >= 0)
-    if (cfd->get_major () == DEV_PTYM_MAJOR &&
-       cfd->get_minor () == get_minor ())
-      return true;
-  return false;
+  restore_reattach_pcon ();
 }
 
 ssize_t __stdcall
@@ -1162,7 +1173,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   char *buf;
   ssize_t nlen;
-  UINT targetCodePage = (check_switch_to_pcon ()) ?
+  UINT targetCodePage = get_ttyp ()->switch_to_pcon ?
     GetConsoleOutputCP () : get_ttyp ()->TermCodePage;
   if (targetCodePage != get_ttyp ()->TermCodePage)
     {
@@ -1189,18 +1200,25 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
       nlen = len;
     }
 
+  /* If not attached this pseudo console, try to attach temporarily. */
+  pidRestore = 0;
+  bool fallback = false;
+  if (get_ttyp ()->switch_to_pcon && pcon_attached_to != get_minor ())
+    if (!try_reattach_pcon ())
+      fallback = true;
+
   DWORD dwMode, flags;
   flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
   if (!(get_ttyp ()->ti.c_oflag & OPOST) ||
       !(get_ttyp ()->ti.c_oflag & ONLCR))
     flags |= DISABLE_NEWLINE_AUTO_RETURN;
-  if (check_switch_to_pcon ())
+  if (get_ttyp ()->switch_to_pcon && !fallback)
     {
       GetConsoleMode (get_output_handle (), &dwMode);
       SetConsoleMode (get_output_handle (), dwMode | flags);
     }
-  HANDLE to =
-    check_switch_to_pcon () ? get_output_handle () : get_output_handle_cyg ();
+  HANDLE to = (get_ttyp ()->switch_to_pcon && !fallback) ?
+    get_output_handle () : get_output_handle_cyg ();
   acquire_output_mutex (INFINITE);
   if (!process_opost_output (to, buf, nlen, false))
     {
@@ -1219,8 +1237,10 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
   release_output_mutex ();
   HeapFree (GetProcessHeap (), 0, buf);
   flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
-  if (check_switch_to_pcon ())
-    SetConsoleMode (get_output_handle (), dwMode | flags);
+  if (get_ttyp ()->switch_to_pcon && !fallback)
+    SetConsoleMode (get_output_handle (), dwMode);
+
+  restore_reattach_pcon ();
 
   /* Push slave output to pseudo console screen buffer */
   if (getPseudoConsole ())
@@ -1361,9 +1381,15 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
            }
          goto out;
        }
-      if (check_switch_to_pcon () &&
-         !get_ttyp ()->mask_switch_to_pcon)
+      if (get_ttyp ()->switch_to_pcon &&
+         (!get_ttyp ()->mask_switch_to_pcon || ALWAYS_USE_PCON))
        {
+         if (!try_reattach_pcon ())
+           {
+             restore_reattach_pcon ();
+             goto do_read_cyg;
+           }
+
          DWORD dwMode;
          GetConsoleMode (get_handle (), &dwMode);
          DWORD flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
@@ -1406,8 +1432,13 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
            ResetEvent (input_available_event);
          ReleaseMutex (input_mutex);
          len = rlen;
+
+         restore_reattach_pcon ();
+         mask_switch_to_pcon (false);
          return;
        }
+
+do_read_cyg:
       if (!bytes_available (bytes_in_pipe))
        {
          ReleaseMutex (input_mutex);
@@ -1675,31 +1706,12 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
     case TIOCSWINSZ:
       if (getPseudoConsole ())
        {
-         /* If not attached pseudo console yet, try to attach
-            temporally. */
-         DWORD pidRestore = 0;
-         if (!pcon_attached[get_minor ()])
-           {
-             if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
-               goto resize_cyg;
-
-             pidRestore = fhandler_console::get_console_process_id
-               (GetCurrentProcessId (), false);
-
-             /* This happens at mintty startup if fhandler_console::
-                need_invisible() is called in stdio_init() in dtable.cc */
-             if (!pidRestore) /* Give up to resize pseudo console */
-               goto resize_cyg;
+         /* If not attached this pseudo console, try to attach temporarily. */
+         pidRestore = 0;
+         if (pcon_attached_to != get_minor ())
+           if (!try_reattach_pcon ())
+             goto cleanup;
 
-             FreeConsole ();
-             if (!AttachConsole (getHelperProcessId ()))
-               {
-                 system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx",
-                                get_minor(), getHelperProcessId (),
-                                this, GetLastError ());
-                 goto cleanup;
-               }
-           }
          COORD size;
          size.X = ((struct winsize *) arg)->ws_col;
          size.Y = ((struct winsize *) arg)->ws_row;
@@ -1717,20 +1729,9 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
          rect.Bottom = size.Y-1;
          SetConsoleWindowInfo (get_output_handle (), TRUE, &rect);
 cleanup:
-         /* Detach from pseudo console and resume. */
-         if (pidRestore)
-           {
-             FreeConsole ();
-             if (!AttachConsole (pidRestore))
-               {
-                 system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx",
-                                get_minor (), pidRestore,
-                                this, GetLastError ());
-                 pcon_attached[get_minor ()] = false;
-               }
-           }
+         restore_reattach_pcon ();
        }
-resize_cyg:
+
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
          || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
        {
@@ -2106,7 +2107,7 @@ fhandler_pty_master::close ()
              ClosePseudoConsole = (VOID (WINAPI *) (HPCON)) func;
              ClosePseudoConsole (getPseudoConsole ());
            }
-         get_ttyp ()->hPseudoConsole = NULL;
+         //get_ttyp ()->hPseudoConsole = NULL; // Do not clear for safty.
          get_ttyp ()->switch_to_pcon = false;
        }
       if (get_ttyp ()->getsid () > 0)
@@ -2160,8 +2161,8 @@ fhandler_pty_master::write (const void *ptr, size_t len)
 
   /* Write terminal input to to_slave pipe instead of output_handle
      if current application is native console application. */
-  if (check_switch_to_pcon () &&
-      !get_ttyp ()->mask_switch_to_pcon)
+  if (get_ttyp ()->switch_to_pcon &&
+      (!get_ttyp ()->mask_switch_to_pcon || ALWAYS_USE_PCON))
     {
       char *buf;
       size_t nlen;
@@ -2770,8 +2771,9 @@ fhandler_pty_slave::fixup_after_attach (bool native_maybe)
       if (fhandler_console::get_console_process_id (getHelperProcessId (),
                                                    true))
        {
-         if (!pcon_attached[get_minor ()])
+         if (pcon_attached_to != get_minor ())
            {
+             pcon_attached_to = get_minor ();
              init_console_handler (true);
 #if USE_OWN_NLS_FUNC
              char locale[ENCODING_LEN + 1] = "C";
@@ -2856,19 +2858,20 @@ fhandler_pty_slave::fixup_after_attach (bool 
native_maybe)
                           "\033[H\033[J", 6, &n, NULL);
 #endif
 
-             pcon_attached[get_minor ()] = true;
              get_ttyp ()->num_pcon_attached_slaves ++;
            }
        }
-      else
-       pcon_attached[get_minor ()] = false;
     }
-  if (pcon_attached[get_minor ()] && native_maybe)
+  if (pcon_attached_to == get_minor () && (native_maybe || ALWAYS_USE_PCON))
     {
       FlushConsoleInputBuffer (get_handle ());
       DWORD mode;
       GetConsoleMode (get_handle (), &mode);
-      SetConsoleMode (get_handle (), mode | ENABLE_ECHO_INPUT);
+      SetConsoleMode (get_handle (),
+                     (mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT) |
+                     ENABLE_ECHO_INPUT |
+                     ENABLE_LINE_INPUT |
+                     ENABLE_PROCESSED_INPUT);
       Sleep (20);
       if (get_ttyp ()->pcon_pid == 0 ||
          kill (get_ttyp ()->pcon_pid, 0) != 0)
@@ -2896,23 +2899,28 @@ fhandler_pty_slave::fixup_after_exec ()
   else if (getPseudoConsole ())
     {
       int used = 0;
+      int attached = 0;
       cygheap_fdenum cfd (false);
       while (cfd.next () >= 0)
-       if (cfd->get_major () == DEV_PTYS_MAJOR &&
-           cfd->get_minor () == get_minor ())
-         used ++;
-
-      /* Call FreeConsole() if no pty slave on this pty is
-        opened and the process is attached to the pseudo
-        console corresponding to this pty. This is needed
-        to make GNU screen and tmux work in Windows 10 1903. */
-      if (used == 1 /* About to close this one */ &&
-         fhandler_console::get_console_process_id (getHelperProcessId (),
-                                                   true))
+       {
+         if (cfd->get_major () == DEV_PTYS_MAJOR ||
+             cfd->get_major () == DEV_CONS_MAJOR)
+           used ++;
+         if (cfd->get_major () == DEV_PTYS_MAJOR &&
+             cfd->get_minor () == pcon_attached_to)
+           attached ++;
+       }
+
+      /* Call FreeConsole() if no tty is opened and the process
+        is attached to console corresponding to tty. This is
+        needed to make GNU screen and tmux work in Windows 10
+        1903. */
+      if (attached == 1 && get_minor () == pcon_attached_to)
+       pcon_attached_to = -1;
+      if (used == 1 /* About to close this tty */)
        {
          init_console_handler (false);
          FreeConsole ();
-         pcon_attached[get_minor ()] = false;
        }
     }
 
@@ -3119,7 +3127,7 @@ fhandler_pty_master::pty_master_fwd_thread ()
        {
          /* Avoid duplicating slave output which is already sent to
             to_master_cyg */
-         if (!check_switch_to_pcon ())
+         if (!get_ttyp ()->switch_to_pcon)
            continue;
 
          /* Avoid setting window title to "cygwin-console-helper.exe" */
@@ -3134,25 +3142,42 @@ fhandler_pty_master::pty_master_fwd_thread ()
              }
            else if ((state == 1 && outbuf[i] == ']') ||
                     (state == 2 && outbuf[i] == '0') ||
-                    (state == 3 && outbuf[i] == ';') ||
-                    (state == 4 && outbuf[i] == '\0'))
+                    (state == 3 && outbuf[i] == ';'))
              {
                state ++;
                continue;
              }
-           else if (state == 5 && outbuf[i] == '\a')
+           else if (state == 4 && outbuf[i] == '\a')
              {
                memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
                state = 0;
                rlen = wlen = start_at + rlen - i - 1;
                continue;
              }
-           else if (state != 4 || outbuf[i] == '\a')
+           else if (outbuf[i] == '\a')
              {
                state = 0;
                continue;
              }
 
+         /* Remove ESC sequence which returns results to console
+            input buffer. Without this, cursor position report
+            is put into the input buffer as a garbage. */
+         /* Remove ESC sequence to report cursor position. */
+         char *p0;
+         while ((p0 = (char *) memmem (outbuf, rlen, "\033[6n", 4)))
+           {
+             memmove (p0, p0+4, rlen - (p0+4 - outbuf));
+             rlen -= 4;
+           }
+         /* Remove ESC sequence to report terminal identity. */
+         while ((p0 = (char *) memmem (outbuf, rlen, "\033[0c", 4)))
+           {
+             memmove (p0, p0+4, rlen - (p0+4 - outbuf));
+             rlen -= 4;
+           }
+         wlen = rlen;
+
          char *buf;
          size_t nlen;
          if (get_ttyp ()->TermCodePage != CP_UTF8)
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 0c089dbe9..a3a7e7505 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -140,20 +140,24 @@ frok::child (volatile char * volatile here)
       {
        fhandler_base *fh = cfd;
        fhandler_pty_master *ptym = (fhandler_pty_master *) fh;
-       if (ptym->getPseudoConsole () &&
-           !fhandler_console::get_console_process_id (
-                               ptym->getHelperProcessId (), true))
-
+       if (ptym->getPseudoConsole ())
          {
            debug_printf ("found a PTY master %d: helper_PID=%d",
                          ptym->get_minor (), ptym->getHelperProcessId ());
-           if (ptym->attach_pcon_in_fork ())
+           if (fhandler_console::get_console_process_id (
+                               ptym->getHelperProcessId (), true))
+             /* Already attached */
+             break;
+           else
              {
-               FreeConsole ();
-               if (!AttachConsole (ptym->getHelperProcessId ()))
-                 /* Error */;
-               else
-                 break;
+               if (ptym->attach_pcon_in_fork ())
+                 {
+                   FreeConsole ();
+                   if (!AttachConsole (ptym->getHelperProcessId ()))
+                     /* Error */;
+                   else
+                     break;
+                 }
              }
          }
       }
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 98612bd0f..4bb28c47b 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -578,53 +578,62 @@ child_info_spawn::worker (const char *prog_arg, const 
char *const *argv,
       pidRestore = fhandler_console::get_console_process_id
        (GetCurrentProcessId (), false);
       fhandler_pty_slave *ptys = NULL;
-      for (int fd = 0; fd < 3; fd ++)
+      int chk_order[] = {1, 0, 2};
+      for (int i = 0; i < 3; i ++)
        {
+         int fd = chk_order[i];
          fhandler_base *fh = ::cygheap->fdtab[fd];
          if (fh && fh->get_major () == DEV_PTYS_MAJOR)
            {
              ptys = (fhandler_pty_slave *) fh;
-             if (ptys->getPseudoConsole () &&
-                 !fhandler_console::get_console_process_id (
-                                    ptys->getHelperProcessId (), true))
+             if (ptys->getPseudoConsole ())
                {
                  DWORD dwHelperProcessId = ptys->getHelperProcessId ();
                  debug_printf ("found a PTY slave %d: helper_PID=%d",
-                               fh->get_minor (), dwHelperProcessId);
-                 FreeConsole ();
-                 if (!AttachConsole (dwHelperProcessId))
+                                   fh->get_minor (), dwHelperProcessId);
+                 if (fhandler_console::get_console_process_id
+                                             (dwHelperProcessId, true))
                    {
-                     /* Fallback */
-                     DWORD target[3] = {
-                       STD_INPUT_HANDLE,
-                       STD_OUTPUT_HANDLE,
-                       STD_ERROR_HANDLE
-                     };
-                     if (fd == 0)
+                     /* Already attached */
+                     attach_to_pcon = true;
+                     break;
+                   }
+                 else
+                   {
+                     FreeConsole ();
+                     if (AttachConsole (dwHelperProcessId))
                        {
-                         ptys->set_handle (ptys->get_handle_cyg ());
-                         SetStdHandle (target[fd],
-                                       ptys->get_handle ());
+                         attach_to_pcon = true;
+                         break;
                        }
                      else
                        {
-                         ptys->set_output_handle (
-                                      ptys->get_output_handle_cyg ());
-                         SetStdHandle (target[fd],
-                                       ptys->get_output_handle ());
+                         /* Fallback */
+                         DWORD target[3] = {
+                           STD_INPUT_HANDLE,
+                           STD_OUTPUT_HANDLE,
+                           STD_ERROR_HANDLE
+                         };
+                         if (fd == 0)
+                           {
+                             ptys->set_handle (ptys->get_handle_cyg ());
+                             SetStdHandle (target[fd],
+                                           ptys->get_handle ());
+                           }
+                         else if (fd < 3)
+                           {
+                             ptys->set_output_handle (
+                                          ptys->get_output_handle_cyg ());
+                             SetStdHandle (target[fd],
+                                           ptys->get_output_handle ());
+                           }
                        }
                    }
-                 else
-                   {
-                     init_console_handler (true);
-                     attach_to_pcon = true;
-                     break;
-                   }
                }
            }
        }
       if (ptys)
-       ptys->fixup_after_attach (true);
+       ptys->fixup_after_attach (!iscygwin ());
 
     loop:
       /* When ruid != euid we create the new process under the current original
-- 
2.21.0

Reply via email to