diff --git a/src/examples/termemu.cc b/src/examples/termemu.cc index fc2bb82..502a0d2 100644 --- a/src/examples/termemu.cc +++ b/src/examples/termemu.cc @@ -148,7 +148,7 @@ int main( void ) } /* Print a frame if the last frame was more than 1/50 seconds ago */ -bool tick( Terminal::Framebuffer &state, const Terminal::Framebuffer &new_frame, +bool tick( Terminal::Framebuffer &state, Terminal::Framebuffer &new_frame, const Terminal::Display &display ) { static bool initialized = false; @@ -165,6 +165,8 @@ bool tick( Terminal::Framebuffer &state, const Terminal::Framebuffer &new_frame, if ( (!initialized) || (diff >= 0.02) ) { + display.downgrade( new_frame ); + std::string update = display.new_frame( initialized, state, new_frame ); swrite( STDOUT_FILENO, update.c_str() ); state = new_frame; @@ -314,7 +316,9 @@ void emulate_terminal( int fd ) break; } - if ( tick( state, complete.get_fb(), display ) ) { /* there was a frame */ + Terminal::Framebuffer new_frame( complete.get_fb() ); + + if ( tick( state, new_frame, display ) ) { /* there was a frame */ poll_timeout = -1; } else { poll_timeout = 20; diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc index 0af3784..94f1837 100644 --- a/src/frontend/stmclient.cc +++ b/src/frontend/stmclient.cc @@ -158,6 +158,9 @@ void STMClient::output_new_frame( void ) /* apply local overlays */ overlays.apply( *new_state ); + /* apply any mutations */ + display.downgrade( *new_state ); + /* calculate minimal difference from where we are */ const string diff( display.new_frame( !repaint_requested, *local_framebuffer, diff --git a/src/terminal/terminaldisplay.h b/src/terminal/terminaldisplay.h index 0161c4c..19174db 100644 --- a/src/terminal/terminaldisplay.h +++ b/src/terminal/terminaldisplay.h @@ -54,9 +54,13 @@ namespace Terminal { bool has_bce; /* erases result in cell filled with background color */ + int posterize_colors; /* downsample input colors >8 to [0..7] */ + void put_cell( bool initialized, FrameState &frame, const Framebuffer &f ) const; public: + void downgrade( Framebuffer &f ) const { if ( posterize_colors ) { f.posterize(); } } + std::string new_frame( bool initialized, const Framebuffer &last, const Framebuffer &f ) const; Display( bool use_environment ); diff --git a/src/terminal/terminaldisplayinit.cc b/src/terminal/terminaldisplayinit.cc index 3b8f972..584479c 100644 --- a/src/terminal/terminaldisplayinit.cc +++ b/src/terminal/terminaldisplayinit.cc @@ -29,7 +29,7 @@ using namespace Terminal; Display::Display( bool use_environment ) - : has_ech( true ), has_bce( true ) + : has_ech( true ), has_bce( true ), posterize_colors( false ) { if ( use_environment ) { int errret = -2; @@ -57,8 +57,7 @@ Display::Display( bool use_environment ) char *val = tigetstr( ech_name ); if ( val == (char *)-1 ) { throw std::string( "Invalid terminfo string capability " ) + ech_name; - } - if ( val == 0 ) { + } else if ( val == 0 ) { has_ech = false; } @@ -67,9 +66,17 @@ Display::Display( bool use_environment ) int bce_val = tigetflag( bce_name ); if ( bce_val == -1 ) { throw std::string( "Invalid terminfo boolean capability " ) + bce_name; - } - if ( bce_val == 0 ) { + } else if ( bce_val == 0 ) { has_bce = false; } + + /* check colors */ + char colors_name[] = "colors"; + int color_val = tigetnum( colors_name ); + if ( color_val == -2 ) { + throw std::string( "Invalid terminfo numeric capability " ) + colors_name; + } else if ( color_val < 256 ) { + posterize_colors = true; + } } } diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc index 80dfbf0..35c6dc2 100644 --- a/src/terminal/terminalframebuffer.cc +++ b/src/terminal/terminalframebuffer.cc @@ -320,6 +320,15 @@ void Framebuffer::soft_reset( void ) ds.clear_saved_cursor(); } +void Framebuffer::posterize( void ) +{ + for ( BOOST_AUTO( i, rows.begin() ); i != rows.end(); i++ ) { + for ( BOOST_AUTO( j, i->cells.begin() ); j != i->cells.end(); j++ ) { + j->renditions.posterize(); + } + } +} + void Framebuffer::resize( int s_width, int s_height ) { assert( s_width > 0 ); @@ -453,6 +462,64 @@ std::string Renditions::sgr( void ) const return ret; } +/* Reduce 256 "standard" colors to the 8 ANSI colors. */ + +/* We could do something fancy like find the nearest system color in + the deltaE(2000) sense, but for "business graphics," the colorimetric + intent is probably less important than just having separate colors + be separate as much as possible. */ + +/* Terminal emulators generally agree on the (R',G',B') values of the + "standard" 256-color pallette beyond #15, but for the first 16 + colors there is disagreement. Most terminal emulators are roughly + self-consistent, except on Ubuntu's gnome-terminal where "ANSI + blue" (#4) has been replaced with the aubergine system-wide + color. See + https://lists.ubuntu.com/archives/ubuntu-devel/2011-March/032726.html + + For purposes of this routine, we just treat the eight colors as + taking each possible assignment of 0 or 255 to their components. + + Terminal emulators that advertise "xterm" are inconsistent on the + handling of initc to change the contents of a cell in the color + pallette. On RIS (reset to initial state) or choosing reset from + the user interface, xterm resets all entries, but gnome-terminal + only resets entries beyond 16. (rxvt doesn't reset any entries, + and Terminal.app ignores initc.) On initc, xterm applies changes + immediately (but slowly), but gnome-terminal's changes are only + prospective unless the user resizes the terminal. + + mosh ignores initc for now, despite advertising xterm-256color. */ + +static const char standard_posterization[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, + 6, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 6, 6, 2, 2, + 6, 6, 6, 6, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, + 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, + 6, 6, 2, 2, 6, 6, 6, 6, 1, 1, 5, 5, 5, 5, 1, 1, + 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 3, 3, 7, 7, 7, 7, + 3, 3, 7, 7, 7, 7, 3, 3, 7, 7, 7, 7, 1, 1, 5, 5, + 5, 5, 1, 1, 5, 5, 5, 5, 3, 3, 7, 7, 7, 7, 3, 3, + 7, 7, 7, 7, 3, 3, 7, 7, 7, 7, 3, 3, 7, 7, 7, 7, + 1, 1, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, 3, 3, 7, 7, + 7, 7, 3, 3, 7, 7, 7, 7, 3, 3, 7, 7, 7, 7, 3, 3, + 7, 7, 7, 7, 1, 1, 5, 5, 5, 5, 1, 1, 5, 5, 5, 5, + 3, 3, 7, 7, 7, 7, 3, 3, 7, 7, 7, 7, 3, 3, 7, 7, + 7, 7, 3, 3, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; + +void Renditions::posterize( void ) +{ + if ( foreground_color ) { + foreground_color = 30 + standard_posterization[ foreground_color - 30 ]; + } + + if ( background_color ) { + background_color = 40 + standard_posterization[ background_color - 40 ]; + } +} + void Row::reset( int background_color ) { for ( std::vector::iterator i = cells.begin(); diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h index ae55612..74f605f 100644 --- a/src/terminal/terminalframebuffer.h +++ b/src/terminal/terminalframebuffer.h @@ -40,6 +40,8 @@ namespace Terminal { void set_rendition( int num ); std::string sgr( void ) const; + void posterize( void ); + bool operator==( const Renditions &x ) const { return (bold == x.bold) && (underlined == x.underlined) @@ -285,6 +287,8 @@ namespace Terminal { void reset_cell( Cell *c ) { c->reset( ds.get_background_rendition() ); } void reset_row( Row *r ) { r->reset( ds.get_background_rendition() ); } + void posterize( void ); + void ring_bell( void ) { bell_count++; } unsigned int get_bell_count( void ) const { return bell_count; }