screen.cpp
cstddef
cstdint
iostream
limits
map
sstream
utility
ftxui/screen/image.hpp
ftxui/screen/pixel.hpp
ftxui/screen/screen.hpp
ftxui/screen/string.hpp
ftxui/screen/terminal.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
src/ftxui/screen/screen.cpp
cstddef
cstdint
functional
iostream
limits
map
sstream
string
utility
vector
ftxui
FTXUI_UNLIKELY
x
(x)
FTXUI_LIKELY
x
(x)

    //Copyright2020ArthurSonzogni.Allrightsreserved.
    //UseofthissourcecodeisgovernedbytheMITlicensethatcanbefoundin
    //theLICENSEfile.
    #include<cstddef>//forsize_t
    #include<cstdint>
    #include<iostream>//foroperator<<,stringstream,basic_ostream,flush,cout,ostream
    #include<limits>
    #include<map>//for_Rb_tree_const_iterator,map,operator!=,operator==
    #include<sstream>//IWYUpragma:keep
    #include<utility>//forpair

    #include"ftxui/screen/image.hpp"//forImage
    #include"ftxui/screen/pixel.hpp"//forPixel
    #include"ftxui/screen/screen.hpp"
    #include"ftxui/screen/string.hpp"//forstring_width
    #include"ftxui/screen/terminal.hpp"//forDimensions,Size

    #ifdefined(_WIN32)
    #defineWIN32_LEAN_AND_MEAN
    #ifndefNOMINMAX
    #defineNOMINMAX
    #endif
    #include<windows.h>
    #endif

    //Macroforhintingthatanexpressionislikelytobefalse.
    #if!defined(FTXUI_UNLIKELY)
    #ifdefined(COMPILER_GCC)||defined(__clang__)
    #defineFTXUI_UNLIKELY(x)__builtin_expect(!!(x),0)
    #else
    #defineFTXUI_UNLIKELY(x)(x)
    #endif//defined(COMPILER_GCC)
    #endif//!defined(FTXUI_UNLIKELY)

    #if!defined(FTXUI_LIKELY)
    #ifdefined(COMPILER_GCC)||defined(__clang__)
    #defineFTXUI_LIKELY(x)__builtin_expect(!!(x),1)
    #else
    #defineFTXUI_LIKELY(x)(x)
    #endif//defined(COMPILER_GCC)
    #endif//!defined(FTXUI_LIKELY)

    namespaceftxui{

    namespace{

    #ifdefined(_WIN32)
    voidWindowsEmulateVT100Terminal(){
    staticbooldone=false;
    if(done)
    return;
    done=true;

    //EnableVTprocessingonstdoutandstdin
    autostdout_handle=GetStdHandle(STD_OUTPUT_HANDLE);

    DWORDout_mode=0;
    GetConsoleMode(stdout_handle,&out_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;

    SetConsoleMode(stdout_handle,out_mode);
    }
    #endif

    //NOLINTNEXTLINE(readability-function-cognitive-complexity)
    voidUpdatePixelStyle(constScreen*screen,
    std::stringstream&ss,
    constPixel&prev,
    constPixel&next){
    //Seehttps://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
    if(FTXUI_UNLIKELY(next.hyperlink!=prev.hyperlink)){
    ss<<"\x1B]8;;"<<screen->Hyperlink(next.hyperlink)<<"\x1B\\";
    }

    //Bold
    if(FTXUI_UNLIKELY((next.bold^prev.bold)|(next.dim^prev.dim))){
    //BOLD_AND_DIM_RESET:
    ss<<((prev.bold&&!next.bold)||(prev.dim&&!next.dim)?"\x1B[22m"
    :"");
    ss<<(next.bold?"\x1B[1m":"");//BOLD_SET
    ss<<(next.dim?"\x1B[2m":"");//DIM_SET
    }

    //Underline
    if(FTXUI_UNLIKELY(next.underlined!=prev.underlined||
    next.underlined_double!=prev.underlined_double)){
    ss<<(next.underlined?"\x1B[4m"//UNDERLINE
    :next.underlined_double?"\x1B[21m"//UNDERLINE_DOUBLE
    :"\x1B[24m");//UNDERLINE_RESET
    }

    //Blink
    if(FTXUI_UNLIKELY(next.blink!=prev.blink)){
    ss<<(next.blink?"\x1B[5m"//BLINK_SET
    :"\x1B[25m");//BLINK_RESET
    }

    //Inverted
    if(FTXUI_UNLIKELY(next.inverted!=prev.inverted)){
    ss<<(next.inverted?"\x1B[7m"//INVERTED_SET
    :"\x1B[27m");//INVERTED_RESET
    }

    //Italics
    if(FTXUI_UNLIKELY(next.italic!=prev.italic)){
    ss<<(next.italic?"\x1B[3m"//ITALIC_SET
    :"\x1B[23m");//ITALIC_RESET
    }

    //StrikeThrough
    if(FTXUI_UNLIKELY(next.strikethrough!=prev.strikethrough)){
    ss<<(next.strikethrough?"\x1B[9m"//CROSSED_OUT
    :"\x1B[29m");//CROSSED_OUT_RESET
    }

    if(FTXUI_UNLIKELY(next.foreground_color!=prev.foreground_color||
    next.background_color!=prev.background_color)){
    ss<<"\x1B["+next.foreground_color.Print(false)+"m";
    ss<<"\x1B["+next.background_color.Print(true)+"m";
    }
    }

    structTileEncoding{
    std::uint8_tleft:2;
    std::uint8_ttop:2;
    std::uint8_tright:2;
    std::uint8_tdown:2;
    std::uint8_tround:1;

    //clang-formatoff
    booloperator<(constTileEncoding&other)const{
    if(left<other.left){returntrue;}
    if(left>other.left){returnfalse;}
    if(top<other.top){returntrue;}
    if(top>other.top){returnfalse;}
    if(right<other.right){returntrue;}
    if(right>other.right){returnfalse;}
    if(down<other.down){returntrue;}
    if(down>other.down){returnfalse;}
    if(round<other.round){returntrue;}
    if(round>other.round){returnfalse;}
    returnfalse;
    }
    //clang-formaton
    };

    //clang-formatoff
    conststd::map<std::string,TileEncoding>tile_encoding={//NOLINT
    {"─",{1,0,1,0,0}},
    {"━",{2,0,2,0,0}},
    {"╍",{2,0,2,0,0}},

    {"│",{0,1,0,1,0}},
    {"┃",{0,2,0,2,0}},
    {"╏",{0,2,0,2,0}},

    {"┌",{0,0,1,1,0}},
    {"┍",{0,0,2,1,0}},
    {"┎",{0,0,1,2,0}},
    {"┏",{0,0,2,2,0}},

    {"┐",{1,0,0,1,0}},
    {"┑",{2,0,0,1,0}},
    {"┒",{1,0,0,2,0}},
    {"┓",{2,0,0,2,0}},

    {"└",{0,1,1,0,0}},
    {"┕",{0,1,2,0,0}},
    {"┖",{0,2,1,0,0}},
    {"┗",{0,2,2,0,0}},

    {"┘",{1,1,0,0,0}},
    {"┙",{2,1,0,0,0}},
    {"┚",{1,2,0,0,0}},
    {"┛",{2,2,0,0,0}},

    {"├",{0,1,1,1,0}},
    {"┝",{0,1,2,1,0}},
    {"┞",{0,2,1,1,0}},
    {"┟",{0,1,1,2,0}},
    {"┠",{0,2,1,2,0}},
    {"┡",{0,2,2,1,0}},
    {"┢",{0,1,2,2,0}},
    {"┣",{0,2,2,2,0}},

    {"┤",{1,1,0,1,0}},
    {"┥",{2,1,0,1,0}},
    {"┦",{1,2,0,1,0}},
    {"┧",{1,1,0,2,0}},
    {"┨",{1,2,0,2,0}},
    {"┩",{2,2,0,1,0}},
    {"┪",{2,1,0,2,0}},
    {"┫",{2,2,0,2,0}},

    {"┬",{1,0,1,1,0}},
    {"┭",{2,0,1,1,0}},
    {"┮",{1,0,2,1,0}},
    {"┯",{2,0,2,1,0}},
    {"┰",{1,0,1,2,0}},
    {"┱",{2,0,1,2,0}},
    {"┲",{1,0,2,2,0}},
    {"┳",{2,0,2,2,0}},

    {"┴",{1,1,1,0,0}},
    {"┵",{2,1,1,0,0}},
    {"┶",{1,1,2,0,0}},
    {"┷",{2,1,2,0,0}},
    {"┸",{1,2,1,0,0}},
    {"┹",{2,2,1,0,0}},
    {"┺",{1,2,2,0,0}},
    {"┻",{2,2,2,0,0}},

    {"┼",{1,1,1,1,0}},
    {"┽",{2,1,1,1,0}},
    {"┾",{1,1,2,1,0}},
    {"┿",{2,1,2,1,0}},
    {"╀",{1,2,1,1,0}},
    {"╁",{1,1,1,2,0}},
    {"╂",{1,2,1,2,0}},
    {"╃",{2,2,1,1,0}},
    {"╄",{1,2,2,1,0}},
    {"╅",{2,1,1,2,0}},
    {"╆",{1,1,2,2,0}},
    {"╇",{2,2,2,1,0}},
    {"╈",{2,1,2,2,0}},
    {"╉",{2,2,1,2,0}},
    {"╊",{1,2,2,2,0}},
    {"╋",{2,2,2,2,0}},

    {"═",{3,0,3,0,0}},
    {"║",{0,3,0,3,0}},

    {"╒",{0,0,3,1,0}},
    {"╓",{0,0,1,3,0}},
    {"╔",{0,0,3,3,0}},

    {"╕",{3,0,0,1,0}},
    {"╖",{1,0,0,3,0}},
    {"╗",{3,0,0,3,0}},

    {"╘",{0,1,3,0,0}},
    {"╙",{0,3,1,0,0}},
    {"╚",{0,3,3,0,0}},

    {"╛",{3,1,0,0,0}},
    {"╜",{1,3,0,0,0}},
    {"╝",{3,3,0,0,0}},

    {"╞",{0,1,3,1,0}},
    {"╟",{0,3,1,3,0}},
    {"╠",{0,3,3,3,0}},

    {"╡",{3,1,0,1,0}},
    {"╢",{1,3,0,3,0}},
    {"╣",{3,3,0,3,0}},

    {"╤",{3,0,3,1,0}},
    {"╥",{1,0,1,3,0}},
    {"╦",{3,0,3,3,0}},

    {"╧",{3,1,3,0,0}},
    {"╨",{1,3,1,0,0}},
    {"╩",{3,3,3,0,0}},

    {"╪",{3,1,3,1,0}},
    {"╫",{1,3,1,3,0}},
    {"╬",{3,3,3,3,0}},

    {"╭",{0,0,1,1,1}},
    {"╮",{1,0,0,1,1}},
    {"╯",{1,1,0,0,1}},
    {"╰",{0,1,1,0,1}},

    {"╴",{1,0,0,0,0}},
    {"╵",{0,1,0,0,0}},
    {"╶",{0,0,1,0,0}},
    {"╷",{0,0,0,1,0}},

    {"╸",{2,0,0,0,0}},
    {"╹",{0,2,0,0,0}},
    {"╺",{0,0,2,0,0}},
    {"╻",{0,0,0,2,0}},

    {"╼",{1,0,2,0,0}},
    {"╽",{0,1,0,2,0}},
    {"╾",{2,0,1,0,0}},
    {"╿",{0,2,0,1,0}},
    };
    //clang-formaton

    template<classA,classB>
    std::map<B,A>InvertMap(conststd::map<A,B>input){
    std::map<B,A>output;
    for(constauto&it:input){
    output[it.second]=it.first;
    }
    returnoutput;
    }

    conststd::map<TileEncoding,std::string>tile_encoding_inverse=//NOLINT
    InvertMap(tile_encoding);

    voidUpgradeLeftRight(std::string&left,std::string&right){
    constautoit_left=tile_encoding.find(left);
    if(it_left==tile_encoding.end()){
    return;
    }
    constautoit_right=tile_encoding.find(right);
    if(it_right==tile_encoding.end()){
    return;
    }

    if(it_left->second.right==0&&it_right->second.left!=0){
    TileEncodingencoding_left=it_left->second;
    encoding_left.right=it_right->second.left;
    constautoit_left_upgrade=tile_encoding_inverse.find(encoding_left);
    if(it_left_upgrade!=tile_encoding_inverse.end()){
    left=it_left_upgrade->second;
    }
    }

    if(it_right->second.left==0&&it_left->second.right!=0){
    TileEncodingencoding_right=it_right->second;
    encoding_right.left=it_left->second.right;
    constautoit_right_upgrade=tile_encoding_inverse.find(encoding_right);
    if(it_right_upgrade!=tile_encoding_inverse.end()){
    right=it_right_upgrade->second;
    }
    }
    }

    voidUpgradeTopDown(std::string&top,std::string&down){
    constautoit_top=tile_encoding.find(top);
    if(it_top==tile_encoding.end()){
    return;
    }
    constautoit_down=tile_encoding.find(down);
    if(it_down==tile_encoding.end()){
    return;
    }

    if(it_top->second.down==0&&it_down->second.top!=0){
    TileEncodingencoding_top=it_top->second;
    encoding_top.down=it_down->second.top;
    constautoit_top_down=tile_encoding_inverse.find(encoding_top);
    if(it_top_down!=tile_encoding_inverse.end()){
    top=it_top_down->second;
    }
    }

    if(it_down->second.top==0&&it_top->second.down!=0){
    TileEncodingencoding_down=it_down->second;
    encoding_down.top=it_top->second.down;
    constautoit_down_top=tile_encoding_inverse.find(encoding_down);
    if(it_down_top!=tile_encoding_inverse.end()){
    down=it_down_top->second;
    }
    }
    }

    boolShouldAttemptAutoMerge(Pixel&pixel){
    returnpixel.automerge&&pixel.character.size()==3;
    }

    }//namespace

    ///Afixeddimension.
    ///@seeFit
    ///@seeFull
    DimensionsDimension::Fixed(intv){
    return{v,v};
    }

    ///Usetheterminaldimensions.
    ///@seeFixed
    ///@seeFit
    DimensionsDimension::Full(){
    returnTerminal::Size();
    }

    //static
    ///Createascreenwiththegivendimensionalongthex-axisandy-axis.
    ScreenScreen::Create(Dimensionswidth,Dimensionsheight){
    return{width.dimx,height.dimy};
    }

    //static
    ///Createascreenwiththegivendimension.
    ScreenScreen::Create(Dimensionsdimension){
    return{dimension.dimx,dimension.dimy};
    }

    Screen::Screen(intdimx,intdimy):Image{dimx,dimy}{
    #ifdefined(_WIN32)
    //Theplacementofthiscallisabitweird,howeverwecanassumethat
    //anybodywhoinstantiatesaScreenobjecteventuallywantstooutput
    //somethingtotheconsole.Ifthatisnotthecase,useaninstanceofImage
    //instead.AswerequireUTF8forallinput/outputoperationswewilljust
    //switchtoUTF8encodinghere
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);
    WindowsEmulateVT100Terminal();
    #endif
    }

    ///Produceastd::stringthatcanbeusedtoprinttheScreenonthe
    ///terminal.
    ///@noteDon'tforgettoflushstdout.Alternatively,youcanuse
    ///Screen::Print();
    std::stringScreen::ToString()const{
    std::stringstreamss;

    constPixeldefault_pixel;
    constPixel*previous_pixel_ref=&default_pixel;

    for(inty=0;y<dimy_;++y){
    //Newlineinbetweentwolines.
    if(y!=0){
    UpdatePixelStyle(this,ss,*previous_pixel_ref,default_pixel);
    previous_pixel_ref=&default_pixel;
    ss<<"\r\n";
    }

    //Afterprintingafullwithcharacter,weneedtoskipthenextcell.
    boolprevious_fullwidth=false;
    for(constauto&pixel:pixels_[y]){
    if(!previous_fullwidth){
    UpdatePixelStyle(this,ss,*previous_pixel_ref,pixel);
    previous_pixel_ref=&pixel;
    if(pixel.character.empty()){
    ss<<"";
    }else{
    ss<<pixel.character;
    }
    }
    previous_fullwidth=(string_width(pixel.character)==2);
    }
    }

    //Resetthestyletodefault:
    UpdatePixelStyle(this,ss,*previous_pixel_ref,default_pixel);

    returnss.str();
    }

    //PrinttheScreentotheterminal.
    voidScreen::Print()const{
    std::cout<<ToString()<<'\0'<<std::flush;
    }

    ///@briefReturnastringtobeprintedinordertoresetthecursorposition
    ///tothebeginningofthescreen.
    ///
    ///```cpp
    ///std::stringreset_position;
    ///while(true){
    ///autodocument=render();
    ///autoscreen=Screen::Create(Dimension::Full(),Dimension::Fit(document));
    ///Render(screen,document);
    ///std::cout<<reset_position<<screen.ToString()<<std::flush;
    ///reset_position=screen.ResetPosition();
    ///
    ///usingnamespacestd::chrono_literals;
    ///std::this_thread::sleep_for(0.01s);
    ///}
    ///```
    ///
    ///@returnThestringtoprintinordertoresetthecursorpositiontothe
    ///beginning.
    std::stringScreen::ResetPosition(boolclear)const{
    std::stringstreamss;
    if(clear){
    ss<<"\r";//MOVE_LEFT;
    ss<<"\x1b[2K";//CLEAR_SCREEN;
    for(inty=1;y<dimy_;++y){
    ss<<"\x1B[1A";//MOVE_UP;
    ss<<"\x1B[2K";//CLEAR_LINE;
    }
    }else{
    ss<<"\r";//MOVE_LEFT;
    for(inty=1;y<dimy_;++y){
    ss<<"\x1B[1A";//MOVE_UP;
    }
    }
    returnss.str();
    }

    ///@briefClearallthepixelfromthescreen.
    voidScreen::Clear(){
    Image::Clear();

    cursor_.x=dimx_-1;
    cursor_.y=dimy_-1;

    hyperlinks_={
    "",
    };
    }

    //clang-formatoff
    voidScreen::ApplyShader(){
    //Mergeboxcharacterstogethers.
    for(inty=0;y<dimy_;++y){
    for(intx=0;x<dimx_;++x){
    //Boxdrawingcharacterusesexactly3byte.
    Pixel&cur=pixels_[y][x];
    if(!ShouldAttemptAutoMerge(cur)){
    continue;
    }

    if(x>0){
    Pixel&left=pixels_[y][x-1];
    if(ShouldAttemptAutoMerge(left)){
    UpgradeLeftRight(left.character,cur.character);
    }
    }
    if(y>0){
    Pixel&top=pixels_[y-1][x];
    if(ShouldAttemptAutoMerge(top)){
    UpgradeTopDown(top.character,cur.character);
    }
    }
    }
    }
    }
    //clang-formaton

    std::uint8_tScreen::RegisterHyperlink(conststd::string&link){
    for(std::size_ti=0;i<hyperlinks_.size();++i){
    if(hyperlinks_[i]==link){
    returni;
    }
    }
    if(hyperlinks_.size()==std::numeric_limits<std::uint8_t>::max()){
    return0;
    }
    hyperlinks_.push_back(link);
    returnhyperlinks_.size()-1;
    }

    conststd::string&Screen::Hyperlink(std::uint8_tid)const{
    if(id>=hyperlinks_.size()){
    returnhyperlinks_[0];
    }
    returnhyperlinks_[id];
    }

    ///@briefReturnthecurrentselectionstyle.
    ///@seeSetSelectionStyle
    constScreen::SelectionStyle&Screen::GetSelectionStyle()const{
    returnselection_style_;
    }

    ///@briefSetthecurrentselectionstyle.
    ///@seeGetSelectionStyle
    voidScreen::SetSelectionStyle(SelectionStyledecorator){
    selection_style_=std::move(decorator);
    }

    }//namespaceftxui