mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-26 12:37:06 +08:00
Introduce WithRestoredIO (#307)
This function allow running a callback with the terminal hooks temporarily uninstalled. Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
This commit is contained in:
parent
cd82fccde7
commit
b4a655ec65
@ -24,6 +24,9 @@ unreleased (development)
|
|||||||
|
|
||||||
#### Component
|
#### Component
|
||||||
- Add the `collapsible` component.
|
- Add the `collapsible` component.
|
||||||
|
- Add the `ScreenInteractive::WithRestoredIO`. This decorates a callback. This
|
||||||
|
runs it with the terminal hooks temporarilly uninstalled. This is useful if
|
||||||
|
you want to execute command using directly stdin/stdout/sterr.
|
||||||
|
|
||||||
### Bug
|
### Bug
|
||||||
|
|
||||||
|
@ -31,3 +31,4 @@ example(slider_rgb)
|
|||||||
example(tab_horizontal)
|
example(tab_horizontal)
|
||||||
example(tab_vertical)
|
example(tab_vertical)
|
||||||
example(toggle)
|
example(toggle)
|
||||||
|
example(with_restored_io)
|
||||||
|
55
examples/component/with_restored_io.cpp
Normal file
55
examples/component/with_restored_io.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "ftxui/component/component.hpp" // for Menu
|
||||||
|
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using namespace ftxui;
|
||||||
|
|
||||||
|
auto screen = ScreenInteractive::Fullscreen();
|
||||||
|
|
||||||
|
// When pressing this button, "screen.WithRestoredIO" will execute the
|
||||||
|
// temporarily uninstall the terminal hook and execute the provided callback
|
||||||
|
// function. This allow running the application in a non-interactive mode.
|
||||||
|
auto btn_run = Button("Execute with restored IO", screen.WithRestoredIO([] {
|
||||||
|
std::system("bash");
|
||||||
|
std::cout << "This is a child program using stdin/stdout." << std::endl;
|
||||||
|
for (int i = 0; i < 10; ++i) {
|
||||||
|
std::cout << "Please enter 10 strings (" << i << "/10)" << std::flush;
|
||||||
|
std::string input;
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
}
|
||||||
|
std::system("bash");
|
||||||
|
}));
|
||||||
|
|
||||||
|
auto btn_quit = Button("Quit", screen.ExitLoopClosure());
|
||||||
|
|
||||||
|
auto layout = Container::Horizontal({
|
||||||
|
btn_run,
|
||||||
|
btn_quit,
|
||||||
|
});
|
||||||
|
|
||||||
|
auto renderer = Renderer(layout, [&] {
|
||||||
|
auto explanation = paragraph(
|
||||||
|
"After clicking this button, the ScreenInteractive will be "
|
||||||
|
"suspended and access to stdin/stdout will temporarilly be "
|
||||||
|
"restore for running a function.");
|
||||||
|
auto element = vbox({
|
||||||
|
explanation | borderEmpty,
|
||||||
|
hbox({
|
||||||
|
btn_run->Render(),
|
||||||
|
filler(),
|
||||||
|
btn_quit->Render(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
element = element | borderEmpty | border | size(WIDTH, LESS_THAN, 80) |
|
||||||
|
size(HEIGHT, LESS_THAN, 20) | center;
|
||||||
|
return element;
|
||||||
|
});
|
||||||
|
|
||||||
|
screen.Loop(renderer);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copyright 2022 Arthur Sonzogni. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
|
// the LICENSE file.
|
@ -20,20 +20,28 @@ using Component = std::shared_ptr<ComponentBase>;
|
|||||||
|
|
||||||
class ScreenInteractive : public Screen {
|
class ScreenInteractive : public Screen {
|
||||||
public:
|
public:
|
||||||
|
using Callback = std::function<void()>;
|
||||||
|
|
||||||
static ScreenInteractive FixedSize(int dimx, int dimy);
|
static ScreenInteractive FixedSize(int dimx, int dimy);
|
||||||
static ScreenInteractive Fullscreen();
|
static ScreenInteractive Fullscreen();
|
||||||
static ScreenInteractive FitComponent();
|
static ScreenInteractive FitComponent();
|
||||||
static ScreenInteractive TerminalOutput();
|
static ScreenInteractive TerminalOutput();
|
||||||
|
|
||||||
void Loop(Component);
|
void Loop(Component);
|
||||||
std::function<void()> ExitLoopClosure();
|
Callback ExitLoopClosure();
|
||||||
|
|
||||||
void PostEvent(Event event);
|
void PostEvent(Event event);
|
||||||
CapturedMouse CaptureMouse();
|
CapturedMouse CaptureMouse();
|
||||||
|
|
||||||
|
// Decorate a function. The outputted one will execute similarly to the
|
||||||
|
// inputted one, but with the currently active screen terminal hooks
|
||||||
|
// temporarily uninstalled.
|
||||||
|
Callback WithRestoredIO(Callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Install();
|
void Install();
|
||||||
void Uninstall();
|
void Uninstall();
|
||||||
|
|
||||||
void Main(Component component);
|
void Main(Component component);
|
||||||
ScreenInteractive* suspended_screen_ = nullptr;
|
ScreenInteractive* suspended_screen_ = nullptr;
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ const std::string DeviceStatusReport(DSRMode ps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
using SignalHandler = void(int);
|
using SignalHandler = void(int);
|
||||||
std::stack<std::function<void()>> on_exit_functions;
|
std::stack<ScreenInteractive::Callback> on_exit_functions;
|
||||||
void OnExit(int signal) {
|
void OnExit(int signal) {
|
||||||
(void)signal;
|
(void)signal;
|
||||||
while (!on_exit_functions.empty()) {
|
while (!on_exit_functions.empty()) {
|
||||||
@ -211,10 +211,10 @@ void OnExit(int signal) {
|
|||||||
|
|
||||||
auto install_signal_handler = [](int sig, SignalHandler handler) {
|
auto install_signal_handler = [](int sig, SignalHandler handler) {
|
||||||
auto old_signal_handler = std::signal(sig, handler);
|
auto old_signal_handler = std::signal(sig, handler);
|
||||||
on_exit_functions.push([&]() { std::signal(sig, old_signal_handler); });
|
on_exit_functions.push([&] { std::signal(sig, old_signal_handler); });
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<void()> on_resize = [] {};
|
ScreenInteractive::Callback on_resize = [] {};
|
||||||
void OnResize(int /* signal */) {
|
void OnResize(int /* signal */) {
|
||||||
on_resize();
|
on_resize();
|
||||||
}
|
}
|
||||||
@ -230,6 +230,8 @@ class CapturedMouseImpl : public CapturedMouseInterface {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
ScreenInteractive* g_active_screen = nullptr;
|
||||||
|
|
||||||
ScreenInteractive::ScreenInteractive(int dimx,
|
ScreenInteractive::ScreenInteractive(int dimx,
|
||||||
int dimy,
|
int dimy,
|
||||||
Dimension dimension,
|
Dimension dimension,
|
||||||
@ -275,7 +277,6 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScreenInteractive::Loop(Component component) {
|
void ScreenInteractive::Loop(Component component) {
|
||||||
static ScreenInteractive* g_active_screen = nullptr;
|
|
||||||
|
|
||||||
// Suspend previously active screen:
|
// Suspend previously active screen:
|
||||||
if (g_active_screen) {
|
if (g_active_screen) {
|
||||||
@ -311,7 +312,22 @@ void ScreenInteractive::Loop(Component component) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Decorate a function. It executes the same way, but with the currently
|
||||||
|
/// active screen terminal hooks temporarilly uninstalled during its execution.
|
||||||
|
/// @param fn The function to decorate.
|
||||||
|
ScreenInteractive::Callback ScreenInteractive::WithRestoredIO(Callback fn) {
|
||||||
|
return [this, fn] {
|
||||||
|
Uninstall();
|
||||||
|
fn();
|
||||||
|
Install();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void ScreenInteractive::Install() {
|
void ScreenInteractive::Install() {
|
||||||
|
// After uninstalling the new configuration, flush it to the terminal to
|
||||||
|
// ensure it is fully applied:
|
||||||
|
on_exit_functions.push([] { Flush(); });
|
||||||
|
|
||||||
on_exit_functions.push([this] { ExitLoopClosure()(); });
|
on_exit_functions.push([this] { ExitLoopClosure()(); });
|
||||||
|
|
||||||
// Install signal handlers to restore the terminal state on exit. The default
|
// Install signal handlers to restore the terminal state on exit. The default
|
||||||
@ -370,12 +386,6 @@ void ScreenInteractive::Install() {
|
|||||||
install_signal_handler(SIGWINCH, OnResize);
|
install_signal_handler(SIGWINCH, OnResize);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Commit state:
|
|
||||||
auto flush = [&] {
|
|
||||||
Flush();
|
|
||||||
on_exit_functions.push([] { Flush(); });
|
|
||||||
};
|
|
||||||
|
|
||||||
auto enable = [&](std::vector<DECMode> parameters) {
|
auto enable = [&](std::vector<DECMode> parameters) {
|
||||||
std::cout << Set(parameters);
|
std::cout << Set(parameters);
|
||||||
on_exit_functions.push([=] { std::cout << Reset(parameters); });
|
on_exit_functions.push([=] { std::cout << Reset(parameters); });
|
||||||
@ -404,7 +414,9 @@ void ScreenInteractive::Install() {
|
|||||||
DECMode::kMouseSgrExtMode,
|
DECMode::kMouseSgrExtMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
flush();
|
// After installing the new configuration, flush it to the terminal to ensure
|
||||||
|
// it is fully applied:
|
||||||
|
Flush();
|
||||||
|
|
||||||
quit_ = false;
|
quit_ = false;
|
||||||
event_listener_ =
|
event_listener_ =
|
||||||
@ -526,8 +538,8 @@ void ScreenInteractive::Draw(Component component) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::function<void()> ScreenInteractive::ExitLoopClosure() {
|
ScreenInteractive::Callback ScreenInteractive::ExitLoopClosure() {
|
||||||
return [this]() {
|
return [this] {
|
||||||
quit_ = true;
|
quit_ = true;
|
||||||
event_sender_.reset();
|
event_sender_.reset();
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user