@@ -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
@@ -1321,6 +1325,7 @@ private void startEscapeSequence() {
1321
1325
mEscapeState = ESC ;
1322
1326
mArgIndex = 0 ;
1323
1327
Arrays .fill (mArgs , -1 );
1328
+ mArgsSubParamsBitSet = 0 ;
1324
1329
}
1325
1330
1326
1331
private void doLinefeed () {
@@ -1805,6 +1810,11 @@ private void doCsi(int b) {
1805
1810
private void selectGraphicRendition () {
1806
1811
if (mArgIndex >= mArgs .length ) mArgIndex = mArgs .length - 1 ;
1807
1812
for (int i = 0 ; i <= mArgIndex ; i ++) {
1813
+ // Skip leading sub parameters:
1814
+ if ((mArgsSubParamsBitSet & (1 << i )) != 0 ) {
1815
+ continue ;
1816
+ }
1817
+
1808
1818
int code = getArg (i , 0 , false );
1809
1819
if (code < 0 ) {
1810
1820
if (mArgIndex > 0 ) {
@@ -1824,7 +1834,19 @@ private void selectGraphicRendition() {
1824
1834
} else if (code == 3 ) {
1825
1835
mEffect |= TextStyle .CHARACTER_ATTRIBUTE_ITALIC ;
1826
1836
} 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
+ }
1828
1850
} else if (code == 5 ) {
1829
1851
mEffect |= TextStyle .CHARACTER_ATTRIBUTE_BLINK ;
1830
1852
} else if (code == 7 ) {
@@ -1853,8 +1875,8 @@ private void selectGraphicRendition() {
1853
1875
mEffect &= ~TextStyle .CHARACTER_ATTRIBUTE_STRIKETHROUGH ;
1854
1876
} else if (code >= 30 && code <= 37 ) {
1855
1877
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.
1858
1880
// This is followed by either "2;$R;$G;$B" to set a 24-bit color or
1859
1881
// "5;$INDEX" to set an indexed color.
1860
1882
if (i + 2 > mArgIndex ) continue ;
@@ -1870,11 +1892,11 @@ private void selectGraphicRendition() {
1870
1892
if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255 ) {
1871
1893
finishSequenceAndLogError ("Invalid RGB: " + red + "," + green + "," + blue );
1872
1894
} 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 ;
1878
1900
}
1879
1901
}
1880
1902
i += 4 ; // "2;P_r;P_g;P_r"
@@ -1883,10 +1905,10 @@ private void selectGraphicRendition() {
1883
1905
int color = getArg (i + 2 , 0 , false );
1884
1906
i += 2 ; // "5;P_s"
1885
1907
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 ;
1890
1912
}
1891
1913
} else {
1892
1914
if (LOG_ESCAPE_SEQUENCES ) Logger .logWarn (mClient , LOG_TAG , "Invalid color index: " + color );
@@ -1900,6 +1922,8 @@ private void selectGraphicRendition() {
1900
1922
mBackColor = code - 40 ;
1901
1923
} else if (code == 49 ) { // Set default background color.
1902
1924
mBackColor = TextStyle .COLOR_INDEX_BACKGROUND ;
1925
+ } else if (code == 59 ) { // Set default underline color.
1926
+ mUnderlineColor = TextStyle .COLOR_INDEX_FOREGROUND ;
1903
1927
} else if (code >= 90 && code <= 97 ) { // Bright foreground colors (aixterm codes).
1904
1928
mForeColor = code - 90 + 8 ;
1905
1929
} else if (code >= 100 && code <= 107 ) { // Bright background color (aixterm codes).
@@ -2149,15 +2173,21 @@ private void scrollDownOneLine() {
2149
2173
/**
2150
2174
* Process the next ASCII character of a parameter.
2151
2175
*
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
2155
2183
* digit sent first. Any parameter greater than 9999 (decimal) is set to 9999
2156
2184
* (decimal). If you do not specify a value, a 0 value is assumed. A 0 value
2157
2185
* or omitted parameter indicates a default value for the sequence. For most
2158
2186
* sequences, the default value is 1.
2159
2187
*
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>
2161
2191
* */
2162
2192
private void parseArg (int b ) {
2163
2193
if (b >= '0' && b <= '9' ) {
@@ -2175,9 +2205,14 @@ private void parseArg(int b) {
2175
2205
mArgs [mArgIndex ] = value ;
2176
2206
}
2177
2207
continueSequence (mEscapeState );
2178
- } else if (b == ';' ) {
2179
- if (mArgIndex < mArgs .length ) {
2208
+ } else if (b == ';' || b == ':' ) {
2209
+ if (mArgIndex + 1 < mArgs .length ) {
2180
2210
mArgIndex ++;
2211
+ if (b == ':' ) {
2212
+ mArgsSubParamsBitSet |= 1 << mArgIndex ;
2213
+ }
2214
+ } else {
2215
+ logError ("Too many parameters when in state: " + mEscapeState );
2181
2216
}
2182
2217
continueSequence (mEscapeState );
2183
2218
} else {
0 commit comments