From 43e5004b6e98cfca48a0452498e63ea31b08a643 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 31 Jan 2022 21:02:25 -0800 Subject: [PATCH] readch_timed to block signals readch_timed is called after reading the escape character \x1b. The escape char may be a standalone key press or part of an escape sequence; fish waits for a little bit (per the fish_escape_delay_ms variable) to see if something else arrives, before treating it as standalone escape-key press. It may happen that a signal is delivered while fish waits. Prior to this change we would treat this signal as a "nothing was read" event, causing escape to be wrongly treated as standalone. Avoid this by using pselect() with a full signal mask, to ensure this call completes. --- src/input_common.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/input_common.cpp b/src/input_common.cpp index d741b96e7..3120363fc 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -2,6 +2,7 @@ #include "config.h" #include +#include #include #include @@ -10,6 +11,7 @@ #endif #include #include +#include #include #include @@ -223,9 +225,27 @@ maybe_t input_event_queue_t::readch_timed() { if (auto evt = try_pop()) { return evt; } - const uint64_t usec_per_msec = 1000; - uint64_t timeout_usec = static_cast(wait_on_escape_ms) * usec_per_msec; - if (fd_readable_set_t::is_fd_readable(in_, timeout_usec)) { + // We are not prepared to handle a signal immediately; we only want to know if we get input on + // our fd before the timeout. Use pselect to block all signals; we will handle signals + // before the next call to getch(). + sigset_t sigs; + sigfillset(&sigs); + + // pselect expects timeouts in nanoseconds. + const uint64_t nsec_per_msec = 1000 * 1000; + const uint64_t nsec_per_sec = nsec_per_msec * 1000; + const uint64_t wait_nsec = wait_on_escape_ms * nsec_per_msec; + struct timespec timeout; + timeout.tv_sec = (wait_nsec) / nsec_per_sec; + timeout.tv_nsec = (wait_nsec) % nsec_per_sec; + + // We have one fd of interest. + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(in_, &fdset); + + int res = pselect(in_ + 1, &fdset, nullptr, nullptr, &timeout, &sigs); + if (res > 0) { return readch(); } return none();