From af96820e072d18c49ac03e80c6f3466d568dc77d Mon Sep 17 00:00:00 2001 From: Michael Gran Date: Tue, 20 Jun 2023 16:04:59 -0700 Subject: [PATCH] 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