15#include <initializer_list>
39#define DEFINE_CONSOLEV2_PROPERTIES
40#define WIN32_LEAN_AND_MEAN
46#error Must be compiled in UNICODE mode
49#include <sys/select.h>
55#if defined(__clang__) && defined(__APPLE__)
56#define quick_exit(a) exit(a)
65 screen->RequestAnimationFrame();
76 std::cout <<
'\0' << std::flush;
86 auto parser = TerminalInputParser(
out->Clone());
108 for (
const auto& r :
records) {
109 switch (r.EventType) {
134#elif defined(__EMSCRIPTEN__)
135#include <emscripten.h>
139 auto parser = TerminalInputParser(std::move(
out));
175 auto parser = TerminalInputParser(std::move(
out));
184 std::array<char, buffer_size>
buffer;
186 for (
size_t i = 0; i < l; ++i) {
260const std::string
CSI =
"\x1b[";
263const std::string
DCS =
"\x1bP";
265const std::string
ST =
"\x1b\\";
272enum class DECMode : std::uint16_t {
278 kMouseVt200Highlight = 1001,
280 kMouseBtnEventMouse = 1002,
281 kMouseAnyEvent = 1003,
284 kMouseSgrExtMode = 1006,
285 kMouseUrxvtMode = 1015,
286 kMouseSgrPixelsMode = 1016,
287 kAlternateScreen = 1049,
291enum class DSRMode : std::uint8_t {
309std::string Set(
const std::vector<DECMode>&
parameters) {
320 return CSI + std::to_string(
int(
ps)) +
"n";
323class CapturedMouseImpl :
public CapturedMouseInterface {
325 explicit CapturedMouseImpl(std::function<
void(
void)>
callback)
328 CapturedMouseImpl(
const CapturedMouseImpl&) =
delete;
329 CapturedMouseImpl(CapturedMouseImpl&&) =
delete;
330 CapturedMouseImpl& operator=(
const CapturedMouseImpl&) =
delete;
331 CapturedMouseImpl& operator=(CapturedMouseImpl&&) =
delete;
334 std::function<
void(
void)> callback_;
339 const auto time_delta = std::chrono::milliseconds(15);
341 out->Send(AnimationTask());
348ScreenInteractive::ScreenInteractive(
int dimx,
352 : Screen(dimx, dimy),
386 Dimension::Fullscreen,
399 Dimension::Fullscreen,
409 Dimension::TerminalOutput,
419 Dimension::FitComponent,
454 task_sender_->Send(std::move(
task));
467 if (animation_requested_) {
470 animation_requested_ =
true;
471 auto now = animation::Clock::now();
474 previous_animation_time_ =
now;
483 if (mouse_captured) {
486 mouse_captured =
true;
487 return std::make_unique<CapturedMouseImpl>(
488 [
this] { mouse_captured =
false; });
501bool ScreenInteractive::HasQuitted() {
506void ScreenInteractive::PreMain() {
511 suspended_screen_->ResetCursorPosition();
513 suspended_screen_->
dimx_ = 0;
514 suspended_screen_->
dimy_ = 0;
517 suspended_screen_->Uninstall();
524 previous_animation_time_ = animation::Clock::now();
528void ScreenInteractive::PostMain() {
530 ResetCursorPosition();
535 if (suspended_screen_) {
549 if (!use_alternative_screen_) {
551 std::cout << std::flush;
570 force_handle_ctrl_c_ =
force;
576 force_handle_ctrl_z_ =
force;
584 return selection_->GetParts();
588 selection_on_change_ = std::move(
callback);
598void ScreenInteractive::Install() {
599 frame_valid_ =
false;
618 std::cout <<
"\033[?25h";
619 std::cout <<
"\033[" + std::to_string(cursor_reset_shape_) +
" q";
709 if (use_alternative_screen_) {
711 DECMode::kAlternateScreen,
721 enable({DECMode::kMouseVt200});
722 enable({DECMode::kMouseAnyEvent});
723 enable({DECMode::kMouseUrxvtMode});
724 enable({DECMode::kMouseSgrExtMode});
732 task_sender_ = task_receiver_->MakeSender();
734 std::thread(&
EventListener, &quit_, task_receiver_->MakeSender());
735 animation_listener_ =
740void ScreenInteractive::Uninstall() {
742 event_listener_.join();
743 animation_listener_.join();
752 if (task_receiver_->Receive(&
task)) {
761 while (task_receiver_->ReceiveNonBlocking(&
task)) {
767 if (selection_data_previous_ != selection_data_) {
768 selection_data_previous_ = selection_data_;
769 if (selection_on_change_) {
770 selection_on_change_();
781 using T = std::decay_t<
decltype(
arg)>;
785 if constexpr (std::is_same_v<T, Event>) {
786 if (
arg.is_cursor_position()) {
787 cursor_x_ =
arg.cursor_x();
788 cursor_y_ =
arg.cursor_y();
792 if (
arg.is_cursor_shape()) {
793 cursor_reset_shape_=
arg.cursor_shape();
797 if (
arg.is_mouse()) {
798 arg.mouse().x -= cursor_x_;
799 arg.mouse().y -= cursor_y_;
818 frame_valid_ =
false;
823 if constexpr (std::is_same_v<T, Closure>) {
829 if constexpr (std::is_same_v<T, AnimationTask>) {
830 if (!animation_requested_) {
834 animation_requested_ =
false;
837 previous_animation_time_ =
now;
841 frame_valid_ =
false;
850bool ScreenInteractive::HandleSelection(
bool handled, Event
event) {
852 selection_pending_ =
nullptr;
853 selection_data_.empty =
false;
854 selection_ =
nullptr;
858 if (!
event.is_mouse()) {
862 auto& mouse =
event.mouse();
869 selection_data_.start_x = mouse.x;
870 selection_data_.start_y = mouse.y;
871 selection_data_.end_x = mouse.x;
872 selection_data_.end_y = mouse.y;
876 if (!selection_pending_) {
881 if ((mouse.x != selection_data_.end_x) ||
882 (mouse.y != selection_data_.end_y)) {
883 selection_data_.end_x = mouse.x;
884 selection_data_.end_y = mouse.y;
885 selection_data_.empty =
false;
892 selection_pending_ =
nullptr;
893 selection_data_.end_x = mouse.x;
894 selection_data_.end_y = mouse.y;
895 selection_data_.empty =
false;
913 switch (dimension_) {
914 case Dimension::Fixed:
918 case Dimension::TerminalOutput:
922 case Dimension::Fullscreen:
926 case Dimension::FitComponent:
933 ResetCursorPosition();
938 if ((
dimx <
dimx_) && !use_alternative_screen_) {
939 std::cout <<
"\033[J";
940 std::cout <<
"\033[H";
947 pixels_ = std::vector<std::vector<Pixel>>(
dimy, std::vector<Pixel>(
dimx));
955#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
964 if (!use_alternative_screen_ && (i % 150 == 0)) {
970 if (!use_alternative_screen_ &&
971 (previous_frame_resized_ || i % 40 == 0)) {
975 previous_frame_resized_ =
resized;
977 selection_ = selection_data_.empty
978 ? std::make_unique<Selection>()
980 selection_data_.start_x, selection_data_.start_y,
981 selection_data_.end_x, selection_data_.end_y);
989 set_cursor_position.clear();
990 reset_cursor_position.clear();
993 set_cursor_position +=
"\x1B[" + std::to_string(
dy) +
"A";
994 reset_cursor_position +=
"\x1B[" + std::to_string(
dy) +
"B";
998 set_cursor_position +=
"\x1B[" + std::to_string(
dx) +
"D";
999 reset_cursor_position +=
"\x1B[" + std::to_string(
dx) +
"C";
1003 set_cursor_position +=
"\033[?25l";
1005 set_cursor_position +=
"\033[?25h";
1006 set_cursor_position +=
1011 std::cout <<
ToString() << set_cursor_position;
1014 frame_valid_ =
true;
1018void ScreenInteractive::ResetCursorPosition() {
1019 std::cout << reset_cursor_position;
1020 reset_cursor_position =
"";
1026 return [
this] {
Exit(); };
1032 Post([
this] { ExitNow(); });
1036void ScreenInteractive::ExitNow() {
1038 task_sender_.reset();
1042void ScreenInteractive::Signal(
int signal) {
1052 ResetCursorPosition();
1058 std::ignore = std::raise(
SIGTSTP);
1071bool ScreenInteractive::SelectionData::operator==(
1072 const ScreenInteractive::SelectionData&
other)
const {
1073 if (empty &&
other.empty) {
1076 if (empty ||
other.empty) {
1079 return start_x ==
other.start_x && start_y ==
other.start_y &&
1080 end_x ==
other.end_x && end_y ==
other.end_y;
1083bool ScreenInteractive::SelectionData::operator!=(
1084 const ScreenInteractive::SelectionData&
other)
const {
1085 return !(*
this ==
other);
std::vector< std::vector< Pixel > > pixels_
bool HasQuitted()
Whether the loop has quitted.
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 FullscreenPrimaryScreen()
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.
std::string GetSelection()
Returns the content of the current selection.
static ScreenInteractive FullscreenAlternateScreen()
void TrackMouse(bool enable=true)
Set whether mouse is tracked and events reported. called outside of the main loop....
void SelectionChange(std::function< void()> callback)
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.
void ForceHandleCtrlC(bool force)
Force FTXUI to handle or not handle Ctrl-C, even if the component catches the Event::CtrlC.
void ForceHandleCtrlZ(bool force)
Force FTXUI to handle or not handle Ctrl-Z, even if the component catches the Event::CtrlZ.
Closure WithRestoredIO(Closure)
Decorate a function. It executes the same way, but with the currently active screen terminal hooks te...
std::string ToString() const
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.
void Clear()
Clear all the pixel from the screen.
void SetFallbackSize(const Dimensions &fallbackSize)
Override terminal size in case auto-detection fails.
Dimensions Size()
Get the terminal size.
std::chrono::duration< float > Duration
std::chrono::time_point< Clock > TimePoint
void RequestAnimationFrame()
std::unique_ptr< CapturedMouseInterface > CapturedMouse
std::shared_ptr< T > Make(Args &&... args)
std::shared_ptr< ComponentBase > Component
std::string to_string(const std::wstring &s)
Convert a UTF8 std::string into a std::wstring.
Element select(Element)
Set the child to be the one selected among its siblings.
std::variant< Event, Closure, AnimationTask > Task
void Render(Screen &screen, const Element &element)
Display an element on a ftxui::Screen.
std::function< void()> Closure
Represent an event. It can be key press event, a terminal resize, or more ...
static const Event Custom
static Event Special(std::string)
An custom event whose meaning is defined by the user of the library.