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