Re: [PATCH v2 3/3] Cygwin: pty: Fixup pty state after a cygwin app exits

Takashi Yano <[email protected]> Tue, 23 Jun 2026 22:31:55 +0900
Newsgroups gmane.os.cygwin.patches
Message-ID <[email protected]>
Hi Mark,

Thanks for reviewing.

On Tue, 23 Jun 2026 01:07:16 -0700
Mark Geisert  wrote:
> Hi Takashi,
> 
> On 6/13/2026 7:09 AM, Takashi Yano wrote:
> > 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:
> >   1) Switch pipe mode to cyg-pipe to nat-pipe.
>                          ^^^^^^^^^^^^^^^^^^^^^^^ this can't be correct

Auch! "from cyg-pipe to nat-pipe"
> 
> >   2) Notify the current cursor position to pseudo console
> > 
> > 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.
> > 
> > Signed-off-by: Takashi Yano <[email protected]>
> > Reviewed-by:
> > ---
> >   winsup/cygwin/fhandler/pty.cc           | 73 ++++++++++++++++++++++++-
> >   winsup/cygwin/local_includes/fhandler.h |  2 +
> >   winsup/cygwin/local_includes/tty.h      |  1 +
> >   3 files changed, 73 insertions(+), 3 deletions(-)
> > 
> > diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
> > index b3a8d57cc..f4473bb69 100644
> > --- a/winsup/cygwin/fhandler/pty.cc
> > +++ b/winsup/cygwin/fhandler/pty.cc
> > @@ -388,6 +388,52 @@ atexit_func (void)
> >       }
> >   }
> >   
> > +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 corsor position */
>                                                             ^^^^^^ typo here

Fixed.

> > +  get_ttyp ()->req_fixup_pcon_cur_pos = true;
> > +  get_ttyp ()->req_xfer_input = true; /* indicates that this "ESC[6n"
> > +					 is just for transfer input */
> > +  get_ttyp ()->pcon_start = true;
> > +  get_ttyp ()->pcon_start_pid = 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 = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
> > +				   get_ttyp ()->nat_pipe_owner_pid);
> > +  HANDLE h_pcon_out = NULL;
> > +  DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_out,
> > +		   GetCurrentProcess (), &h_pcon_out,
> > +		   0, TRUE, DUPLICATE_SAME_ACCESS);
> > +  CloseHandle (pcon_owner);
> > +  DWORD target_pid = get_ttyp ()->nat_pipe_owner_pid;
> > +  DWORD resume_pid =
> > +    fhandler_pty_common::attach_console_temporarily (target_pid);
> > +  COORD cur_pos = {(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 = 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 () == myself->pgid)
> > +    req_fixup_pcon_state ();
> > +
> >     /* This used to always call fhandler_pty_common::close when we were execing
> >        but that caused multiple closes of the handles associated with this pty.
> >        Since close_all_files is not called until after the cygwin process has
> > @@ -2478,7 +2540,14 @@ fhandler_pty_master::write (const void *ptr, size_t len)
> >   	      /* 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. */
> 
> The above comment describes the req_xfer case, but says nothing about 
> the the req_fixup_pcon_cur_pos case now inserted before it.

I'll modify the comment above like:
      /* 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. */

Does this look OK to you?

> > -	      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 = false;
> > +		}
> > +	      else if (!get_ttyp ()->req_xfer_input)
> >   		WriteFile (to_slave_nat, wpbuf, ixput, &n, NULL);
> >   	      ixput = 0;
> >   	      state = 0;
> > @@ -4100,8 +4169,6 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp, DWORD force_switch_to)
> >   	  ttyp->pcon_activated = false;
> >   	  ttyp->switch_to_nat_pipe = false;
> >   	  ttyp->nat_pipe_owner_pid = 0;
> > -	  ttyp->pcon_start = false;
> > -	  ttyp->pcon_start_pid = 0;
> >   	}
> >         if (ttyp->pcon_handle_ready_event)
> >   	{
> > diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h
> > index 322592bf1..2fa30cbce 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);
> >   };
> >   
> >   #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
> > @@ -2639,6 +2640,7 @@ public:
> >     void get_master_fwd_thread_param (master_fwd_thread_param_t *p);
> >     bool need_send_ctrl_c_event ();
> >     void apply_line_edit_to_transferred_input ();
> > +  void fixup_pcon_cursor_position (int x, int y);
> >   };
> >   
> >   class fhandler_dev_null: public fhandler_base
> > diff --git a/winsup/cygwin/local_includes/tty.h b/winsup/cygwin/local_includes/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;
> >   
> >   public:
> >     HANDLE from_master_nat () const { return _from_master_nat; }
> 
> Otherwise all LGTM.  Let me know what you think about my comments.


-- 
Takashi Yano <[email protected]>