diff --git a/src/env_dispatch.cpp b/src/env_dispatch.cpp index 9c3882ce0..0e1f8c4a8 100644 --- a/src/env_dispatch.cpp +++ b/src/env_dispatch.cpp @@ -317,6 +317,7 @@ static std::unique_ptr create_dispatch_table() { var_dispatch_table->add(L"fish_term256", handle_fish_term_change); var_dispatch_table->add(L"fish_term24bit", handle_fish_term_change); var_dispatch_table->add(L"fish_escape_delay_ms", update_wait_on_escape_ms); + var_dispatch_table->add(L"fish_sequence_key_delay_ms", update_wait_on_sequence_key_ms); var_dispatch_table->add(L"fish_emoji_width", guess_emoji_width); var_dispatch_table->add(L"fish_ambiguous_width", handle_change_ambiguous_width); var_dispatch_table->add(L"LINES", handle_term_size_change); diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index 1731003ec..bb7afeaaf 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -237,7 +237,7 @@ static void process_input(bool continuous_mode, bool verbose) { if (reader_test_and_clear_interrupted()) { evt = char_event_t{shell_modes.c_cc[VINTR]}; } else { - evt = queue.readch_timed(); + evt = queue.readch_timed_esc(); } if (!evt || !evt->is_char()) { output_bind_command(bind_chars); diff --git a/src/input.cpp b/src/input.cpp index 25c3f20d1..4831953ff 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -465,19 +465,24 @@ class event_queue_peeker_t { /// Check if the next event is the given character. This advances the index on success only. /// If \p timed is set, then return false if this (or any other) character had a timeout. - bool next_is_char(wchar_t c, bool timed = false) { + bool next_is_char(wchar_t c, bool escaped = false) { assert(idx_ <= peeked_.size() && "Index must not be larger than dequeued event count"); // See if we had a timeout already. - if (timed && had_timeout_) { + if (escaped && had_timeout_) { return false; } // Grab a new event if we have exhausted what we have already peeked. // Use either readch or readch_timed, per our param. if (idx_ == peeked_.size()) { char_event_t newevt{L'\0'}; - if (!timed) { - newevt = event_queue_.readch(); - } else if (auto mevt = event_queue_.readch_timed()) { + if (!escaped) { + if (auto mevt = event_queue_.readch_timed_sequence_key()) { + newevt = mevt.acquire(); + } else { + had_timeout_ = true; + return false; + } + } else if (auto mevt = event_queue_.readch_timed_esc()) { newevt = mevt.acquire(); } else { had_timeout_ = true; @@ -542,7 +547,7 @@ static bool have_mouse_tracking_csi(event_queue_peeker_t *peeker) { // Maximum length of any CSI is NPAR (which is nominally 16), although this does not account for // user input intermixed with pseudo input generated by the tty emulator. // Check for the CSI first. - if (!peeker->next_is_char(L'\x1b') || !peeker->next_is_char(L'[', true /* timed */)) { + if (!peeker->next_is_char(L'\x1b') || !peeker->next_is_char(L'[', true /* escaped */)) { return false; } @@ -593,8 +598,8 @@ static bool try_peek_sequence(event_queue_peeker_t *peeker, const wcstring &str) for (wchar_t c : str) { // If we just read an escape, we need to add a timeout for the next char, // to distinguish between the actual escape key and an "alt"-modifier. - bool timed = prev == L'\x1B'; - if (!peeker->next_is_char(c, timed)) { + bool escaped = prev == L'\x1B'; + if (!peeker->next_is_char(c, escaped)) { return false; } prev = c; diff --git a/src/input_common.cpp b/src/input_common.cpp index d2e6e78c0..daa1e7c2a 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -34,6 +34,9 @@ #define WAIT_ON_ESCAPE_DEFAULT 30 static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; +#define WAIT_ON_SEQUENCE_KEY_INFINITE (-1) +static int wait_on_sequence_key_ms = WAIT_ON_SEQUENCE_KEY_INFINITE; + input_event_queue_t::input_event_queue_t(int in) : in_(in) {} /// Internal function used by readch to read one byte. @@ -138,6 +141,26 @@ void update_wait_on_escape_ms(const environment_t& vars) { } } +// Update the wait_on_sequence_key_ms value in response to the fish_sequence_key_delay_ms user variable being +// set. +void update_wait_on_sequence_key_ms(const environment_t& vars) { + auto sequence_key_time_ms = vars.get(L"fish_sequence_key_delay_ms"); + if (sequence_key_time_ms.missing_or_empty()) { + wait_on_sequence_key_ms = WAIT_ON_SEQUENCE_KEY_INFINITE; + return; + } + + long tmp = fish_wcstol(sequence_key_time_ms->as_string().c_str()); + if (errno || tmp < 10 || tmp >= 5000) { + std::fwprintf(stderr, + L"ignoring fish_sequence_key_delay_ms: value '%ls' " + L"is not an integer or is < 10 or >= 5000 ms\n", + sequence_key_time_ms->as_string().c_str()); + } else { + wait_on_sequence_key_ms = static_cast(tmp); + } +} + maybe_t input_event_queue_t::try_pop() { if (queue_.empty()) { return none(); @@ -216,7 +239,18 @@ char_event_t input_event_queue_t::readch() { } } -maybe_t input_event_queue_t::readch_timed() { +maybe_t input_event_queue_t::readch_timed_esc() { + return readch_timed(wait_on_escape_ms); +} + +maybe_t input_event_queue_t::readch_timed_sequence_key() { + if (wait_on_sequence_key_ms == WAIT_ON_SEQUENCE_KEY_INFINITE) { + return readch(); + } + return readch_timed(wait_on_sequence_key_ms); +} + +maybe_t input_event_queue_t::readch_timed(const int wait_time_ms) { if (auto evt = try_pop()) { return evt; } @@ -229,7 +263,7 @@ maybe_t input_event_queue_t::readch_timed() { // 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; + const uint64_t wait_nsec = wait_time_ms * nsec_per_msec; struct timespec timeout; timeout.tv_sec = (wait_nsec) / nsec_per_sec; timeout.tv_nsec = (wait_nsec) % nsec_per_sec; diff --git a/src/input_common.h b/src/input_common.h index bef8b1694..88c85cec8 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -188,6 +188,7 @@ class char_event_t { /// Adjust the escape timeout. class environment_t; void update_wait_on_escape_ms(const environment_t &vars); +void update_wait_on_sequence_key_ms(const environment_t& vars); /// A class which knows how to produce a stream of input events. /// This is a base class; you may subclass it for its override points. @@ -204,7 +205,10 @@ class input_event_queue_t { /// Like readch(), except it will wait at most WAIT_ON_ESCAPE milliseconds for a /// character to be available for reading. /// \return none on timeout, the event on success. - maybe_t readch_timed(); + maybe_t readch_timed(const int wait_time_ms); + + maybe_t readch_timed_esc(); + maybe_t readch_timed_sequence_key(); /// Enqueue a character or a readline function to the queue of unread characters that /// readch will return before actually reading from fd 0.