diff --git a/src/terminal/parserstate.cc b/src/terminal/parserstate.cc index 747b7c66..8a401bf0 100644 --- a/src/terminal/parserstate.cc +++ b/src/terminal/parserstate.cc @@ -171,7 +171,7 @@ Transition CSI_Entry::input_state_rule( wchar_t ch ) const return Transition( std::make_shared(), &family->s_Ground ); } - if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) { + if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3A ) || ( ch == 0x3B ) ) { return Transition( std::make_shared(), &family->s_CSI_Param ); } @@ -179,10 +179,6 @@ Transition CSI_Entry::input_state_rule( wchar_t ch ) const return Transition( std::make_shared(), &family->s_CSI_Param ); } - if ( ch == 0x3A ) { - return Transition( &family->s_CSI_Ignore ); - } - if ( ( 0x20 <= ch ) && ( ch <= 0x2F ) ) { return Transition( std::make_shared(), &family->s_CSI_Intermediate ); } @@ -196,11 +192,11 @@ Transition CSI_Param::input_state_rule( wchar_t ch ) const return Transition( std::make_shared() ); } - if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3B ) ) { + if ( ( ( 0x30 <= ch ) && ( ch <= 0x39 ) ) || ( ch == 0x3A ) || ( ch == 0x3B ) ) { return Transition( std::make_shared() ); } - if ( ( ch == 0x3A ) || ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) ) { + if ( ( 0x3C <= ch ) && ( ch <= 0x3F ) ) { return Transition( &family->s_CSI_Ignore ); } diff --git a/src/terminal/terminaldispatcher.cc b/src/terminal/terminaldispatcher.cc index 7515ca70..799fda2f 100644 --- a/src/terminal/terminaldispatcher.cc +++ b/src/terminal/terminaldispatcher.cc @@ -51,7 +51,7 @@ Dispatcher::Dispatcher() void Dispatcher::newparamchar( const Parser::Param* act ) { assert( act->char_present ); - assert( ( act->ch == ';' ) || ( ( act->ch >= '0' ) && ( act->ch <= '9' ) ) ); + assert( ( act->ch == ';' ) || ( 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 ); @@ -87,44 +87,53 @@ void Dispatcher::parse_params( void ) while ( 1 ) { const char* segment_end = strchr( segment_begin, ';' ); - if ( segment_end == NULL ) { - break; - } - errno = 0; - char* endptr; - long val = strtol( segment_begin, &endptr, 10 ); - if ( endptr == segment_begin ) { - val = -1; - } + Param p( 0 ); + + bool first = true; + bool valid = false; + while ( 1 ) { + const char* ext_end = strchr( segment_begin, ':' ); - if ( val > PARAM_MAX || errno == ERANGE ) { - val = -1; errno = 0; + char* endptr; + long val = strtol( segment_begin, &endptr, 10 ); + if ( endptr == segment_begin ) { + val = -1; + } + + if ( val > PARAM_MAX || errno == ERANGE ) { + val = -1; + errno = 0; + } + + if ( errno == 0 || segment_begin == endptr ) { + if ( first ) { + p.val = val; + valid = true; + } else { + p.exts.push_back( val ); + } + } + + first = false; + + if ( ext_end == NULL || ( segment_end && ext_end > segment_end ) ) { + break; + } + + segment_begin = ext_end + 1; } - if ( errno == 0 || segment_begin == endptr ) { - parsed_params.push_back( val ); + if ( valid ) { + parsed_params.push_back( p ); } - segment_begin = segment_end + 1; - } - - /* get last param */ - errno = 0; - char* endptr; - long val = strtol( segment_begin, &endptr, 10 ); - if ( endptr == segment_begin ) { - val = -1; - } - - if ( val > PARAM_MAX || errno == ERANGE ) { - val = -1; - errno = 0; - } + if ( segment_end == NULL ) { + break; + } - if ( errno == 0 || segment_begin == endptr ) { - parsed_params.push_back( val ); + segment_begin = segment_end + 1; } parsed = true; @@ -132,19 +141,28 @@ void Dispatcher::parse_params( void ) int Dispatcher::getparam( size_t N, int defaultval ) { - int ret = defaultval; + return getparamext( N, defaultval ).val; +} + +Dispatcher::Param const& Dispatcher::getparamext( size_t N, int defaultval ) +{ if ( !parsed ) { parse_params(); } + static Param notfound( defaultval ); + notfound.val = defaultval; + + Param* ret = ¬found; + if ( parsed_params.size() > N ) { - ret = parsed_params[N]; + ret = &parsed_params[N]; } - if ( ret < 1 ) - ret = defaultval; + if ( ret->val < 1 ) + ret->val = defaultval; - return ret; + return *ret; } int Dispatcher::param_count( void ) diff --git a/src/terminal/terminaldispatcher.h b/src/terminal/terminaldispatcher.h index 47a10c3a..4180da0a 100644 --- a/src/terminal/terminaldispatcher.h +++ b/src/terminal/terminaldispatcher.h @@ -89,9 +89,20 @@ DispatchRegistry& get_global_dispatch_registry( void ); class Dispatcher { +public: + struct Param + { + Param( int val ) : val( val ) {} + + int val {}; + std::vector exts; + + bool operator==( Param const& other ) const { return val == other.val && exts == other.exts; } + }; + private: std::string params; - std::vector parsed_params; + std::vector parsed_params; bool parsed; std::string dispatch_chars; @@ -107,6 +118,7 @@ class Dispatcher Dispatcher(); int getparam( size_t N, int defaultval ); + Param const& getparamext( size_t N, int defaultval ); int param_count( void ); void newparamchar( const Parser::Param* act ); diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc index aa55a06e..b05456ef 100644 --- a/src/terminal/terminalframebuffer.cc +++ b/src/terminal/terminalframebuffer.cc @@ -449,7 +449,7 @@ void DrawState::resize( int s_width, int s_height ) } Renditions::Renditions( color_type s_background ) - : foreground_color( 0 ), background_color( s_background ), attributes( 0 ) + : foreground_color( 0 ), background_color( s_background ), underline_color( 0 ), attributes( 0 ) {} /* This routine cannot be used to set a color beyond the 16-color set. */ @@ -457,7 +457,7 @@ void Renditions::set_rendition( color_type num ) { if ( num == 0 ) { clear_attributes(); - foreground_color = background_color = 0; + foreground_color = background_color = underline_color = 0; return; } @@ -467,6 +467,9 @@ void Renditions::set_rendition( color_type num ) } else if ( num == 49 ) { background_color = 0; return; + } else if ( num == 59 ) { + underline_color = 0; + return; } if ( ( 30 <= num ) && ( num <= 37 ) ) { /* foreground color in 8-color set */ @@ -500,8 +503,16 @@ void Renditions::set_rendition( color_type num ) set_attribute( italic, value ); break; case 4: + case 401: + set_attribute( underlined, true ); + break; case 24: - set_attribute( underlined, value ); + case 400: + set_attribute( underlined, false ); + set_attribute( underline_double, false ); + set_attribute( underline_curl, false ); + set_attribute( underline_dotted, false ); + set_attribute( underline_dashed, false ); break; case 5: case 25: @@ -519,6 +530,19 @@ void Renditions::set_rendition( color_type num ) case 29: set_attribute( strikethrough, value ); break; + + case 402: + set_attribute( underline_double, true ); + break; + case 403: + set_attribute( underline_curl, true ); + break; + case 404: + set_attribute( underline_dotted, true ); + break; + case 405: + set_attribute( underline_dashed, true ); + break; default: break; /* ignore unknown rendition */ } @@ -542,6 +566,11 @@ void Renditions::set_background_color( int num ) } } +void Renditions::set_underline_color( int num ) +{ + underline_color = num; +} + std::string Renditions::sgr( void ) const { std::string ret; @@ -564,6 +593,14 @@ std::string Renditions::sgr( void ) const ret.append( ";8" ); if ( get_attribute( strikethrough ) ) ret.append( ";9" ); + if ( get_attribute( underline_double ) ) + ret.append( ";4:2" ); + if ( get_attribute( underline_curl ) ) + ret.append( ";4:3" ); + if ( get_attribute( underline_dotted ) ) + ret.append( ";4:4" ); + if ( get_attribute( underline_dashed ) ) + ret.append( ";4:5" ); if ( foreground_color ) { // Since foreground_color is a 25-bit field, it is promoted to an int when @@ -607,6 +644,19 @@ std::string Renditions::sgr( void ) const } ret.append( col ); } + if ( underline_color ) { + if ( is_true_color( underline_color ) ) { + snprintf( col, + sizeof( col ), + ";58:2::%d:%d:%d", + ( underline_color >> 16 ) & 0xff, + ( underline_color >> 8 ) & 0xff, + underline_color & 0xff ); + } else { + snprintf( col, sizeof( col ), ";58:5:%d", underline_color ); + } + ret.append( col ); + } ret.append( "m" ); return ret; diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h index e0da04db..aa35c113 100644 --- a/src/terminal/terminalframebuffer.h +++ b/src/terminal/terminalframebuffer.h @@ -71,19 +71,24 @@ class Renditions inverse, invisible, strikethrough, - SIZE + underline_double, + underline_curl, + underline_dotted, + underline_dashed, } attribute_type; private: static const uint64_t true_color_mask = 0x1000000; - uint64_t foreground_color : 25; - uint64_t background_color : 25; - uint64_t attributes : 8; + uint32_t foreground_color; + uint32_t background_color; + uint32_t underline_color; + uint16_t attributes; public: Renditions( color_type s_background ); void set_foreground_color( int num ); void set_background_color( int num ); + void set_underline_color( int num ); void set_rendition( color_type num ); std::string sgr( void ) const; @@ -100,7 +105,7 @@ class Renditions bool operator==( const Renditions& x ) const { return ( attributes == x.attributes ) && ( foreground_color == x.foreground_color ) - && ( background_color == x.background_color ); + && ( background_color == x.background_color ) && ( underline_color == x.underline_color ); } void set_attribute( attribute_type attr, bool val ) { @@ -348,6 +353,7 @@ class DrawState void set_foreground_color( int x ) { renditions.set_foreground_color( x ); } void set_background_color( int x ) { renditions.set_background_color( x ); } + void set_underline_color( int x ) { renditions.set_underline_color( x ); } void add_rendition( color_type x ) { renditions.set_rendition( x ); } const Renditions& get_renditions( void ) const { return renditions; } Renditions& get_renditions( void ) { return renditions; } diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc index e4fdde43..cb6ea546 100644 --- a/src/terminal/terminalfunctions.cc +++ b/src/terminal/terminalfunctions.cc @@ -434,7 +434,10 @@ static Function func_Ctrl_BEL( CONTROL, "\x07", Ctrl_BEL ); static void CSI_SGR( Framebuffer* fb, Dispatcher* dispatch ) { for ( int i = 0; i < dispatch->param_count(); i++ ) { - int rendition = dispatch->getparam( i, 0 ); + Dispatcher::Param const &rendition_param = dispatch->getparamext( i, 0 ); + int rendition = rendition_param.val; + std::vector const &exts = rendition_param.exts; + /* We need to special-case the handling of [34]8 ; 5 ; Ps, because Ps of 0 in that case does not mean reset to default, even though it means that otherwise (as usually renditions are applied @@ -457,15 +460,56 @@ static void CSI_SGR( Framebuffer* fb, Dispatcher* dispatch ) color = Renditions::make_true_color( red, green, blue ); - if ( rendition == 38 ) { - fb->ds.set_foreground_color( color ); - } else { + ( rendition == 38 ) ? + fb->ds.set_foreground_color( color ) : fb->ds.set_background_color( color ); - } + i += 4; continue; } + /* underline color support */ + if (rendition == 58) { + unsigned int color = 0; + + if (exts.size() == 2 && exts[0] == 5) { + color = exts[1]; + } + else if (exts.size() >= 4 && exts[0] == 2) { + // color space ID _should_ be the first argument after the "2", + // but some applications don't put that parameter there. + int offset = exts.size() > 4 ? 1 : 0; + unsigned int red = exts[1 + offset]; + unsigned int green = exts[2 + offset]; + unsigned int blue = exts[3 + offset]; + + color = Renditions::make_true_color( red, green, blue ); + } + else if (dispatch->param_count() - i >= 3 && + dispatch->getparam( i+1, -1 ) == 5) { + color = dispatch->getparam( i+2, 0 ); + i += 2; + } + else if (dispatch->param_count() - i >= 5 && + dispatch->getparam( i+1, -1 ) == 2) { + unsigned int red = dispatch->getparam(i+2, 0); + unsigned int green = dispatch->getparam(i+3, 0); + unsigned int blue = dispatch->getparam(i+4, 0); + + color = Renditions::make_true_color( red, green, blue ); + i += 4; + } + + fb->ds.set_underline_color(color); + continue; + } + + /* undercurl extension support */ + if (rendition == 4 && exts.size() > 0) { + int style = exts[0]; + rendition = 400 + style; + } + fb->ds.add_rendition( rendition ); } }