Hi Takashi,

On Thu, 18 Dec 2025, Takashi Yano wrote:

> In Windows 11, the command "rlwrap cmd" outputs garbaged screen.
> This is because rlwrap treats text between NLs as a line, while
> pseudo console sometimes omits NL before "CSIm;nH". This issue
> does not happen in Windows 10. This patch fixes the issue by
> adding CR NL before "CSIm:nH" into the output from the pseudo
> console if the OS is Windows 11.
> 
> Reviewed-by: Corinna Vinschen <[email protected]>
> Signed-off-by: Takashi Yano <[email protected]>
> ---
>  winsup/cygwin/fhandler/pty.cc         | 44 +++++++++++++++++++++++++++
>  winsup/cygwin/local_includes/wincap.h |  2 ++
>  winsup/cygwin/wincap.cc               | 11 +++++++
>  3 files changed, 57 insertions(+)
> 
> diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
> index 3b0b4f073..7acedc165 100644
> --- a/winsup/cygwin/fhandler/pty.cc
> +++ b/winsup/cygwin/fhandler/pty.cc
> @@ -2775,6 +2775,50 @@ fhandler_pty_master::pty_master_fwd_thread (const 
> master_fwd_thread_param_t *p)
>           else
>             state = 0;
>  
> +       /* Workaround for rlwrap in Win11. rlwrap treats text between
> +          NLs as a line, however, pseudo console in Win11 somtimes
> +          omits NL before "CSIm;nH". This does not happen in Win10. */
> +       if (wincap.has_pcon_omit_nl_before_cursor_move ())
> +         {
> +           state = 0;

The pattern of the first two `for()` loops in this function is to reset
both `state` and `start_at` (even if the `/* Remove OSC Ps ; ? BEL/ST */`
loop seems not to reset either, which might be a bug). Should `start_at`
be re-set to 0 here, too?

> +           for (DWORD i = 0; i < rlen; i++)
> +             if (state == 0 && outbuf[i] == '\033')
> +               {
> +                 start_at = i;
> +                 state++;
> +                 continue;
> +               }
> +             else if (state == 1 && outbuf[i] == '[')
> +               {
> +                 state++;
> +                 continue;
> +               }
> +             else if (state == 2
> +                      && (isdigit (outbuf[i]) || outbuf[i] == ';'))
> +               continue;
> +             else if (state == 2 && outbuf[i] == 'H')
> +               {
> +                 /* Add omitted CR NL before "CSIm;nH". However, when the
> +                    cusor is on the bottom-most line, adding NL might cause
> +                    unexpected scrolling. To avoid this, add "CSI H" before
> +                    CR NL. */
> +                 const char *ins = "\033[H\r\n";
> +                 const int ins_len = strlen (ins);
> +                 if (rlen + ins_len <= NT_MAX_PATH)

How can we avoid this problem when running out of buffer space?

Ciao,
Johannes

> +                   {
> +                     memmove (&outbuf[start_at + ins_len],
> +                              &outbuf[start_at], rlen - start_at);
> +                     memcpy (&outbuf[start_at], ins, ins_len);
> +                     rlen += ins_len;
> +                     i += ins_len;
> +                   }
> +                 state = 0;
> +                 continue;
> +               }
> +             else
> +               state = 0;
> +         }
> +
>         if (p->ttyp->term_code_page != CP_UTF8)
>           {
>             size_t nlen = NT_MAX_PATH;
> diff --git a/winsup/cygwin/local_includes/wincap.h 
> b/winsup/cygwin/local_includes/wincap.h
> index 2416eee1d..0496d807e 100644
> --- a/winsup/cygwin/local_includes/wincap.h
> +++ b/winsup/cygwin/local_includes/wincap.h
> @@ -34,6 +34,7 @@ struct wincaps
>      unsigned has_con_broken_tabs                             : 1;
>      unsigned has_user_shstk                                  : 1;
>      unsigned has_alloc_console_with_options                  : 1;
> +    unsigned has_pcon_omit_nl_before_cursor_move             : 1;
>    };
>  };
>  
> @@ -92,6 +93,7 @@ public:
>    bool       IMPLEMENT (has_con_broken_tabs)
>    bool       IMPLEMENT (has_user_shstk)
>    bool       IMPLEMENT (has_alloc_console_with_options)
> +  bool       IMPLEMENT (has_pcon_omit_nl_before_cursor_move)
>  
>    void disable_case_sensitive_dirs ()
>    {
> diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc
> index 91caefe9b..f94b9f60b 100644
> --- a/winsup/cygwin/wincap.cc
> +++ b/winsup/cygwin/wincap.cc
> @@ -33,6 +33,7 @@ static const wincaps wincap_8_1 = {
>      has_con_broken_tabs:false,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -56,6 +57,7 @@ static const wincaps  wincap_10_1507 = {
>      has_con_broken_tabs:false,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -79,6 +81,7 @@ static const wincaps  wincap_10_1607 = {
>      has_con_broken_tabs:false,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -102,6 +105,7 @@ static const wincaps wincap_10_1703 = {
>      has_con_broken_tabs:true,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -125,6 +129,7 @@ static const wincaps wincap_10_1709 = {
>      has_con_broken_tabs:true,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -148,6 +153,7 @@ static const wincaps wincap_10_1803 = {
>      has_con_broken_tabs:true,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -171,6 +177,7 @@ static const wincaps wincap_10_1809 = {
>      has_con_broken_tabs:true,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -194,6 +201,7 @@ static const wincaps wincap_10_1903 = {
>      has_con_broken_tabs:true,
>      has_user_shstk:false,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -217,6 +225,7 @@ static const wincaps wincap_10_2004 = {
>      has_con_broken_tabs:true,
>      has_user_shstk:true,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:false,
>    },
>  };
>  
> @@ -240,6 +249,7 @@ static const wincaps wincap_11 = {
>      has_con_broken_tabs:false,
>      has_user_shstk:true,
>      has_alloc_console_with_options:false,
> +    has_pcon_omit_nl_before_cursor_move:true,
>    },
>  };
>  
> @@ -263,6 +273,7 @@ static const wincaps wincap_11_24h2 = {
>      has_con_broken_tabs:false,
>      has_user_shstk:true,
>      has_alloc_console_with_options:true,
> +    has_pcon_omit_nl_before_cursor_move:true,
>    },
>  };
>  
> -- 
> 2.51.0
> 
> 

Reply via email to