diff --git a/src/examples/parse.cc b/src/examples/parse.cc index 93fabf4..f9f72bf 100644 --- a/src/examples/parse.cc +++ b/src/examples/parse.cc @@ -195,9 +195,10 @@ static int vt_parser( int fd, Parser::UTF8Parser *parser ) } /* feed to parser */ + Parser::Actions actions; for ( int i = 0; i < bytes_read; i++ ) { - std::list actions = parser->input( buf[ i ] ); - for ( std::list::iterator j = actions.begin(); + parser->input( buf[ i ], actions ); + for ( Parser::Actions::iterator j = actions.begin(); j != actions.end(); j++ ) { @@ -218,6 +219,7 @@ static int vt_parser( int fd, Parser::UTF8Parser *parser ) fflush( stdout ); } + actions.clear(); } return 0; diff --git a/src/frontend/terminaloverlay.cc b/src/frontend/terminaloverlay.cc index ccb33be..0fe75ad 100644 --- a/src/frontend/terminaloverlay.cc +++ b/src/frontend/terminaloverlay.cc @@ -666,9 +666,10 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) } last_byte = the_byte; - list actions( parser.input( the_byte ) ); + Parser::Actions actions; + parser.input( the_byte, actions ); - for ( list::iterator it = actions.begin(); + for ( Parser::Actions::iterator it = actions.begin(); it != actions.end(); it++ ) { Parser::Action *act = *it; @@ -678,7 +679,8 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) act->name().c_str(), act->char_present ? act->ch : L'_' ); */ - if ( typeid( *act ) == typeid( Parser::Print ) ) { + const std::type_info& type_act = typeid( *act ); + if ( type_act == typeid( Parser::Print ) ) { /* make new prediction */ init_cursor( fb ); @@ -809,7 +811,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) newline_carriage_return( fb ); } } - } else if ( typeid( *act ) == typeid( Parser::Execute ) ) { + } else if ( type_act == typeid( Parser::Execute ) ) { if ( act->char_present && (act->ch == 0x0d) /* CR */ ) { become_tentative(); newline_carriage_return( fb ); @@ -817,10 +819,10 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) // fprintf( stderr, "Execute 0x%x\n", act->ch ); become_tentative(); } - } else if ( typeid( *act ) == typeid( Parser::Esc_Dispatch ) ) { + } else if ( type_act == typeid( Parser::Esc_Dispatch ) ) { // fprintf( stderr, "Escape sequence\n" ); become_tentative(); - } else if ( typeid( *act ) == typeid( Parser::CSI_Dispatch ) ) { + } else if ( type_act == typeid( Parser::CSI_Dispatch ) ) { if ( act->char_present && (act->ch == L'C') ) { /* right arrow */ init_cursor( fb ); if ( cursor().col < fb.ds.get_width() - 1 ) { @@ -838,8 +840,6 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) // fprintf( stderr, "CSI sequence %lc\n", act->ch ); become_tentative(); } - } else if ( typeid( *act ) == typeid( Parser::Clear ) ) { - } delete act; diff --git a/src/statesync/completeterminal.cc b/src/statesync/completeterminal.cc index b1c5376..7cfe1bf 100644 --- a/src/statesync/completeterminal.cc +++ b/src/statesync/completeterminal.cc @@ -46,16 +46,17 @@ string Complete::act( const string &str ) { for ( unsigned int i = 0; i < str.size(); i++ ) { /* parse octet into up to three actions */ - list actions( parser.input( str[ i ] ) ); + parser.input( str[ i ], actions ); /* apply actions to terminal and delete them */ - for ( list::iterator it = actions.begin(); + for ( Actions::iterator it = actions.begin(); it != actions.end(); it++ ) { Action *act = *it; act->act_on_terminal( &terminal ); delete act; } + actions.clear(); } return terminal.read_octets_to_host(); diff --git a/src/statesync/completeterminal.h b/src/statesync/completeterminal.h index f06f621..8dc8aca 100644 --- a/src/statesync/completeterminal.h +++ b/src/statesync/completeterminal.h @@ -48,6 +48,11 @@ namespace Terminal { Terminal::Emulator terminal; Terminal::Display display; + // Only used locally by act(), but kept here as a performance optimization, + // to avoid construction/destruction. It must always be empty + // outside calls to act() to keep horrible things from happening. + Parser::Actions actions; + typedef std::list< std::pair > input_history_type; input_history_type input_history; uint64_t echo_ack; @@ -56,7 +61,7 @@ namespace Terminal { public: Complete( size_t width, size_t height ) : parser(), terminal( width, height ), display( false ), - input_history(), echo_ack( 0 ) {} + actions(), input_history(), echo_ack( 0 ) {} std::string act( const std::string &str ); std::string act( const Parser::Action *act ); diff --git a/src/terminal/parser.cc b/src/terminal/parser.cc index 9bbc31e..9db53c7 100644 --- a/src/terminal/parser.cc +++ b/src/terminal/parser.cc @@ -41,21 +41,19 @@ const Parser::StateFamily Parser::family; static void append_or_delete( Parser::Action *act, - std::list &vec ) + Parser::Actions &vec ) { assert( act ); - if ( typeid( *act ) != typeid( Parser::Ignore ) ) { + if ( !act->ignore() ) { vec.push_back( act ); } else { delete act; } } -std::list Parser::Parser::input( wchar_t ch ) +void Parser::Parser::input( wchar_t ch, Actions &ret ) { - std::list ret; - Transition tx = state->input( ch ); if ( tx.next_state != NULL ) { @@ -69,8 +67,6 @@ std::list Parser::Parser::input( wchar_t ch ) append_or_delete( tx.next_state->enter(), ret ); state = tx.next_state; } - - return ret; } Parser::UTF8Parser::UTF8Parser() @@ -80,7 +76,7 @@ Parser::UTF8Parser::UTF8Parser() buf[0] = '\0'; } -std::list Parser::UTF8Parser::input( char c ) +void Parser::UTF8Parser::input( char c, Actions &ret ) { assert( buf_len < BUF_SIZE ); @@ -94,7 +90,6 @@ std::list Parser::UTF8Parser::input( char c ) size_t total_bytes_parsed = 0; size_t orig_buf_len = buf_len; - std::list ret; /* this routine is somewhat complicated in order to comply with Unicode 6.0, section 3.9, "Best Practices for using U+FFFD" */ @@ -138,7 +133,7 @@ std::list Parser::UTF8Parser::input( char c ) /* Cast to unsigned for checks, because some platforms (e.g. ARM) use uint32_t as wchar_t, causing compiler warning on "pwc > 0" check. */ - uint64_t pwcheck = pwc; + const uint32_t pwcheck = pwc; if ( pwcheck > 0x10FFFF ) { /* outside Unicode range */ pwc = (wchar_t) 0xFFFD; @@ -153,13 +148,10 @@ std::list Parser::UTF8Parser::input( char c ) pwc = (wchar_t) 0xFFFD; } - std::list vec = parser.input( pwc ); - ret.insert( ret.end(), vec.begin(), vec.end() ); + parser.input( pwc, ret ); total_bytes_parsed += bytes_parsed; } - - return ret; } Parser::Parser::Parser( const Parser &other ) diff --git a/src/terminal/parser.h b/src/terminal/parser.h index 3a55fd3..08d66f8 100644 --- a/src/terminal/parser.h +++ b/src/terminal/parser.h @@ -37,7 +37,6 @@ http://www.vt100.net/emu/dec_ansi_parser */ #include -#include #include #include "parsertransition.h" @@ -59,7 +58,7 @@ namespace Parser { Parser & operator=( const Parser & ); ~Parser() {} - std::list input( wchar_t ch ); + void input( wchar_t ch, Actions &actions ); bool operator==( const Parser &x ) const { @@ -81,7 +80,7 @@ namespace Parser { public: UTF8Parser(); - std::list input( char c ); + void input( char c, Actions &actions ); bool operator==( const UTF8Parser &x ) const { diff --git a/src/terminal/parseraction.cc b/src/terminal/parseraction.cc index 019d5e7..29c260b 100644 --- a/src/terminal/parseraction.cc +++ b/src/terminal/parseraction.cc @@ -43,9 +43,9 @@ std::string Action::str( void ) char thechar[ 10 ] = { 0 }; if ( char_present ) { if ( iswprint( ch ) ) - snprintf( thechar, 10, "(%lc)", (wint_t)ch ); + snprintf( thechar, 10, "(%lc)", static_cast(ch) ); else - snprintf( thechar, 10, "(0x%x)", (unsigned int)ch ); + snprintf( thechar, 10, "(0x%x)", static_cast(ch) ); } return name() + std::string( thechar ); diff --git a/src/terminal/parseraction.h b/src/terminal/parseraction.h index ce55490..3032d63 100644 --- a/src/terminal/parseraction.h +++ b/src/terminal/parseraction.h @@ -34,6 +34,7 @@ #define PARSERACTION_HPP #include +#include namespace Terminal { class Emulator; @@ -43,8 +44,8 @@ namespace Parser { class Action { public: - bool char_present; wchar_t ch; + bool char_present; mutable bool handled; std::string str( void ); @@ -53,14 +54,20 @@ namespace Parser { virtual void act_on_terminal( Terminal::Emulator * ) const {}; - Action() : char_present( false ), ch( -1 ), handled( false ) {}; + virtual bool ignore() const { return false; } + + Action() : ch( -1 ), char_present( false ), handled( false ) {}; virtual ~Action() {}; virtual bool operator==( const Action &other ) const; }; + typedef std::vector Actions; + class Ignore : public Action { - public: std::string name( void ) { return std::string( "Ignore" ); } + public: + std::string name( void ) { return std::string( "Ignore" ); } + bool ignore() const { return true; } }; class Print : public Action { public: diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc index 69cba24..521199a 100644 --- a/src/terminal/terminalframebuffer.cc +++ b/src/terminal/terminalframebuffer.cc @@ -78,18 +78,12 @@ Framebuffer::Framebuffer( int s_width, int s_height ) void Framebuffer::scroll( int N ) { if ( N >= 0 ) { - for ( int i = 0; i < N; i++ ) { - delete_line( ds.get_scrolling_region_top_row() ); - ds.move_row( -1, true ); - } + delete_line( ds.get_scrolling_region_top_row(), N ); + ds.move_row( -N, true ); } else { N = -N; - - for ( int i = 0; i < N; i++ ) { - rows.insert( rows.begin() + ds.get_scrolling_region_top_row(), newrow() ); - rows.erase( rows.begin() + ds.get_scrolling_region_bottom_row() + 1 ); - ds.move_row( 1, true ); - } + insert_line( ds.get_scrolling_region_top_row(), N ); + ds.move_row( N, true ); } } @@ -258,32 +252,62 @@ void DrawState::restore_cursor( void ) new_grapheme(); } -void Framebuffer::insert_line( int before_row ) +void Framebuffer::insert_line( int before_row, int count ) { if ( (before_row < ds.get_scrolling_region_top_row()) || (before_row > ds.get_scrolling_region_bottom_row() + 1) ) { return; } - rows.insert( rows.begin() + before_row, newrow() ); - rows.erase( rows.begin() + ds.get_scrolling_region_bottom_row() + 1 ); + int max_scroll = ds.get_scrolling_region_bottom_row() + 1 - before_row; + if ( count > max_scroll ) { + count = max_scroll; + } + + if ( count == 0 ) { + return; + } + + // delete old rows + rows_type::iterator start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - count; + rows.erase( start, start + count ); + // insert a block of dummy rows + start = rows.begin() + before_row; + rows.insert( start, count, Row( 0, 0 ) ); + // then replace with real new rows + start = rows.begin() + before_row; + for (rows_type::iterator i = start; i < start + count; i++) { + *i = newrow(); + } } -void Framebuffer::delete_line( int row ) +void Framebuffer::delete_line( int row, int count ) { if ( (row < ds.get_scrolling_region_top_row()) || (row > ds.get_scrolling_region_bottom_row()) ) { return; } - int insertbefore = ds.get_scrolling_region_bottom_row() + 1; - if ( insertbefore == ds.get_height() ) { - rows.push_back( newrow() ); - } else { - rows.insert( rows.begin() + insertbefore, newrow() ); + int max_scroll = ds.get_scrolling_region_bottom_row() + 1 - row; + if ( count > max_scroll ) { + count = max_scroll; } - rows.erase( rows.begin() + row ); + if ( count == 0 ) { + return; + } + + // delete old rows + rows_type::iterator start = rows.begin() + row; + rows.erase( start, start + count ); + // insert a block of dummy rows + start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - count; + rows.insert( start, count, Row( 0, 0 ) ); + // then replace with real new rows + start = rows.begin() + ds.get_scrolling_region_bottom_row() + 1 - count; + for (rows_type::iterator i = start; i < start + count; i++) { + *i = newrow(); + } } void Row::insert_cell( int col, int background_color ) diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h index 720482d..b89a9b0 100644 --- a/src/terminal/terminalframebuffer.h +++ b/src/terminal/terminalframebuffer.h @@ -326,8 +326,8 @@ namespace Terminal { void apply_renditions_to_current_cell( void ); - void insert_line( int before_row ); - void delete_line( int row ); + void insert_line( int before_row, int count ); + void delete_line( int row, int count ); void insert_cell( int row, int col ); void delete_cell( int row, int col ); diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc index 35c8055..14004ce 100644 --- a/src/terminal/terminalfunctions.cc +++ b/src/terminal/terminalfunctions.cc @@ -441,9 +441,7 @@ static void CSI_IL( Framebuffer *fb, Dispatcher *dispatch ) { int lines = dispatch->getparam( 0, 1 ); - for ( int i = 0; i < lines; i++ ) { - fb->insert_line( fb->ds.get_cursor_row() ); - } + fb->insert_line( fb->ds.get_cursor_row(), lines ); /* vt220 manual and Ecma-48 say to move to first column */ /* but xterm and gnome-terminal don't */ @@ -457,9 +455,7 @@ static void CSI_DL( Framebuffer *fb, Dispatcher *dispatch ) { int lines = dispatch->getparam( 0, 1 ); - for ( int i = 0; i < lines; i++ ) { - fb->delete_line( fb->ds.get_cursor_row() ); - } + fb->delete_line( fb->ds.get_cursor_row(), lines ); /* same story -- xterm and gnome-terminal don't move to first column */