[newlib-cygwin/cygwin-3_6-branch] Cygwin: pty: Add workaround for handling of backspace when pcon enabled
Takashi Yano
tyan0@sourceware.org
Sun Mar 29 00:43:20 GMT 2026
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=5fd695db64bc5b9310b65bcf71c50a849c01aac0
commit 5fd695db64bc5b9310b65bcf71c50a849c01aac0
Author: Takashi Yano <takashi.yano@nifty.ne.jp>
Date: Sun Feb 22 23:48:23 2026 +0900
Cygwin: pty: Add workaround for handling of backspace when pcon enabled
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.
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.
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
Reviewed-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
(cherry picked from commit 9ac383f5c2139107570e69a051ab06e56414f91d)
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/console.cc
index 80d6e34fc..20c20de52 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 = &a[i].Event.KeyEvent;
const KEY_EVENT_RECORD *bk = &b[i].Event.KeyEvent;
+ WCHAR c1 = ak->uChar.UnicodeChar;
+ WCHAR c2 = bk->uChar.UnicodeChar;
+ if (inside_pcon)
+ {
+ /* Workaround for pseudo console in Windows 11 */
+ if (c1 == 8) /* Ctrl-H */
+ c1 = 127; /* Backspace */
+ if (c2 == 8) /* Ctrl-H */
+ c2 = 127; /* Backspace */
+ }
/* On Windows 11, conhost normalizes wRepeatCount from 0 to 1
on readback. Treat them as equivalent for comparison. */
WORD r1 = ak->wRepeatCount;
@@ -327,7 +337,7 @@ inrec_eq (const INPUT_RECORD *a, const INPUT_RECORD *b, DWORD n)
if (r2 == 0)
r2 = 1;
if (ak->bKeyDown != bk->bKeyDown
- || ak->uChar.UnicodeChar != bk->uChar.UnicodeChar
+ || c1 != c2
|| r1 != r2)
return false;
}
diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index 1f57b7fc0..2a4d412ae 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -1933,7 +1933,8 @@ fhandler_pty_master::fhandler_pty_master (int unit, dev_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 (NULL),
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 = via;
if (unit >= 0)
@@ -2114,6 +2115,10 @@ fhandler_pty_master::close (int flag)
termios_printf ("error closing from_master %p, %E", from_master);
from_master = NULL;
+ if (h_pcon_in_dupped)
+ ForceCloseHandle (h_pcon_in_dupped);
+ h_pcon_in_dupped = NULL;
+
return 0;
}
@@ -2224,28 +2229,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 = (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)
+ {
+ 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;
+ }
+ }
+
+ /* 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;
@@ -3148,6 +3202,8 @@ fhandler_pty_master::fixup_after_fork (HANDLE parent)
from_slave_nat = arch->from_slave_nat;
to_slave_nat = arch->to_slave_nat;
#endif
+ h_pcon_in_dupped = NULL;
+ nat_pipe_owner_pid_dupped = 0;
report_tty_counts (this, "inherited master", "");
}
@@ -4001,6 +4057,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 9fa73899c..a4feeec24 100644
--- a/winsup/cygwin/local_includes/fhandler.h
+++ b/winsup/cygwin/local_includes/fhandler.h
@@ -2560,6 +2560,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; }
More information about the Cygwin-cvs
mailing list