[newlib-cygwin] Cygwin: pty: Fixup pty state after a cygwin app exits

Takashi Yano via Cygwin-cvs <[email protected]> Wed, 24 Jun 2026 13:11:25 +0000 (GMT)
Newsgroups gmane.os.cygwin.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3Db34394d456b=
6d46fc112273183fc0cae5a613e18

commit b34394d456b6d46fc112273183fc0cae5a613e18
Author: Takashi Yano <[email protected]>
Date:   Sat Jun 13 22:02:14 2026 +0900

    Cygwin: pty: Fixup pty state after a cygwin app exits
   =20
    Previously, the cygwin process on pty is always a child of another
    cygwin app on pty. If a cygwin app is a child of non-cygwin app
    in pseudo console, it was running on console originating from
    pseudo console. Now, the child of a non-cygwin app on pseudo console
    is running on pty, so, it is necessary to restore the pty state
    to the state where the parent process is running. This patch
    does the following fixup when the cygwin process on pty exits:
     1) Switch pipe mode from cyg-pipe to nat-pipe.
     2) Notify the current cursor position to pseudo console
   =20
    These prevent the problems:
     1) Run 'cat' in cmd.exe and stop it by Ctrl-C. After that
        cmd.exe cannot receive key input.
     2) Run 'ps' in cmd.exe. The cursor position will not be
        maintained correctly after that.
   =20
    Signed-off-by: Takashi Yano <[email protected]>
    Reviewed-by: Mark Geisert <[email protected]>

Diff:
---
 winsup/cygwin/fhandler/pty.cc           | 79 +++++++++++++++++++++++++++++=
++--
 winsup/cygwin/local_includes/fhandler.h |  2 +
 winsup/cygwin/local_includes/tty.h      |  1 +
 3 files changed, 78 insertions(+), 4 deletions(-)

diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index 06babcde1..35e320507 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -388,6 +388,52 @@ atexit_func (void)
     }
 }
=20
+void
+fhandler_pty_slave::req_fixup_pcon_state (void)
+{
+  while (true)
+    {
+      WaitForSingleObject (input_mutex, mutex_timeout);
+      if (!get_ttyp ()->pcon_start_pid)
+	break;
+      /* Another request is on going. */
+      ReleaseMutex (input_mutex);
+      yield ();
+    }
+
+  DWORD n;
+  /* indicates that this "ESC[6n" is just for fixing-up cursor position */
+  get_ttyp ()->req_fixup_pcon_cur_pos =3D true;
+  get_ttyp ()->req_xfer_input =3D true; /* indicates that this "ESC[6n"
+					 is just for transfer input */
+  get_ttyp ()->pcon_start =3D true;
+  get_ttyp ()->pcon_start_pid =3D myself->pid;
+  WriteFile (get_output_handle (), "\033[6n", 4, &n, NULL);
+  ReleaseMutex (input_mutex);
+  while (get_ttyp ()->pcon_start_pid)
+    /* wait for completion of fixing-up in master::write(). */
+    yield ();
+}
+
+void
+fhandler_pty_master::fixup_pcon_cursor_position (int x, int y)
+{
+  HANDLE pcon_owner =3D OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+				   get_ttyp ()->nat_pipe_owner_pid);
+  HANDLE h_pcon_out =3D NULL;
+  DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_out,
+		   GetCurrentProcess (), &h_pcon_out,
+		   0, TRUE, DUPLICATE_SAME_ACCESS);
+  CloseHandle (pcon_owner);
+  DWORD target_pid =3D get_ttyp ()->nat_pipe_owner_pid;
+  DWORD resume_pid =3D
+    fhandler_pty_common::attach_console_temporarily (target_pid);
+  COORD cur_pos =3D {(SHORT) (x - 1), (SHORT) (y - 1)};
+  SetConsoleCursorPosition (h_pcon_out, cur_pos);
+  fhandler_pty_common::resume_from_temporarily_attach (resume_pid);
+  CloseHandle (h_pcon_out);
+}
+
 #define DEF_HOOK(name) static __typeof__ (name) *name##_Orig
 /* CreateProcess() is hooked for GDB etc. */
 DEF_HOOK (CreateProcessA);
