screen_interactive.cpp
ftxui/component/screen_interactive.hpp
algorithm
array
atomic
chrono
csignal
cstdint
cstdio
ftxui/component/task.hpp
ftxui/screen/screen.hpp
functional
initializer_list
iostream
memory
stack
string
thread
tuple
type_traits
utility
variant
vector
ftxui/component/animation.hpp
ftxui/component/captured_mouse.hpp
ftxui/component/component_base.hpp
ftxui/component/event.hpp
ftxui/component/loop.hpp
ftxui/component/receiver.hpp
ftxui/component/terminal_input_parser.hpp
ftxui/dom/node.hpp
ftxui/dom/requirement.hpp
ftxui/screen/pixel.hpp
ftxui/screen/terminal.hpp
ftxui/screen/util.hpp
sys/select.h
termios.h
unistd.h
ftxui/component/animation.hpp
ftxui/component/captured_mouse.hpp
ftxui/component/component_base.hpp
ftxui/component/event.hpp
ftxui/component/loop.hpp
ftxui/component/mouse.hpp
ftxui/component/receiver.hpp
ftxui/component/screen_interactive.hpp
ftxui/component/task.hpp
ftxui/dom/canvas.hpp
ftxui/dom/deprecated.hpp
ftxui/dom/direction.hpp
ftxui/dom/elements.hpp
ftxui/dom/flexbox_config.hpp
ftxui/dom/linear_gradient.hpp
ftxui/dom/node.hpp
ftxui/dom/requirement.hpp
ftxui/dom/selection.hpp
ftxui/screen/box.hpp
ftxui/screen/color.hpp
ftxui/screen/image.hpp
ftxui/screen/pixel.hpp
ftxui/screen/screen.hpp
ftxui/screen/string.hpp
ftxui/screen/terminal.hpp
ftxui/util/autoreset.hpp
ftxui/util/ref.hpp
src/ftxui/component/screen_interactive.cpp
ftxui/component/terminal_input_parser.hpp
ftxui/screen/util.hpp
algorithm
array
atomic
chrono
condition_variable
csignal
cstddef
cstdint
cstdio
ftxui/dom/take_any_args.hpp
functional
initializer_list
iostream
memory
mutex
optional
queue
sstream
stack
string
sys/select.h
termios.h
thread
tuple
type_traits
unistd.h
unordered_map
utility
variant
vector
ftxui
ftxui::animation

    //Copyright2020ArthurSonzogni.Allrightsreserved.
    //UseofthissourcecodeisgovernedbytheMITlicensethatcanbefoundin
    //theLICENSEfile.
    #include"ftxui/component/screen_interactive.hpp"
    #include<algorithm>//forcopy,max,min
    #include<array>//forarray
    #include<atomic>
    #include<chrono>//foroperator-,milliseconds,operator>=,duration,common_type<>::type,time_point
    #include<csignal>//forsignal,SIGTSTP,SIGABRT,SIGWINCH,raise,SIGFPE,SIGILL,SIGINT,SIGSEGV,SIGTERM,__sighandler_t,size_t
    #include<cstdint>
    #include<cstdio>//forfileno,stdin
    #include<ftxui/component/task.hpp>//forTask,Closure,AnimationTask
    #include<ftxui/screen/screen.hpp>//forPixel,Screen::Cursor,Screen,Screen::Cursor::Hidden
    #include<functional>//forfunction
    #include<initializer_list>//forinitializer_list
    #include<iostream>//forcout,ostream,operator<<,basic_ostream,endl,flush
    #include<memory>
    #include<stack>//forstack
    #include<string>
    #include<thread>//forthread,sleep_for
    #include<tuple>//for_Swallow_assign,ignore
    #include<type_traits>//fordecay_t
    #include<utility>//formove,swap
    #include<variant>//forvisit,variant
    #include<vector>//forvector
    #include"ftxui/component/animation.hpp"//forTimePoint,Clock,Duration,Params,RequestAnimationFrame
    #include"ftxui/component/captured_mouse.hpp"//forCapturedMouse,CapturedMouseInterface
    #include"ftxui/component/component_base.hpp"//forComponentBase
    #include"ftxui/component/event.hpp"//forEvent
    #include"ftxui/component/loop.hpp"//forLoop
    #include"ftxui/component/receiver.hpp"//forReceiverImpl,Sender,MakeReceiver,SenderImpl,Receiver
    #include"ftxui/component/terminal_input_parser.hpp"//forTerminalInputParser
    #include"ftxui/dom/node.hpp"//forNode,Render
    #include"ftxui/dom/requirement.hpp"//forRequirement
    #include"ftxui/screen/pixel.hpp"//forPixel
    #include"ftxui/screen/terminal.hpp"//forDimensions,Size
    #include"ftxui/screen/util.hpp"//forutil::clamp

    #ifdefined(_WIN32)
    #defineDEFINE_CONSOLEV2_PROPERTIES
    #defineWIN32_LEAN_AND_MEAN
    #ifndefNOMINMAX
    #defineNOMINMAX
    #endif
    #include<windows.h>
    #ifndefUNICODE
    #errorMustbecompiledinUNICODEmode
    #endif
    #else
    #include<sys/select.h>//forselect,FD_ISSET,FD_SET,FD_ZERO,fd_set,timeval
    #include<termios.h>//fortcsetattr,termios,tcgetattr,TCSANOW,cc_t,ECHO,ICANON,VMIN,VTIME
    #include<unistd.h>//forSTDIN_FILENO,read
    #endif

    //QuickexitismissinginstandardCLangheaders
    #ifdefined(__clang__)&&defined(__APPLE__)
    #definequick_exit(a)exit(a)
    #endif

    namespaceftxui{

    namespaceanimation{
    voidRequestAnimationFrame(){
    auto*screen=ScreenInteractive::Active();
    if(screen){
    screen->RequestAnimationFrame();
    }
    }
    }//namespaceanimation

    namespace{

    ScreenInteractive*g_active_screen=nullptr;//NOLINT

    voidFlush(){
    //Emscriptendoesn'timplementflush.Weinterpretzeroasflush.
    std::cout<<'\0'<<std::flush;
    }

    constexprinttimeout_milliseconds=20;
    [[maybe_unused]]constexprinttimeout_microseconds=
    timeout_milliseconds*1000;
    #ifdefined(_WIN32)

    voidEventListener(std::atomic<bool>*quit,Sender<Task>out){
    autoconsole=GetStdHandle(STD_INPUT_HANDLE);
    autoparser=TerminalInputParser(out->Clone());
    while(!*quit){
    //ThrottleReadConsoleInputbywaiting250ms,thiswaitfunctionwill
    //returnifthereisinputintheconsole.
    autowait_result=WaitForSingleObject(console,timeout_milliseconds);
    if(wait_result==WAIT_TIMEOUT){
    parser.Timeout(timeout_milliseconds);
    continue;
    }

    DWORDnumber_of_events=0;
    if(!GetNumberOfConsoleInputEvents(console,&number_of_events))
    continue;
    if(number_of_events<=0)
    continue;

    std::vector<INPUT_RECORD>records{number_of_events};
    DWORDnumber_of_events_read=0;
    ReadConsoleInput(console,records.data(),(DWORD)records.size(),
    &number_of_events_read);
    records.resize(number_of_events_read);

    for(constauto&r:records){
    switch(r.EventType){
    caseKEY_EVENT:{
    autokey_event=r.Event.KeyEvent;
    //ignoreUPkeyevents
    if(key_event.bKeyDown==FALSE)
    continue;
    std::wstringwstring;
    wstring+=key_event.uChar.UnicodeChar;
    for(autoit:to_string(wstring)){
    parser.Add(it);
    }
    }break;
    caseWINDOW_BUFFER_SIZE_EVENT:
    out->Send(Event::Special({0}));
    break;
    caseMENU_EVENT:
    caseFOCUS_EVENT:
    caseMOUSE_EVENT:
    //TODO(mauve):Implementlater.
    break;
    }
    }
    }
    }

    #elifdefined(__EMSCRIPTEN__)
    #include<emscripten.h>

    //Readcharfromtheterminal.
    voidEventListener(std::atomic<bool>*quit,Sender<Task>out){
    autoparser=TerminalInputParser(std::move(out));

    charc;
    while(!*quit){
    while(read(STDIN_FILENO,&c,1),c)
    parser.Add(c);

    emscripten_sleep(1);
    parser.Timeout(1);
    }
    }

    extern"C"{
    EMSCRIPTEN_KEEPALIVE
    voidftxui_on_resize(intcolumns,introws){
    Terminal::SetFallbackSize({
    columns,
    rows,
    });
    std::raise(SIGWINCH);
    }
    }

    #else//POSIX(Linux&Mac)

    intCheckStdinReady(intusec_timeout){
    timevaltv={0,usec_timeout};//NOLINT
    fd_setfds;
    FD_ZERO(&fds);//NOLINT
    FD_SET(STDIN_FILENO,&fds);//NOLINT
    select(STDIN_FILENO+1,&fds,nullptr,nullptr,&tv);//NOLINT
    returnFD_ISSET(STDIN_FILENO,&fds);//NOLINT
    }

    //Readcharfromtheterminal.
    voidEventListener(std::atomic<bool>*quit,Sender<Task>out){
    autoparser=TerminalInputParser(std::move(out));

    while(!*quit){
    if(!CheckStdinReady(timeout_microseconds)){
    parser.Timeout(timeout_milliseconds);
    continue;
    }

    constsize_tbuffer_size=100;
    std::array<char,buffer_size>buffer;//NOLINT;
    size_tl=read(fileno(stdin),buffer.data(),buffer_size);//NOLINT
    for(size_ti=0;i<l;++i){
    parser.Add(buffer[i]);//NOLINT
    }
    }
    }
    #endif

    std::stack<Closure>on_exit_functions;//NOLINT
    voidOnExit(){
    while(!on_exit_functions.empty()){
    on_exit_functions.top()();
    on_exit_functions.pop();
    }
    }

    std::atomic<int>g_signal_exit_count=0;//NOLINT
    #if!defined(_WIN32)
    std::atomic<int>g_signal_stop_count=0;//NOLINT
    std::atomic<int>g_signal_resize_count=0;//NOLINT
    #endif

    //Asyncsignalsafefunction
    voidRecordSignal(intsignal){
    switch(signal){
    caseSIGABRT:
    caseSIGFPE:
    caseSIGILL:
    caseSIGINT:
    caseSIGSEGV:
    caseSIGTERM:
    g_signal_exit_count++;
    break;

    #if!defined(_WIN32)
    caseSIGTSTP://NOLINT
    g_signal_stop_count++;
    break;

    caseSIGWINCH://NOLINT
    g_signal_resize_count++;
    break;
    #endif

    default:
    break;
    }
    }

    voidExecuteSignalHandlers(){
    intsignal_exit_count=g_signal_exit_count.exchange(0);
    while(signal_exit_count--){
    ScreenInteractive::Private::Signal(*g_active_screen,SIGABRT);
    }

    #if!defined(_WIN32)
    intsignal_stop_count=g_signal_stop_count.exchange(0);
    while(signal_stop_count--){
    ScreenInteractive::Private::Signal(*g_active_screen,SIGTSTP);
    }

    intsignal_resize_count=g_signal_resize_count.exchange(0);
    while(signal_resize_count--){
    ScreenInteractive::Private::Signal(*g_active_screen,SIGWINCH);
    }
    #endif
    }

    voidInstallSignalHandler(intsig){
    autoold_signal_handler=std::signal(sig,RecordSignal);
    on_exit_functions.emplace(
    [=]{std::ignore=std::signal(sig,old_signal_handler);});
    }

    //CSI:ControlSequenceIntroducer
    conststd::stringCSI="\x1b[";//NOLINT
    //
    //DCS:DeviceControlString
    conststd::stringDCS="\x1bP";//NOLINT
    //ST:StringTerminator
    conststd::stringST="\x1b\\";//NOLINT

    //DECRQSS:RequestStatusString
    //DECSCUSR:SetCursorStyle
    conststd::stringDECRQSS_DECSCUSR=DCS+"$qq"+ST;//NOLINT

    //DEC:DigitalEquipmentCorporation
    enumclassDECMode:std::uint16_t{
    kLineWrap=7,
    kCursor=25,

    kMouseX10=9,
    kMouseVt200=1000,
    kMouseVt200Highlight=1001,

    kMouseBtnEventMouse=1002,
    kMouseAnyEvent=1003,

    kMouseUtf8=1005,
    kMouseSgrExtMode=1006,
    kMouseUrxvtMode=1015,
    kMouseSgrPixelsMode=1016,
    kAlternateScreen=1049,
    };

    //DeviceStatusReport(DSR){
    enumclassDSRMode:std::uint8_t{
    kCursor=6,
    };

    std::stringSerialize(conststd::vector<DECMode>&parameters){
    boolfirst=true;
    std::stringout;
    for(constDECModeparameter:parameters){
    if(!first){
    out+=";";
    }
    out+=std::to_string(int(parameter));
    first=false;
    }
    returnout;
    }

    //DECPrivateModeSet(DECSET)
    std::stringSet(conststd::vector<DECMode>&parameters){
    returnCSI+"?"+Serialize(parameters)+"h";
    }

    //DECPrivateModeReset(DECRST)
    std::stringReset(conststd::vector<DECMode>&parameters){
    returnCSI+"?"+Serialize(parameters)+"l";
    }

    //DeviceStatusReport(DSR)
    std::stringDeviceStatusReport(DSRModeps){
    returnCSI+std::to_string(int(ps))+"n";
    }

    classCapturedMouseImpl:publicCapturedMouseInterface{
    public:
    explicitCapturedMouseImpl(std::function<void(void)>callback)
    :callback_(std::move(callback)){}
    ~CapturedMouseImpl()override{callback_();}
    CapturedMouseImpl(constCapturedMouseImpl&)=delete;
    CapturedMouseImpl(CapturedMouseImpl&&)=delete;
    CapturedMouseImpl&operator=(constCapturedMouseImpl&)=delete;
    CapturedMouseImpl&operator=(CapturedMouseImpl&&)=delete;

    private:
    std::function<void(void)>callback_;
    };

    voidAnimationListener(std::atomic<bool>*quit,Sender<Task>out){
    //Animationataround60fps.
    constautotime_delta=std::chrono::milliseconds(15);
    while(!*quit){
    out->Send(AnimationTask());
    std::this_thread::sleep_for(time_delta);
    }
    }

    }//namespace

    ScreenInteractive::ScreenInteractive(intdimx,
    intdimy,
    Dimensiondimension,
    booluse_alternative_screen)
    :Screen(dimx,dimy),
    dimension_(dimension),
    use_alternative_screen_(use_alternative_screen){
    task_receiver_=MakeReceiver<Task>();
    }

    //static
    ScreenInteractiveScreenInteractive::FixedSize(intdimx,intdimy){
    return{
    dimx,
    dimy,
    Dimension::Fixed,
    false,
    };
    }

    ///@ingroupcomponent
    ///CreateaScreenInteractivetakingthefullterminalsize.Thisisusingthe
    ///alternatescreenbuffertoavoidmessingwiththeterminalcontent.
    ///@noteThisisthesameas`ScreenInteractive::FullscreenAlternateScreen()`
    //static
    ScreenInteractiveScreenInteractive::Fullscreen(){
    returnFullscreenAlternateScreen();
    }

    ///@ingroupcomponent
    ///CreateaScreenInteractivetakingthefullterminalsize.Theprimaryscreen
    ///bufferisbeingused.Itmeansiftheterminalisresized,theprevious
    ///contentmightmessupwiththeterminalcontent.
    //static
    ScreenInteractiveScreenInteractive::FullscreenPrimaryScreen(){
    return{
    0,
    0,
    Dimension::Fullscreen,
    false,
    };
    }

    ///@ingroupcomponent
    ///CreateaScreenInteractivetakingthefullterminalsize.Thisisusingthe
    ///alternatescreenbuffertoavoidmessingwiththeterminalcontent.
    //static
    ScreenInteractiveScreenInteractive::FullscreenAlternateScreen(){
    return{
    0,
    0,
    Dimension::Fullscreen,
    true,
    };
    }

    //static
    ScreenInteractiveScreenInteractive::TerminalOutput(){
    return{
    0,
    0,
    Dimension::TerminalOutput,
    false,
    };
    }

    //static
    ScreenInteractiveScreenInteractive::FitComponent(){
    return{
    0,
    0,
    Dimension::FitComponent,
    false,
    };
    }

    ///@ingroupcomponent
    ///@briefSetwhethermouseistrackedandeventsreported.
    ///calledoutsideofthemainloop.E.g`ScreenInteractive::Loop(...)`.
    ///@paramenableWhethertoenablemouseeventtracking.
    ///@noteThismusebecalledoutsideofthemainloop.E.g.beforecalling
    ///`ScreenInteractive::Loop`.
    ///@noteMousetrackingisenabledbydefault.
    ///@noteMousetrackingisonlysupportedonterminalsthatsupportsit.
    ///
    ///###Example
    ///
    ///```cpp
    ///autoscreen=ScreenInteractive::TerminalOutput();
    ///screen.TrackMouse(false);
    ///screen.Loop(component);
    ///```
    voidScreenInteractive::TrackMouse(boolenable){
    track_mouse_=enable;
    }

    ///@briefAddatasktothemainloop.
    ///Itwillbeexecutedlater,aftereveryotherscheduledtasks.
    ///@ingroupcomponent
    voidScreenInteractive::Post(Tasktask){
    //Task/Eventssenttowardinactivescreenorscreenwaitingtobecome
    //inactivearedropped.
    if(!task_sender_){
    return;
    }

    task_sender_->Send(std::move(task));
    }

    ///@briefAddaneventtothemainloop.
    ///Itwillbeexecutedlater,aftereveryotherscheduledevents.
    ///@ingroupcomponent
    voidScreenInteractive::PostEvent(Eventevent){
    Post(event);
    }

    ///@briefAddatasktodrawthescreenonemoretime,untilalltheanimations
    ///aredone.
    voidScreenInteractive::RequestAnimationFrame(){
    if(animation_requested_){
    return;
    }
    animation_requested_=true;
    autonow=animation::Clock::now();
    constautotime_histeresis=std::chrono::milliseconds(33);
    if(now-previous_animation_time_>=time_histeresis){
    previous_animation_time_=now;
    }
    }

    ///@briefTrytogettheuniquelockaboutbehingabletocapturethemouse.
    ///@returnAuniquelockifthemouseisnotalreadycaptured,otherwisea
    ///null.
    ///@ingroupcomponent
    CapturedMouseScreenInteractive::CaptureMouse(){
    if(mouse_captured){
    returnnullptr;
    }
    mouse_captured=true;
    returnstd::make_unique<CapturedMouseImpl>(
    [this]{mouse_captured=false;});
    }

    ///@briefExecutethemainloop.
    ///@paramcomponentThecomponenttodraw.
    ///@ingroupcomponent
    voidScreenInteractive::Loop(Componentcomponent){//NOLINT
    classLooploop(this,std::move(component));
    loop.Run();
    }

    ///@briefReturnwhetherthemainloophasbeenquit.
    ///@ingroupcomponent
    boolScreenInteractive::HasQuitted(){
    returntask_receiver_->HasQuitted();
    }

    //private
    voidScreenInteractive::PreMain(){
    //Suspendpreviouslyactivescreen:
    if(g_active_screen){
    std::swap(suspended_screen_,g_active_screen);
    //Resetcursorpositiontothetopofthescreenandclearthescreen.
    suspended_screen_->ResetCursorPosition();
    std::cout<<suspended_screen_->ResetPosition(/*clear=*/true);
    suspended_screen_->dimx_=0;
    suspended_screen_->dimy_=0;

    //Resetdimensionstoforcedrawingthescreenagainnexttime:
    suspended_screen_->Uninstall();
    }

    //Thisscreenisnowactive:
    g_active_screen=this;
    g_active_screen->Install();

    previous_animation_time_=animation::Clock::now();
    }

    //private
    voidScreenInteractive::PostMain(){
    //Putcursorpositionattheendofthedrawing.
    ResetCursorPosition();

    g_active_screen=nullptr;

    //Restoresuspendedscreen.
    if(suspended_screen_){
    //Clearscreen,andputthecursoratthebeginningofthedrawing.
    std::cout<<ResetPosition(/*clear=*/true);
    dimx_=0;
    dimy_=0;
    Uninstall();
    std::swap(g_active_screen,suspended_screen_);
    g_active_screen->Install();
    }else{
    Uninstall();

    std::cout<<'\r';
    //Onfinalexit,keepthecurrentdrawingandresetcursorpositionone
    //lineafterit.
    if(!use_alternative_screen_){
    std::cout<<'\n';
    std::cout<<std::flush;
    }
    }
    }

    ///@briefDecorateafunction.Itexecutesthesameway,butwiththecurrently
    ///activescreenterminalhookstemporarillyuninstalledduringitsexecution.
    ///@paramfnThefunctiontodecorate.
    ClosureScreenInteractive::WithRestoredIO(Closurefn){//NOLINT
    return[this,fn]{
    Uninstall();
    fn();
    Install();
    };
    }

    ///@briefForceFTXUItohandleornothandleCtrl-C,evenifthecomponent
    ///catchestheEvent::CtrlC.
    voidScreenInteractive::ForceHandleCtrlC(boolforce){
    force_handle_ctrl_c_=force;
    }

    ///@briefForceFTXUItohandleornothandleCtrl-Z,evenifthecomponent
    ///catchestheEvent::CtrlZ.
    voidScreenInteractive::ForceHandleCtrlZ(boolforce){
    force_handle_ctrl_z_=force;
    }

    ///@briefReturnsthecontentofthecurrentselection
    std::stringScreenInteractive::GetSelection(){
    if(!selection_){
    return"";
    }
    returnselection_->GetParts();
    }

    voidScreenInteractive::SelectionChange(std::function<void()>callback){
    selection_on_change_=std::move(callback);
    }

    ///@briefReturnthecurrentlyactivescreen,ornullifnone.
    //static
    ScreenInteractive*ScreenInteractive::Active(){
    returng_active_screen;
    }

    //private
    voidScreenInteractive::Install(){
    frame_valid_=false;

    //Flushthebufferforstdouttoensurewhatevertheuserhasprintedbefore
    //isfullyappliedbeforewestartmodifyingtheterminalconfiguration.This
    //isimportant,becauseweareusingtwodifferentchannels(stdoutvs
    //termios/WinAPI)tocommunicatewiththeterminalemulatorbelow.See
    //https://github.com/ArthurSonzogni/FTXUI/issues/846
    Flush();

    //Afteruninstallingthenewconfiguration,flushittotheterminalto
    //ensureitisfullyapplied:
    on_exit_functions.emplace([]{Flush();});

    on_exit_functions.emplace([this]{ExitLoopClosure()();});

    //Requesttheterminaltoreportthecurrentcursorshape.Wewillrestoreit
    //onexit.
    std::cout<<DECRQSS_DECSCUSR;
    on_exit_functions.emplace([this]{
    std::cout<<"\033[?25h";//Enablecursor.
    std::cout<<"\033["+std::to_string(cursor_reset_shape_)+"q";
    });

    //Installsignalhandlerstorestoretheterminalstateonexit.Thedefault
    //signalhandlersarerestoredonexit.
    for(constintsignal:{SIGTERM,SIGSEGV,SIGINT,SIGILL,SIGABRT,SIGFPE}){
    InstallSignalHandler(signal);
    }

    //Savetheoldterminalconfigurationandrestoreitonexit.
    #ifdefined(_WIN32)
    //EnableVTprocessingonstdoutandstdin
    autostdout_handle=GetStdHandle(STD_OUTPUT_HANDLE);
    autostdin_handle=GetStdHandle(STD_INPUT_HANDLE);

    DWORDout_mode=0;
    DWORDin_mode=0;
    GetConsoleMode(stdout_handle,&out_mode);
    GetConsoleMode(stdin_handle,&in_mode);
    on_exit_functions.push([=]{SetConsoleMode(stdout_handle,out_mode);});
    on_exit_functions.push([=]{SetConsoleMode(stdin_handle,in_mode);});

    //https://docs.microsoft.com/en-us/windows/console/setconsolemode
    constintenable_virtual_terminal_processing=0x0004;
    constintdisable_newline_auto_return=0x0008;
    out_mode|=enable_virtual_terminal_processing;
    out_mode|=disable_newline_auto_return;

    //https://docs.microsoft.com/en-us/windows/console/setconsolemode
    constintenable_line_input=0x0002;
    constintenable_echo_input=0x0004;
    constintenable_virtual_terminal_input=0x0200;
    constintenable_window_input=0x0008;
    in_mode&=~enable_echo_input;
    in_mode&=~enable_line_input;
    in_mode|=enable_virtual_terminal_input;
    in_mode|=enable_window_input;

    SetConsoleMode(stdin_handle,in_mode);
    SetConsoleMode(stdout_handle,out_mode);
    #else
    for(constintsignal:{SIGWINCH,SIGTSTP}){
    InstallSignalHandler(signal);
    }

    structtermiosterminal;//NOLINT
    tcgetattr(STDIN_FILENO,&terminal);
    on_exit_functions.emplace(
    [=]{tcsetattr(STDIN_FILENO,TCSANOW,&terminal);});

    //Enablingrawterminalinputmode
    terminal.c_iflag&=~IGNBRK;//Disableignoringbreakcondition
    terminal.c_iflag&=~BRKINT;//Disablebreakcausinginputandoutputtobe
    //flushed
    terminal.c_iflag&=~PARMRK;//Disablemarkingparityerrors.
    terminal.c_iflag&=~ISTRIP;//Disablestriping8thbitoffcharacters.
    terminal.c_iflag&=~INLCR;//DisablemappingNLtoCR.
    terminal.c_iflag&=~IGNCR;//DisableignoringCR.
    terminal.c_iflag&=~ICRNL;//DisablemappingCRtoNL.
    terminal.c_iflag&=~IXON;//DisableXON/XOFFflowcontrolonoutput

    terminal.c_lflag&=~ECHO;//Disableechoinginputcharacters.
    terminal.c_lflag&=~ECHONL;//Disableechoingnewlinecharacters.
    terminal.c_lflag&=~ICANON;//DisableCanonicalmode.
    terminal.c_lflag&=~ISIG;//Disablesendingsignalwhenhitting:
    //-=>DSUSP
    //-C-Z=>SUSP
    //-C-C=>INTR
    //-C-d=>QUIT
    terminal.c_lflag&=~IEXTEN;//Disableextendedinputprocessing
    terminal.c_cflag|=CS8;//8bitsperbyte

    terminal.c_cc[VMIN]=0;//Minimumnumberofcharactersfornon-canonical
    //read.
    terminal.c_cc[VTIME]=0;//Timeoutindecisecondsfornon-canonicalread.

    tcsetattr(STDIN_FILENO,TCSANOW,&terminal);

    #endif

    autoenable=[&](conststd::vector<DECMode>&parameters){
    std::cout<<Set(parameters);
    on_exit_functions.emplace([=]{std::cout<<Reset(parameters);});
    };

    autodisable=[&](conststd::vector<DECMode>&parameters){
    std::cout<<Reset(parameters);
    on_exit_functions.emplace([=]{std::cout<<Set(parameters);});
    };

    if(use_alternative_screen_){
    enable({
    DECMode::kAlternateScreen,
    });
    }

    disable({
    //DECMode::kCursor,
    DECMode::kLineWrap,
    });

    if(track_mouse_){
    enable({DECMode::kMouseVt200});
    enable({DECMode::kMouseAnyEvent});
    enable({DECMode::kMouseUrxvtMode});
    enable({DECMode::kMouseSgrExtMode});
    }

    //Afterinstallingthenewconfiguration,flushittotheterminalto
    //ensureitisfullyapplied:
    Flush();

    quit_=false;
    task_sender_=task_receiver_->MakeSender();
    event_listener_=
    std::thread(&EventListener,&quit_,task_receiver_->MakeSender());
    animation_listener_=
    std::thread(&AnimationListener,&quit_,task_receiver_->MakeSender());
    }

    //private
    voidScreenInteractive::Uninstall(){
    ExitNow();
    event_listener_.join();
    animation_listener_.join();
    OnExit();
    }

    //private
    //NOLINTNEXTLINE
    voidScreenInteractive::RunOnceBlocking(Componentcomponent){
    ExecuteSignalHandlers();
    Tasktask;
    if(task_receiver_->Receive(&task)){
    HandleTask(component,task);
    }
    RunOnce(component);
    }

    //private
    voidScreenInteractive::RunOnce(Componentcomponent){
    Tasktask;
    while(task_receiver_->ReceiveNonBlocking(&task)){
    HandleTask(component,task);
    ExecuteSignalHandlers();
    }
    Draw(std::move(component));

    if(selection_data_previous_!=selection_data_){
    selection_data_previous_=selection_data_;
    if(selection_on_change_){
    selection_on_change_();
    Post(Event::Custom);
    }
    }
    }

    //private
    //NOLINTNEXTLINE
    voidScreenInteractive::HandleTask(Componentcomponent,Task&task){
    std::visit(
    [&](auto&&arg){
    usingT=std::decay_t<decltype(arg)>;

    //clang-formatoff
    //HandleEvent.
    ifconstexpr(std::is_same_v<T,Event>){
    if(arg.is_cursor_position()){
    cursor_x_=arg.cursor_x();
    cursor_y_=arg.cursor_y();
    return;
    }

    if(arg.is_cursor_shape()){
    cursor_reset_shape_=arg.cursor_shape();
    return;
    }

    if(arg.is_mouse()){
    arg.mouse().x-=cursor_x_;
    arg.mouse().y-=cursor_y_;
    }

    arg.screen_=this;

    boolhandled=component->OnEvent(arg);

    handled=HandleSelection(handled,arg);

    if(arg==Event::CtrlC&&(!handled||force_handle_ctrl_c_)){
    RecordSignal(SIGABRT);
    }

    #if!defined(_WIN32)
    if(arg==Event::CtrlZ&&(!handled||force_handle_ctrl_z_)){
    RecordSignal(SIGTSTP);
    }
    #endif

    frame_valid_=false;
    return;
    }

    //Handlecallback
    ifconstexpr(std::is_same_v<T,Closure>){
    arg();
    return;
    }

    //HandleAnimation
    ifconstexpr(std::is_same_v<T,AnimationTask>){
    if(!animation_requested_){
    return;
    }

    animation_requested_=false;
    constanimation::TimePointnow=animation::Clock::now();
    constanimation::Durationdelta=now-previous_animation_time_;
    previous_animation_time_=now;

    animation::Paramsparams(delta);
    component->OnAnimation(params);
    frame_valid_=false;
    return;
    }
    },
    task);
    //clang-formaton
    }

    //private
    boolScreenInteractive::HandleSelection(boolhandled,Eventevent){
    if(handled){
    selection_pending_=nullptr;
    selection_data_.empty=true;
    selection_=nullptr;
    returntrue;
    }

    if(!event.is_mouse()){
    returnfalse;
    }

    auto&mouse=event.mouse();
    if(mouse.button!=Mouse::Left){
    returnfalse;
    }

    if(mouse.motion==Mouse::Pressed){
    selection_pending_=CaptureMouse();
    selection_data_.start_x=mouse.x;
    selection_data_.start_y=mouse.y;
    selection_data_.end_x=mouse.x;
    selection_data_.end_y=mouse.y;
    returnfalse;
    }

    if(!selection_pending_){
    returnfalse;
    }

    if(mouse.motion==Mouse::Moved){
    if((mouse.x!=selection_data_.end_x)||
    (mouse.y!=selection_data_.end_y)){
    selection_data_.end_x=mouse.x;
    selection_data_.end_y=mouse.y;
    selection_data_.empty=false;
    }

    returntrue;
    }

    if(mouse.motion==Mouse::Released){
    selection_pending_=nullptr;
    selection_data_.end_x=mouse.x;
    selection_data_.end_y=mouse.y;
    selection_data_.empty=false;
    returntrue;
    }

    returnfalse;
    }

    //private
    //NOLINTNEXTLINE
    voidScreenInteractive::Draw(Componentcomponent){
    if(frame_valid_){
    return;
    }
    autodocument=component->Render();
    intdimx=0;
    intdimy=0;
    autoterminal=Terminal::Size();
    document->ComputeRequirement();
    switch(dimension_){
    caseDimension::Fixed:
    dimx=dimx_;
    dimy=dimy_;
    break;
    caseDimension::TerminalOutput:
    dimx=terminal.dimx;
    dimy=util::clamp(document->requirement().min_y,0,terminal.dimy);
    break;
    caseDimension::Fullscreen:
    dimx=terminal.dimx;
    dimy=terminal.dimy;
    break;
    caseDimension::FitComponent:
    dimx=util::clamp(document->requirement().min_x,0,terminal.dimx);
    dimy=util::clamp(document->requirement().min_y,0,terminal.dimy);
    break;
    }

    constboolresized=(dimx!=dimx_)||(dimy!=dimy_);
    ResetCursorPosition();
    std::cout<<ResetPosition(/*clear=*/resized);

    //Iftheterminalwidthdecrease,theterminalemulatorwillstartwrapping
    //linesandmakethedisplaydirty.Weshouldclearitcompletely.
    if((dimx<dimx_)&&!use_alternative_screen_){
    std::cout<<"\033[J";//clearterminaloutput
    std::cout<<"\033[H";//movecursortohomeposition
    }

    //Resizethescreenifneeded.
    if(resized){
    dimx_=dimx;
    dimy_=dimy;
    pixels_=std::vector<std::vector<Pixel>>(dimy,std::vector<Pixel>(dimx));
    cursor_.x=dimx_-1;
    cursor_.y=dimy_-1;
    }

    //Periodicallyrequesttheterminalemulatortheframepositionrelativeto
    //thescreen.Thisisusefulforconvertingmousepositionreportedin
    //screen'scoordinatestoframe'scoordinates.
    #ifdefined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
    //Microsoft'sterminalsuffersfroma[bug].Whenreportingthecursor
    //position,severaloutputsequencesaremixedtogetherintogarbage.
    //ThiscausesFTXUIusertoseesome"1;1;R"sequencesintotheInput
    //component.See[issue].Solutionistorequestcursorpositionless
    //often.[bug]:https://github.com/microsoft/terminal/pull/7583[issue]:
    //https://github.com/ArthurSonzogni/FTXUI/issues/136
    staticinti=-3;
    ++i;
    if(!use_alternative_screen_&&(i%150==0)){//NOLINT
    std::cout<<DeviceStatusReport(DSRMode::kCursor);
    }
    #else
    staticinti=-3;
    ++i;
    if(!use_alternative_screen_&&
    (previous_frame_resized_||i%40==0)){//NOLINT
    std::cout<<DeviceStatusReport(DSRMode::kCursor);
    }
    #endif
    previous_frame_resized_=resized;

    selection_=selection_data_.empty
    ?std::make_unique<Selection>()
    :std::make_unique<Selection>(
    selection_data_.start_x,selection_data_.start_y,//
    selection_data_.end_x,selection_data_.end_y);
    Render(*this,document.get(),*selection_);

    //SetcursorpositionforuserusingtoolstoinsertCJKcharacters.
    {
    constintdx=dimx_-1-cursor_.x+int(dimx_!=terminal.dimx);
    constintdy=dimy_-1-cursor_.y;

    set_cursor_position.clear();
    reset_cursor_position.clear();

    if(dy!=0){
    set_cursor_position+="\x1B["+std::to_string(dy)+"A";
    reset_cursor_position+="\x1B["+std::to_string(dy)+"B";
    }

    if(dx!=0){
    set_cursor_position+="\x1B["+std::to_string(dx)+"D";
    reset_cursor_position+="\x1B["+std::to_string(dx)+"C";
    }

    if(cursor_.shape==Cursor::Hidden){
    set_cursor_position+="\033[?25l";
    }else{
    set_cursor_position+="\033[?25h";
    set_cursor_position+=
    "\033["+std::to_string(int(cursor_.shape))+"q";
    }
    }

    std::cout<<ToString()<<set_cursor_position;
    Flush();
    Clear();
    frame_valid_=true;
    }

    //private
    voidScreenInteractive::ResetCursorPosition(){
    std::cout<<reset_cursor_position;
    reset_cursor_position="";
    }

    ///@briefReturnafunctiontoexitthemainloop.
    ///@ingroupcomponent
    ClosureScreenInteractive::ExitLoopClosure(){
    return[this]{Exit();};
    }

    ///@briefExitthemainloop.
    ///@ingroupcomponent
    voidScreenInteractive::Exit(){
    Post([this]{ExitNow();});
    }

    //private:
    voidScreenInteractive::ExitNow(){
    quit_=true;
    task_sender_.reset();
    }

    //private:
    voidScreenInteractive::Signal(intsignal){
    if(signal==SIGABRT){
    Exit();
    return;
    }

    //WindowsdonosupportSIGTSTP/SIGWINCH
    #if!defined(_WIN32)
    if(signal==SIGTSTP){
    Post([&]{
    ResetCursorPosition();
    std::cout<<ResetPosition(/*clear*/true);//Cursortothebeginning
    Uninstall();
    dimx_=0;
    dimy_=0;
    Flush();
    std::ignore=std::raise(SIGTSTP);
    Install();
    });
    return;
    }

    if(signal==SIGWINCH){
    Post(Event::Special({0}));
    return;
    }
    #endif
    }

    boolScreenInteractive::SelectionData::operator==(
    constScreenInteractive::SelectionData&other)const{
    if(empty&&other.empty){
    returntrue;
    }
    if(empty||other.empty){
    returnfalse;
    }
    returnstart_x==other.start_x&&start_y==other.start_y&&
    end_x==other.end_x&&end_y==other.end_y;
    }

    boolScreenInteractive::SelectionData::operator!=(
    constScreenInteractive::SelectionData&other)const{
    return!(*this==other);
    }

    }//namespaceftxui.