Support for CTRL Z

This commit is contained in:
ArthurSonzogni 2024-04-27 15:01:03 +02:00
parent 5fc51b557e
commit 2b73998d56
No known key found for this signature in database
GPG Key ID: 41D98248C074CD6C
5 changed files with 108 additions and 17 deletions

View File

@ -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()`.

View File

@ -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();
@ -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;

View File

@ -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() {
@ -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.
@ -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;
}

View File

@ -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

View File

@ -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