[PATCH v8 2/7] Cygwin: pty: Add workaround for handling of backspace when pcon enabled

Johannes Schindelin Johannes.Schindelin@gmx.de
Sat Mar 28 13:34:35 GMT 2026


Hi Takashi,

On Sat, 28 Mar 2026, Takashi Yano wrote:

> On Sat, 28 Mar 2026 19:55:46 +0900 Takashi Yano wrote:
>
> > @@ -2241,28 +2246,75 @@ fhandler_pty_master::write (const void *ptr, size_t len)
> >      { /* Reaches here when non-cygwin app is foreground and pseudo console
> >  	 is activated. */
> >        tmp_pathbuf tp;
> > -      char *buf = (char *) ptr;
> > +      char *buf = tp.c_get ();
> >        size_t nlen = len;
> >        if (get_ttyp ()->term_code_page != CP_UTF8)
> >  	{
> >  	  static mbstate_t mbp;
> > -	  buf = tp.c_get ();
> >  	  nlen = NT_MAX_PATH;
> >  	  convert_mb_str (CP_UTF8, buf, &nlen,
> >  			  get_ttyp ()->term_code_page, (const char *) ptr, len,
> >  			  &mbp);
> >  	}
> > +      else
> > +	memcpy (buf, ptr, nlen);
> >  
> > -      for (size_t i = 0; i < nlen; i++)
> > +      if (get_ttyp ()->nat_pipe_owner_pid != nat_pipe_owner_pid_dupped
> > +	  && !nat_pipe_owner_self (get_ttyp ()->nat_pipe_owner_pid))
> > +	{
> > +	  if (h_pcon_in_dupped)
> > +	    ForceCloseHandle (h_pcon_in_dupped);
> > +	  h_pcon_in_dupped = NULL;
> > +	  nat_pipe_owner_pid_dupped = 0;
> > +	  HANDLE pcon_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
> > +					   get_ttyp ()->nat_pipe_owner_pid);
> > +	  if (pcon_owner)
> > +	    {
> > +	      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
> > +			       GetCurrentProcess (), &h_pcon_in_dupped,
> > +			       0, FALSE, DUPLICATE_SAME_ACCESS);
> > +	      nat_pipe_owner_pid_dupped = get_ttyp ()->nat_pipe_owner_pid;
> > +	      CloseHandle(pcon_owner);
> > +	    }
> > +	}
> > +      else
> > +	{
> > +	  h_pcon_in_dupped = get_ttyp ()->h_pcon_in;
> > +	  nat_pipe_owner_pid_dupped = get_ttyp ()->nat_pipe_owner_pid;
> > +	}
> 
> Oops! This is wrong.
> 
> Should be:
> +      if (get_ttyp ()->nat_pipe_owner_pid != nat_pipe_owner_pid_dupped)
> +       {
> +         if (!nat_pipe_owner_self (get_ttyp ()->nat_pipe_owner_pid))
> +           {
> +             if (h_pcon_in_dupped)
> +               ForceCloseHandle (h_pcon_in_dupped);
> +             h_pcon_in_dupped = NULL;
> +             nat_pipe_owner_pid_dupped = 0;
> +             HANDLE pcon_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
> +                                              get_ttyp ()->nat_pipe_owner_pid);
> +             if (pcon_owner)
> +               {
> +                 DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
> +                                  GetCurrentProcess (), &h_pcon_in_dupped,
> +                                  0, FALSE, DUPLICATE_SAME_ACCESS);
> +                 nat_pipe_owner_pid_dupped = get_ttyp ()->nat_pipe_owner_pid;
> +                 CloseHandle(pcon_owner);
> +               }
> +           }
> +         else
> +           {
> +             h_pcon_in_dupped = get_ttyp ()->h_pcon_in;
> +             nat_pipe_owner_pid_dupped = get_ttyp ()->nat_pipe_owner_pid;
> +           }
> +       }