@@ -1162,6 +1208,19 @@ err_no_msg:
 bool
 fhandler_pty_slave::open_setup (int flags)
 {
+  if (get_ttyp ()->pcon_activated)
+    {
+      HANDLE pcon_owner =3D OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+				       get_ttyp ()->nat_pipe_owner_pid);
+      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
+		       GetCurrentProcess (), &get_handle_nat (),
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_out,
+		       GetCurrentProcess (), &get_output_handle_nat (),
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      CloseHandle (pcon_owner);
+    }
+
   set_flags ((flags & ~O_TEXT) | O_BINARY);
   myself->set_ctty (this, flags);
   report_tty_counts (this, "opened", "");
@@ -1171,6 +1230,9 @@ fhandler_pty_slave::open_setup (int flags)
 void
 fhandler_pty_slave::cleanup ()
 {
+  if (get_ttyp ()->pcon_activated && get_ttyp ()->getpgid () =3D=3D myself=
->pgid)
+    req_fixup_pcon_state ();
+
   /* This used to always call fhandler_pty_common::close when we were exec=
ing
      but that caused multiple closes of the handles associated with this p=
ty.
      Since close_all_files is not called until after the cygwin process has
@@ -2488,10 +2550,21 @@ fhandler_pty_master::write (const void *ptr, size_t=
 len)
 	    }
 	  if (state =3D=3D 2)
 	    {
-	      /* req_xfer_input is true if "ESC[6n" was sent just for
+	      /* req_fixup_pcon_cur_pos is true if "ESC[6n" was sent
+		 for requesting cursor-position-fixup that is needed
+		 when a non-cygwin app executes a cygwin app and the
+		 cygwin app exits.
+		 req_xfer_input is true if "ESC[6n" was sent just for
 		 triggering transfer_input() in master. In this case,
 		 the response sequence should not be written. */
-	      if (!get_ttyp ()->req_xfer_input)
+	      if (get_ttyp ()->req_fixup_pcon_cur_pos)
+		{
+		  int x, y;
+		  sscanf (wpbuf, "\033[%d;%dR", &y, &x);
+		  fixup_pcon_cursor_position (x, y);
+		  get_ttyp ()->req_fixup_pcon_cur_pos =3D false;
+		}
+	      else if (!get_ttyp ()->req_xfer_input)
 		WriteFile (to_slave_nat, wpbuf, ixput, &n, NULL);
 	      ixput =3D 0;
 	      state =3D 0;
@@ -4113,8 +4186,6 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp, D=
WORD force_switch_to)
 	  ttyp->pcon_activated =3D false;
 	  ttyp->switch_to_nat_pipe =3D false;
 	  ttyp->nat_pipe_owner_pid =3D 0;
-	  ttyp->pcon_start =3D false;
-	  ttyp->pcon_start_pid =3D 0;
 	}
       if (ttyp->pcon_handle_ready_event)
 	{
diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_=
includes/fhandler.h
index 6c358a570..8e9cbef4b 100644
--- a/winsup/cygwin/local_includes/fhandler.h
+++ b/winsup/cygwin/local_includes/fhandler.h
@@ -2533,6 +2533,7 @@ class fhandler_pty_slave: public fhandler_pty_common
   void setpgid_aux (pid_t pid);
   static void release_ownership_of_nat_pipe (tty *ttyp, fhandler_termios *=
fh);
   void replace_nat_handles (HANDLE new_input, HANDLE new_output);
+  void req_fixup_pcon_state (void);
 };
=20
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2641,6 +2642,7 @@ public:
   void apply_line_edit_to_transferred_input ();
   line_edit_status line_edit_maybe (const char *p, size_t len, termios&,
 				    ssize_t *n);
+  void fixup_pcon_cursor_position (int x, int y);
 };
=20
 class fhandler_dev_null: public fhandler_base
diff --git a/winsup/cygwin/local_includes/tty.h b/winsup/cygwin/local_inclu=
des/tty.h
index 507f7772e..c5102eb81 100644
--- a/winsup/cygwin/local_includes/tty.h
+++ b/winsup/cygwin/local_includes/tty.h
@@ -145,6 +145,7 @@ private:
   xfer_dir pty_input_state;
   bool discard_input;
   bool stop_fwd_thread;
+  bool req_fixup_pcon_cur_pos;
=20
 public:
   HANDLE from_master_nat () const { return _from_master_nat; }