diff --git a/custom-prompt/focus_utils.cc b/custom-prompt/focus_utils.cc index 30058ea..49f80d0 100644 --- a/custom-prompt/focus_utils.cc +++ b/custom-prompt/focus_utils.cc @@ -33,50 +33,59 @@ bool terminal_has_focus(void) #include #include -#define BUFSIZE 255 - /** * Temporarily modify standard input to allow non-blocking reads. */ class NonBlockingStandardInputGuard { private: - bool error_occurred; + bool m_error_occurred; termios prev_termios, curr_termios; public: NonBlockingStandardInputGuard(void); + bool error_occurred(void) const; ~NonBlockingStandardInputGuard(); }; /** * Enable non-canonical mode. */ -NonBlockingStandardInputGuard::NonBlockingStandardInputGuard(void) : error_occurred(false) +NonBlockingStandardInputGuard::NonBlockingStandardInputGuard(void) : m_error_occurred(false) { if (tcgetattr(STDIN_FILENO, &this->prev_termios) == -1) { - this->error_occurred = true; + this->m_error_occurred = true; return; } this->curr_termios = this->prev_termios; this->curr_termios.c_lflag &= ~(ECHO | ICANON); - // Block until sufficiently many bytes are available from standard input. - this->curr_termios.c_cc[VMIN] = BUFSIZE; - // But not for too long. + // Set a timeout on standard input reads. + this->curr_termios.c_cc[VMIN] = 0; this->curr_termios.c_cc[VTIME] = 1; if (tcsetattr(STDIN_FILENO, TCSANOW, &this->curr_termios) == -1) { - this->error_occurred = true; + this->m_error_occurred = true; } } +/** + * Check whether an error occurred while trying to get or set terminal + * attributes. + * + * @return Error indicator. + */ +bool NonBlockingStandardInputGuard::error_occurred(void) const +{ + return this->m_error_occurred; +} + /** * Disable non-canonical mode. */ NonBlockingStandardInputGuard::~NonBlockingStandardInputGuard() { - if (!this->error_occurred) + if (!this->m_error_occurred) { tcsetattr(STDIN_FILENO, TCSANOW, &this->prev_termios); } @@ -84,14 +93,42 @@ NonBlockingStandardInputGuard::~NonBlockingStandardInputGuard() bool terminal_has_focus(void) { - char buf[BUFSIZE]; + char buf[1024]; ssize_t count; { - NonBlockingStandardInputGuard _; - // Enable and immediately disable focus reporting. - std::clog << "\x1b\x5b?1004h\x1b\x5b?1004l"; + NonBlockingStandardInputGuard guard; + if (guard.error_occurred()) + { + return false; + } + + // Consume standard input so that anything entered previously is + // removed and it is ready to receive focus escape sequences. There is + // a small chance that the user types something after standard input is + // consumed but before the terminal sends the sequences. This rare + // failure is acceptable. + while ((count = read(STDIN_FILENO, buf, sizeof buf / sizeof *buf)) > 0) + { + LOG_DEBUG(logger, "Cleared standard input", { { "count", count } }); + } + if (count < 0) + { + return false; + } + + // Enable focus reporting. + std::clog << "\x1b\x5b?1004h"; count = read(STDIN_FILENO, buf, sizeof buf / sizeof *buf); + + // Disable focus reporting. If it is disabled immediately after + // enabling it instead of here, the terminal may never send any focus + // escape sequences. There is a small chance that more sequences get + // written to standard input before focus reporting gets disabled but + // after the previous sequences have been read. This rare failure is + // acceptable. + std::clog << "\x1b\x5b?1004l"; } + LOG_DEBUG(logger, "Read non-blocking standard input", { { "count", count } }); if (count <= 0) {