From 93922f102fae1c2f47e06c4206aae46fbba16af1 Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Sat, 26 Jun 2021 00:42:08 +0200 Subject: [PATCH] Do not throw SIGINT after catching SIGINT signal (#128) This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/117 --- CMakeLists.txt | 1 + src/ftxui/component/screen_interactive.cpp | 24 ++++----- .../component/screen_interactive_test.cpp | 52 +++++++++++++++++++ 3 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 src/ftxui/component/screen_interactive_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3034051f..b9232848 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,6 +226,7 @@ if (FTXUI_BUILD_TESTS AND ${CMAKE_VERSION} VERSION_GREATER "3.11.4") src/ftxui/component/input_test.cpp src/ftxui/component/radiobox_test.cpp src/ftxui/component/receiver_test.cpp + src/ftxui/component/screen_interactive_test.cpp src/ftxui/component/terminal_input_parser_test.cpp src/ftxui/component/toggle_test.cpp src/ftxui/dom/gauge_test.cpp diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index 67085ca0..839bb98d 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -1,7 +1,8 @@ #include // for fileno, stdin #include // for copy, max, min -#include // for signal, SIGINT, SIGWINCH -#include // for exit, NULL +#include // for signal, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGWINCH +#include // for NULL +#include // for initializer_list #include // for cout, ostream, basic_ostream, operator<<, endl, flush #include // for stack #include // for thread @@ -12,7 +13,7 @@ #include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/event.hpp" // for Event #include "ftxui/component/mouse.hpp" // for Mouse -#include "ftxui/component/receiver.hpp" // for ReceiverImpl, SenderImpl, MakeReceiver +#include "ftxui/component/receiver.hpp" // for ReceiverImpl, MakeReceiver, Sender, SenderImpl, Receiver #include "ftxui/component/screen_interactive.hpp" #include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser #include "ftxui/dom/node.hpp" // for Node, Render @@ -31,8 +32,8 @@ #endif #else #include // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set -#include // for tcsetattr, tcgetattr, cc_t -#include // for STDIN_FILENO, read +#include // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME +#include // for STDIN_FILENO, read #endif // Quick exit is missing in standard CLang headers @@ -201,12 +202,11 @@ const std::string DeviceStatusReport(DSRMode ps) { using SignalHandler = void(int); std::stack> on_exit_functions; void OnExit(int signal) { + (void)signal; while (!on_exit_functions.empty()) { on_exit_functions.top()(); on_exit_functions.pop(); } - if (signal) - std::raise(signal); } auto install_signal_handler = [](int sig, SignalHandler handler) { @@ -279,12 +279,12 @@ CapturedMouse ScreenInteractive::CaptureMouse() { void ScreenInteractive::Loop(Component component) { on_exit_functions.push([this] { ExitLoopClosure()(); }); - // Install a SIGINT handler and restore the old handler on exit. - auto old_sigint_handler = std::signal(SIGINT, OnExit); - on_exit_functions.push( - [old_sigint_handler]() { std::signal(SIGINT, old_sigint_handler); }); + // Install signal handlers to restore the terminal state on exit. The default + // signal handlers are restored on exit. + for (int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE}) + install_signal_handler(signal, OnExit); - // Save the old terminal configuration and restore it on exit. + // Save the old terminal configuration and restore it on exit. #if defined(_WIN32) // Enable VT processing on stdout and stdin auto stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); diff --git a/src/ftxui/component/screen_interactive_test.cpp b/src/ftxui/component/screen_interactive_test.cpp new file mode 100644 index 00000000..77fbafee --- /dev/null +++ b/src/ftxui/component/screen_interactive_test.cpp @@ -0,0 +1,52 @@ +#include // for Message +#include // for TestPartResult +#include + +#include "ftxui/component/component.hpp" +#include "ftxui/component/screen_interactive.hpp" +#include "ftxui/dom/elements.hpp" +#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ + +using namespace ftxui; + +namespace { +bool TestSignal(int signal) { + int called = 0; + // The tree of components. This defines how to navigate using the keyboard. + auto component = Renderer([&] { + called++; + std::raise(signal); + called++; + return text(L""); + }); + + auto screen = ScreenInteractive::FitComponent(); + screen.Loop(component); + + EXPECT_EQ(called, 2); + return true; +} +} // namespace + +TEST(ScreenInteractive, Signal_SIGTERM) { + TestSignal(SIGTERM); +} +TEST(ScreenInteractive, Signal_SIGSEGV) { + TestSignal(SIGSEGV); +} +TEST(ScreenInteractive, Signal_SIGINT) { + TestSignal(SIGINT); +} +TEST(ScreenInteractive, Signal_SIGILL) { + TestSignal(SIGILL); +} +TEST(ScreenInteractive, Signal_SIGABRT) { + TestSignal(SIGABRT); +} +TEST(ScreenInteractive, Signal_SIGFPE) { + TestSignal(SIGFPE); +} + +// Copyright 2021 Arthur Sonzogni. All rights reserved. +// Use of this source code is governed by the MIT license that can be found in +// the LICENSE file.