mike121 pushed a commit to branch main
in repository guile.

commit af96820e072d18c49ac03e80c6f3466d568dc77d
Author: Michael Gran <spk...@yahoo.com>
AuthorDate: Tue Jun 20 16:04:59 2023 -0700

    Windows 11: for fport input from the console, ignore terminal returns
    
    There is an apparent bug in Windows 11 (not Windows 10) where,
    when reading from an fd backed by the Console, a single
    return character will always be available.
    
    * libguile/posix-w32.c (console_has_return_keyevent_w32): new procedure
    * libguile/posix-w32.h: declare console_has_return_keyevent_w32
    * libguile/fports.c [__MINGW32__](fport_input_waiting): ignore return 
keyevent
---
 libguile/fports.c    | 15 ++++++++++++++-
 libguile/posix-w32.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 libguile/posix-w32.h |  1 +
 3 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/libguile/fports.c b/libguile/fports.c
index e355c3358..51c5b3736 100644
--- a/libguile/fports.c
+++ b/libguile/fports.c
@@ -59,6 +59,9 @@
 #include "pairs.h"
 #include "ports-internal.h"
 #include "posix.h"
+#ifdef __MINGW32__
+# include "posix-w32.h"
+#endif
 #include "read.h"
 #include "strings.h"
 #include "symbols.h"
@@ -483,7 +486,17 @@ fport_input_waiting (SCM port)
   if (poll (&pollfd, 1, 0) < 0)
     scm_syserror ("fport_input_waiting");
 
-  return pollfd.revents & POLLIN ? 1 : 0;
+  if ((pollfd.revents & POLLIN) == 0)
+    return 0;
+
+#ifdef __MINGW32__
+  /* Work around Windows 11 bug where there's always a return character
+   * in the console input queue. */
+  if (console_has_return_keyevent_w32 (fdes))
+    return 0;
+#endif
+
+  return 1;
 }
 
 
diff --git a/libguile/posix-w32.c b/libguile/posix-w32.c
index 5f6c0b759..239fd1156 100644
--- a/libguile/posix-w32.c
+++ b/libguile/posix-w32.c
@@ -289,6 +289,54 @@ dlerror_w32 ()
     snprintf (dlerror_str, DLERROR_LEN, "error %ld: %s", (long) dw, msg_buf);
   return dlerror_str;
 }
+
+/* Check for the Windows 11 bug where there's a return character in the
+ * console input queue despite draining the input. */
+int
+console_has_return_keyevent_w32 (int fdes)
+{
+  HANDLE h;
+  DWORD mode;
+
+  h = (HANDLE) _get_osfhandle (fdes);
+  if (h == -1)
+    return 0;
+  if (GetConsoleMode (h, &mode) == 0)
+    return 0;
+
+  // Rarely need more than 1 INPUT_RECORD for this test, but just in
+  // case there is a mouse event in the queue.
+#define NBUFFER 8
+  INPUT_RECORD irbuffer[NBUFFER];
+  BOOL bRet;
+  DWORD avail;
+  int i;
+  int n_chars = 0;
+  int n_returns = 0;
+
+  while (1)
+    {
+      bRet = PeekConsoleInput (h, irbuffer, NBUFFER, &avail);
+      if (!bRet || avail == 0)
+        break;
+
+      for (i = 0; i < avail; i++)
+        if (irbuffer[i].EventType == KEY_EVENT)
+          {
+            n_chars ++;
+            if (irbuffer[i].Event.KeyEvent.uChar.AsciiChar == 13)
+              n_returns ++;
+          }
+      if (avail < NBUFFER)
+        break;
+    }
+
+  if (n_chars == 1 && n_returns == 1)
+    return 1;
+  return 0;
+#undef NBUFFER
+}
+
 int
 getpagesize_w32 (void)
 {
diff --git a/libguile/posix-w32.h b/libguile/posix-w32.h
index a3c27f6f9..5478f91f7 100644
--- a/libguile/posix-w32.h
+++ b/libguile/posix-w32.h
@@ -74,6 +74,7 @@ SCM_INTERNAL void *dlopen_w32 (const char *name, int flags);
 SCM_INTERNAL void *dlsym_w32 (void *handle, const char *name);
 SCM_INTERNAL int dlclose_w32 (void *handle);
 SCM_INTERNAL char *dlerror_w32 (void);
+SCM_INTERNAL int console_has_return_keyevent_w32 (int fdes);
 SCM_INTERNAL int getpagesize_w32 (void);
 
 #define HAVE_UNAME 1

Reply via email to