mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-25 20:27:31 +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
|
||||
- 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
|
||||
|
||||
|
@ -31,3 +31,4 @@ example(slider_rgb)
|
||||
example(tab_horizontal)
|
||||
example(tab_vertical)
|
||||
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 {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
static ScreenInteractive FixedSize(int dimx, int dimy);
|
||||
static ScreenInteractive Fullscreen();
|
||||
static ScreenInteractive FitComponent();
|
||||
static ScreenInteractive TerminalOutput();
|
||||
|
||||
void Loop(Component);
|
||||
std::function<void()> ExitLoopClosure();
|
||||
Callback ExitLoopClosure();
|
||||
|
||||
void PostEvent(Event event);
|
||||
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:
|
||||
void Install();
|
||||
void Uninstall();
|
||||
|
||||
void Main(Component component);
|
||||
ScreenInteractive* suspended_screen_ = nullptr;
|
||||
|
||||
|
@ -200,7 +200,7 @@ const std::string DeviceStatusReport(DSRMode ps) {
|
||||
}
|
||||
|
||||
using SignalHandler = void(int);
|
||||
std::stack<std::function<void()>> on_exit_functions;
|
||||
std::stack<ScreenInteractive::Callback> on_exit_functions;
|
||||
void OnExit(int signal) {
|
||||
(void)signal;
|
||||
while (!on_exit_functions.empty()) {
|
||||
@ -211,10 +211,10 @@ void OnExit(int signal) {
|
||||
|
||||
auto install_signal_handler = [](int sig, SignalHandler 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 */) {
|
||||
on_resize();
|
||||
}
|
||||
@ -230,6 +230,8 @@ class CapturedMouseImpl : public CapturedMouseInterface {
|
||||
|
||||
} // namespace
|
||||
|
||||
ScreenInteractive* g_active_screen = nullptr;
|
||||
|
||||
ScreenInteractive::ScreenInteractive(int dimx,
|
||||
int dimy,
|
||||
Dimension dimension,
|
||||
@ -275,7 +277,6 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
|
||||
}
|
||||
|
||||
void ScreenInteractive::Loop(Component component) {
|
||||
static ScreenInteractive* g_active_screen = nullptr;
|
||||
|
||||
// Suspend previously 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() {
|
||||
// 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()(); });
|
||||
|
||||
// Install signal handlers to restore the terminal state on exit. The default
|
||||
@ -370,12 +386,6 @@ void ScreenInteractive::Install() {
|
||||
install_signal_handler(SIGWINCH, OnResize);
|
||||
#endif
|
||||
|
||||
// Commit state:
|
||||
auto flush = [&] {
|
||||
Flush();
|
||||
on_exit_functions.push([] { Flush(); });
|
||||
};
|
||||
|
||||
auto enable = [&](std::vector<DECMode> parameters) {
|
||||
std::cout << Set(parameters);
|
||||
on_exit_functions.push([=] { std::cout << Reset(parameters); });
|
||||
@ -404,7 +414,9 @@ void ScreenInteractive::Install() {
|
||||
DECMode::kMouseSgrExtMode,
|
||||
});
|
||||
|
||||
flush();
|
||||
// After installing the new configuration, flush it to the terminal to ensure
|
||||
// it is fully applied:
|
||||
Flush();
|
||||
|
||||
quit_ = false;
|
||||
event_listener_ =
|
||||
@ -526,8 +538,8 @@ void ScreenInteractive::Draw(Component component) {
|
||||
}
|
||||
}
|
||||
|
||||
std::function<void()> ScreenInteractive::ExitLoopClosure() {
|
||||
return [this]() {
|
||||
ScreenInteractive::Callback ScreenInteractive::ExitLoopClosure() {
|
||||
return [this] {
|
||||
quit_ = true;
|
||||
event_sender_.reset();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user