From 023eb182d0c5b3de2fdbc08a0cbcb09f27dc95a0 Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Sat, 13 Aug 2011 15:06:37 -0400 Subject: [PATCH] Refactor display to be outside of terminal and add some const correctness --- parseraction.cpp | 4 +- termemu.cpp | 12 ++++-- terminal.cpp | 12 +++--- terminal.hpp | 3 +- terminaldisplay.cpp | 96 ++++++++++++++++++++++------------------- terminaldisplay.hpp | 22 ++++------ terminalframebuffer.cpp | 4 +- terminalframebuffer.hpp | 60 ++++++++++++++------------ terminalfunctions.cpp | 16 +++---- 9 files changed, 123 insertions(+), 106 deletions(-) diff --git a/parseraction.cpp b/parseraction.cpp index f5fdf12..3a66845 100644 --- a/parseraction.cpp +++ b/parseraction.cpp @@ -70,9 +70,11 @@ void UserByte::act_on_terminal( Terminal::Emulator *emu ) { emu->dispatch.terminal_to_host.append( emu->user.input( this, emu->fb.ds.application_mode_cursor_keys ) ); - if ( c == 0x0c ) { /* Ctrl-L */ + /* + if ( c == 0x0c ) { // Ctrl-L emu->display.invalidate(); } + */ } void Resize::act_on_terminal( Terminal::Emulator *emu ) diff --git a/termemu.cpp b/termemu.cpp index 34ae148..bdddc6d 100644 --- a/termemu.cpp +++ b/termemu.cpp @@ -122,7 +122,7 @@ int main( void ) } /* Print a frame if the last frame was more than 1/50 seconds ago */ -bool tick( Terminal::Emulator *e ) +bool tick( Terminal::Emulator *e, Terminal::Display *d, Terminal::Framebuffer &state ) { static bool initialized = false; static struct timeval last_time; @@ -138,8 +138,10 @@ bool tick( Terminal::Emulator *e ) if ( (!initialized) || (diff >= 0.02) ) { - std::string update = e->new_frame(); + std::string update = d->new_frame( initialized, state, e->get_fb() ); swrite( STDOUT_FILENO, update.c_str() ); + state = e->get_fb(); + initialized = true; last_time = this_time; @@ -198,6 +200,8 @@ void emulate_terminal( int fd ) /* open parser and terminal */ Parser::UTF8Parser parser; Terminal::Emulator terminal( window_size.ws_col, window_size.ws_row ); + Terminal::Display display; + Terminal::Framebuffer state( window_size.ws_col, window_size.ws_row ); struct pollfd pollfds[ 3 ]; @@ -255,15 +259,17 @@ void emulate_terminal( int fd ) break; } - if ( tick( &terminal ) ) { /* there was a frame */ + if ( tick( &terminal, &display, state ) ) { /* there was a frame */ poll_timeout = -1; } else { poll_timeout = 20; } } + /* XXX last frame std::string update = terminal.new_frame(); swrite( STDOUT_FILENO, update.c_str() ); + */ swrite( STDOUT_FILENO, terminal.close().c_str() ); } diff --git a/terminal.cpp b/terminal.cpp index fbba772..9414e8d 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -10,7 +10,7 @@ using namespace Terminal; Emulator::Emulator( size_t s_width, size_t s_height ) - : fb( s_width, s_height ), dispatch(), user(), display( s_width, s_height ) + : fb( s_width, s_height ), dispatch(), user() {} std::string Emulator::read_octets_to_host( void ) @@ -32,7 +32,7 @@ void Emulator::print( Parser::Print *act ) int chwidth = act->ch == L'\0' ? -1 : wcwidth( act->ch ); - Cell *this_cell = fb.get_cell(); + Cell *this_cell = fb.get_mutable_cell(); Cell *combining_cell = fb.get_combining_cell(); /* can be null if we were resized */ @@ -40,7 +40,7 @@ void Emulator::print( Parser::Print *act ) case 1: /* normal character */ case 2: /* wide character */ if ( fb.ds.auto_wrap_mode && fb.ds.next_print_will_wrap ) { - fb.get_row( -1 )->wrap = true; + fb.get_mutable_row( -1 )->wrap = true; fb.ds.move_col( 0 ); fb.move_rows_autoscroll( 1 ); } @@ -50,7 +50,7 @@ void Emulator::print( Parser::Print *act ) && (chwidth == 2) && (fb.ds.get_cursor_col() == fb.ds.get_width() - 1) ) { fb.reset_cell( this_cell ); - fb.get_row( -1 )->wrap = false; + fb.get_mutable_row( -1 )->wrap = false; /* There doesn't seem to be a consistent way to get the downstream terminal emulator to set the wrap-around copy-and-paste flag on a row that ends with an empty cell @@ -65,7 +65,7 @@ void Emulator::print( Parser::Print *act ) } } - this_cell = fb.get_cell(); + this_cell = fb.get_mutable_cell(); fb.reset_cell( this_cell ); this_cell->contents.push_back( act->ch ); @@ -74,7 +74,7 @@ void Emulator::print( Parser::Print *act ) if ( chwidth == 2 ) { /* erase overlapped cell */ if ( fb.ds.get_cursor_col() + 1 < fb.ds.get_width() ) { - fb.reset_cell( fb.get_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) ); + fb.reset_cell( fb.get_mutable_cell( fb.ds.get_cursor_row(), fb.ds.get_cursor_col() + 1 ) ); } } diff --git a/terminal.hpp b/terminal.hpp index 50400ae..2ff5789 100644 --- a/terminal.hpp +++ b/terminal.hpp @@ -32,7 +32,6 @@ namespace Terminal { Framebuffer fb; Dispatcher dispatch; UserInput user; - Display display; /* action methods */ void print( Parser::Print *act ); @@ -50,7 +49,7 @@ namespace Terminal { std::string open( void ); /* put user cursor keys in application mode */ std::string close( void ); /* restore user cursor keys */ - std::string new_frame( void ) { return display.new_frame( fb ); } + const Framebuffer & get_fb( void ) { return fb; } }; } diff --git a/terminaldisplay.cpp b/terminaldisplay.cpp index 8621df2..fc2a756 100644 --- a/terminaldisplay.cpp +++ b/terminaldisplay.cpp @@ -6,15 +6,15 @@ using namespace Terminal; /* Print a new "frame" to the terminal, using ANSI/ECMA-48 escape codes. */ -std::string Display::new_frame( Framebuffer &f ) +std::string Display::new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) { - FrameState frame; + FrameState frame( last ); char tmp[ 64 ]; /* has window title changed? */ if ( (!initialized) - || (f.get_window_title() != last_frame.get_window_title()) ) { + || (f.get_window_title() != frame.last_frame.get_window_title()) ) { /* set window title */ frame.append( "\033]0;[stm] " ); std::vector window_title = f.get_window_title(); @@ -29,7 +29,7 @@ std::string Display::new_frame( Framebuffer &f ) /* has reverse video state changed? */ if ( (!initialized) - || (f.ds.reverse_video != last_frame.ds.reverse_video) ) { + || (f.ds.reverse_video != frame.last_frame.ds.reverse_video) ) { /* set reverse video */ snprintf( tmp, 64, "\033[?5%c", (f.ds.reverse_video ? 'h' : 'l') ); frame.append( tmp ); @@ -37,13 +37,17 @@ std::string Display::new_frame( Framebuffer &f ) /* has size changed? */ if ( (!initialized) - || (f.ds.get_width() != last_frame.ds.get_width()) - || (f.ds.get_height() != last_frame.ds.get_height()) ) { + || (f.ds.get_width() != frame.last_frame.ds.get_width()) + || (f.ds.get_height() != frame.last_frame.ds.get_height()) ) { /* clear screen */ frame.append( "\033[0m\033[H\033[2J" ); initialized = false; - cursor_x = cursor_y = 0; - current_rendition_string = "\033[0m"; + frame.cursor_x = frame.cursor_y = 0; + frame.current_rendition_string = "\033[0m"; + } else { + frame.cursor_x = frame.last_frame.ds.get_cursor_col(); + frame.cursor_y = frame.last_frame.ds.get_cursor_row(); + frame.current_rendition_string = frame.last_frame.ds.get_renditions().sgr(); } /* shortcut -- has display moved up by a certain number of lines? */ @@ -54,7 +58,7 @@ std::string Display::new_frame( Framebuffer &f ) int scroll_height = 0; for ( int row = 0; row < f.ds.get_height(); row++ ) { - if ( *(f.get_row( 0 )) == *(last_frame.get_row( row )) ) { + if ( *(f.get_row( 0 )) == *(frame.last_frame.get_row( row )) ) { /* found a scroll */ lines_scrolled = row; scroll_height = 1; @@ -64,7 +68,7 @@ std::string Display::new_frame( Framebuffer &f ) lines_scrolled + region_height < f.ds.get_height(); region_height++ ) { if ( *(f.get_row( region_height )) - == *(last_frame.get_row( lines_scrolled + region_height )) ) { + == *(frame.last_frame.get_row( lines_scrolled + region_height )) ) { scroll_height = region_height + 1; } else { break; @@ -79,17 +83,17 @@ std::string Display::new_frame( Framebuffer &f ) frame.y = scroll_height; if ( lines_scrolled ) { - if ( cursor_y != f.ds.get_height() - 1 ) { + if ( frame.cursor_y != f.ds.get_height() - 1 ) { snprintf( tmp, 64, "\033[%d;%dH", f.ds.get_height(), 1 ); frame.append( tmp ); - cursor_y = f.ds.get_height() - 1; - cursor_x = 0; + frame.cursor_y = f.ds.get_height() - 1; + frame.cursor_x = 0; } - if ( current_rendition_string != "\033[0m" ) { + if ( frame.current_rendition_string != "\033[0m" ) { frame.append( "\033[0m" ); - current_rendition_string = "\033[0m"; + frame.current_rendition_string = "\033[0m"; } for ( int i = 0; i < lines_scrolled; i++ ) { @@ -98,9 +102,9 @@ std::string Display::new_frame( Framebuffer &f ) for ( int i = 0; i < f.ds.get_height(); i++ ) { if ( i + lines_scrolled < f.ds.get_height() ) { - *(last_frame.get_row( i )) = *(last_frame.get_row( i + lines_scrolled )); + *(frame.last_frame.get_mutable_row( i )) = *(frame.last_frame.get_row( i + lines_scrolled )); } else { - last_frame.get_row( i )->reset( 0 ); + frame.last_frame.get_mutable_row( i )->reset( 0 ); } } } @@ -113,53 +117,53 @@ std::string Display::new_frame( Framebuffer &f ) for ( frame.x = 0; frame.x < f.ds.get_width(); /* let put_cell() handle advance */ ) { last_x = frame.x; - put_cell( frame, f ); + put_cell( initialized, frame, f ); /* To hint that a word-select should group the end of one line with the beginning of the next, we let the real cursor actually wrap around in cases where it wrapped around for us. */ - if ( (cursor_x >= f.ds.get_width()) + if ( (frame.cursor_x >= f.ds.get_width()) && (frame.y < f.ds.get_height() - 1) && f.get_row( frame.y )->wrap - && (!initialized || !last_frame.get_row( frame.y )->wrap) ) { + && (!initialized || !frame.last_frame.get_row( frame.y )->wrap) ) { /* next write will wrap */ - cursor_x = 0; - cursor_y++; + frame.cursor_x = 0; + frame.cursor_y++; } } /* Turn off wrap */ if ( (frame.y < f.ds.get_height() - 1) && (!f.get_row( frame.y )->wrap) - && (!initialized || last_frame.get_row( frame.y )->wrap) ) { + && (!initialized || frame.last_frame.get_row( frame.y )->wrap) ) { frame.x = last_x; if ( initialized ) { - last_frame.reset_cell( last_frame.get_cell( frame.y, frame.x ) ); + frame.last_frame.reset_cell( frame.last_frame.get_mutable_cell( frame.y, frame.x ) ); } snprintf( tmp, 64, "\033[%d;%dH\033[K", frame.y + 1, frame.x + 1 ); frame.append( tmp ); - cursor_x = frame.x; + frame.cursor_x = frame.x; - put_cell( frame, f ); + put_cell( initialized, frame, f ); } } /* has cursor location changed? */ if ( (!initialized) - || (f.ds.get_cursor_row() != cursor_y) - || (f.ds.get_cursor_col() != cursor_x) ) { + || (f.ds.get_cursor_row() != frame.cursor_y) + || (f.ds.get_cursor_col() != frame.cursor_x) ) { snprintf( tmp, 64, "\033[%d;%dH", f.ds.get_cursor_row() + 1, f.ds.get_cursor_col() + 1 ); frame.append( tmp ); - cursor_x = f.ds.get_cursor_col(); - cursor_y = f.ds.get_cursor_row(); + frame.cursor_x = f.ds.get_cursor_col(); + frame.cursor_y = f.ds.get_cursor_row(); } /* has cursor visibility changed? */ if ( (!initialized) - || (f.ds.cursor_visible != last_frame.ds.cursor_visible) ) { + || (f.ds.cursor_visible != frame.last_frame.ds.cursor_visible) ) { if ( f.ds.cursor_visible ) { frame.append( "\033[?25h" ); } else { @@ -167,44 +171,48 @@ std::string Display::new_frame( Framebuffer &f ) } } - last_frame = f; - initialized = true; + /* have renditions changed? */ + if ( (!initialized) + || (f.ds.get_renditions().sgr() != frame.current_rendition_string) ) { + frame.append( f.ds.get_renditions().sgr() ); + frame.current_rendition_string = f.ds.get_renditions().sgr(); + } return frame.str; } -void Display::put_cell( FrameState &frame, Framebuffer &f ) +void Display::put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) { char tmp[ 64 ]; - Cell *cell = f.get_cell( frame.y, frame.x ); + const Cell *cell = f.get_cell( frame.y, frame.x ); if ( initialized - && ( *cell == *(last_frame.get_cell( frame.y, frame.x )) ) ) { + && ( *cell == *(frame.last_frame.get_cell( frame.y, frame.x )) ) ) { frame.x += cell->width; return; } - if ( (frame.x != cursor_x) || (frame.y != cursor_y) ) { + if ( (frame.x != frame.cursor_x) || (frame.y != frame.cursor_y) ) { snprintf( tmp, 64, "\033[%d;%dH", frame.y + 1, frame.x + 1 ); frame.append( tmp ); - cursor_x = frame.x; - cursor_y = frame.y; + frame.cursor_x = frame.x; + frame.cursor_y = frame.y; } std::string rendition_str = cell->renditions.sgr(); - if ( current_rendition_string != rendition_str ) { + if ( frame.current_rendition_string != rendition_str ) { /* print renditions */ frame.append( rendition_str ); - current_rendition_string = rendition_str; + frame.current_rendition_string = rendition_str; } if ( cell->contents.empty() ) { /* see how far we can stretch a clear */ int clear_count = 0; for ( int col = frame.x; col < f.ds.get_width(); col++ ) { - Cell *other_cell = f.get_cell( frame.y, col ); + const Cell *other_cell = f.get_cell( frame.y, col ); if ( (cell->renditions == other_cell->renditions) && (other_cell->contents.empty()) ) { clear_count++; @@ -225,7 +233,7 @@ void Display::put_cell( FrameState &frame, Framebuffer &f ) frame.append( tmp ); } - for ( std::vector::iterator i = cell->contents.begin(); + for ( std::vector::const_iterator i = cell->contents.begin(); i != cell->contents.end(); i++ ) { snprintf( tmp, 64, "%lc", *i ); @@ -233,5 +241,5 @@ void Display::put_cell( FrameState &frame, Framebuffer &f ) } frame.x += cell->width; - cursor_x += cell->width; + frame.cursor_x += cell->width; } diff --git a/terminaldisplay.hpp b/terminaldisplay.hpp index dae7bcc..1014751 100644 --- a/terminaldisplay.hpp +++ b/terminaldisplay.hpp @@ -10,27 +10,23 @@ namespace Terminal { int x, y; std::string str; - FrameState() : x(0), y(0), str() {} + int cursor_x, cursor_y; + std::string current_rendition_string; + + Framebuffer last_frame; + + FrameState( const Framebuffer &s_last ) : x(0), y(0), str(), cursor_x(0), cursor_y(0), current_rendition_string(), last_frame( s_last ) {} void append( std::string s ) { str.append( s ); } }; class Display { private: - bool initialized; - Framebuffer last_frame; - std::string current_rendition_string; - int cursor_x, cursor_y; - - void put_cell( FrameState &frame, Framebuffer &f ); + void put_cell( bool initialized, FrameState &frame, const Framebuffer &f ); public: - Display( int width, int height ) - : initialized( false ), last_frame( width, height ), - current_rendition_string(), cursor_x( -1 ), cursor_y( -1 ) - {} + Display() {} - std::string new_frame( Framebuffer &f ); - void invalidate( void ) { initialized = false; } + std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ); }; } diff --git a/terminalframebuffer.cpp b/terminalframebuffer.cpp index 2e6335e..f67b380 100644 --- a/terminalframebuffer.cpp +++ b/terminalframebuffer.cpp @@ -199,7 +199,7 @@ std::vector DrawState::get_tabs( void ) void Framebuffer::apply_renditions_to_current_cell( void ) { - get_cell()->renditions = ds.get_renditions(); + get_mutable_cell()->renditions = ds.get_renditions(); } SavedCursor::SavedCursor() @@ -373,7 +373,7 @@ void Renditions::set_rendition( int num ) } } -std::string Renditions::sgr( void ) +std::string Renditions::sgr( void ) const { std::string ret; diff --git a/terminalframebuffer.hpp b/terminalframebuffer.hpp index a5f9e85..4a99f27 100644 --- a/terminalframebuffer.hpp +++ b/terminalframebuffer.hpp @@ -18,7 +18,7 @@ namespace Terminal { Renditions( int s_background ); void set_rendition( int num ); - std::string sgr( void ); + std::string sgr( void ) const; bool operator==( const Renditions &x ) const { @@ -119,12 +119,12 @@ namespace Terminal { void move_row( int N, bool relative = false ); void move_col( int N, bool relative = false, bool implicit = false ); - int get_cursor_col( void ) { return cursor_col; } - int get_cursor_row( void ) { return cursor_row; } - int get_combining_char_col( void ) { return combining_char_col; } - int get_combining_char_row( void ) { return combining_char_row; } - int get_width( void ) { return width; } - int get_height( void ) { return height; } + int get_cursor_col( void ) const { return cursor_col; } + int get_cursor_row( void ) const { return cursor_row; } + int get_combining_char_col( void ) const { return combining_char_col; } + int get_combining_char_row( void ) const { return combining_char_row; } + int get_width( void ) const { return width; } + int get_height( void ) const { return height; } void set_tab( void ); void clear_tab( int col ); @@ -134,14 +134,14 @@ namespace Terminal { void set_scrolling_region( int top, int bottom ); - int get_scrolling_region_top_row( void ) { return scrolling_region_top_row; } - int get_scrolling_region_bottom_row( void ) { return scrolling_region_bottom_row; } + int get_scrolling_region_top_row( void ) const { return scrolling_region_top_row; } + int get_scrolling_region_bottom_row( void ) const { return scrolling_region_bottom_row; } int limit_top( void ); int limit_bottom( void ); void add_rendition( int x ) { renditions.set_rendition( x ); } - Renditions get_renditions( void ) { return renditions; } + Renditions get_renditions( void ) const { return renditions; } int get_background_rendition( void ) { return renditions.background_color; } void save_cursor( void ); @@ -167,36 +167,42 @@ namespace Terminal { void scroll( int N ); void move_rows_autoscroll( int rows ); - Row *get_row( int row ) + const Row *get_row( int row ) const { if ( row == -1 ) row = ds.get_cursor_row(); return &rows[ row ]; } - inline Cell *get_cell( void ) + inline const Cell *get_cell( void ) const { -#ifdef DEBUG - assert( ds.get_cursor_row() >= 0 ); - assert( ds.get_cursor_col() >= 0 ); - assert( ds.get_cursor_row() < ds.get_height() ); - assert( ds.get_cursor_col() < ds.get_width() ); -#endif - return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ]; } - inline Cell *get_cell( int row, int col ) + inline const Cell *get_cell( int row, int col ) const { if ( row == -1 ) row = ds.get_cursor_row(); if ( col == -1 ) col = ds.get_cursor_col(); -#ifdef DEBUG - assert( row >= 0 ); - assert( col >= 0 ); - assert( row < ds.get_height() ); - assert( col < ds.get_width() ); -#endif + return &rows[ row ].cells[ col ]; + } + + Row *get_mutable_row( int row ) + { + if ( row == -1 ) row = ds.get_cursor_row(); + + return &rows[ row ]; + } + + inline Cell *get_mutable_cell( void ) + { + return &rows[ ds.get_cursor_row() ].cells[ ds.get_cursor_col() ]; + } + + inline Cell *get_mutable_cell( int row, int col ) + { + if ( row == -1 ) row = ds.get_cursor_row(); + if ( col == -1 ) col = ds.get_cursor_col(); return &rows[ row ].cells[ col ]; } @@ -215,7 +221,7 @@ namespace Terminal { void soft_reset( void ); void set_window_title( std::vector s ) { window_title = s; } - std::vector get_window_title( void ) { return window_title; } + std::vector get_window_title( void ) const { return window_title; } void resize( int s_width, int s_height ); diff --git a/terminalfunctions.cpp b/terminalfunctions.cpp index 5d860d2..7d9c464 100644 --- a/terminalfunctions.cpp +++ b/terminalfunctions.cpp @@ -12,7 +12,7 @@ using namespace Terminal; static void clearline( Framebuffer *fb, int row, int start, int end ) { for ( int col = start; col <= end; col++ ) { - fb->reset_cell( fb->get_cell( row, col ) ); + fb->reset_cell( fb->get_mutable_cell( row, col ) ); } } @@ -27,7 +27,7 @@ void CSI_EL( Framebuffer *fb, Dispatcher *dispatch ) clearline( fb, -1, 0, fb->ds.get_cursor_col() ); break; case 2: /* all of line */ - fb->reset_row( fb->get_row( -1 ) ); + fb->reset_row( fb->get_mutable_row( -1 ) ); break; } } @@ -40,18 +40,18 @@ void CSI_ED( Framebuffer *fb, Dispatcher *dispatch ) { case 0: /* active position to end of screen, inclusive */ clearline( fb, -1, fb->ds.get_cursor_col(), fb->ds.get_width() - 1 ); for ( int y = fb->ds.get_cursor_row() + 1; y < fb->ds.get_height(); y++ ) { - fb->reset_row( fb->get_row( y ) ); + fb->reset_row( fb->get_mutable_row( y ) ); } break; case 1: /* start of screen to active position, inclusive */ for ( int y = 0; y < fb->ds.get_cursor_row(); y++ ) { - fb->reset_row( fb->get_row( y ) ); + fb->reset_row( fb->get_mutable_row( y ) ); } clearline( fb, -1, 0, fb->ds.get_cursor_col() ); break; case 2: /* entire screen */ for ( int y = 0; y < fb->ds.get_height(); y++ ) { - fb->reset_row( fb->get_row( y ) ); + fb->reset_row( fb->get_mutable_row( y ) ); } break; } @@ -114,8 +114,8 @@ void Esc_DECALN( Framebuffer *fb, Dispatcher *dispatch __attribute((unused)) ) { for ( int y = 0; y < fb->ds.get_height(); y++ ) { for ( int x = 0; x < fb->ds.get_width(); x++ ) { - fb->reset_cell( fb->get_cell( y, x ) ); - fb->get_cell( y, x )->contents.push_back( L'E' ); + fb->reset_cell( fb->get_mutable_cell( y, x ) ); + fb->get_mutable_cell( y, x )->contents.push_back( L'E' ); } } } @@ -215,7 +215,7 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) { fb->ds.move_row( 0 ); fb->ds.move_col( 0 ); for ( int y = 0; y < fb->ds.get_height(); y++ ) { - fb->reset_row( fb->get_row( y ) ); + fb->reset_row( fb->get_mutable_row( y ) ); } return NULL; case 5: /* reverse video */