@@ -84,8 +84,8 @@ public final class TerminalEmulator {
84
84
/** Escape processing: "ESC _" or Application Program Command (APC), followed by Escape. */
85
85
private static final int ESC_APC_ESCAPE = 21 ;
86
86
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 ;
89
89
90
90
/** Needs to be large enough to contain reasonable OSC 52 pastes. */
91
91
private static final int MAX_OSC_STRING_LENGTH = 8192 ;
@@ -178,6 +178,8 @@ public final class TerminalEmulator {
178
178
private int mArgIndex ;
179
179
/** Holds the arguments of the current escape sequence. */
180
180
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 ;
181
183
182
184
/** Holds OSC and device control arguments, which can be strings. */
183
185
private final StringBuilder mOSCOrDeviceControlArgs = new StringBuilder ();
@@ -238,15 +240,17 @@ public final class TerminalEmulator {
238
240
private boolean mCursorBlinkState ;
239
241
240
242
/**
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.
242
244
* For a 24-bit value the top byte (0xff000000) is set.
243
245
*
246
+ * <p>Note that the underline color is currently parsed but not yet used during rendering.
247
+ *
244
248
* @see TextStyle
245
249
*/
246
- int mForeColor , mBackColor ;
250
+ int mForeColor , mBackColor , mUnderlineColor ;
247
251
248
252
/** Current {@link TextStyle} effect. */
249
- private int mEffect ;
253
+ int mEffect ;
250
254
251
255
/**
252
256
* The number of scrolled lines since last calling {@link #clearScrollCounter()}. Used for moving selection up along
@@ -1324,6 +1328,7 @@ private void startEscapeSequence() {
1324
1328
mEscapeState = ESC ;
1325
1329
mArgIndex = 0 ;
1326
1330
Arrays .fill (mArgs , -1 );
1331
+ mArgsSubParamsBitSet = 0 ;
1327
1332
}
1328
1333
1329
1334
private void doLinefeed () {
@@ -1808,6 +1813,11 @@ private void doCsi(int b) {
1808
1813
private void selectGraphicRendition () {
1809
1814
if (mArgIndex >= mArgs .length ) mArgIndex = mArgs .length - 1 ;
1810
1815
for (int i = 0 ; i <= mArgIndex ; i ++) {
1816
+ // Skip leading sub parameters:
1817
+ if ((mArgsSubParamsBitSet & (1 << i )) != 0 ) {
1818
+ continue ;
1819
+ }
1820
+
1811
1821
int code = getArg (i , 0 , false );
1812
1822
if (code < 0 ) {
1813
1823
if (mArgIndex > 0 ) {
@@ -1827,7 +1837,19 @@ private void selectGraphicRendition() {
1827
1837
} else if (code == 3 ) {
1828
1838
mEffect |= TextStyle .CHARACTER_ATTRIBUTE_ITALIC ;
1829
1839
} else if (code == 4 ) {
1830
- mEffect |= TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1840
+ if (i + 1 <= mArgIndex && ((mArgsSubParamsBitSet & (1 << (i + 1 ))) != 0 )) {
1841
+ // Sub parameter, see https://sw.kovidgoyal.net/kitty/underlines/
1842
+ i ++;
1843
+ if (mArgs [i ] == 0 ) {
1844
+ // No underline.
1845
+ mEffect &= ~TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1846
+ } else {
1847
+ // Different variations of underlines: https://sw.kovidgoyal.net/kitty/underlines/
1848
+ mEffect |= TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1849
+ }
1850
+ } else {
1851
+ mEffect |= TextStyle .CHARACTER_ATTRIBUTE_UNDERLINE ;
1852
+ }
1831
1853
} else if (code == 5 ) {
1832
1854
mEffect |= TextStyle .CHARACTER_ATTRIBUTE_BLINK ;
1833
1855
} else if (code == 7 ) {
@@ -1856,8 +1878,8 @@ private void selectGraphicRendition() {
1856
1878
mEffect &= ~TextStyle .CHARACTER_ATTRIBUTE_STRIKETHROUGH ;
1857
1879
} else if (code >= 30 && code <= 37 ) {
1858
1880
mForeColor = code - 30 ;
1859
- } else if (code == 38 || code == 48 ) {
1860
- // Extended set foreground(38)/background (48) color.
1881
+ } else if (code == 38 || code == 48 || code == 58 ) {
1882
+ // Extended set foreground(38)/background(48)/underline(58 ) color.
1861
1883
// This is followed by either "2;$R;$G;$B" to set a 24-bit color or
1862
1884
// "5;$INDEX" to set an indexed color.
1863
1885
if (i + 2 > mArgIndex ) continue ;
@@ -1873,11 +1895,11 @@ private void selectGraphicRendition() {
1873
1895
if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255 ) {
1874
1896
finishSequenceAndLogError ("Invalid RGB: " + red + "," + green + "," + blue );
1875
1897
} else {
1876
- int argbColor = 0xff000000 | (red << 16 ) | (green << 8 ) | blue ;
1877
- if (code == 38 ) {
1878
- mForeColor = argbColor ;
1879
- } else {
1880
- mBackColor = argbColor ;
1898
+ int argbColor = 0xff_00_00_00 | (red << 16 ) | (green << 8 ) | blue ;
1899
+ switch (code ) {
1900
+ case 38 : mForeColor = argbColor ; break ;
1901
+ case 48 : mBackColor = argbColor ; break ;
1902
+ case 58 : mUnderlineColor = argbColor ; break ;
1881
1903
}
1882
1904
}
1883
1905
i += 4 ; // "2;P_r;P_g;P_r"
@@ -1886,10 +1908,10 @@ private void selectGraphicRendition() {
1886
1908
int color = getArg (i + 2 , 0 , false );
1887
1909
i += 2 ; // "5;P_s"
1888
1910
if (color >= 0 && color < TextStyle .NUM_INDEXED_COLORS ) {
1889
- if (code == 38 ) {
1890
- mForeColor = color ;
1891
- } else {
1892
- mBackColor = color ;
1911
+ switch (code ) {
1912
+ case 38 : mForeColor = color ; break ;
1913
+ case 48 : mBackColor = color ; break ;
1914
+ case 58 : mUnderlineColor = color ; break ;
1893
1915
}
1894
1916
} else {
1895
1917
if (LOG_ESCAPE_SEQUENCES ) Logger .logWarn (mClient , LOG_TAG , "Invalid color index: " + color );
@@ -1903,6 +1925,8 @@ private void selectGraphicRendition() {
1903
1925
mBackColor = code - 40 ;
1904
1926
} else if (code == 49 ) { // Set default background color.
1905
1927
mBackColor = TextStyle .COLOR_INDEX_BACKGROUND ;
1928
+ } else if (code == 59 ) { // Set default underline color.
1929
+ mUnderlineColor = TextStyle .COLOR_INDEX_FOREGROUND ;
1906
1930
} else if (code >= 90 && code <= 97 ) { // Bright foreground colors (aixterm codes).
1907
1931
mForeColor = code - 90 + 8 ;
1908
1932
} else if (code >= 100 && code <= 107 ) { // Bright background color (aixterm codes).
@@ -2152,15 +2176,21 @@ private void scrollDownOneLine() {
2152
2176
/**
2153
2177
* Process the next ASCII character of a parameter.
2154
2178
*
2155
- * Parameter characters modify the action or interpretation of the sequence. You can use up to
2156
- * 16 parameters per sequence. You must use the ; character to separate parameters.
2157
- * All parameters are unsigned, positive decimal integers, with the most significant
2179
+ * <p>You must use the ; character to separate parameters and : to separate sub-parameters.
2180
+ *
2181
+ * <p>Parameter characters modify the action or interpretation of the sequence. Originally
2182
+ * you can use up to 16 parameters per sequence, but following at least xterm and alacritty
2183
+ * we use a common space for parameters and sub-parameters, allowing 32 in total.
2184
+ *
2185
+ * <p>All parameters are unsigned, positive decimal integers, with the most significant
2158
2186
* digit sent first. Any parameter greater than 9999 (decimal) is set to 9999
2159
2187
* (decimal). If you do not specify a value, a 0 value is assumed. A 0 value
2160
2188
* or omitted parameter indicates a default value for the sequence. For most
2161
2189
* sequences, the default value is 1.
2162
2190
*
2163
- * https://vt100.net/docs/vt510-rm/chapter4.html#S4.3.3
2191
+ * <p>References:
2192
+ * <a href="https://vt100.net/docs/vt510-rm/chapter4.html#S4.3.3">VT510 Video Terminal Programmer Information: Control Sequences</a>
2193
+ * <a href="https://github.com/alacritty/vte/issues/22">alacritty/vte: Implement colon separated CSI parameters</a>
2164
2194
* */
2165
2195
private void parseArg (int b ) {
2166
2196
if (b >= '0' && b <= '9' ) {
@@ -2178,9 +2208,14 @@ private void parseArg(int b) {
2178
2208
mArgs [mArgIndex ] = value ;
2179
2209
}
2180
2210
continueSequence (mEscapeState );
2181
- } else if (b == ';' ) {
2182
- if (mArgIndex < mArgs .length ) {
2211
+ } else if (b == ';' || b == ':' ) {
2212
+ if (mArgIndex + 1 < mArgs .length ) {
2183
2213
mArgIndex ++;
2214
+ if (b == ':' ) {
2215
+ mArgsSubParamsBitSet |= 1 << mArgIndex ;
2216
+ }
2217
+ } else {
2218
+ logError ("Too many parameters when in state: " + mEscapeState );
2184
2219
}
2185
2220
continueSequence (mEscapeState );
2186
2221
} else {
0 commit comments