diff --git a/app/src/main/java/com/termux/terminal/TerminalBuffer.java b/app/src/main/java/com/termux/terminal/TerminalBuffer.java index b57a8d00..8a78bb83 100644 --- a/app/src/main/java/com/termux/terminal/TerminalBuffer.java +++ b/app/src/main/java/com/termux/terminal/TerminalBuffer.java @@ -140,7 +140,7 @@ public final class TerminalBuffer { * @param newRows The number of rows the screen should have. * @param cursor An int[2] containing the (column, row) cursor location. */ - public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor, int currentStyle, boolean altScreen) { + public void resize(int newColumns, int newRows, int newTotalRows, int[] cursor, long currentStyle, boolean altScreen) { // newRows > mTotalRows should not normally happen since mTotalRows is TRANSCRIPT_ROWS (10000): if (newColumns == mColumns && newRows <= mTotalRows) { // Fast resize where just the rows changed. @@ -237,7 +237,7 @@ public final class TerminalBuffer { } int currentOldCol = 0; - int styleAtCol = 0; + long styleAtCol = 0; for (int i = 0; i < lastNonSpaceIndex; i++) { // Note that looping over java character, not cells. char c = oldLine.mText[i]; @@ -321,7 +321,7 @@ public final class TerminalBuffer { * @param bottomMargin One line after the last line that is scrolled. * @param style the style for the newly exposed line. */ - public void scrollDownOneLine(int topMargin, int bottomMargin, int style) { + public void scrollDownOneLine(int topMargin, int bottomMargin, long style) { if (topMargin > bottomMargin - 1 || topMargin < 0 || bottomMargin > mScreenRows) throw new IllegalArgumentException("topMargin=" + topMargin + ", bottomMargin=" + bottomMargin + ", mScreenRows=" + mScreenRows); @@ -374,7 +374,7 @@ public final class TerminalBuffer { * InvalidParemeterException will be thrown. Typically this is called with a "val" argument of 32 to clear a block * of characters. */ - public void blockSet(int sx, int sy, int w, int h, int val, int style) { + public void blockSet(int sx, int sy, int w, int h, int val, long style) { if (sx < 0 || sx + w > mColumns || sy < 0 || sy + h > mScreenRows) { throw new IllegalArgumentException( "Illegal arguments! blockSet(" + sx + ", " + sy + ", " + w + ", " + h + ", " + val + ", " + mColumns + ", " + mScreenRows + ")"); @@ -388,14 +388,14 @@ public final class TerminalBuffer { return (mLines[row] == null) ? (mLines[row] = new TerminalRow(mColumns, 0)) : mLines[row]; } - public void setChar(int column, int row, int codePoint, int style) { + public void setChar(int column, int row, int codePoint, long style) { if (row >= mScreenRows || column >= mColumns) throw new IllegalArgumentException("row=" + row + ", column=" + column + ", mScreenRows=" + mScreenRows + ", mColumns=" + mColumns); row = externalToInternalRow(row); allocateFullLineIfNecessary(row).setChar(column, codePoint, style); } - public int getStyleAt(int externalRow, int column) { + public long getStyleAt(int externalRow, int column) { return allocateFullLineIfNecessary(externalToInternalRow(externalRow)).getStyle(column); } @@ -407,7 +407,7 @@ public final class TerminalBuffer { int startOfLine = (rectangular || y == top) ? left : leftMargin; int endOfLine = (rectangular || y + 1 == bottom) ? right : rightMargin; for (int x = startOfLine; x < endOfLine; x++) { - int currentStyle = line.getStyle(x); + long currentStyle = line.getStyle(x); int foreColor = TextStyle.decodeForeColor(currentStyle); int backColor = TextStyle.decodeBackColor(currentStyle); int effect = TextStyle.decodeEffect(currentStyle); diff --git a/app/src/main/java/com/termux/terminal/TerminalEmulator.java b/app/src/main/java/com/termux/terminal/TerminalEmulator.java index 23f171af..2cdf0955 100644 --- a/app/src/main/java/com/termux/terminal/TerminalEmulator.java +++ b/app/src/main/java/com/termux/terminal/TerminalEmulator.java @@ -206,10 +206,15 @@ public final class TerminalEmulator { */ private boolean mAboutToAutoWrap; - /** Foreground and background color indices, 0..255. */ + /** + * Current foreground and background colors. Can either be a color index in [0,259] or a truecolor (24-bit) value. + * For a 24-bit value the top byte (0xff000000) is set. + * + * @see TextStyle + */ int mForeColor, mBackColor; - /** Current TextStyle effect */ + /** Current {@link TextStyle} effect. */ private int mEffect; /** @@ -611,7 +616,7 @@ public final class TerminalEmulator { int left = Math.min(getArg(argIndex++, 1, true) + effectiveLeftMargin, effectiveRightMargin + 1); int bottom = Math.min(getArg(argIndex++, mRows, true) + effectiveTopMargin, effectiveBottomMargin); int right = Math.min(getArg(argIndex, mColumns, true) + effectiveLeftMargin, effectiveRightMargin); - int style = getStyle(); + long style = getStyle(); for (int row = top - 1; row < bottom; row++) for (int col = left - 1; col < right; col++) if (!selective || (TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0) @@ -953,7 +958,7 @@ public final class TerminalEmulator { unknownSequence(b); break; } - int style = getStyle(); + long style = getStyle(); for (int row = startRow; row < endRow; row++) { for (int col = startCol; col < endCol; col++) { if ((TextStyle.decodeEffect(mScreen.getStyleAt(row, col)) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0) @@ -1687,43 +1692,42 @@ public final class TerminalEmulator { } else if (code >= 30 && code <= 37) { mForeColor = code - 30; } else if (code == 38 || code == 48) { - // ISO-8613-3 controls to set foreground (38) or background (48) colors. - // P_s = (38|48) ; 2 ; P_r ; P_g ; P_b => Set to RGB value in range (0-255). - // P_s = (38|48) ; 5 ; P_s => Set to indexed color. - if (i + 2 <= mArgIndex) { - int color = -1; - int firstArg = mArgs[i + 1]; - if (firstArg == 2) { - if (i + 4 > mArgIndex) { - Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments"); - } else { - int red = mArgs[i + 2], green = mArgs[i + 3], blue = mArgs[i + 4]; - if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255) { - finishSequenceAndLogError("Invalid RGB: " + red + "," + green + "," + blue); - } else { - // TODO: Implement 24 bit color. - finishSequenceAndLogError("Unimplemented RGB: " + red + "," + green + "," + blue); - } - i += 4; // "2;P_r;P_g;P_r" - } - } else if (firstArg == 5) { - color = mArgs[i + 2]; - i += 2; // "5;P_s" + // Extended set foreground(38)/background (48) color. + // This is followed by either "2;$R;$G;$B" to set a 24-bit color or + // "5;$INDEX" to set an indexed color. + if (i + 2 > mArgIndex) continue; + int firstArg = mArgs[i + 1]; + if (firstArg == 2) { + if (i + 4 > mArgIndex) { + Log.w(EmulatorDebug.LOG_TAG, "Too few CSI" + code + ";2 RGB arguments"); } else { - finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg); - } - if (i != -1) { - if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) { - if (code == 38) { - mForeColor = color; - } else { - mBackColor = color; - } + int red = mArgs[i + 2], green = mArgs[i + 3], blue = mArgs[i + 4]; + if (red < 0 || green < 0 || blue < 0 || red > 255 || green > 255 || blue > 255) { + finishSequenceAndLogError("Invalid RGB: " + red + "," + green + "," + blue); } else { - if (LOG_ESCAPE_SEQUENCES) - Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color); + int argbColor = 0xff000000 | (red << 16) | (green << 8) | blue; + if (code == 38) { + mForeColor = argbColor; + } else { + mBackColor = argbColor; + } } + i += 4; // "2;P_r;P_g;P_r" } + } else if (firstArg == 5) { + int color = mArgs[i + 2]; + i += 2; // "5;P_s" + if (color >= 0 && color < TextStyle.NUM_INDEXED_COLORS) { + if (code == 38) { + mForeColor = color; + } else { + mBackColor = color; + } + } else { + if (LOG_ESCAPE_SEQUENCES) Log.w(EmulatorDebug.LOG_TAG, "Invalid color index: " + color); + } + } else { + finishSequenceAndLogError("Invalid ISO-8613-3 SGR first argument: " + firstArg); } } else if (code == 39) { // Set default foreground color. mForeColor = TextStyle.COLOR_INDEX_FOREGROUND; @@ -1924,7 +1928,7 @@ public final class TerminalEmulator { mScreen.blockSet(sx, sy, w, h, ' ', getStyle()); } - private int getStyle() { + private long getStyle() { return TextStyle.encode(mForeColor, mBackColor, mEffect); } diff --git a/app/src/main/java/com/termux/terminal/TerminalRow.java b/app/src/main/java/com/termux/terminal/TerminalRow.java index eb34d39c..d069545a 100644 --- a/app/src/main/java/com/termux/terminal/TerminalRow.java +++ b/app/src/main/java/com/termux/terminal/TerminalRow.java @@ -20,13 +20,13 @@ public final class TerminalRow { /** If this row has been line wrapped due to text output at the end of line. */ boolean mLineWrap; /** The style bits of each cell in the row. See {@link TextStyle}. */ - final int[] mStyle; + final long[] mStyle; /** Construct a blank row (containing only whitespace, ' ') with a specified style. */ - public TerminalRow(int columns, int style) { + public TerminalRow(int columns, long style) { mColumns = columns; mText = new char[(int) (SPARE_CAPACITY_FACTOR * columns)]; - mStyle = new int[columns]; + mStyle = new long[columns]; clear(style); } @@ -112,14 +112,14 @@ public final class TerminalRow { return false; } - public void clear(int style) { + public void clear(long style) { Arrays.fill(mText, ' '); Arrays.fill(mStyle, style); mSpaceUsed = (short) mColumns; } // https://github.com/steven676/Android-Terminal-Emulator/commit/9a47042620bec87617f0b4f5d50568535668fe26 - public void setChar(int columnToSet, int codePoint, int style) { + public void setChar(int columnToSet, int codePoint, long style) { mStyle[columnToSet] = style; final int newCodePointDisplayWidth = WcWidth.width(codePoint); @@ -225,7 +225,7 @@ public final class TerminalRow { return true; } - public final int getStyle(int column) { + public final long getStyle(int column) { return mStyle[column]; } diff --git a/app/src/main/java/com/termux/terminal/TerminalSession.java b/app/src/main/java/com/termux/terminal/TerminalSession.java index 41ed12b1..2eb446a0 100644 --- a/app/src/main/java/com/termux/terminal/TerminalSession.java +++ b/app/src/main/java/com/termux/terminal/TerminalSession.java @@ -173,7 +173,7 @@ public final class TerminalSession extends TerminalOutput { * @param rows The number of rows in the terminal window. */ public void initializeEmulator(int columns, int rows) { - mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */5000); + mEmulator = new TerminalEmulator(this, columns, rows, /* transcript= */2000); int[] processId = new int[1]; mTerminalFileDescriptor = JNI.createSubprocess(mShellPath, mCwd, mArgs, mEnv, processId, rows, columns); diff --git a/app/src/main/java/com/termux/terminal/TextStyle.java b/app/src/main/java/com/termux/terminal/TextStyle.java index 2986c483..0da289c2 100644 --- a/app/src/main/java/com/termux/terminal/TextStyle.java +++ b/app/src/main/java/com/termux/terminal/TextStyle.java @@ -1,11 +1,13 @@ package com.termux.terminal; /** - * Encodes effects, foreground and background colors into a 32 bit integer, which are stored for each cell in a terminal + * Encodes effects, foreground and background colors into a 64 bit long, which are stored for each cell in a terminal * row in {@link TerminalRow#mStyle}. *
- * The foreground and background colors take 9 bits each, leaving (32-9-9)=14 bits for effect flags. Using 9 for now - * (the different CHARACTER_ATTRIBUTE_* bits). + * The bit layout is: + * - 16 flags (11 currently used). + * - 24 for foreground color (only 9 first bits if a color index). + * - 24 for background color (only 9 first bits if a color index). */ public final class TextStyle { @@ -25,6 +27,10 @@ public final class TextStyle { public final static int CHARACTER_ATTRIBUTE_PROTECTED = 1 << 7; /** Dim colors. Also known as faint or half intensity. */ public final static int CHARACTER_ATTRIBUTE_DIM = 1 << 8; + /** If true (24-bit) color is used for the cell for foreground. */ + private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND = 1 << 9; + /** If true (24-bit) color is used for the cell for foreground. */ + private final static int CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND= 1 << 10; public final static int COLOR_INDEX_FOREGROUND = 256; public final static int COLOR_INDEX_BACKGROUND = 257; @@ -34,22 +40,47 @@ public final class TextStyle { public final static int NUM_INDEXED_COLORS = 259; /** Normal foreground and background colors and no effects. */ - final static int NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0); + final static long NORMAL = encode(COLOR_INDEX_FOREGROUND, COLOR_INDEX_BACKGROUND, 0); - static int encode(int foreColor, int backColor, int effect) { - return ((effect & 0b111111111) << 18) | ((foreColor & 0b111111111) << 9) | (backColor & 0b111111111); + static long encode(int foreColor, int backColor, int effect) { + long result = effect & 0b111111111; + if ((0xff000000 & foreColor) == 0xff000000) { + // 24-bit color. + result |= CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND | (((long) foreColor & 0x00ffffffL) << 40L); + } else { + // Indexed color. + result |= (((long) foreColor) & 0b111111111L) << 40; + } + if ((0xff000000 & backColor) == 0xff000000) { + // 24-bit color. + result |= CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND | (((long) backColor & 0x00ffffffL) << 16L); + } else { + // Indexed color. + result |= (((long) backColor) & 0b111111111L) << 16L; + } + + return result; } - public static int decodeForeColor(int encodedColor) { - return (encodedColor >> 9) & 0b111111111; + public static int decodeForeColor(long style) { + if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_FOREGROUND) == 0) { + return (int) ((style >>> 40) & 0b111111111L); + } else { + return 0xff000000 | (int) ((style >>> 40) & 0x00ffffffL); + } + } - public static int decodeBackColor(int encodedColor) { - return encodedColor & 0b111111111; + public static int decodeBackColor(long style) { + if ((style & CHARACTER_ATTRIBUTE_TRUECOLOR_BACKGROUND) == 0) { + return (int) ((style >>> 16) & 0b111111111L); + } else { + return 0xff000000 | (int) ((style >>> 16) & 0x00ffffffL); + } } - public static int decodeEffect(int encodedColor) { - return (encodedColor >> 18) & 0b111111111; + public static int decodeEffect(long style) { + return (int) (style & 0b11111111111); } } diff --git a/app/src/main/java/com/termux/view/TerminalRenderer.java b/app/src/main/java/com/termux/view/TerminalRenderer.java index 4d24b059..d48d3b98 100644 --- a/app/src/main/java/com/termux/view/TerminalRenderer.java +++ b/app/src/main/java/com/termux/view/TerminalRenderer.java @@ -82,7 +82,7 @@ final class TerminalRenderer { final char[] line = lineObject.mText; final int charsUsedInLine = lineObject.getSpaceUsed(); - int lastRunStyle = 0; + long lastRunStyle = 0; boolean lastRunInsideCursor = false; int lastRunStartColumn = -1; int lastRunStartIndex = 0; @@ -97,7 +97,7 @@ final class TerminalRenderer { final int codePoint = charIsHighsurrogate ? Character.toCodePoint(charAtIndex, line[currentCharIndex + 1]) : charAtIndex; final int codePointWcWidth = WcWidth.width(codePoint); final boolean insideCursor = (column >= selx1 && column <= selx2) || (cursorX == column || (codePointWcWidth == 2 && cursorX == column + 1)); - final int style = lineObject.getStyle(column); + final long style = lineObject.getStyle(column); // Check if the measured text width for this code point is not the same as that expected by wcwidth(). // This could happen for some fonts which are not truly monospace, or for more exotic characters such as @@ -154,9 +154,17 @@ final class TerminalRenderer { * @param reverseVideo if the screen is rendered with the global reverse video flag set */ private void drawTextRun(Canvas canvas, char[] text, int[] palette, float y, int startColumn, int runWidthColumns, int startCharIndex, int runWidthChars, - float mes, boolean cursor, int textStyle, boolean reverseVideo) { + float mes, boolean cursor, long textStyle, boolean reverseVideo) { int foreColor = TextStyle.decodeForeColor(textStyle); int backColor = TextStyle.decodeBackColor(textStyle); + + int foreColorIndex = -1; + if ((foreColor & 0xff000000) != 0xff000000) { + foreColorIndex = foreColor; + foreColor = palette[foreColor]; + } + if ((backColor & 0xff000000) != 0xff000000) backColor = palette[backColor]; + final int effect = TextStyle.decodeEffect(textStyle); float left = startColumn * mFontWidth; float right = left + runWidthColumns * mFontWidth; @@ -180,9 +188,9 @@ final class TerminalRenderer { backColor = tmp; } - if (backColor != TextStyle.COLOR_INDEX_BACKGROUND) { + if (backColor != palette[TextStyle.COLOR_INDEX_BACKGROUND]) { // Only draw non-default background. - mTextPaint.setColor(palette[backColor]); + mTextPaint.setColor(backColor); canvas.drawRect(left, y - mFontLineSpacingAndAscent + mFontAscent, right, y, mTextPaint); } @@ -195,26 +203,25 @@ final class TerminalRenderer { final boolean dim = (effect & TextStyle.CHARACTER_ATTRIBUTE_DIM) != 0; // Let bold have bright colors if applicable (one of the first 8): - final int actualForeColor = foreColor + (bold && foreColor < 8 ? 8 : 0); + if (bold && foreColorIndex >= 0 && foreColorIndex < 8) foreColor = palette[foreColorIndex + 8]; - int foreColorARGB = palette[actualForeColor]; if (dim) { - int red = (0xFF & (foreColorARGB >> 16)); - int green = (0xFF & (foreColorARGB >> 8)); - int blue = (0xFF & foreColorARGB); + int red = (0xFF & (foreColor >> 16)); + int green = (0xFF & (foreColor >> 8)); + int blue = (0xFF & foreColor); // Dim color handling used by libvte which in turn took it from xterm // (https://bug735245.bugzilla-attachments.gnome.org/attachment.cgi?id=284267): red = red * 2 / 3; green = green * 2 / 3; blue = blue * 2 / 3; - foreColorARGB = 0xFF000000 + (red << 16) + (green << 8) + blue; + foreColor = 0xFF000000 + (red << 16) + (green << 8) + blue; } mTextPaint.setFakeBoldText(bold); mTextPaint.setUnderlineText(underline); mTextPaint.setTextSkewX(italic ? -0.35f : 0.f); mTextPaint.setStrikeThruText(strikeThrough); - mTextPaint.setColor(foreColorARGB); + mTextPaint.setColor(foreColor); // The text alignment is the default Paint.Align.LEFT. canvas.drawText(text, startCharIndex, runWidthChars, left, y - mFontLineSpacingAndAscent, mTextPaint); diff --git a/app/src/test/java/com/termux/terminal/CursorAndScreenTest.java b/app/src/test/java/com/termux/terminal/CursorAndScreenTest.java index 5584d74a..567e627b 100644 --- a/app/src/test/java/com/termux/terminal/CursorAndScreenTest.java +++ b/app/src/test/java/com/termux/terminal/CursorAndScreenTest.java @@ -18,7 +18,7 @@ public class CursorAndScreenTest extends TerminalTestCase { assertLinesAre("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY"); for (int row = 0; row < 5; row++) { for (int col = 0; col < 5; col++) { - int s = getStyleAt(row, col); + long s = getStyleAt(row, col); Assert.assertEquals(col, TextStyle.decodeForeColor(s)); Assert.assertEquals(row, TextStyle.decodeBackColor(s)); } @@ -28,7 +28,7 @@ public class CursorAndScreenTest extends TerminalTestCase { assertLinesAre("KLMNO", "PQRST", "UVWXY", " ", " "); for (int row = 0; row < 3; row++) { for (int col = 0; col < 5; col++) { - int s = getStyleAt(row, col); + long s = getStyleAt(row, col); Assert.assertEquals(col, TextStyle.decodeForeColor(s)); Assert.assertEquals(row + 2, TextStyle.decodeBackColor(s)); } @@ -43,7 +43,7 @@ public class CursorAndScreenTest extends TerminalTestCase { for (int col = 0; col < 5; col++) { int wantedForeground = (row == 1 || row == 2) ? 98 : col; int wantedBackground = (row == 1 || row == 2) ? 99 : (row == 0 ? 2 : row); - int s = getStyleAt(row, col); + long s = getStyleAt(row, col); Assert.assertEquals(wantedForeground, TextStyle.decodeForeColor(s)); Assert.assertEquals(wantedBackground, TextStyle.decodeBackColor(s)); } diff --git a/app/src/test/java/com/termux/terminal/ResizeTest.java b/app/src/test/java/com/termux/terminal/ResizeTest.java index 6305721d..d0251da8 100644 --- a/app/src/test/java/com/termux/terminal/ResizeTest.java +++ b/app/src/test/java/com/termux/terminal/ResizeTest.java @@ -93,7 +93,7 @@ public class ResizeTest extends TerminalTestCase { enterString("\033[2J"); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { - int style = getStyleAt(r, c); + long style = getStyleAt(r, c); assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(129, TextStyle.decodeBackColor(style)); } @@ -105,7 +105,7 @@ public class ResizeTest extends TerminalTestCase { // After resize, screen should still be same color: for (int r = 0; r < rows - 2; r++) { for (int c = 0; c < cols; c++) { - int style = getStyleAt(r, c); + long style = getStyleAt(r, c); assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(129, TextStyle.decodeBackColor(style)); } @@ -116,7 +116,7 @@ public class ResizeTest extends TerminalTestCase { resize(cols, rows); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { - int style = getStyleAt(r, c); + long style = getStyleAt(r, c); assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals("wrong at row=" + r, r >= 3 ? 200 : 129, TextStyle.decodeBackColor(style)); } diff --git a/app/src/test/java/com/termux/terminal/ScreenBufferTest.java b/app/src/test/java/com/termux/terminal/ScreenBufferTest.java index ffd1f5dd..08de2905 100644 --- a/app/src/test/java/com/termux/terminal/ScreenBufferTest.java +++ b/app/src/test/java/com/termux/terminal/ScreenBufferTest.java @@ -1,6 +1,6 @@ package com.termux.terminal; -public class ScreenBufferTest extends TerminalTest { +public class ScreenBufferTest extends TerminalTestCase { public void testBasics() { TerminalBuffer screen = new TerminalBuffer(5, 3, 3); diff --git a/app/src/test/java/com/termux/terminal/TerminalTest.java b/app/src/test/java/com/termux/terminal/TerminalTest.java index f3939cfc..67249dda 100644 --- a/app/src/test/java/com/termux/terminal/TerminalTest.java +++ b/app/src/test/java/com/termux/terminal/TerminalTest.java @@ -147,12 +147,11 @@ public class TerminalTest extends TerminalTestCase { enterString("\033[38;5;119m"); assertEquals(119, mTerminal.mForeColor); assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor); - enterString("\033[48;5;129m"); assertEquals(119, mTerminal.mForeColor); assertEquals(129, mTerminal.mBackColor); - // Invalid parameter: + // Invalid parameter: enterString("\033[48;8;129m"); assertEquals(119, mTerminal.mForeColor); assertEquals(129, mTerminal.mBackColor); @@ -161,7 +160,31 @@ public class TerminalTest extends TerminalTestCase { enterString("\033[38;5;178;48;5;179;m"); assertEquals(178, mTerminal.mForeColor); assertEquals(179, mTerminal.mBackColor); - } + + // 24 bit colors: + enterString(("\033[0m")); // Reset fg and bg colors. + enterString("\033[38;2;255;127;2m"); + int expectedForeground = 0xff000000 | (255 << 16) | (127 << 8) | 2; + assertEquals(expectedForeground, mTerminal.mForeColor); + assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor); + enterString("\033[48;2;1;2;254m"); + int expectedBackground = 0xff000000 | (1 << 16) | (2 << 8) | 254; + assertEquals(expectedForeground, mTerminal.mForeColor); + assertEquals(expectedBackground, mTerminal.mBackColor); + + // 24 bit colors, set fg and bg at once: + enterString(("\033[0m")); // Reset fg and bg colors. + assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, mTerminal.mForeColor); + assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, mTerminal.mBackColor); + enterString("\033[38;2;255;127;2;48;2;1;2;254m"); + assertEquals(expectedForeground, mTerminal.mForeColor); + assertEquals(expectedBackground, mTerminal.mBackColor); + + // 24 bit colors, invalid input: + enterString("\033[38;2;300;127;2;48;2;1;300;254m"); + assertEquals(expectedForeground, mTerminal.mForeColor); + assertEquals(expectedBackground, mTerminal.mBackColor); + } public void testBackgroundColorErase() { final int rows = 3; @@ -169,7 +192,7 @@ public class TerminalTest extends TerminalTestCase { withTerminalSized(cols, rows); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { - int style = getStyleAt(r, c); + long style = getStyleAt(r, c); assertEquals(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.decodeForeColor(style)); assertEquals(TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.decodeBackColor(style)); } @@ -182,7 +205,7 @@ public class TerminalTest extends TerminalTestCase { enterString("\033[2J"); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { - int style = getStyleAt(r, c); + long style = getStyleAt(r, c); assertEquals(119, TextStyle.decodeForeColor(style)); assertEquals(129, TextStyle.decodeBackColor(style)); } @@ -193,7 +216,7 @@ public class TerminalTest extends TerminalTestCase { enterString("\033[2L"); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { - int style = getStyleAt(r, c); + long style = getStyleAt(r, c); assertEquals((r == 0 || r == 1) ? 139 : 129, TextStyle.decodeBackColor(style)); } } diff --git a/app/src/test/java/com/termux/terminal/TerminalTestCase.java b/app/src/test/java/com/termux/terminal/TerminalTestCase.java index fd0e55a9..15b5a7e0 100644 --- a/app/src/test/java/com/termux/terminal/TerminalTestCase.java +++ b/app/src/test/java/com/termux/terminal/TerminalTestCase.java @@ -240,7 +240,7 @@ public abstract class TerminalTestCase extends TestCase { } /** For testing only. Encoded style according to {@link TextStyle}. */ - public int getStyleAt(int externalRow, int column) { + public long getStyleAt(int externalRow, int column) { return mTerminal.getScreen().getStyleAt(externalRow, column); } @@ -296,7 +296,7 @@ public abstract class TerminalTestCase extends TestCase { } public void assertForegroundColorAt(int externalRow, int column, int color) { - int style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column); + long style = mTerminal.getScreen().mLines[mTerminal.getScreen().externalToInternalRow(externalRow)].getStyle(column); assertEquals(color, TextStyle.decodeForeColor(style)); } diff --git a/app/src/test/java/com/termux/terminal/TextStyleTest.java b/app/src/test/java/com/termux/terminal/TextStyleTest.java index ed6d97eb..48134335 100644 --- a/app/src/test/java/com/termux/terminal/TextStyleTest.java +++ b/app/src/test/java/com/termux/terminal/TextStyleTest.java @@ -13,7 +13,7 @@ public class TextStyleTest extends TestCase { for (int fx : ALL_EFFECTS) { for (int fg = 0; fg < TextStyle.NUM_INDEXED_COLORS; fg++) { for (int bg = 0; bg < TextStyle.NUM_INDEXED_COLORS; bg++) { - int encoded = TextStyle.encode(fg, bg, fx); + long encoded = TextStyle.encode(fg, bg, fx); assertEquals(fg, TextStyle.decodeForeColor(encoded)); assertEquals(bg, TextStyle.decodeBackColor(encoded)); assertEquals(fx, TextStyle.decodeEffect(encoded)); @@ -22,7 +22,23 @@ public class TextStyleTest extends TestCase { } } - public void testEncodingCombinations() { + public void testEncoding24Bit() { + int[] values = {255, 240, 127, 1, 0}; + for (int red : values) { + for (int green : values) { + for (int blue : values) { + int argb = 0xFF000000 | (red << 16) | (green << 8) | blue; + long encoded = TextStyle.encode(argb, 0, 0); + assertEquals(argb, TextStyle.decodeForeColor(encoded)); + encoded = TextStyle.encode(0, argb, 0); + assertEquals(argb, TextStyle.decodeBackColor(encoded)); + } + } + } + } + + + public void testEncodingCombinations() { for (int f1 : ALL_EFFECTS) { for (int f2 : ALL_EFFECTS) { int combined = f1 | f2; @@ -32,13 +48,13 @@ public class TextStyleTest extends TestCase { } public void testEncodingStrikeThrough() { - int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, + long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH) != 0); } public void testEncodingProtected() { - int encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, + long encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND, TextStyle.CHARACTER_ATTRIBUTE_STRIKETHROUGH); assertTrue((TextStyle.decodeEffect(encoded) & TextStyle.CHARACTER_ATTRIBUTE_PROTECTED) == 0); encoded = TextStyle.encode(TextStyle.COLOR_INDEX_FOREGROUND, TextStyle.COLOR_INDEX_BACKGROUND,