diff --git a/examples/borders.cpp b/examples/borders.cpp index 0fd88b7..523ea47 100644 --- a/examples/borders.cpp +++ b/examples/borders.cpp @@ -10,7 +10,7 @@ int main() { rawterm::enable_raw_mode(); rawterm::enter_alt_screen(); - rawterm::enable_signals(); + auto signals_thread = rawterm::enable_signals(); rawterm::Cursor::cursor_hide(); rawterm::Cursor cur; @@ -29,5 +29,6 @@ int main() { std::ignore = rawterm::wait_for_input(); rawterm::Cursor::cursor_show(); + signals_thread.join(); return 0; } diff --git a/examples/keys.cpp b/examples/keys.cpp index 042f676..e2304aa 100644 --- a/examples/keys.cpp +++ b/examples/keys.cpp @@ -11,7 +11,7 @@ int main() { rawterm::enable_raw_mode(); rawterm::enter_alt_screen(); - rawterm::enable_signals(); + auto signals_thread = rawterm::enable_signals(); rawterm::clear_screen(); @@ -42,5 +42,8 @@ int main() { // Optional explicit call to exit_alt_screen to return to standard screen. // This will happen even without calling this function explicitly rawterm::exit_alt_screen(); + + // Don't forget to join back to the main thread + signals_thread.join(); return 0; } diff --git a/rawterm/core.cpp b/rawterm/core.cpp index 4cdacc4..f1a34b1 100644 --- a/rawterm/core.cpp +++ b/rawterm/core.cpp @@ -1,13 +1,17 @@ #include "core.h" #include +#include #include +#include +#include #include "cursor.h" #include "exceptions.h" namespace rawterm { namespace detail { + [[nodiscard]] bool is_debug() { auto raw_env_var = std::getenv("RAWTERM_DEBUG"); if (raw_env_var == nullptr) { @@ -19,6 +23,7 @@ namespace rawterm { } } // namespace detail + rawterm::Mod Key::getMod() { if (mod.empty()) { return rawterm::Mod::None; @@ -97,44 +102,42 @@ namespace rawterm { std::cout << "\x1B[?1049l" << std::flush; } - bool is_signals_enabled = false; - void enable_signals() { + inline bool is_signals_enabled = false; + [[nodiscard]] std::thread enable_signals() { #if __linux__ is_signals_enabled = true; -#endif - } - void sigtstp_handler(std::function func) { - if (detail::sigtstp_called) { - func(); - detail::sigtstp_called = false; - } + std::thread thr([] { + // Backgrounding + std::signal(SIGTSTP, [](int) { + detail::sig_sent = Signal::SIG_TSTP; + rawterm::exit_alt_screen(); + std::raise(SIGSTOP); + }); + + // Foregrounding + std::signal(SIGCONT, [](int) { + detail::sig_sent = Signal::SIG_CONT; + rawterm::enter_alt_screen(); + rawterm::enable_raw_mode(); + }); + + // Window resizing + std::signal(SIGWINCH, [](int) { detail::sig_sent = Signal::SIG_WINCH; }); + }); + + return thr; +#endif } - void sigcont_handler(std::function func) { - if (detail::sigcont_called) { + void signal_handler(Signal sig, std::function func) { + if (detail::sig_sent == sig) { func(); - detail::sigcont_called = false; + detail::sig_sent = Signal::NONE; } } [[nodiscard]] const std::optional process_keypress() { -#if __linux__ - // Backgrounding - std::signal(SIGTSTP, [](int) { - detail::sigtstp_called = true; - rawterm::exit_alt_screen(); - std::raise(SIGSTOP); - }); - - // Foregrounding - std::signal(SIGCONT, [](int) { - detail::sigcont_called = true; - rawterm::enter_alt_screen(); - rawterm::enable_raw_mode(); - }); -#endif - std::string characters = std::string(32, '\0'); int pollResult = poll(&detail::fd, 1, 0); @@ -148,9 +151,14 @@ namespace rawterm { } else if (pollResult == 0) { return {}; + // interrupted system call -- SIGWINCH interrupting poll() + } else if (errno == 4) { + return {}; + // Error } else { - throw rawterm::KeypressError("An unknown error occured"); + throw rawterm::KeypressError( + std::format("A poll error occured: {} - {}", errno, std::strerror(errno))); } std::stringstream ss; diff --git a/rawterm/core.h b/rawterm/core.h index aa75631..be49a03 100644 --- a/rawterm/core.h +++ b/rawterm/core.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -32,13 +33,13 @@ #include "screen.h" namespace rawterm { + enum class Signal { SIG_TSTP, SIG_CONT, SIG_WINCH, NONE }; namespace detail { #if __linux__ inline termios orig; #endif - inline bool sigtstp_called = false; - inline bool sigcont_called = false; + inline Signal sig_sent = Signal::NONE; // Used for polling in process_keypress() inline pollfd fd {STDIN_FILENO, POLLIN, POLLOUT}; @@ -105,9 +106,8 @@ namespace rawterm { [[maybe_unused]] int enable_raw_mode(); void enter_alt_screen(); void exit_alt_screen(); - void enable_signals(); - void sigtstp_handler(std::function); - void sigcont_handler(std::function); + [[nodiscard]] std::thread enable_signals(); + void signal_handler(Signal, std::function); [[nodiscard]] const std::optional process_keypress(); const rawterm::Key wait_for_input(); [[nodiscard]] const rawterm::Pos get_term_size();