diff --git a/Makefile b/Makefile index 3535420..f0d7d63 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp -objects = parserstate.o parser.o templates.o terminal.o parseraction.o +source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalcsi.cpp +objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalcsi.o executables = parse termemu CXX = g++ diff --git a/parseraction.cpp b/parseraction.cpp index b2c9567..794b47d 100644 --- a/parseraction.cpp +++ b/parseraction.cpp @@ -12,3 +12,18 @@ void Execute::act_on_terminal( Terminal::Emulator *emu ) { emu->execute( this ); } + +void Clear::act_on_terminal( Terminal::Emulator *emu ) +{ + emu->clear(); +} + +void Param::act_on_terminal( Terminal::Emulator *emu ) +{ + emu->param( this ); +} + +void CSI_Dispatch::act_on_terminal( Terminal::Emulator *emu ) +{ + emu->CSI_dispatch( this ); +} diff --git a/parseraction.hpp b/parseraction.hpp index 16749d7..631bac1 100644 --- a/parseraction.hpp +++ b/parseraction.hpp @@ -31,23 +31,30 @@ namespace Parser { void act_on_terminal( Terminal::Emulator *emu ); }; class Execute : public Action { - public: std::string name( void ) { return std::string( "Execute" ); } + public: + std::string name( void ) { return std::string( "Execute" ); } void act_on_terminal( Terminal::Emulator *emu ); }; class Clear : public Action { - public: std::string name( void ) { return std::string( "Clear" ); } + public: + std::string name( void ) { return std::string( "Clear" ); } + void act_on_terminal( Terminal::Emulator *emu ); }; class Collect : public Action { public: std::string name( void ) { return std::string( "Collect" ); } }; class Param : public Action { - public: std::string name( void ) { return std::string( "Param" ); } + public: + std::string name( void ) { return std::string( "Param" ); } + void act_on_terminal( Terminal::Emulator *emu ); }; class Esc_Dispatch : public Action { public: std::string name( void ) { return std::string( "Esc_Dispatch" ); } }; class CSI_Dispatch : public Action { - public: std::string name( void ) { return std::string( "CSI_Dispatch" ); } + public: + std::string name( void ) { return std::string( "CSI_Dispatch" ); } + void act_on_terminal( Terminal::Emulator *emu ); }; class Hook : public Action { public: std::string name( void ) { return std::string( "Hook" ); } diff --git a/templates.cpp b/templates.cpp index d94069a..9530e4f 100644 --- a/templates.cpp +++ b/templates.cpp @@ -13,3 +13,4 @@ template class std::vector; template class std::deque; template class std::vector; template class std::vector; +template class std::vector; diff --git a/terminal.cpp b/terminal.cpp index 9035d04..eabbd4c 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include "terminal.hpp" @@ -34,7 +37,8 @@ Emulator::Emulator( size_t s_width, size_t s_height ) width( s_width ), height( s_height ), cursor_col( 0 ), cursor_row( 0 ), combining_char_col( 0 ), combining_char_row( 0 ), - rows( height, Row( width ) ) + rows( height, Row( width ) ), + params(), dispatch_chars(), parsed_params() { } @@ -103,7 +107,7 @@ void Emulator::execute( Parser::Execute *act ) case 0x08: /* BS */ if ( cursor_col > 0 ) { cursor_col--; - rows[ cursor_row ].cells[ cursor_col ].contents.clear(); + newgrapheme(); /* this is not xterm's behavior */ } break; } @@ -146,8 +150,8 @@ void Emulator::debug_printout( FILE *f ) { fprintf( f, "\033[H\033[2J" ); - for ( size_t y = 0; y < height; y++ ) { - for ( size_t x = 0; x < width; x++ ) { + for ( int y = 0; y < height; y++ ) { + for ( int x = 0; x < width; x++ ) { fprintf( f, "\033[%d;%dH", y + 1, x + 1 ); Cell *cell = &rows[ y ].cells[ x ]; for ( std::vector::iterator i = cell->contents.begin(); @@ -162,3 +166,79 @@ void Emulator::debug_printout( FILE *f ) fflush( NULL ); } + +void Emulator::param( Parser::Param *act ) +{ + assert( act->char_present ); + assert( (act->ch == ';') || ( (act->ch >= '0') && (act->ch <= '9') ) ); + if ( params.length() < 100 ) { + /* enough for 16 five-char params plus 15 semicolons */ + params.push_back( act->ch ); + } +} + +void Emulator::collect( Parser::Collect *act ) +{ + assert( act->char_present ); + if ( ( dispatch_chars.length() < 8 ) /* never should need more than 2 */ + && ( act->ch <= 255 ) ) { /* ignore non-8-bit */ + dispatch_chars.push_back( act->ch ); + } +} + +void Emulator::clear( void ) +{ + params.clear(); + dispatch_chars.clear(); +} + +void Emulator::CSI_dispatch( Parser::CSI_Dispatch *act ) +{ + /* add final char to dispatch key */ + assert( act->char_present ); + Parser::Collect act2; + act2.char_present = true; + act2.ch = act->ch; + collect( &act2 ); + + if ( dispatch_chars == "K" ) { + CSI_EL(); + } else if ( (dispatch_chars == "A") + || (dispatch_chars == "B") + || (dispatch_chars == "C") + || (dispatch_chars == "D") + || (dispatch_chars == "H") ) { + CSI_cursormove(); + } +} + +void Emulator::parse_params( void ) +{ + parsed_params.clear(); + const char *str = params.c_str(); + const char *segment_begin = str; + + while ( 1 ) { + const char *segment_end = strchr( segment_begin, ';' ); + if ( segment_end == NULL ) { + break; + } + + errno = 0; + char *endptr; + int val = strtol( segment_begin, &endptr, 10 ); + if ( (errno == 0) && (endptr != segment_begin) ) { + parsed_params.push_back( val ); + } + + segment_begin = segment_end + 1; + } + + /* get last param */ + errno = 0; + char *endptr; + int val = strtol( segment_begin, &endptr, 10 ); + if ( (errno == 0) && (endptr != segment_begin) ) { + parsed_params.push_back( val ); + } +} diff --git a/terminal.hpp b/terminal.hpp index 44c399a..7ef0e52 100644 --- a/terminal.hpp +++ b/terminal.hpp @@ -31,24 +31,40 @@ namespace Terminal { class Emulator { friend void Parser::Print::act_on_terminal( Emulator * ); friend void Parser::Execute::act_on_terminal( Emulator * ); + friend void Parser::Clear::act_on_terminal( Emulator * ); + friend void Parser::Param::act_on_terminal( Emulator * ); + friend void Parser::CSI_Dispatch::act_on_terminal( Emulator * ); private: Parser::UTF8Parser parser; - size_t width, height; - size_t cursor_col, cursor_row; - size_t combining_char_col, combining_char_row; + int width, height; + int cursor_col, cursor_row; + int combining_char_col, combining_char_row; std::deque rows; + std::string params; + std::string dispatch_chars; + /* action methods */ void print( Parser::Print *act ); void execute( Parser::Execute *act ); + void param( Parser::Param *act ); + void collect( Parser::Collect *act ); + void clear( void ); + void CSI_dispatch( Parser::CSI_Dispatch *act ); void scroll( int N ); void autoscroll( void ); - void newgrapheme( void ); + void parse_params( void ); + std::vector parsed_params; + + /* CSI methods */ + void CSI_EL( void ); + void CSI_cursormove( void ); + public: Emulator( size_t s_width, size_t s_height ); ~Emulator(); diff --git a/terminalcsi.cpp b/terminalcsi.cpp new file mode 100644 index 0000000..1fa06cb --- /dev/null +++ b/terminalcsi.cpp @@ -0,0 +1,51 @@ +#include + +#include "terminal.hpp" + +using namespace Terminal; + +void Emulator::CSI_EL( void ) +{ + if ( params == "1" ) { /* start of screen to active position, inclusive */ + for ( int x = 0; x <= cursor_col; x++ ) { + rows[ cursor_row ].cells[ x ].contents.clear(); + } + } else if ( params == "2" ) { /* all of line */ + rows[ cursor_row ] = Row( width ); + } else { /* active position to end of line, inclusive */ + for ( int x = cursor_col; x < width; x++ ) { + rows[ cursor_row ].cells[ x ].contents.clear(); + } + } +} + +void Emulator::CSI_cursormove( void ) +{ + parse_params(); + int num = 1; + if ( parsed_params.size() >= 1 ) { + num = parsed_params[ 0 ]; + } + + switch ( dispatch_chars[ 0 ] ) { + case 'A': + cursor_row -= num; + break; + case 'B': + cursor_row += num; + break; + case 'C': + cursor_col += num; + break; + case 'D': + cursor_col -= num; + break; + } + + if ( cursor_row < 0 ) cursor_row = 0; + if ( cursor_row >= height ) cursor_row = height - 1; + if ( cursor_col < 0 ) cursor_col = 0; + if ( cursor_col >= width ) cursor_col = width; + + newgrapheme(); +}