Make Terminal::Framebuffer::Rows shared and copy-on-write.

* Support both std:: and std::tr1:: shared_ptr. FreeBSD 10 now uses C++11 by default.
* Remove Framebuffer pointers in STMClient
This commit is contained in:
John Hood
2014-06-14 18:21:38 -04:00
parent c090d257f2
commit b41bad918d
8 changed files with 186 additions and 83 deletions
+13 -17
View File
@@ -143,25 +143,20 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
}
int frame_y = 0;
Row blankrow( 0, 0 );
Framebuffer::rows_p_type rows(frame.last_frame.get_p_rows());
Framebuffer::row_pointer blank_row;
Framebuffer::rows_type rows( frame.last_frame.get_rows() );
/* Extend rows if we've gotten a resize and new is wider than old */
if ( frame.last_frame.ds.get_width() < f.ds.get_width() ) {
for ( Framebuffer::rows_p_type::iterator p = rows.begin(); p != rows.end(); p++ ) {
Row *bigger_row = new Row( **p );
bigger_row->cells.resize( f.ds.get_width(), Cell( f.ds.get_background_rendition() ) );
*p = bigger_row;
for ( Framebuffer::rows_type::iterator p = rows.begin(); p != rows.end(); p++ ) {
*p = Framebuffer::row_pointer( new Row( **p ) );
(*p)->cells.resize( f.ds.get_width(), Cell( f.ds.get_background_rendition() ) );
}
}
/* Add rows if we've gotten a resize and new is taller than old */
if ( static_cast<int>( rows.size() ) < f.ds.get_height() ) {
size_t orig_size = rows.size();
// get a proper blank row
blankrow = Row( f.ds.get_width(), 0 );
rows.resize( f.ds.get_height() );
for ( Framebuffer::rows_p_type::iterator p = rows.begin() + orig_size; p != rows.end(); p++ ) {
*p = new Row( blankrow );
}
blank_row = Framebuffer::row_pointer( new Row( f.ds.get_width(), 0 ) );
rows.resize( f.ds.get_height(), blank_row );
}
/* shortcut -- has display moved up by a certain number of lines? */
@@ -171,7 +166,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
for ( int row = 0; row < f.ds.get_height(); row++ ) {
const Row *new_row = f.get_row( 0 );
const Row *old_row = rows.at( row );
const Row *old_row = &*rows.at( row );
if ( new_row == old_row || *new_row == *old_row ) {
/* if row 0, we're looking at ourselves and probably didn't scroll */
if ( row == 0 ) {
@@ -201,6 +196,10 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
frame_y = scroll_height;
if ( lines_scrolled ) {
/* Now we need a proper blank row. */
if ( blank_row.get() == NULL ) {
blank_row = Framebuffer::row_pointer( new Row( f.ds.get_width(), 0 ) );
}
frame.update_rendition( initial_rendition(), true );
int top_margin = 0;
@@ -234,15 +233,12 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
frame.cursor_x = frame.cursor_y = -1;
}
/* Create a full-width blank row to represent newly-scrolled area */
blankrow = Row( f.ds.get_width(), 0 );
/* do the move in our local index */
for ( int i = top_margin; i <= bottom_margin; i++ ) {
if ( i + lines_scrolled <= bottom_margin ) {
rows.at( i ) = rows.at( i + lines_scrolled );
} else {
rows.at( i ) = &blankrow;
rows.at( i ) = blank_row;
}
}
}
+40 -23
View File
@@ -84,12 +84,31 @@ DrawState::DrawState( int s_width, int s_height )
}
Framebuffer::Framebuffer( int s_width, int s_height )
: rows( s_height, Row( s_width, 0 ) ), icon_name(), window_title(), bell_count( 0 ), title_initialized( false ), ds( s_width, s_height )
: rows( s_height, row_pointer(new Row( s_width, 0 ))), icon_name(), window_title(), bell_count( 0 ), title_initialized( false ), ds( s_width, s_height )
{
assert( s_height > 0 );
assert( s_width > 0 );
}
Framebuffer::Framebuffer( const Framebuffer &other )
: rows( other.rows ), icon_name( other.icon_name ), window_title( other.window_title ),
bell_count( other.bell_count ), title_initialized( other.title_initialized ), ds( other.ds )
{
}
Framebuffer & Framebuffer::operator=( const Framebuffer &other )
{
if ( this != &other ) {
rows = other.rows;
icon_name = other.icon_name;
window_title = other.window_title;
bell_count = other.bell_count;
title_initialized = other.title_initialized;
ds = other.ds;
}
return *this;
}
void Framebuffer::scroll( int N )
{
if ( N >= 0 ) {
@@ -286,14 +305,9 @@ void Framebuffer::insert_line( int before_row, int count )
// 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
// insert new 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();
}
rows.insert( start, count, newrow());
}
void Framebuffer::delete_line( int row, int count )
@@ -317,12 +331,7 @@ void Framebuffer::delete_line( int row, int count )
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();
}
rows.insert( start, count, newrow());
}
Row::Row( size_t s_width, color_type background_color )
@@ -355,12 +364,12 @@ void Row::delete_cell( int col, color_type background_color )
void Framebuffer::insert_cell( int row, int col )
{
rows.at( row ).insert_cell( col, ds.get_background_rendition() );
get_mutable_row( row )->insert_cell( col, ds.get_background_rendition() );
}
void Framebuffer::delete_cell( int row, int col )
{
rows.at( row ).delete_cell( col, ds.get_background_rendition() );
get_mutable_row( row )->delete_cell( col, ds.get_background_rendition() );
}
void Framebuffer::reset( void )
@@ -388,8 +397,8 @@ void Framebuffer::posterize( void )
for ( rows_type::iterator i = rows.begin();
i != rows.end();
i++ ) {
for ( Row::cells_type::iterator j = i->cells.begin();
j != i->cells.end();
for ( Row::cells_type::iterator j = (*i)->cells.begin();
j != (*i)->cells.end();
j++ ) {
j->renditions.posterize();
}
@@ -401,15 +410,23 @@ void Framebuffer::resize( int s_width, int s_height )
assert( s_width > 0 );
assert( s_height > 0 );
int oldheight = ds.get_height();
int oldwidth = ds.get_width();
ds.resize( s_width, s_height );
rows.resize( s_height, newrow() );
row_pointer blankrow( newrow());
if ( oldheight != s_height ) {
rows.resize( s_height, blankrow );
}
if (oldwidth == s_width) {
return;
}
for ( rows_type::iterator i = rows.begin();
i != rows.end();
i != rows.end() && *i != blankrow;
i++ ) {
i->set_wrap( false );
i->cells.resize( s_width, Cell( ds.get_background_rendition() ) );
*i = Framebuffer::row_pointer( new Row( **i ) );
(*i)->set_wrap( false );
(*i)->cells.resize( s_width, Cell( ds.get_background_rendition() ) );
}
}
+37 -18
View File
@@ -42,9 +42,12 @@
#include <string>
#include <list>
#include "shared.h"
/* Terminal framebuffer */
namespace Terminal {
using shared::shared_ptr;
typedef uint16_t color_type;
class Renditions {
@@ -288,41 +291,48 @@ namespace Terminal {
};
class Framebuffer {
// To minimize copying of rows and cells, we use shared_ptr to
// share unchanged rows between multiple Framebuffers. If we
// write to a row in a Framebuffer and it is shared with other
// owners, we copy it first. The shared_ptr naturally manages the
// usage of the actual rows themselves.
//
// We gain a couple of free extras by doing this:
//
// * A quick check for equality between rows in different
// Framebuffers is to simply compare the pointer values. If they
// are equal, then the rows are obviously identical.
// * If no row is shared, the frame has not been modified.
public:
typedef std::vector<wchar_t> title_type;
typedef std::vector<const Row *> rows_p_type;
typedef shared_ptr<Row> row_pointer;
typedef std::vector<row_pointer> rows_type; /* can be either std::vector or std::deque */
private:
typedef std::vector<Row> rows_type;
rows_type rows;
title_type icon_name;
title_type window_title;
unsigned int bell_count;
bool title_initialized; /* true if the window title has been set via an OSC */
Row newrow( void ) { return Row( ds.get_width(), ds.get_background_rendition() ); }
row_pointer newrow( void ) { return row_pointer( new Row( ds.get_width(), ds.get_background_rendition())); }
public:
Framebuffer( int s_width, int s_height );
Framebuffer( const Framebuffer &other );
Framebuffer &operator=( const Framebuffer &other );
DrawState ds;
const rows_type &get_rows() const { return rows; }
void scroll( int N );
void move_rows_autoscroll( int rows );
rows_p_type get_p_rows() const
{
rows_p_type retval;
for ( size_t i = 0; i < rows.size(); i++ ) {
retval.push_back( &rows.at(i) );
}
return retval;
}
const Row *get_row( int row ) const
inline const Row *get_row( int row ) const
{
if ( row == -1 ) row = ds.get_cursor_row();
return &rows.at( row );
return rows.at( row ).get();
}
inline const Cell *get_cell( int row = -1, int col = -1 ) const
@@ -330,17 +340,26 @@ namespace Terminal {
if ( row == -1 ) row = ds.get_cursor_row();
if ( col == -1 ) col = ds.get_cursor_col();
return &rows.at( row ).cells.at( col );
return &rows.at( row )->cells.at( col );
}
Row *get_mutable_row( int row )
{
return const_cast<Row *>(get_row( row ));
if ( row == -1 ) row = ds.get_cursor_row();
row_pointer &mutable_row = rows.at( row );
// If the row is shared, copy it.
if (!mutable_row.unique()) {
mutable_row = row_pointer( new Row( *mutable_row ));
}
return mutable_row.get();
}
inline Cell *get_mutable_cell( int row = -1, int col = -1 )
Cell *get_mutable_cell( int row = -1, int col = -1 )
{
return const_cast<Cell *>(get_cell( row, col ));
if ( row == -1 ) row = ds.get_cursor_row();
if ( col == -1 ) col = ds.get_cursor_col();
return &get_mutable_row( row )->cells.at( col );
}
Cell *get_combining_cell( void );