[newlib-cygwin] Cygwin: pty: Add workaround for handling of backspace when pcon enabled
Takashi Yano via Cygwin-cvs <[email protected]> Sun, 29 Mar 2026 00:43:50 +0000 (GMT)
| Newsgroups | gmane.os.cygwin.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3D9ac383f5c21= 39107570e69a051ab06e56414f91d commit 9ac383f5c2139107570e69a051ab06e56414f91d Author: Takashi Yano <[email protected]> Date: Sun Feb 22 23:48:23 2026 +0900 Cygwin: pty: Add workaround for handling of backspace when pcon enabled =20 In Windows 11, pseudo console has an undesired key conversion that the Ctrl-H is translated into Ctrl-Backspace (not Backspace). The reverse VT input path in conhost's `_DoControlCharacter()` maps the byte 0x08 to a Ctrl+Backspace key event (VK_BACK with LEFT_CTRL_PRESSED and character 0x7F). This was introduced in PR #3935 (Jan 2020) to make Ctrl+Backspace delete whole words. In September 2022, PR #13894 rewrote the forward path to properly implement DECBKM (Backarrow Key Mode), but the reverse path was never updated to match, breaking the roundtrip. =20 Due to this behaviour, inrec_eq() in cons_master_thread() fails to compare backspace/Ctrl-H events in the input record sequence. This patch is a workaround for the issue that replaces Ctrl-H with backspace (0x7f), which will be translated into Ctrl-H in pseudo console. =20 Signed-off-by: Takashi Yano <[email protected]> Reviewed-by: Johannes Schindelin <[email protected]> Diff: --- winsup/cygwin/fhandler/console.cc | 12 ++++- winsup/cygwin/fhandler/pty.cc | 78 +++++++++++++++++++++++++++++= ---- winsup/cygwin/local_includes/fhandler.h | 2 + 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/con= sole.cc index 2f46bbc6c..2a52ba575 100644 --- a/winsup/cygwin/fhandler/console.cc +++ b/winsup/cygwin/fhandler/console.cc @@ -318,6 +318,16 @@ inrec_eq (const INPUT_RECORD *a, const INPUT_RECORD *b= , DWORD n) written event. Therefore they are ignored. */ const KEY_EVENT_RECORD *ak =3D &a[i].Event.KeyEvent; const KEY_EVENT_RECORD *bk =3D &b[i].Event.KeyEvent; + WCHAR c1 =3D ak->uChar.UnicodeChar; + WCHAR c2 =3D bk->uChar.UnicodeChar; + if (inside_pcon) + { + /* Workaround for pseudo console in Windows 11 */ + if (c1 =3D=3D 8) /* Ctrl-H */ + c1 =3D 127; /* Backspace */ + if (c2 =3D=3D 8) /* Ctrl-H */ + c2 =3D 127; /* Backspace */ + } /* On Windows 11, conhost normalizes wRepeatCount from 0 to 1 on readback. Treat them as equivalent for comparison. */ WORD r1 =3D ak->wRepeatCount; @@ -327,7 +337,7 @@ inrec_eq (const INPUT_RECORD *a, const INPUT_RECORD *b,= DWORD n) if (r2 =3D=3D 0) r2 =3D 1; if (ak->bKeyDown !=3D bk->bKeyDown - || ak->uChar.UnicodeChar !=3D bk->uChar.UnicodeChar + || c1 !=3D c2 || r1 !=3D r2) return false; } diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc index fbc6152e5..257630345 100644 --- a/winsup/cygwin/fhandler/pty.cc +++ b/winsup/cygwin/fhandler/pty.cc @@ -1950,7 +1950,8 @@ fhandler_pty_master::fhandler_pty_master (int unit, d= ev_t via) master_thread (NULL), from_master_nat (NULL), to_master_nat (NULL), from_slave_nat (NULL), to_slave_nat (NULL), echo_r (NULL), echo_w (NUL= L), dwProcessId (0), to_master (NULL), from_master (NULL), - master_fwd_thread (NULL) + master_fwd_thread (NULL), h_pcon_in_dupped (NULL), + nat_pipe_owner_pid_dupped (0) { dev_referred_via =3D via; if (unit >=3D 0) @@ -2131,6 +2132,10 @@ fhandler_pty_master::close (int flag) termios_printf ("error closing from_master %p, %E", from_master); from_master =3D NULL; =20 + if (h_pcon_in_dupped) + ForceCloseHandle (h_pcon_in_dupped); + h_pcon_in_dupped =3D NULL; + return 0; } =20 @@ -2241,28 +2246,77 @@ 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 =3D (char *) ptr; + char *buf =3D tp.c_get (); size_t nlen =3D len; if (get_ttyp ()->term_code_page !=3D CP_UTF8) { static mbstate_t mbp; - buf =3D tp.c_get (); nlen =3D 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); =20 - for (size_t i =3D 0; i < nlen; i++) + if (get_ttyp ()->nat_pipe_owner_pid !=3D 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 =3D NULL; + nat_pipe_owner_pid_dupped =3D 0; + HANDLE pcon_owner =3D 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 =3D get_ttyp ()->nat_pipe_owner_pid; + CloseHandle (pcon_owner); + } + } + else + { + h_pcon_in_dupped =3D get_ttyp ()->h_pcon_in; + nat_pipe_owner_pid_dupped =3D get_ttyp ()->nat_pipe_owner_pid; + } + } + + /* Retrieve console mode */ + DWORD cons_mode =3D 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 =3D + 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 =3D nlen; + for (size_t i =3D 0, j =3D 0; i < len; i++) { process_sig_state r =3D process_sigs (buf[i], get_ttyp (), this); - if (r =3D=3D done_with_debugger) + if (r !=3D done_with_debugger) { - for (size_t j =3D i; j < nlen - 1; j++) - buf[j] =3D buf[j + 1]; - nlen--; - i--; + char c =3D 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 =3D=3D '\010') /* Ctrl-H */ + c =3D '\177'; /* Backspace */ + buf[j++] =3D c; } + else + nlen--; } =20 DWORD n; @@ -3145,6 +3199,8 @@ fhandler_pty_master::fixup_after_fork (HANDLE parent) from_slave_nat =3D arch->from_slave_nat; to_slave_nat =3D arch->to_slave_nat; #endif + h_pcon_in_dupped =3D NULL; + nat_pipe_owner_pid_dupped =3D 0; report_tty_counts (this, "inherited master", ""); } =20 @@ -3998,6 +4054,10 @@ fhandler_pty_slave::transfer_input (tty::xfer_dir di= r, HANDLE from, tty *ttyp, if (r[i].EventType =3D=3D KEY_EVENT && r[i].Event.KeyEvent.bKeyDown) { DWORD ctrl_key_state =3D r[i].Event.KeyEvent.dwControlKeyState; + if (r[i].Event.KeyEvent.uChar.AsciiChar =3D=3D '\010' /* Ctrl-H */ + && !(ctrl_key_state & ALT_PRESSED)) + /* Workaround for pseudo console in Windows 11 */ + r[i].Event.KeyEvent.uChar.AsciiChar =3D '\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; =20 public: HANDLE get_echo_handle () const { return echo_r; }