@@ -84,8 +84,8 @@ public final class TerminalEmulator {
8484 /** Escape processing: "ESC _" or Application Program Command (APC), followed by Escape. */
8585 private static final int ESC_APC_ESCAPE = 21 ;
8686
87- /** The number of parameter arguments. This name comes from the ANSI standard for terminal escape codes . */
88- private static final int MAX_ESCAPE_PARAMETERS = 16 ;
87+ /** The number of parameter arguments including colon separated sub-parameters . */
88+ private static final int MAX_ESCAPE_PARAMETERS = 32 ;
8989
9090 /** Needs to be large enough to contain reasonable OSC 52 pastes. */
9191 private static final int MAX_OSC_STRING_LENGTH = 8192 ;
@@ -178,6 +178,8 @@ public final class TerminalEmulator {
178178 private int mArgIndex ;
179179 /** Holds the arguments of the current escape sequence. */
180180 private final int [] mArgs = new int [MAX_ESCAPE_PARAMETERS ];
181+ /** Holds the bit flags which arguments are sub parameters (after a colon) - bit N is set if <code>mArgs[N]</code> is a sub parameter. */
182+ private int mArgsSubParamsBitSet = 0 ;
181183
182184 /** Holds OSC and device control arguments, which can be strings. */
183185 private final StringBuilder mOSCOrDeviceControlArgs = new StringBuilder ();
@@ -238,15 +240,17 @@ public final class TerminalEmulator {
238240 private boolean mCursorBlinkState ;
239241
240242 /**
241- * Current foreground and background colors. Can either be a color index in [0,259] or a truecolor (24-bit) value.
243+ * Current foreground, background and underline colors. Can either be a color index in [0,259] or a truecolor (24-bit) value.
242244 * For a 24-bit value the top byte (0xff000000) is set.
243245 *
246+ * <p>Note that the underline color is currently parsed but not yet used during rendering.
247+ *
244248 * @see TextStyle
245249 */
246- int mForeColor , mBackColor ;
250+ int mForeColor , mBackColor , mUnderlineColor ;
247251
248252 /** Current {@link TextStyle} effect. */
249- private int mEffect ;
253+ int mEffect ;
250254
251255 /**
252256 * The number of scrolled lines since last calling {@link #clearScrollCounter()}. Used for moving selection up along
@@ -1321,6 +1325,7 @@ private void startEscapeSequence() {
13211325 mEscapeState = ESC ;
13221326 mArgIndex = 0 ;
13231327 Arrays .fill (mArgs , -1 );
1328+ mArgsSubParamsBitSet = 0 ;
13241329 }
13251330
13261331 private void doLinefeed () {
@@ -1805,6 +1810,11 @@ private void doCsi(int b) {
18051810 private void selectGraphicRendition () {
18061811 if (mArgIndex >= mArgs .length ) mArgIndex = mArgs .length - 1 ;
18071812 for (int i = 0 ; i <= mArgIndex ; i ++) {
1813+ // Skip leading sub parameters:
1814+ if ((mArgsSubParamsBitSet & (1 << i )) != 0 ) {
1815+ continue ;
1816+ }
1817+
18081818 int code = getArg (i , 0 , false );
18091819 if (code < 0 ) {
18101820 if (mArgIndex > 0 ) {
@@ -1824,7 +1834,19 @@ private void selectGraphicRendition() {
18241834 } else if (code == 3 ) {
18251835 mEffect |= TextStyle .CHARACTER_ATTRIBUTE_ITALIC ;
18261836 } else if (code == 4 ) {
1827- mEffect |= TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1837+ if (i + 1 <= mArgIndex && ((mArgsSubParamsBitSet & (1 << (i + 1 ))) != 0 )) {
1838+ // Sub parameter, see https://sw.kovidgoyal.net/kitty/underlines/
1839+ i ++;
1840+ if (mArgs [i ] == 0 ) {
1841+ // No underline.
1842+ mEffect &= ~TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1843+ } else {
1844+ // Different variations of underlines: https://sw.kovidgoyal.net/kitty/underlines/
1845+ mEffect |= TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1846+ }
1847+ } else {
1848+ mEffect |= TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1849+ }
18281850 } else if (code == 5 ) {
18291851 mEffect |= TextStyle .CHARACTER_ATTRIBUTE_BLINK ;
18301852 } else if (code == 7 ) {
@@ -1853,8 +1875,8 @@ private void selectGraphicRendition() {
18531875 mEffect &= ~TextStyle .CHARACTER_ATTRIBUTE_STRIKETHROUGH ;
18541876 } else if (code >= 30 && code <= 37 ) {
18551877 mForeColor = code - 30 ;
1856- } else if (code == 38 || code == 48 ) {
1857- // Extended set foreground(38)/background (48) color.
1878+ } else if (code == 38 || code == 48 || code == 58 ) {
1879+ // Extended set foreground(38)/background(48)/underline(58 ) color.
18581880 // This is followed by either "2;$R;$G;$B" to set a 24-bit color or
18591881 // "5;$INDEX" to set an indexed color.
18601882 if (i + 2 > mArgIndex ) continue ;
@@ -1870,11 +1892,11 @@ private void selectGraphicRendition() {
18701892 if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255 ) {
18711893 finishSequenceAndLogError ("Invalid RGB: " + red + "," + green + "," + blue );
18721894 } else {
1873- int argbColor = 0xff000000 | (red << 16 ) | (green << 8 ) | blue ;
1874- if (code == 38 ) {
1875- mForeColor = argbColor ;
1876- } else {
1877- mBackColor = argbColor ;
1895+ int argbColor = 0xff_00_00_00 | (red << 16 ) | (green << 8 ) | blue ;
1896+ switch (code ) {
1897+ case 38 : mForeColor = argbColor ; break ;
1898+ case 48 : mBackColor = argbColor ; break ;
1899+ case 58 : mUnderlineColor = argbColor ; break ;
18781900 }
18791901 }
18801902 i += 4 ; // "2;P_r;P_g;P_r"
@@ -1883,10 +1905,10 @@ private void selectGraphicRendition() {
18831905 int color = getArg (i + 2 , 0 , false );
18841906 i += 2 ; // "5;P_s"
18851907 if (color >= 0 && color < TextStyle .NUM_INDEXED_COLORS ) {
1886- if (code == 38 ) {
1887- mForeColor = color ;
1888- } else {
1889- mBackColor = color ;
1908+ switch (code ) {
1909+ case 38 : mForeColor = color ; break ;
1910+ case 48 : mBackColor = color ; break ;
1911+ case 58 : mUnderlineColor = color ; break ;
18901912 }
18911913 } else {
18921914 if (LOG_ESCAPE_SEQUENCES ) Logger .logWarn (mClient , LOG_TAG , "Invalid color index: " + color );
@@ -1900,6 +1922,8 @@ private void selectGraphicRendition() {
19001922 mBackColor = code - 40 ;
19011923 } else if (code == 49 ) { // Set default background color.
19021924 mBackColor = TextStyle .COLOR_INDEX_BACKGROUND ;
1925+ } else if (code == 59 ) { // Set default underline color.
1926+ mUnderlineColor = TextStyle .COLOR_INDEX_FOREGROUND ;
19031927 } else if (code >= 90 && code <= 97 ) { // Bright foreground colors (aixterm codes).
19041928 mForeColor = code - 90 + 8 ;
19051929 } else if (code >= 100 && code <= 107 ) { // Bright background color (aixterm codes).
@@ -2149,15 +2173,21 @@ private void scrollDownOneLine() {
21492173 /**
21502174 * Process the next ASCII character of a parameter.
21512175 *
2152- * Parameter characters modify the action or interpretation of the sequence. You can use up to
2153- * 16 parameters per sequence. You must use the ; character to separate parameters.
2154- * All parameters are unsigned, positive decimal integers, with the most significant
2176+ * <p>You must use the ; character to separate parameters and : to separate sub-parameters.
2177+ *
2178+ * <p>Parameter characters modify the action or interpretation of the sequence. Originally
2179+ * you can use up to 16 parameters per sequence, but following at least xterm and alacritty
2180+ * we use a common space for parameters and sub-parameters, allowing 32 in total.
2181+ *
2182+ * <p>All parameters are unsigned, positive decimal integers, with the most significant
21552183 * digit sent first. Any parameter greater than 9999 (decimal) is set to 9999
21562184 * (decimal). If you do not specify a value, a 0 value is assumed. A 0 value
21572185 * or omitted parameter indicates a default value for the sequence. For most
21582186 * sequences, the default value is 1.
21592187 *
2160- * https://vt100.net/docs/vt510-rm/chapter4.html#S4.3.3
2188+ * <p>References:
2189+ * <a href="https://vt100.net/docs/vt510-rm/chapter4.html#S4.3.3">VT510 Video Terminal Programmer Information: Control Sequences</a>
2190+ * <a href="https://github.com/alacritty/vte/issues/22">alacritty/vte: Implement colon separated CSI parameters</a>
21612191 * */
21622192 private void parseArg (int b ) {
21632193 if (b >= '0' && b <= '9' ) {
@@ -2175,9 +2205,14 @@ private void parseArg(int b) {
21752205 mArgs [mArgIndex ] = value ;
21762206 }
21772207 continueSequence (mEscapeState );
2178- } else if (b == ';' ) {
2179- if (mArgIndex < mArgs .length ) {
2208+ } else if (b == ';' || b == ':' ) {
2209+ if (mArgIndex + 1 < mArgs .length ) {
21802210 mArgIndex ++;
2211+ if (b == ':' ) {
2212+ mArgsSubParamsBitSet |= 1 << mArgIndex ;
2213+ }
2214+ } else {
2215+ logError ("Too many parameters when in state: " + mEscapeState );
21812216 }
21822217 continueSequence (mEscapeState );
21832218 } else {
0 commit comments