Skip to content

Commit

Permalink
Support for CTRL Z
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurSonzogni committed Apr 27, 2024
1 parent c40df85 commit 886cb47
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ current (development)

### Component
- Feature: Add support for raw input. Allowing more keys to be detected.
- Feature: Add `ScreenInteractive::ForceHandleCtrlC(false)` to allow component
to fully override the default `Ctrl+C` handler.
- Feature: Add `ScreenInteractive::ForceHandleCtrlZ(false)` to allow component
to fully override the default `Ctrl+Z` handler.
- Feature: Add `Mouse::WeelLeft` and `Mouse::WeelRight` events on supported
terminals.
- Feature: Add `Event::DebugString()`.
Expand Down
12 changes: 12 additions & 0 deletions include/ftxui/component/screen_interactive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ class ScreenInteractive : public Screen {
// temporarily uninstalled.
Closure WithRestoredIO(Closure);

// FTXUI implements handlers for Ctrl-C and Ctrl-Z. By default, these handlers
// are executed, even if the component catches the event. This avoid users
// handling every event to be trapped in the application. However, in some
// cases, the application may want to handle these events itself. In this case,
// the application can force FTXUI to not handle these events by calling the
// following functions with force=true.
void ForceHandleCtrlC(bool force);
void ForceHandleCtrlZ(bool force);

private:
void ExitNow();

Expand Down Expand Up @@ -114,6 +123,9 @@ class ScreenInteractive : public Screen {

bool frame_valid_ = false;

bool force_handle_ctrl_c_ = true;
bool force_handle_ctrl_z_ = true;

// The style of the cursor to restore on exit.
int cursor_reset_shape_ = 1;

Expand Down
29 changes: 22 additions & 7 deletions src/ftxui/component/screen_interactive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,19 @@ Closure ScreenInteractive::WithRestoredIO(Closure fn) { // NOLINT
};
}


/// @brief Force FTXUI to handle or not handle Ctrl-C, even if the component
/// catches the Event::CtrlC.
void ScreenInteractive::ForceHandleCtrlC(bool force) {
force_handle_ctrl_c_ = force;
}

/// @brief Force FTXUI to handle or not handle Ctrl-Z, even if the component
/// catches the Event::CtrlZ.
void ScreenInteractive::ForceHandleCtrlZ(bool force) {
force_handle_ctrl_z_ = force;
}

/// @brief Return the currently active screen, or null if none.
// static
ScreenInteractive* ScreenInteractive::Active() {
Expand Down Expand Up @@ -657,7 +670,7 @@ void ScreenInteractive::Install() {
// - C-C => INTR
// - C-d => QUIT
terminal.c_lflag &= ~IEXTEN; // Disable extended input processing
terminal.c_cflag |= (CS8); // 8 bits per byte
terminal.c_cflag |= CS8; // 8 bits per byte

terminal.c_cc[VMIN] = 0; // Minimum number of characters for non-canonical
// read.
Expand Down Expand Up @@ -765,14 +778,16 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {

const bool handled = component->OnEvent(arg);

if (arg == Event::CtrlC) {
Exit();
}

if (arg == Event::CtrlZ) {
// How to handle SIGTSTP manually?
if (arg == Event::CtrlC && (!handled || force_handle_ctrl_c_)) {
RecordSignal(SIGABRT);
}

#if !defined(_WIN32)
if (arg == Event::CtrlZ && (!handled || force_handle_ctrl_z_)) {
RecordSignal(SIGTSTP);
}
#endif

frame_valid_ = false;
return;
}
Expand Down
67 changes: 67 additions & 0 deletions src/ftxui/component/screen_interactive_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,71 @@ TEST(ScreenInteractive, PostTaskToNonActive) {
screen.Post([] {});
}

TEST(ScreenInteractive, CtrlC) {
auto screen = ScreenInteractive::FitComponent();
bool called = false;
auto component = Renderer([&] {
if (!called) {
called = true;
screen.PostEvent(Event::CtrlC);
}
return text("");
});
screen.Loop(component);
}

TEST(ScreenInteractive, CtrlC_Forced) {
auto screen = ScreenInteractive::FitComponent();
screen.ForceHandleCtrlC(true);
auto component = Renderer([&] {
screen.PostEvent(Event::CtrlC);
return text("");
});

int ctrl_c_count = 0;
component |= CatchEvent([&](Event event) {
if (event != Event::CtrlC) {
return false;
}

++ctrl_c_count;

if (ctrl_c_count == 100) {
return false;
}

return true;
});
screen.Loop(component);

ASSERT_LE(ctrl_c_count, 50);
}

TEST(ScreenInteractive, CtrlC_NotForced) {
auto screen = ScreenInteractive::FitComponent();
screen.ForceHandleCtrlC(false);
auto component = Renderer([&] {
screen.PostEvent(Event::CtrlC);
return text("");
});

int ctrl_c_count = 0;
component |= CatchEvent([&](Event event) {
if (event != Event::CtrlC) {
return false;
}

++ctrl_c_count;

if (ctrl_c_count == 100) {
return false;
}

return true;
});
screen.Loop(component);

ASSERT_GE(ctrl_c_count, 50);
}

} // namespace ftxui
13 changes: 3 additions & 10 deletions src/ftxui/component/terminal_input_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,9 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
if (!Eat()) {
return UNCOMPLETED;
}

switch (Current()) {
case 24: // CAN NOLINT
case 26: // SUB NOLINT
return DROP;

case '\x1B':
return ParseESC();
default:
break;

if (Current() == '\x1B') {
return ParseESC();
}

if (Current() < 32) { // C0 NOLINT
Expand Down

0 comments on commit 886cb47

Please sign in to comment.