mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-07-30 17:31:11 +08:00
Support for CTRL Z
This commit is contained in:
parent
5fc51b557e
commit
2b73998d56
@ -6,6 +6,10 @@ current (development)
|
|||||||
|
|
||||||
### Component
|
### Component
|
||||||
- Feature: Add support for raw input. Allowing more keys to be detected.
|
- 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
|
- Feature: Add `Mouse::WeelLeft` and `Mouse::WeelRight` events on supported
|
||||||
terminals.
|
terminals.
|
||||||
- Feature: Add `Event::DebugString()`.
|
- Feature: Add `Event::DebugString()`.
|
||||||
|
@ -59,6 +59,15 @@ class ScreenInteractive : public Screen {
|
|||||||
// temporarily uninstalled.
|
// temporarily uninstalled.
|
||||||
Closure WithRestoredIO(Closure);
|
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:
|
private:
|
||||||
void ExitNow();
|
void ExitNow();
|
||||||
|
|
||||||
@ -114,6 +123,9 @@ class ScreenInteractive : public Screen {
|
|||||||
|
|
||||||
bool frame_valid_ = false;
|
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.
|
// The style of the cursor to restore on exit.
|
||||||
int cursor_reset_shape_ = 1;
|
int cursor_reset_shape_ = 1;
|
||||||
|
|
||||||
|
@ -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.
|
/// @brief Return the currently active screen, or null if none.
|
||||||
// static
|
// static
|
||||||
ScreenInteractive* ScreenInteractive::Active() {
|
ScreenInteractive* ScreenInteractive::Active() {
|
||||||
@ -657,7 +670,7 @@ void ScreenInteractive::Install() {
|
|||||||
// - C-C => INTR
|
// - C-C => INTR
|
||||||
// - C-d => QUIT
|
// - C-d => QUIT
|
||||||
terminal.c_lflag &= ~IEXTEN; // Disable extended input processing
|
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
|
terminal.c_cc[VMIN] = 0; // Minimum number of characters for non-canonical
|
||||||
// read.
|
// read.
|
||||||
@ -765,14 +778,16 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
|
|||||||
|
|
||||||
const bool handled = component->OnEvent(arg);
|
const bool handled = component->OnEvent(arg);
|
||||||
|
|
||||||
if (arg == Event::CtrlC) {
|
if (arg == Event::CtrlC && (!handled || force_handle_ctrl_c_)) {
|
||||||
Exit();
|
RecordSignal(SIGABRT);
|
||||||
}
|
|
||||||
|
|
||||||
if (arg == Event::CtrlZ) {
|
|
||||||
// How to handle SIGTSTP manually?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
if (arg == Event::CtrlZ && (!handled || force_handle_ctrl_z_)) {
|
||||||
|
RecordSignal(SIGTSTP);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
frame_valid_ = false;
|
frame_valid_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -64,4 +64,71 @@ TEST(ScreenInteractive, PostTaskToNonActive) {
|
|||||||
screen.Post([] {});
|
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
|
} // namespace ftxui
|
||||||
|
@ -169,16 +169,9 @@ TerminalInputParser::Output TerminalInputParser::Parse() {
|
|||||||
if (!Eat()) {
|
if (!Eat()) {
|
||||||
return UNCOMPLETED;
|
return UNCOMPLETED;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (Current()) {
|
if (Current() == '\x1B') {
|
||||||
case 24: // CAN NOLINT
|
return ParseESC();
|
||||||
case 26: // SUB NOLINT
|
|
||||||
return DROP;
|
|
||||||
|
|
||||||
case '\x1B':
|
|
||||||
return ParseESC();
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Current() < 32) { // C0 NOLINT
|
if (Current() < 32) { // C0 NOLINT
|
||||||
|
Loading…
Reference in New Issue
Block a user