Ah, that makes sense. Even the `else` clause should be guarded by the
`nat_pipe_owner_pid != nat_pipe_owner_pid_dupped` guard.

About the patch itself: I am not super-hyped about the proliferation of
handles, but honestly, I do not see any good alternative either.

Thank you!
Johannes

> 
> > +
> > +      /* Retrieve console mode */
> > +      DWORD cons_mode = ENABLE_VIRTUAL_TERMINAL_INPUT;
> > +      if (h_pcon_in_dupped && memchr (buf, '\010' /* Ctrl-H */, nlen))
> > +	{
> > +	  if (!nat_pipe_owner_self (nat_pipe_owner_pid_dupped))
> > +	    {
> > +	      DWORD resume_pid =
> > +		attach_console_temporarily (nat_pipe_owner_pid_dupped);
> > +	      GetConsoleMode (h_pcon_in_dupped, &cons_mode);
> > +	      resume_from_temporarily_attach (resume_pid);
> > +	    }
> > +	  else
> > +	    GetConsoleMode (h_pcon_in_dupped, &cons_mode);
> > +	}
> > +
> > +      len = nlen;
> > +      for (size_t i = 0, j = 0; i < len; i++)
> >  	{
> >  	  process_sig_state r = process_sigs (buf[i], get_ttyp (), this);
> > -	  if (r == done_with_debugger)
> > +	  if (r != done_with_debugger)
> >  	    {
> > -	      for (size_t j = i; j < nlen - 1; j++)
> > -		buf[j] = buf[j + 1];
> > -	      nlen--;
> > -	      i--;
> > +	      char c = buf[i];
> > +	      /* Workaround for pseudo console in Windows 11 */
> > +	      if (!(cons_mode & ENABLE_VIRTUAL_TERMINAL_INPUT))
> > +		/* Undesired backspace conversion in pseudo console does
> > +		   not happen if ENABLE_VIRTUAL_TERMINAL_INPUT is set. */
> > +		if (c == '\010') /* Ctrl-H */
> > +		  c = '\177';  /* Backspace */
> > +	      buf[j++] = c;
> >  	    }
> > +	  else
> > +	    nlen--;
> >  	}
> >  
> >        DWORD n;
> > @@ -3998,6 +4050,10 @@ fhandler_pty_slave::transfer_input (tty::xfer_dir dir, HANDLE from, tty *ttyp,
> >  	    if (r[i].EventType == KEY_EVENT && r[i].Event.KeyEvent.bKeyDown)
> >  	      {
> >  		DWORD ctrl_key_state = r[i].Event.KeyEvent.dwControlKeyState;
> > +		if (r[i].Event.KeyEvent.uChar.AsciiChar == '\010' /* Ctrl-H */
> > +		    && !(ctrl_key_state & ALT_PRESSED))
> > +		  /* Workaround for pseudo console in Windows 11 */
> > +		  r[i].Event.KeyEvent.uChar.AsciiChar = '\177'; /* Backspace */
> >  		if (r[i].Event.KeyEvent.uChar.AsciiChar)
> >  		  {
> >  		    if ((ctrl_key_state & ALT_PRESSED)
> > diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h
> > index 16f55b4f7..7ea04a26c 100644
> > --- a/winsup/cygwin/local_includes/fhandler.h
> > +++ b/winsup/cygwin/local_includes/fhandler.h
> > @@ -2564,6 +2564,8 @@ private:
> >    HANDLE thread_param_copied_event;
> >    HANDLE helper_goodbye;
> >    HANDLE helper_h_process;
> > +  HANDLE h_pcon_in_dupped;
> > +  DWORD nat_pipe_owner_pid_dupped;
> >  
> >  public:
> >    HANDLE get_echo_handle () const { return echo_r; }
> > -- 
> > 2.51.0
> > 
> 
> 
> -- 
> Takashi Yano <takashi.yano@nifty.ne.jp>
> 


More information about the Cygwin-patches mailing list