[newlib-cygwin] Cygwin: console: Fix master thread for OpenConsole.exe

Takashi Yano via Cygwin-cvs <[email protected]> Sun, 19 Apr 2026 01:39:13 +0000 (GMT)
Newsgroups gmane.os.cygwin.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3Dea25ba7baf4=
18720dab65e794baf7701c4f72821

commit ea25ba7baf418720dab65e794baf7701c4f72821
Author: Takashi Yano <[email protected]>
Date:   Wed Mar 25 20:20:01 2026 +0900

    Cygwin: console: Fix master thread for OpenConsole.exe
   =20
    If the console is originating from a pseudo console, current master
    thread code does not work as expected if ENABLE_VIRTUAL_TERMINAL_INPUT
    flag is set, particularly when OpenConsole.exe is used. This is because
    the pseudo console does not preserve all the key event as is.
    All bKeyDown =3D=3D 0 events will be omitted from the input record writ=
ten
    by WriteConsoleInput() and events regarding pressing shift/control/alt
    keys will be dropped as well.
   =20
    This patch adds strip_inrec() function to remove all the key events
    of bKeyDown =3D=3D 0 or UnicodeChar =3D=3D 0 before comparing/writing i=
nput
    record. This function is called only when the console is originating
    from a pseudo console and ENABLE_VIRTUAL_TERMINAL_INPUT flag is set.
   =20
    Signed-off-by: Takashi Yano <[email protected]>
    Reviewed-by: Johannes Schindelin <[email protected]>

Diff:
---
 winsup/cygwin/fhandler/console.cc | 42 +++++++++++++++++++++++++++++++++++=
++++
 1 file changed, 42 insertions(+)

diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/con=
sole.cc
index 39c4f6ff0..c76347f6f 100644
--- a/winsup/cygwin/fhandler/console.cc
+++ b/winsup/cygwin/fhandler/console.cc
@@ -305,6 +305,23 @@ cons_master_thread (VOID *arg)
   return 0;
 }
=20
+static inline DWORD
+strip_inrec (INPUT_RECORD *r, DWORD n)
+{
+  /* Pseudo console with OpenConsole.exe removes the events
+     whose bKeyDown is 0 as well as ones whose charcode is 0. */
+  DWORD j =3D 0;
+  for (DWORD i =3D 0; i < n; i++)
+    {
+      if (r[i].EventType !=3D KEY_EVENT)
+	r[j++] =3D r[i];
+      else if (r[i].Event.KeyEvent.bKeyDown
+	       && r[i].Event.KeyEvent.uChar.UnicodeChar)
+	r[j++] =3D r[i];
+    }
+  return j;
+}
+
 /* Compare two INPUT_RECORD sequences */
 static inline bool
 inrec_eq (const INPUT_RECORD *a, const INPUT_RECORD *b, DWORD n)
@@ -417,6 +434,8 @@ fhandler_console::cons_master_thread (handle_set_t *p, =
tty *ttyp)
   while (con.owner =3D=3D GetCurrentProcessId ())
     {
       DWORD total_read, n, i;
+      DWORD mode;
+      bool need_strip =3D false;
=20
       if (con.disable_master_thread)
 	{
@@ -472,6 +491,23 @@ fhandler_console::cons_master_thread (handle_set_t *p,=
 tty *ttyp)
 	{
 	case WAIT_OBJECT_0:
 	  acquire_attach_mutex (mutex_timeout);
+	  /* When ENABLE_VIRTUAL_TERMINAL_INPUT is set, the key events
+	     are not preserved as is. Particularly, when OpenConsole.exe
+	     is used, the following key events are simplified so much.
+	     Writing the events:
+		 press shift key -> press 'A' key ->
+		 lease 'A' key -> release shift key
+	     results in only one key event with:
+		 uChar.UnicodeChar =3D 0x41,
+		 wVirtualKeyCode =3D 0,
+		 wVirtualScanCode =3D 0,
+		 dwControlKeyState =3D 0,
+		 bKeyDown =3D 1
+	     Therefore, we need fixup the input record by calling
+	     strip_inrec() if the ENABLE_VIRTUAL_TERMINAL_INPUT flag is
+	     set, so that the input records are compared as expected. */
+	  GetConsoleMode (p->input_handle, &mode);
+	  need_strip =3D inside_pcon && (mode & ENABLE_VIRTUAL_TERMINAL_INPUT);
 	  total_read =3D 0;
 	  while (cygwait (p->input_handle, (DWORD) 0) =3D=3D WAIT_OBJECT_0
 		 && total_read < inrec_size)
@@ -483,6 +519,8 @@ fhandler_console::cons_master_thread (handle_set_t *p, =
tty *ttyp)
 	      total_read +=3D len;
 	    }
 	  release_attach_mutex ();
+	  if (need_strip)
+	    total_read =3D strip_inrec (input_rec, total_read);
 	  break;
 	case WAIT_TIMEOUT:
 	  con.num_processed =3D 0;
@@ -607,6 +645,8 @@ remove_record:
 	      acquire_attach_mutex (mutex_timeout);
 	      PeekConsoleInputW (p->input_handle, input_tmp, inrec_size, &n);
 	      release_attach_mutex ();
+	      if (need_strip)
+		n =3D strip_inrec (input_tmp, n);
 	      if (n < min (total_read, inrec_size))
 		break; /* Someone has read input without acquiring
 			  input_mutex. ConEmu cygwin-connector? */
@@ -625,6 +665,8 @@ remove_record:
 		  n +=3D len;
 		}
 	      release_attach_mutex ();
+	      if (need_strip)
+		n =3D strip_inrec (input_tmp, n);
 	      bool fixed =3D false;
 	      for (DWORD ofs =3D n - total_read; ofs > 0; ofs--)
 		{