FTXUI  5.0.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
screen_interactive.cpp
Go to the documentation of this file.
1// Copyright 2020 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
4#include <algorithm> // for copy, max, min
5#include <array> // for array
6#include <chrono> // for operator-, milliseconds, operator>=, duration, common_type<>::type, time_point
7#include <csignal> // for signal, SIGTSTP, SIGABRT, SIGWINCH, raise, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, __sighandler_t, size_t
8#include <cstdio> // for fileno, stdin
9#include <ftxui/component/task.hpp> // for Task, Closure, AnimationTask
10#include <ftxui/screen/screen.hpp> // for Pixel, Screen::Cursor, Screen, Screen::Cursor::Hidden
11#include <functional> // for function
12#include <initializer_list> // for initializer_list
13#include <iostream> // for cout, ostream, operator<<, basic_ostream, endl, flush
14#include <stack> // for stack
15#include <thread> // for thread, sleep_for
16#include <tuple> // for _Swallow_assign, ignore
17#include <type_traits> // for decay_t
18#include <utility> // for move, swap
19#include <variant> // for visit, variant
20#include <vector> // for vector
21
22#include "ftxui/component/animation.hpp" // for TimePoint, Clock, Duration, Params, RequestAnimationFrame
23#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
24#include "ftxui/component/component_base.hpp" // for ComponentBase
25#include "ftxui/component/event.hpp" // for Event
26#include "ftxui/component/loop.hpp" // for Loop
27#include "ftxui/component/receiver.hpp" // for ReceiverImpl, Sender, MakeReceiver, SenderImpl, Receiver
29#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
30#include "ftxui/dom/node.hpp" // for Node, Render
31#include "ftxui/dom/requirement.hpp" // for Requirement
32#include "ftxui/screen/terminal.hpp" // for Dimensions, Size
33
34#if defined(_WIN32)
35#define DEFINE_CONSOLEV2_PROPERTIES
36#define WIN32_LEAN_AND_MEAN
37#ifndef NOMINMAX
38#define NOMINMAX
39#endif
40#include <windows.h>
41#ifndef UNICODE
42#error Must be compiled in UNICODE mode
43#endif
44#else
45#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set, timeval
46#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
47#include <unistd.h> // for STDIN_FILENO, read
48#endif
49
50// Quick exit is missing in standard CLang headers
51#if defined(__clang__) && defined(__APPLE__)
52#define quick_exit(a) exit(a)
53#endif
54
55namespace ftxui {
56
57namespace animation {
60 if (screen) {
61 screen->RequestAnimationFrame();
62 }
63}
64} // namespace animation
65
66namespace {
67
68ScreenInteractive* g_active_screen = nullptr; // NOLINT
69
70void Flush() {
71 // Emscripten doesn't implement flush. We interpret zero as flush.
72 std::cout << '\0' << std::flush;
73}
74
75constexpr int timeout_milliseconds = 20;
76[[maybe_unused]] constexpr int timeout_microseconds =
78#if defined(_WIN32)
79
80void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
82 auto parser = TerminalInputParser(out->Clone());
83 while (!*quit) {
84 // Throttle ReadConsoleInput by waiting 250ms, this wait function will
85 // return if there is input in the console.
89 continue;
90 }
91
94 continue;
95 if (number_of_events <= 0)
96 continue;
97
98 std::vector<INPUT_RECORD> records{number_of_events};
103
104 for (const auto& r : records) {
105 switch (r.EventType) {
106 case KEY_EVENT: {
107 auto key_event = r.Event.KeyEvent;
108 // ignore UP key events
109 if (key_event.bKeyDown == FALSE)
110 continue;
111 std::wstring wstring;
112 wstring += key_event.uChar.UnicodeChar;
113 for (auto it : to_string(wstring)) {
114 parser.Add(it);
115 }
116 } break;
118 out->Send(Event::Special({0}));
119 break;
120 case MENU_EVENT:
121 case FOCUS_EVENT:
122 case MOUSE_EVENT:
123 // TODO(mauve): Implement later.
124 break;
125 }
126 }
127 }
128}
129
130#elif defined(__EMSCRIPTEN__)
131#include <emscripten.h>
132
133// Read char from the terminal.
134void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
136 auto parser = TerminalInputParser(std::move(out));
137
138 char c;
139 while (!*quit) {
140 while (read(STDIN_FILENO, &c, 1), c)
141 parser.Add(c);
142
144 parser.Timeout(1);
145 }
146}
147
148extern "C" {
150void ftxui_on_resize(int columns, int rows) {
152 columns,
153 rows,
154 });
155 std::raise(SIGWINCH);
156}
157}
158
159#else // POSIX (Linux & Mac)
160
162 timeval tv = {0, usec_timeout};
163 fd_set fds;
164 FD_ZERO(&fds); // NOLINT
165 FD_SET(STDIN_FILENO, &fds); // NOLINT
166 select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &tv); // NOLINT
167 return FD_ISSET(STDIN_FILENO, &fds); // NOLINT
168}
169
170// Read char from the terminal.
171void EventListener(std::atomic<bool>* quit, Sender<Task> out) {
172 auto parser = TerminalInputParser(std::move(out));
173
174 while (!*quit) {
177 continue;
178 }
179
180 const size_t buffer_size = 100;
181 std::array<char, buffer_size> buffer; // NOLINT;
182 size_t l = read(fileno(stdin), buffer.data(), buffer_size); // NOLINT
183 for (size_t i = 0; i < l; ++i) {
184 parser.Add(buffer[i]); // NOLINT
185 }
186 }
187}
188#endif
189
190std::stack<Closure> on_exit_functions; // NOLINT
191void OnExit() {
192 while (!on_exit_functions.empty()) {
193 on_exit_functions.top()();
194 on_exit_functions.pop();
195 }
196}
197
198std::atomic<int> g_signal_exit_count = 0; // NOLINT
199#if !defined(_WIN32)
200std::atomic<int> g_signal_stop_count = 0; // NOLINT
201std::atomic<int> g_signal_resize_count = 0; // NOLINT
202#endif
203
204// Async signal safe function
205void RecordSignal(int signal) {
206 switch (signal) {
207 case SIGABRT:
208 case SIGFPE:
209 case SIGILL:
210 case SIGINT:
211 case SIGSEGV:
212 case SIGTERM:
214 break;
215
216#if !defined(_WIN32)
217 case SIGTSTP:
219 break;
220
221 case SIGWINCH:
223 break;
224#endif
225
226 default:
227 break;
228 }
229}
230
232 int signal_exit_count = g_signal_exit_count.exchange(0);
233 while (signal_exit_count--) {
235 }
236
237#if !defined(_WIN32)
238 int signal_stop_count = g_signal_stop_count.exchange(0);
239 while (signal_stop_count--) {
241 }
242
244 while (signal_resize_count--) {
246 }
247#endif
248}
249
250void InstallSignalHandler(int sig) {
251 auto old_signal_handler = std::signal(sig, RecordSignal);
253 [=] { std::ignore = std::signal(sig, old_signal_handler); });
254}
255
256const std::string CSI = "\x1b["; // NOLINT
257
258// DEC: Digital Equipment Corporation
259enum class DECMode {
260 kLineWrap = 7,
261 kCursor = 25,
262
263 kMouseX10 = 9,
264 kMouseVt200 = 1000,
265 kMouseVt200Highlight = 1001,
266
267 kMouseBtnEventMouse = 1002,
268 kMouseAnyEvent = 1003,
269
270 kMouseUtf8 = 1005,
271 kMouseSgrExtMode = 1006,
272 kMouseUrxvtMode = 1015,
273 kMouseSgrPixelsMode = 1016,
274 kAlternateScreen = 1049,
275};
276
277// Device Status Report (DSR) {
278enum class DSRMode {
279 kCursor = 6,
280};
281
282std::string Serialize(const std::vector<DECMode>& parameters) {
283 bool first = true;
284 std::string out;
285 for (const DECMode parameter : parameters) {
286 if (!first) {
287 out += ";";
288 }
289 out += std::to_string(int(parameter));
290 first = false;
291 }
292 return out;
293}
294
295// DEC Private Mode Set (DECSET)
296std::string Set(const std::vector<DECMode>& parameters) {
297 return CSI + "?" + Serialize(parameters) + "h";
298}
299
300// DEC Private Mode Reset (DECRST)
301std::string Reset(const std::vector<DECMode>& parameters) {
302 return CSI + "?" + Serialize(parameters) + "l";
303}
304
305// Device Status Report (DSR)
306std::string DeviceStatusReport(DSRMode ps) {
307 return CSI + std::to_string(int(ps)) + "n";
308}
309
310class CapturedMouseImpl : public CapturedMouseInterface {
311 public:
312 explicit CapturedMouseImpl(std::function<void(void)> callback)
313 : callback_(std::move(callback)) {}
314 ~CapturedMouseImpl() override { callback_(); }
315 CapturedMouseImpl(const CapturedMouseImpl&) = delete;
316 CapturedMouseImpl(CapturedMouseImpl&&) = delete;
317 CapturedMouseImpl& operator=(const CapturedMouseImpl&) = delete;
318 CapturedMouseImpl& operator=(CapturedMouseImpl&&) = delete;
319
320 private:
321 std::function<void(void)> callback_;
322};
323
324void AnimationListener(std::atomic<bool>* quit, Sender<Task> out) {
325 // Animation at around 60fps.
326 const auto time_delta = std::chrono::milliseconds(15);
327 while (!*quit) {
328 out->Send(AnimationTask());
329 std::this_thread::sleep_for(time_delta);
330 }
331}
332
333} // namespace
334
335ScreenInteractive::ScreenInteractive(int dimx,
336 int dimy,
337 Dimension dimension,
339 : Screen(dimx, dimy),
340 dimension_(dimension),
341 use_alternative_screen_(use_alternative_screen) {
342 task_receiver_ = MakeReceiver<Task>();
343}
344
345// static
347 return {
348 dimx,
349 dimy,
350 Dimension::Fixed,
351 false,
352 };
353}
354
355// static
357 return {
358 0,
359 0,
360 Dimension::Fullscreen,
361 true,
362 };
363}
364
365// static
367 return {
368 0,
369 0,
370 Dimension::TerminalOutput,
371 false,
372 };
373}
374
375// static
377 return {
378 0,
379 0,
380 Dimension::FitComponent,
381 false,
382 };
383}
384
385/// @ingroup component
386/// @brief Set whether mouse is tracked and events reported.
387/// called outside of the main loop. E.g `ScreenInteractive::Loop(...)`.
388/// @param enable Whether to enable mouse event tracking.
389/// @note This muse be called outside of the main loop. E.g. before calling
390/// `ScreenInteractive::Loop`.
391/// @note Mouse tracking is enabled by default.
392/// @note Mouse tracking is only supported on terminals that supports it.
393///
394/// ### Example
395///
396/// ```cpp
397/// auto screen = ScreenInteractive::TerminalOutput();
398/// screen.TrackMouse(false);
399/// screen.Loop(component);
400/// ```
402 track_mouse_ = enable;
403}
404
405/// @brief Add a task to the main loop.
406/// It will be executed later, after every other scheduled tasks.
407/// @ingroup component
409 // Task/Events sent toward inactive screen or screen waiting to become
410 // inactive are dropped.
411 if (!task_sender_) {
412 return;
413 }
414
415 task_sender_->Send(std::move(task));
416}
417
418/// @brief Add an event to the main loop.
419/// It will be executed later, after every other scheduled events.
420/// @ingroup component
424
425/// @brief Add a task to draw the screen one more time, until all the animations
426/// are done.
428 if (animation_requested_) {
429 return;
430 }
431 animation_requested_ = true;
432 auto now = animation::Clock::now();
433 const auto time_histeresis = std::chrono::milliseconds(33);
434 if (now - previous_animation_time_ >= time_histeresis) {
435 previous_animation_time_ = now;
436 }
437}
438
439/// @brief Try to get the unique lock about behing able to capture the mouse.
440/// @return A unique lock if the mouse is not already captured, otherwise a
441/// null.
442/// @ingroup component
444 if (mouse_captured) {
445 return nullptr;
446 }
447 mouse_captured = true;
448 return std::make_unique<CapturedMouseImpl>(
449 [this] { mouse_captured = false; });
450}
451
452/// @brief Execute the main loop.
453/// @param component The component to draw.
454/// @ingroup component
456 class Loop loop(this, std::move(component));
457 loop.Run();
458}
459
460/// @brief Return whether the main loop has been quit.
461/// @ingroup component
462bool ScreenInteractive::HasQuitted() {
463 return task_receiver_->HasQuitted();
464}
465
466// private
467void ScreenInteractive::PreMain() {
468 // Suspend previously active screen:
469 if (g_active_screen) {
470 std::swap(suspended_screen_, g_active_screen);
471 // Reset cursor position to the top of the screen and clear the screen.
472 suspended_screen_->ResetCursorPosition();
473 std::cout << suspended_screen_->ResetPosition(/*clear=*/true);
474 suspended_screen_->dimx_ = 0;
475 suspended_screen_->dimy_ = 0;
476
477 // Reset dimensions to force drawing the screen again next time:
478 suspended_screen_->Uninstall();
479 }
480
481 // This screen is now active:
482 g_active_screen = this;
483 g_active_screen->Install();
484
485 previous_animation_time_ = animation::Clock::now();
486}
487
488// private
489void ScreenInteractive::PostMain() {
490 // Put cursor position at the end of the drawing.
491 ResetCursorPosition();
492
493 g_active_screen = nullptr;
494
495 // Restore suspended screen.
496 if (suspended_screen_) {
497 // Clear screen, and put the cursor at the beginning of the drawing.
498 std::cout << ResetPosition(/*clear=*/true);
499 dimx_ = 0;
500 dimy_ = 0;
501 Uninstall();
502 std::swap(g_active_screen, suspended_screen_);
503 g_active_screen->Install();
504 } else {
505 Uninstall();
506
507 std::cout << '\r';
508 // On final exit, keep the current drawing and reset cursor position one
509 // line after it.
510 if (!use_alternative_screen_) {
511 std::cout << std::endl;
512 }
513 }
514}
515
516/// @brief Decorate a function. It executes the same way, but with the currently
517/// active screen terminal hooks temporarilly uninstalled during its execution.
518/// @param fn The function to decorate.
520 return [this, fn] {
521 Uninstall();
522 fn();
523 Install();
524 };
525}
526
527/// @brief Return the currently active screen, or null if none.
528// static
532
533// private
534void ScreenInteractive::Install() {
535 frame_valid_ = false;
536
537 // After uninstalling the new configuration, flush it to the terminal to
538 // ensure it is fully applied:
539 on_exit_functions.push([] { Flush(); });
540
541 on_exit_functions.push([this] { ExitLoopClosure()(); });
542
543 // Install signal handlers to restore the terminal state on exit. The default
544 // signal handlers are restored on exit.
545 for (const int signal : {SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE}) {
547 }
548
549// Save the old terminal configuration and restore it on exit.
550#if defined(_WIN32)
551 // Enable VT processing on stdout and stdin
554
555 DWORD out_mode = 0;
556 DWORD in_mode = 0;
561
562 // https://docs.microsoft.com/en-us/windows/console/setconsolemode
563 const int enable_virtual_terminal_processing = 0x0004;
564 const int disable_newline_auto_return = 0x0008;
567
568 // https://docs.microsoft.com/en-us/windows/console/setconsolemode
569 const int enable_line_input = 0x0002;
570 const int enable_echo_input = 0x0004;
571 const int enable_virtual_terminal_input = 0x0200;
572 const int enable_window_input = 0x0008;
577
580#else
581 for (const int signal : {SIGWINCH, SIGTSTP}) {
583 }
584
585 struct termios terminal; // NOLINT
588
589 terminal.c_lflag &= ~ICANON; // NOLINT Non canonique terminal.
590 terminal.c_lflag &= ~ECHO; // NOLINT Do not print after a key press.
591 terminal.c_cc[VMIN] = 0;
592 terminal.c_cc[VTIME] = 0;
593 // auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
594 // fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
595 // on_exit_functions.push([=] { fcntl(STDIN_FILENO, F_GETFL, oldf); });
596
598
599#endif
600
601 auto enable = [&](const std::vector<DECMode>& parameters) {
602 std::cout << Set(parameters);
603 on_exit_functions.push([=] { std::cout << Reset(parameters); });
604 };
605
606 auto disable = [&](const std::vector<DECMode>& parameters) {
607 std::cout << Reset(parameters);
608 on_exit_functions.push([=] { std::cout << Set(parameters); });
609 };
610
611 if (use_alternative_screen_) {
612 enable({
613 DECMode::kAlternateScreen,
614 });
615 }
616
617 on_exit_functions.push([=] {
618 std::cout << "\033[?25h"; // Enable cursor.
619 std::cout << "\033[?1 q"; // Cursor block blinking.
620 });
621
622 disable({
623 // DECMode::kCursor,
624 DECMode::kLineWrap,
625 });
626
627 if (track_mouse_) {
628 enable({DECMode::kMouseVt200});
629 enable({DECMode::kMouseAnyEvent});
630 enable({DECMode::kMouseUrxvtMode});
631 enable({DECMode::kMouseSgrExtMode});
632 }
633
634 // After installing the new configuration, flush it to the terminal to
635 // ensure it is fully applied:
636 Flush();
637
638 quit_ = false;
639 task_sender_ = task_receiver_->MakeSender();
640 event_listener_ =
641 std::thread(&EventListener, &quit_, task_receiver_->MakeSender());
642 animation_listener_ =
643 std::thread(&AnimationListener, &quit_, task_receiver_->MakeSender());
644}
645
646// private
647void ScreenInteractive::Uninstall() {
648 ExitNow();
649 event_listener_.join();
650 animation_listener_.join();
651 OnExit();
652}
653
654// private
655// NOLINTNEXTLINE
656void ScreenInteractive::RunOnceBlocking(Component component) {
658 Task task;
659 if (task_receiver_->Receive(&task)) {
660 HandleTask(component, task);
661 }
662 RunOnce(component);
663}
664
665// private
666void ScreenInteractive::RunOnce(Component component) {
667 Task task;
668 while (task_receiver_->ReceiveNonBlocking(&task)) {
669 HandleTask(component, task);
671 }
672 Draw(std::move(component));
673}
674
675// private
676void ScreenInteractive::HandleTask(Component component, Task& task) {
677 // clang-format off
678 std::visit([&](auto&& arg) {
679 using T = std::decay_t<decltype(arg)>;
680
681 // Handle Event.
682 if constexpr (std::is_same_v<T, Event>) {
683 if (arg.is_cursor_reporting()) {
684 cursor_x_ = arg.cursor_x();
685 cursor_y_ = arg.cursor_y();
686 return;
687 }
688
689 if (arg.is_mouse()) {
690 arg.mouse().x -= cursor_x_;
691 arg.mouse().y -= cursor_y_;
692 }
693
694 arg.screen_ = this;
695 component->OnEvent(arg);
696 frame_valid_ = false;
697 return;
698 }
699
700 // Handle callback
701 if constexpr (std::is_same_v<T, Closure>) {
702 arg();
703 return;
704 }
705
706 // Handle Animation
707 if constexpr (std::is_same_v<T, AnimationTask>) {
708 if (!animation_requested_) {
709 return;
710 }
711
712 animation_requested_ = false;
713 const animation::TimePoint now = animation::Clock::now();
714 const animation::Duration delta = now - previous_animation_time_;
715 previous_animation_time_ = now;
716
717 animation::Params params(delta);
718 component->OnAnimation(params);
719 frame_valid_ = false;
720 return;
721 }
722 },
723 task);
724 // clang-format on
725}
726
727// private
728// NOLINTNEXTLINE
729void ScreenInteractive::Draw(Component component) {
730 if (frame_valid_) {
731 return;
732 }
733 auto document = component->Render();
734 int dimx = 0;
735 int dimy = 0;
736 auto terminal = Terminal::Size();
737 document->ComputeRequirement();
738 switch (dimension_) {
739 case Dimension::Fixed:
740 dimx = dimx_;
741 dimy = dimy_;
742 break;
743 case Dimension::TerminalOutput:
744 dimx = terminal.dimx;
745 dimy = document->requirement().min_y;
746 break;
747 case Dimension::Fullscreen:
748 dimx = terminal.dimx;
749 dimy = terminal.dimy;
750 break;
751 case Dimension::FitComponent:
752 dimx = std::min(document->requirement().min_x, terminal.dimx);
753 dimy = std::min(document->requirement().min_y, terminal.dimy);
754 break;
755 }
756
757 const bool resized = (dimx != dimx_) || (dimy != dimy_);
758 ResetCursorPosition();
759 std::cout << ResetPosition(/*clear=*/resized);
760
761 // Resize the screen if needed.
762 if (resized) {
763 dimx_ = dimx;
764 dimy_ = dimy;
765 pixels_ = std::vector<std::vector<Pixel>>(dimy, std::vector<Pixel>(dimx));
766 cursor_.x = dimx_ - 1;
767 cursor_.y = dimy_ - 1;
768 }
769
770 // Periodically request the terminal emulator the frame position relative to
771 // the screen. This is useful for converting mouse position reported in
772 // screen's coordinates to frame's coordinates.
773#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
774 // Microsoft's terminal suffers from a [bug]. When reporting the cursor
775 // position, several output sequences are mixed together into garbage.
776 // This causes FTXUI user to see some "1;1;R" sequences into the Input
777 // component. See [issue]. Solution is to request cursor position less
778 // often. [bug]: https://github.com/microsoft/terminal/pull/7583 [issue]:
779 // https://github.com/ArthurSonzogni/FTXUI/issues/136
780 static int i = -3;
781 ++i;
782 if (!use_alternative_screen_ && (i % 150 == 0)) { // NOLINT
783 std::cout << DeviceStatusReport(DSRMode::kCursor);
784 }
785#else
786 static int i = -3;
787 ++i;
788 if (!use_alternative_screen_ &&
789 (previous_frame_resized_ || i % 40 == 0)) { // NOLINT
790 std::cout << DeviceStatusReport(DSRMode::kCursor);
791 }
792#endif
793 previous_frame_resized_ = resized;
794
795 Render(*this, document);
796
797 // Set cursor position for user using tools to insert CJK characters.
798 {
799 const int dx = dimx_ - 1 - cursor_.x + int(dimx_ != terminal.dimx);
800 const int dy = dimy_ - 1 - cursor_.y;
801
802 set_cursor_position = "\x1B[" + std::to_string(dy) + "A" + //
803 "\x1B[" + std::to_string(dx) + "D";
804 reset_cursor_position = "\x1B[" + std::to_string(dy) + "B" + //
805 "\x1B[" + std::to_string(dx) + "C";
806
807 if (cursor_.shape == Cursor::Hidden) {
808 set_cursor_position += "\033[?25l";
809 } else {
810 set_cursor_position += "\033[?25h";
811 set_cursor_position +=
812 "\033[" + std::to_string(int(cursor_.shape)) + " q";
813 }
814 }
815
816 std::cout << ToString() << set_cursor_position;
817 Flush();
818 Clear();
819 frame_valid_ = true;
820}
821
822// private
823void ScreenInteractive::ResetCursorPosition() {
824 std::cout << reset_cursor_position;
825 reset_cursor_position = "";
826}
827
828/// @brief Return a function to exit the main loop.
829/// @ingroup component
831 return [this] { Exit(); };
832}
833
834/// @brief Exit the main loop.
835/// @ingroup component
837 Post([this] { ExitNow(); });
838}
839
840// private:
841void ScreenInteractive::ExitNow() {
842 quit_ = true;
843 task_sender_.reset();
844}
845
846// private:
847void ScreenInteractive::Signal(int signal) {
848 if (signal == SIGABRT) {
849 OnExit();
850 return;
851 }
852
853// Windows do no support SIGTSTP / SIGWINCH
854#if !defined(_WIN32)
855 if (signal == SIGTSTP) {
856 Post([&] {
857 ResetCursorPosition();
858 std::cout << ResetPosition(/*clear*/ true); // Cursor to the beginning
859 Uninstall();
860 dimx_ = 0;
861 dimy_ = 0;
862 Flush();
863 std::ignore = std::raise(SIGTSTP);
864 Install();
865 });
866 return;
867 }
868
869 if (signal == SIGWINCH) {
870 Post(Event::Special({0}));
871 return;
872 }
873#endif
874}
875
876} // namespace ftxui.
bool HasQuitted()
Whether the loop has quitted.
Definition loop.cpp:32
static void Signal(ScreenInteractive &s, int signal)
static ScreenInteractive TerminalOutput()
void Exit()
Exit the main loop.
static ScreenInteractive FixedSize(int dimx, int dimy)
void PostEvent(Event event)
Add an event to the main loop. It will be executed later, after every other scheduled events.
void Post(Task task)
Add a task to the main loop. It will be executed later, after every other scheduled tasks.
static ScreenInteractive FitComponent()
static ScreenInteractive Fullscreen()
static ScreenInteractive * Active()
Return the currently active screen, or null if none.
CapturedMouse CaptureMouse()
Try to get the unique lock about behing able to capture the mouse.
void TrackMouse(bool enable=true)
Set whether mouse is tracked and events reported. called outside of the main loop....
void RequestAnimationFrame()
Add a task to draw the screen one more time, until all the animations are done.
Closure ExitLoopClosure()
Return a function to exit the main loop.
Closure WithRestoredIO(Closure)
Decorate a function. It executes the same way, but with the currently active screen terminal hooks te...
int dimy() const
Definition screen.hpp:85
std::string ToString() const
Definition screen.cpp:416
std::string ResetPosition(bool clear=false) const
Return a string to be printed in order to reset the cursor position to the beginning of the screen.
Definition screen.cpp:500
Cursor cursor_
Definition screen.hpp:124
void Clear()
Clear all the pixel from the screen.
Definition screen.cpp:519
int dimx() const
Definition screen.hpp:84
std::vector< std::vector< Pixel > > pixels_
Definition screen.hpp:123
void SetFallbackSize(const Dimensions &fallbackSize)
Override terminal size in case auto-detection fails.
Definition terminal.cpp:124
Dimensions Size()
Get the terminal size.
Definition terminal.cpp:94
std::chrono::duration< float > Duration
Definition animation.hpp:24
std::chrono::time_point< Clock > TimePoint
Definition animation.hpp:23
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< ComponentBase > Component
std::string to_string(const std::wstring &s)
Convert a UTF8 std::string into a std::wstring.
Definition string.cpp:1565
Element select(Element)
Set the child to be the one selected among its siblings.
Definition frame.cpp:152
Component Slider(SliderOption< T > options)
A slider in any direction.
Definition slider.cpp:339
std::variant< Event, Closure, AnimationTask > Task
Definition task.hpp:14
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
Definition node.cpp:47
std::function< void()> Closure
Definition task.hpp:13
Represent an event. It can be key press event, a terminal resize, or more ...
Definition event.hpp:29
static Event Special(std::string)
An custom event whose meaning is defined by the user of the library.
Definition event.cpp:56