When the cursor position report ("CSI m;n R") is transferred from
cyg-pipe to nat-pipe, it is undesirably converted into Fn3 key by
pseudo console. This patch adds a workaround to prevent this
unintended conversion for cursor position report by enabling
ENABLE_VIRTUAL_TERMINAL_INPUT flag temporarily.

Addresses: https://cygwin.com/pipermail/cygwin/2026-June/259776.html
Reported-by: Koichi Murase <[email protected]>
Signed-off-by: Takashi Yano <[email protected]>
Reviewed-by:
---
 winsup/cygwin/fhandler/pty.cc      | 53 +++++++++++++++++++++++++++++-
 winsup/cygwin/local_includes/tty.h |  1 +
 2 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index e60e30230..e0fc67ae1 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -2445,7 +2445,6 @@ fhandler_pty_master::write (const void *ptr, size_t len)
              ixput = 0;
              state = 0;
              wp_tid = 0;
-             get_ttyp ()->req_xfer_input = false;
              if (!get_ttyp ()->pcon_start && !get_ttyp ()->pcon_start_csi_c)
                break;
            }
@@ -2460,6 +2459,20 @@ fhandler_pty_master::write (const void *ptr, size_t len)
              && pp && pp->pgid == get_ttyp ()->getpgid ()
              && get_ttyp ()->pty_input_state_eq (tty::to_cyg))
            {
+             if (!get_ttyp ()->req_xfer_input)
+               {
+                 HANDLE pcon_handle_ready_event =
+                   get_ttyp ()->pcon_handle_ready_event;
+                 get_handle_from_process (get_ttyp ()->nat_pipe_owner_pid,
+                                          pcon_handle_ready_event);
+                 if (pcon_handle_ready_event)
+                   {
+                     cygwait (pcon_handle_ready_event, INFINITE);
+                     ResetEvent (pcon_handle_ready_event);
+                     CloseHandle (pcon_handle_ready_event);
+                   }
+               }
+
              /* This accept_input() call is needed in order to transfer input
                 which is not accepted yet to non-cygwin pipe. */
              WaitForSingleObject (input_mutex, mutex_timeout);
@@ -2473,6 +2486,7 @@ fhandler_pty_master::write (const void *ptr, size_t len)
              release_attach_mutex ();
              ReleaseMutex (input_mutex);
            }
+         get_ttyp ()->req_xfer_input = false;
          get_ttyp ()->pcon_start_pid = 0;
        }
       if (len == 0)
@@ -3767,6 +3781,8 @@ fhandler_pty_slave::setup_pseudoconsole ()
       si.StartupInfo.hStdOutput = NULL;
       si.StartupInfo.hStdError = NULL;
 
+      get_ttyp ()->pcon_handle_ready_event =
+       CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
       get_ttyp ()->pcon_activated = true;
       get_ttyp ()->pcon_start = true;
       get_ttyp ()->pcon_start_pid = myself->pid;
@@ -3853,6 +3869,7 @@ skip_create:
       /* Discard the pseudo console handler container here.
         Reconstruct it temporary when it is needed. */
       HeapFree (GetProcessHeap (), 0, hp);
+      SetEvent (get_ttyp ()->pcon_handle_ready_event);
     }
 
   acquire_attach_mutex (mutex_timeout);
@@ -4060,6 +4077,11 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp, 
DWORD force_switch_to)
          ttyp->pcon_start = false;
          ttyp->pcon_start_pid = 0;
        }
+      if (ttyp->pcon_handle_ready_event)
+       {
+         CloseHandle (ttyp->pcon_handle_ready_event);
+         ttyp->pcon_handle_ready_event = NULL;
+       }
     }
   else
     { /* Just detach from the pseudo console if I am not owner. */
@@ -4308,6 +4330,26 @@ fhandler_pty_slave::transfer_input (tty::xfer_dir dir, 
HANDLE from, tty *ttyp,
 
   UINT cp_from = 0, cp_to = 0;
 
+  HANDLE h_pcon_in = NULL;
+  DWORD con_mode = 0;
+  if (ttyp->pcon_activated && dir == tty::to_nat)
+    {
+      /* Escape sequences such as the cursor position report ("CSI m;n R")
+        are undesirably converted into an Fn3 key by pseudo console.
+        To privent this unintended conversion, temporarily enable
+        ENABLE_VIRTUAL_TERMINAL_INPUT flag. */
+      h_pcon_in =
+       get_handle_from_process (ttyp->nat_pipe_owner_pid, ttyp->h_pcon_in);
+      if (h_pcon_in)
+       {
+         DWORD target_pid = ttyp->nat_pipe_owner_pid;
+         DWORD resume_pid = attach_console_temporarily (target_pid);
+         GetConsoleMode (h_pcon_in, &con_mode);
+         SetConsoleMode (h_pcon_in, con_mode | ENABLE_VIRTUAL_TERMINAL_INPUT);
+         resume_from_temporarily_attach (resume_pid);
+       }
+    }
+
   if (dir == tty::to_nat)
     {
       cp_from = ttyp->term_code_page;
@@ -4422,6 +4464,15 @@ fhandler_pty_slave::transfer_input (tty::xfer_dir dir, 
HANDLE from, tty *ttyp,
     }
   CloseHandle (to);
 
+  if (h_pcon_in)
+    {
+      DWORD target_pid = ttyp->nat_pipe_owner_pid;
+      DWORD resume_pid = attach_console_temporarily (target_pid);
+      SetConsoleMode (h_pcon_in, con_mode);
+      resume_from_temporarily_attach (resume_pid);
+      CloseHandle (h_pcon_in);
+    }
+
   ttyp->pty_input_state = dir;
   /* Fix input_available_event which indicates availability in cyg pipe. */
   if (dir == tty::to_nat) /* all data is transfered to nat pipe,
diff --git a/winsup/cygwin/local_includes/tty.h 
b/winsup/cygwin/local_includes/tty.h
index 4fbebd820..0adad03e6 100644
--- a/winsup/cygwin/local_includes/tty.h
+++ b/winsup/cygwin/local_includes/tty.h
@@ -125,6 +125,7 @@ private:
   bool pcon_start_csi_c;
   bool switch_to_nat_pipe;
   DWORD nat_pipe_owner_pid;
+  HANDLE pcon_handle_ready_event;
   UINT term_code_page;
   ULONGLONG fwd_last_time;
   bool fwd_not_empty;
-- 
2.51.0

Reply via email to