From 6d4f20edd6ab7d2990e1699833629af6031afcd9 Mon Sep 17 00:00:00 2001 From: Jonah Graham Date: Sun, 10 May 2020 14:39:08 -0400 Subject: [PATCH] Bug 540737: Add 8 and 24-bit color support to terminal Change-Id: Iab3b648fb3bfa8f43f333371bd118e90a3a182f2 --- .../terminal/emulator/VT100Emulator.java | 130 +++++++++++++++++- .../terminal/textcanvas/StyleMap.java | 20 +++ .../tm/terminal/model/TerminalColor.java | 43 +++++- .../tm/terminal/model/TerminalStyle.java | 124 ++++++++++++++--- .../model/AbstractITerminalTextDataTest.java | 5 +- .../model/TerminalTextDataSnapshotTest.java | 5 +- .../model/TerminalTextDataWindowTest.java | 5 +- .../model/TerminalTextTestHelper.java | 5 +- .../terminal/test/ui/TerminalTextUITest.java | 2 + .../terminal/test/ui/VT100DataSource.java | 106 +++++++++++++- .../eclipse/tm/terminal/model/StyleTest.java | 6 +- .../terminal/model/TerminalColorUITest.java | 72 ++++++++++ 12 files changed, 481 insertions(+), 42 deletions(-) diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java index f6c20042b25..03baff99903 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/emulator/VT100Emulator.java @@ -40,9 +40,11 @@ import static org.eclipse.tm.terminal.model.TerminalColor.YELLOW; import java.io.IOException; import java.io.Reader; +import java.util.Arrays; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.RGB; import org.eclipse.tm.internal.terminal.control.impl.ITerminalControlForText; import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; @@ -859,6 +861,19 @@ public class VT100Emulator implements ControlListener { while (parameterIndex < totalParameters && ansiParameters[parameterIndex].length() > 0) { int ansiParameter = getAnsiParameter(parameterIndex); + if (ansiParameter == 1) { + String parameter = ansiParameters[parameterIndex].toString(); + // Special case for ITU's T.416 foreground/background color specification + // which uses : to separate parameters instead of ; + if (parameter.startsWith("38:") || parameter.startsWith("48:")) { //$NON-NLS-1$ //$NON-NLS-2$ + String[] split = parameter.split(":"); //$NON-NLS-1$ + ProcessExtendedColorsReturn retval = processExtendedColors(split, style, true); + style = retval.style(); + parameterIndex++; + continue; + } + + } switch (ansiParameter) { case 0: @@ -974,6 +989,14 @@ public class VT100Emulator implements ControlListener { style = style.setBackground(text.getDefaultStyle()); break; + case 38: // Foreground color defined by sequence + case 48: // Background color defined by sequence + CharSequence[] params = Arrays.copyOfRange(ansiParameters, parameterIndex, ansiParameters.length); + ProcessExtendedColorsReturn retval = processExtendedColors(params, style, false); + parameterIndex += retval.consumed() - 1; + style = retval.style(); + break; + default: Logger.log("Unsupported graphics rendition parameter: " + ansiParameter); //$NON-NLS-1$ break; @@ -984,6 +1007,110 @@ public class VT100Emulator implements ControlListener { text.setStyle(style); } + private interface ProcessExtendedColorsReturn { + /** + * @return the new style + */ + TerminalStyle style(); + + /** + * @return number of parameters consumed + */ + int consumed(); + } + + /** + * + * @param params array of parameters, starting with 38 or 48 being the command + * @param colorspace if a colorspace may be included (ITU T.416 mode) + */ + private ProcessExtendedColorsReturn processExtendedColors(CharSequence[] paramStrings, TerminalStyle style, + boolean colorspace) { + int params[] = new int[paramStrings.length]; + for (int i = 0; i < params.length; i++) { + try { + int parseInt = Integer.parseInt(paramStrings[i].toString()); + int inMagnitude = parseInt % 256; + int inRange = inMagnitude < 0 ? inMagnitude + 256 : inMagnitude; + params[i] = inRange; + } catch (NumberFormatException ex) { + params[i] = 0; + } + } + + boolean foreground = params[0] == 38; + int consumed = 1; + if (params.length > 1) { + int colorDepth = params[1]; + switch (colorDepth) { + case 2: // 24-bit RGB color + int r = 0, g = 0, b = 0; + if (colorspace) { + if (params.length < 6) { + Logger.log( + "Not enough parameters for 24-bit color depth, expected 5, one for color space and one for each of RGB"); //$NON-NLS-1$ + } + } else { + if (params.length < 5) { + Logger.log("Not enough parameters for 24-bit color depth, expected 3, one for each of RGB"); //$NON-NLS-1$ + } + } + int start = colorspace ? 3 : 2; + if (params.length > start + 0) { + r = params[start + 0]; + } + if (params.length > start + 1) { + g = params[start + 1]; + } + if (params.length > start + 2) { + b = params[start + 2]; + } + + RGB rgb = new RGB(r, g, b); + if (foreground) { + style = style.setForeground(rgb); + } else { + style = style.setBackground(rgb); + } + consumed = Math.min(6, params.length); + break; + case 5: // 8-bit color table lookup + int index = 0; + if (params.length < 3) { + Logger.log("Missing parameter for 8-bit color depth"); //$NON-NLS-1$ + } else { + index = params[2]; + } + if (foreground) { + style = style.setForeground(index); + } else { + style = style.setBackground(index); + } + consumed = Math.min(3, params.length); + break; + default: + Logger.log("Unsupported color depth " + colorDepth + " for: " + params[0]); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } else { + Logger.log("Missing color depth for " + params[0]); //$NON-NLS-1$ + } + + TerminalStyle finalStyle = style; + int finalConsumed = consumed; + return new ProcessExtendedColorsReturn() { + @Override + public TerminalStyle style() { + return finalStyle; + } + + @Override + public int consumed() { + return finalConsumed; + } + }; + } + /** * This method responds to an ANSI Device Status Report (DSR) command from * the remote endpoint requesting the ready status or the cursor position. @@ -1096,7 +1223,7 @@ public class VT100Emulator implements ControlListener { * most recent escape sequence. * * @return The parameterIndexth numeric ANSI parameter or -1 if the - * index is out of range. + * index is out of range or 1 if parse failed (1 is also a legitimate value) */ private int getAnsiParameter(int parameterIndex) { if (parameterIndex < 0 || parameterIndex >= ansiParameters.length) { @@ -1109,6 +1236,7 @@ public class VT100Emulator implements ControlListener { if (parameter.length() == 0) return 1; + // return 1 on failed parseInt int parameterValue = 1; // Don't trust the remote endpoint to send well formed numeric diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java index badc43b6730..f2af6cbe4de 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/internal/terminal/textcanvas/StyleMap.java @@ -74,6 +74,16 @@ public class StyleMap { public RGB getForegrondRGB(TerminalStyle style) { style = defaultIfNull(style); + RGB foregroundRGB; + if (style.isReverse()) { + foregroundRGB = style.getBackgroundRGB(); + } else { + foregroundRGB = style.getForegroundRGB(); + } + if (foregroundRGB != null) { + return foregroundRGB; + } + TerminalColor color; if (style.isReverse()) { color = style.getBackgroundTerminalColor(); @@ -91,6 +101,16 @@ public class StyleMap { public RGB getBackgroundRGB(TerminalStyle style) { style = defaultIfNull(style); + RGB backgroundRGB; + if (style.isReverse()) { + backgroundRGB = style.getForegroundRGB(); + } else { + backgroundRGB = style.getBackgroundRGB(); + } + if (backgroundRGB != null) { + return backgroundRGB; + } + TerminalColor color; if (style.isReverse()) { color = style.getForegroundTerminalColor(); diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java index 6ed0a1e89a3..0bc12ebabdb 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalColor.java @@ -20,6 +20,15 @@ import org.eclipse.swt.widgets.Display; * the colors with well known names defined by the ANSI Escape Sequences, plus other colors needed * to render a display (such as Background color). * + * Rather than name all the colors when using ANSI 8-bit indexed colors, the indexed colors + * can be accessed via the {@link #getIndexedRGBColor(int)} or {@link #getIndexedRGBColor(int)} + * (use {@link #isIndexedTerminalColor(int)} to determine which one is appropriate. + * + * The {@link TerminalStyle} supports any arbitrary color by using {@link RGB} defined colors. + * This class provides the connection between the names exposed to the user in preferences + * and their use in the terminal, along with how colors change when other attributes (such as + * bright and invertColors) are applied to them. + * * @since 5.0 */ public enum TerminalColor { @@ -158,11 +167,37 @@ public enum TerminalColor { } /** - * FOR TEST ONLY. + * Query for whether the 8-bit color index will return a named color, in which case + * {@link #getIndexedTerminalColor(int)} must be called to get the named color. Use + * {@link #convertColor(boolean, boolean)} if this method returns false. * - * @noreference This enum method is not intended to be referenced by clients. + * @param index 8-bit index. + * @return true for named colors, false for RGB colors */ - public static TerminalColor getForTest(int c) { - return TerminalColor.values()[c % TerminalColor.values().length]; + public static boolean isIndexedTerminalColor(int index) { + Assert.isLegal(index >= 0 && index < 256, "Invalid 8-bit table index out of range 0-255"); //$NON-NLS-1$ + return index < table8bitIndexedTerminalColors.length && index >= 0; + } + + /** + * Return the named color for the given 8-bit index. + * + * @param index 8-bit index in 0-15 range. + * @return named color + */ + public static TerminalColor getIndexedTerminalColor(int index) { + Assert.isLegal(isIndexedTerminalColor(index), "Invalid table index used for ANSI Color"); //$NON-NLS-1$ + return table8bitIndexedTerminalColors[index]; + } + + /** + * Return the RGB color for the given 8-bit index. + * + * @param index 8-bit index in 16-255 range. + * @return RGB color + */ + public static RGB getIndexedRGBColor(int index) { + Assert.isLegal(index >= 16 && index < 256, "Invalid table index used for RGB Color"); //$NON-NLS-1$ + return table8bitIndexedRGB[index - 16]; } } diff --git a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java index f63d0f0be2b..d81d4b8466e 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java +++ b/terminal/plugins/org.eclipse.tm.terminal.control/src/org/eclipse/tm/terminal/model/TerminalStyle.java @@ -14,6 +14,8 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.eclipse.core.runtime.Assert; +import org.eclipse.swt.graphics.RGB; import org.eclipse.tm.internal.terminal.control.impl.TerminalPlugin; import org.eclipse.tm.internal.terminal.provisional.api.Logger; @@ -29,6 +31,8 @@ import org.eclipse.tm.internal.terminal.provisional.api.Logger; public class TerminalStyle { private final TerminalColor fForegroundTerminalColor; private final TerminalColor fBackgroundTerminalColor; + private final RGB fForegroundRGB; + private final RGB fBackgroundRGB; private final boolean fBold; private final boolean fBlink; private final boolean fUnderline; @@ -50,10 +54,16 @@ public class TerminalStyle { } }); - private TerminalStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, boolean bold, - boolean blink, boolean underline, boolean reverse) { + private TerminalStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, + RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse) { + Assert.isLegal(foregroundTerminalColor == null || foregroundRGB == null, + "Only one of ANSI or RGB colors can be specified as a foreground color"); //$NON-NLS-1$ + Assert.isLegal(backgroundTerminalColor == null || backgroundRGB == null, + "Only one of ANSI or RGB colors can be specified as a background color"); //$NON-NLS-1$ fForegroundTerminalColor = foregroundTerminalColor; fBackgroundTerminalColor = backgroundTerminalColor; + fForegroundRGB = foregroundRGB; + fBackgroundRGB = backgroundRGB; fBold = bold; fBlink = blink; fUnderline = underline; @@ -61,51 +71,105 @@ public class TerminalStyle { } public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, - boolean bold, boolean blink, boolean underline, boolean reverse) { - TerminalStyle style = new TerminalStyle(foregroundTerminalColor, backgroundTerminalColor, bold, blink, - underline, reverse); + RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, boolean underline, boolean reverse) { + TerminalStyle style = new TerminalStyle(foregroundTerminalColor, backgroundTerminalColor, foregroundRGB, + backgroundRGB, bold, blink, underline, reverse); // If set had a computeIfAbsent we would use a set, instead just store 1-2-1 mapping return fgStyles.computeIfAbsent(style, (s) -> style); } + public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor, + boolean bold, boolean blink, boolean underline, boolean reverse) { + return getStyle(foregroundTerminalColor, backgroundTerminalColor, null, null, bold, blink, underline, reverse); + } + + public static TerminalStyle getStyle(RGB foregroundRGB, RGB backgroundRGB, boolean bold, boolean blink, + boolean underline, boolean reverse) { + return getStyle(null, null, foregroundRGB, backgroundRGB, bold, blink, underline, reverse); + } + public static TerminalStyle getDefaultStyle() { return getStyle(TerminalColor.FOREGROUND, TerminalColor.BACKGROUND); } public static TerminalStyle getStyle(TerminalColor foregroundTerminalColor, TerminalColor backgroundTerminalColor) { - return getStyle(foregroundTerminalColor, backgroundTerminalColor, false, false, false, false); + return getStyle(foregroundTerminalColor, backgroundTerminalColor, null, null, false, false, false, false); } public TerminalStyle setForeground(TerminalColor foregroundTerminalColor) { - return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, fBold, fBlink, fUnderline, fReverse); + return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, null, fBackgroundRGB, fBold, fBlink, + fUnderline, fReverse); } public TerminalStyle setBackground(TerminalColor backgroundTerminalColor) { - return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fBold, fBlink, fUnderline, fReverse); + return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fForegroundRGB, null, fBold, fBlink, + fUnderline, fReverse); + } + + public TerminalStyle setForeground(RGB foregroundRGB) { + return getStyle(null, fBackgroundTerminalColor, foregroundRGB, fBackgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } + + public TerminalStyle setBackground(RGB backgroundRGB) { + return getStyle(fForegroundTerminalColor, null, fForegroundRGB, backgroundRGB, fBold, fBlink, fUnderline, + fReverse); } public TerminalStyle setForeground(TerminalStyle other) { - return getStyle(other.fForegroundTerminalColor, fBackgroundTerminalColor, fBold, fBlink, fUnderline, fReverse); + return getStyle(other.fForegroundTerminalColor, fBackgroundTerminalColor, other.fForegroundRGB, fBackgroundRGB, + fBold, fBlink, fUnderline, fReverse); } public TerminalStyle setBackground(TerminalStyle other) { - return getStyle(fForegroundTerminalColor, other.fBackgroundTerminalColor, fBold, fBlink, fUnderline, fReverse); + return getStyle(fForegroundTerminalColor, other.fBackgroundTerminalColor, fForegroundRGB, other.fBackgroundRGB, + fBold, fBlink, fUnderline, fReverse); + } + + public TerminalStyle setForeground(int eightBitindexedColor) { + boolean isIndexTerminalColor = TerminalColor.isIndexedTerminalColor(eightBitindexedColor); + if (isIndexTerminalColor) { + TerminalColor foregroundTerminalColor = TerminalColor.getIndexedTerminalColor(eightBitindexedColor); + return getStyle(foregroundTerminalColor, fBackgroundTerminalColor, null, fBackgroundRGB, fBold, fBlink, + fUnderline, fReverse); + } else { + RGB foregroundRGB = TerminalColor.getIndexedRGBColor(eightBitindexedColor); + return getStyle(null, fBackgroundTerminalColor, foregroundRGB, fBackgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } + } + + public TerminalStyle setBackground(int eightBitindexedColor) { + boolean isIndexTerminalColor = TerminalColor.isIndexedTerminalColor(eightBitindexedColor); + if (isIndexTerminalColor) { + TerminalColor backgroundTerminalColor = TerminalColor.getIndexedTerminalColor(eightBitindexedColor); + return getStyle(fForegroundTerminalColor, backgroundTerminalColor, fForegroundRGB, null, fBold, fBlink, + fUnderline, fReverse); + } else { + RGB backgroundRGB = TerminalColor.getIndexedRGBColor(eightBitindexedColor); + return getStyle(fForegroundTerminalColor, null, fForegroundRGB, backgroundRGB, fBold, fBlink, fUnderline, + fReverse); + } } public TerminalStyle setBold(boolean bold) { - return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, bold, fBlink, fUnderline, fReverse); + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, bold, + fBlink, fUnderline, fReverse); } public TerminalStyle setBlink(boolean blink) { - return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fBold, blink, fUnderline, fReverse); + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + blink, fUnderline, fReverse); } public TerminalStyle setUnderline(boolean underline) { - return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fBold, fBlink, underline, fReverse); + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + fBlink, underline, fReverse); } public TerminalStyle setReverse(boolean reverse) { - return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fBold, fBlink, fUnderline, reverse); + return getStyle(fForegroundTerminalColor, fBackgroundTerminalColor, fForegroundRGB, fBackgroundRGB, fBold, + fBlink, fUnderline, reverse); } public TerminalColor getForegroundTerminalColor() { @@ -116,6 +180,14 @@ public class TerminalStyle { return fBackgroundTerminalColor; } + public RGB getForegroundRGB() { + return fForegroundRGB; + } + + public RGB getBackgroundRGB() { + return fBackgroundRGB; + } + public boolean isBlink() { return fBlink; } @@ -137,9 +209,11 @@ public class TerminalStyle { final int prime = 31; int result = 1; result = prime * result + ((fBackgroundTerminalColor == null) ? 0 : fBackgroundTerminalColor.hashCode()); + result = prime * result + ((fBackgroundRGB == null) ? 0 : fBackgroundRGB.hashCode()); result = prime * result + (fBlink ? 1231 : 1237); result = prime * result + (fBold ? 1231 : 1237); result = prime * result + ((fForegroundTerminalColor == null) ? 0 : fForegroundTerminalColor.hashCode()); + result = prime * result + ((fForegroundRGB == null) ? 0 : fForegroundRGB.hashCode()); result = prime * result + (fReverse ? 1231 : 1237); result = prime * result + (fUnderline ? 1231 : 1237); return result; @@ -156,12 +230,22 @@ public class TerminalStyle { TerminalStyle other = (TerminalStyle) obj; if (fBackgroundTerminalColor != other.fBackgroundTerminalColor) return false; + if (fBackgroundRGB == null) { + if (other.fBackgroundRGB != null) + return false; + } else if (!fBackgroundRGB.equals(other.fBackgroundRGB)) + return false; if (fBlink != other.fBlink) return false; if (fBold != other.fBold) return false; if (fForegroundTerminalColor != other.fForegroundTerminalColor) return false; + if (fForegroundRGB == null) { + if (other.fForegroundRGB != null) + return false; + } else if (!fForegroundRGB.equals(other.fForegroundRGB)) + return false; if (fReverse != other.fReverse) return false; if (fUnderline != other.fUnderline) @@ -173,9 +257,17 @@ public class TerminalStyle { public String toString() { StringBuffer result = new StringBuffer(); result.append("Style(foreground="); //$NON-NLS-1$ - result.append(fForegroundTerminalColor); + if (fForegroundTerminalColor != null) { + result.append(fForegroundTerminalColor); + } else { + result.append(fForegroundRGB); + } result.append(", background="); //$NON-NLS-1$ - result.append(fBackgroundTerminalColor); + if (fForegroundTerminalColor != null) { + result.append(fBackgroundTerminalColor); + } else { + result.append(fBackgroundRGB); + } if (fBlink) result.append(", blink"); //$NON-NLS-1$ if (fBold) diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/AbstractITerminalTextDataTest.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/AbstractITerminalTextDataTest.java index 9a85f508043..ac7399229d3 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/AbstractITerminalTextDataTest.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/AbstractITerminalTextDataTest.java @@ -14,7 +14,6 @@ package org.eclipse.tm.internal.terminal.model; import org.eclipse.tm.terminal.model.ITerminalTextData; import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; import org.eclipse.tm.terminal.model.LineSegment; -import org.eclipse.tm.terminal.model.TerminalColor; import org.eclipse.tm.terminal.model.TerminalStyle; import junit.framework.TestCase; @@ -338,13 +337,13 @@ abstract public class AbstractITerminalTextDataTest extends TestCase { for (int line = 0; line < term.getHeight(); line++) { for (int column = 0; column < term.getWidth(); column++) { char c = (char) ('a' + column + line); - term.setChar(line, column, c, style.setForeground(TerminalColor.getForTest(c))); + term.setChar(line, column, c, style.setForeground(c)); } } for (int line = 0; line < term.getHeight(); line++) { for (int column = 0; column < term.getWidth(); column++) { char c = (char) ('a' + column + line); - assertSame(style.setForeground(TerminalColor.getForTest(c)), term.getStyle(line, column)); + assertSame(style.setForeground(c), term.getStyle(line, column)); } } diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshotTest.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshotTest.java index 775f4d2e703..56b9b145b42 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshotTest.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataSnapshotTest.java @@ -13,7 +13,6 @@ package org.eclipse.tm.internal.terminal.model; import org.eclipse.tm.terminal.model.ITerminalTextData; import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; import org.eclipse.tm.terminal.model.ITerminalTextDataSnapshot; -import org.eclipse.tm.terminal.model.TerminalColor; import org.eclipse.tm.terminal.model.TerminalStyle; import junit.framework.TestCase; @@ -322,7 +321,7 @@ public class TerminalTextDataSnapshotTest extends TestCase { for (int line = 0; line < term.getHeight(); line++) { for (int column = 0; column < term.getWidth(); column++) { char c = (char) ('a' + column + line); - term.setChar(line, column, c, style.setForeground(TerminalColor.getForTest(c))); + term.setChar(line, column, c, style.setForeground(c)); } } ITerminalTextDataSnapshot snapshot = term.makeSnapshot(); @@ -331,7 +330,7 @@ public class TerminalTextDataSnapshotTest extends TestCase { for (int line = 0; line < term.getHeight(); line++) { for (int column = 0; column < term.getWidth(); column++) { char c = (char) ('a' + column + line); - assertSame(style.setForeground(TerminalColor.getForTest(c)), snapshot.getStyle(line, column)); + assertSame(style.setForeground(c), snapshot.getStyle(line, column)); } } diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindowTest.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindowTest.java index a0a12dd8313..d681687a7ef 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindowTest.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextDataWindowTest.java @@ -17,7 +17,6 @@ import java.util.ArrayList; import org.eclipse.tm.terminal.model.ITerminalTextData; import org.eclipse.tm.terminal.model.LineSegment; -import org.eclipse.tm.terminal.model.TerminalColor; import org.eclipse.tm.terminal.model.TerminalStyle; public class TerminalTextDataWindowTest extends AbstractITerminalTextDataTest { @@ -214,7 +213,7 @@ public class TerminalTextDataWindowTest extends AbstractITerminalTextDataTest { for (int line = 0; line < term.getHeight(); line++) { for (int column = 0; column < term.getWidth(); column++) { char c = (char) ('a' + column + line); - term.setChar(line, column, c, style.setForeground(TerminalColor.getForTest(c))); + term.setChar(line, column, c, style.setForeground(c)); } } for (int line = 0; line < term.getHeight(); line++) { @@ -222,7 +221,7 @@ public class TerminalTextDataWindowTest extends AbstractITerminalTextDataTest { char c = (char) ('a' + column + line); TerminalStyle s = null; if (line >= fOffset && line < fOffset + fSize) - s = style.setForeground(TerminalColor.getForTest(c)); + s = style.setForeground(c); assertSame(s, term.getStyle(line, column)); } } diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextTestHelper.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextTestHelper.java index 7acfc8d7d13..205d304614f 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextTestHelper.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/model/TerminalTextTestHelper.java @@ -15,7 +15,6 @@ package org.eclipse.tm.internal.terminal.model; import org.eclipse.tm.terminal.model.ITerminalTextData; import org.eclipse.tm.terminal.model.ITerminalTextDataReadOnly; -import org.eclipse.tm.terminal.model.TerminalColor; import org.eclipse.tm.terminal.model.TerminalStyle; public class TerminalTextTestHelper { @@ -66,7 +65,7 @@ public class TerminalTextTestHelper { term.setDimensions(s.length(), 1); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); - term.setChar(i, 0, c, style.setForeground(TerminalColor.getForTest(c))); + term.setChar(i, 0, c, style.setForeground(c)); } } @@ -105,7 +104,7 @@ public class TerminalTextTestHelper { yy++; xx = column; } else { - term.setChar(yy, xx, c, style.setForeground(TerminalColor.getForTest(c))); + term.setChar(yy, xx, c, style.setForeground(c)); xx++; } } diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/TerminalTextUITest.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/TerminalTextUITest.java index 694a70f4fb7..483a1feaaee 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/TerminalTextUITest.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/TerminalTextUITest.java @@ -97,6 +97,8 @@ public class TerminalTextUITest { addDataReader(composite, reader); reader = new DataReader("Fast", fTerminalModel, new FastDataSource(), status); addDataReader(composite, reader); + reader = new DataReader("Colors", fTerminalModel, new VT100DataSource(), status); + addDataReader(composite, reader); reader = new DataReader("Random", fTerminalModel, new RandomDataSource(), status); addDataReader(composite, reader); for (int i = 0; i < args.length; i++) { diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/VT100DataSource.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/VT100DataSource.java index 0a1653892ee..e42d99c39f3 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/VT100DataSource.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/test/ui/VT100DataSource.java @@ -14,6 +14,7 @@ *******************************************************************************/ package org.eclipse.tm.internal.terminal.test.ui; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -40,18 +41,111 @@ final class VT100DataSource implements IDataSource { volatile int fAvailable; volatile int fRead; private final String fFile; + private final String fANSIEscapeColors; VT100DataSource(String file) { fFile = file; + fANSIEscapeColors = null; + } + + VT100DataSource() { + fFile = null; + StringBuffer input = new StringBuffer(); + input.append(" 8-bit "); + for (int i = 0; i < 255; i++) { + input.append("\033[38:5:"); + input.append(i); + input.append("m"); + input.append(String.format(" %3s ", i)); + input.append("\033[0m"); + if ((i + 1) % 6 == 4) { + input.append("\n"); + } + } + input.append("\n"); + input.append(" 8-bit "); + for (int i = 0; i < 255; i++) { + input.append("\033[48:5:"); + input.append(i); + input.append("m"); + input.append(String.format(" %3s ", i)); + input.append("\033[0m"); + if ((i + 1) % 6 == 4) { + input.append("\n"); + } + } + input.append("\n"); + + input.append("\n"); + input.append(" 24-bit (RGB incremented by 15)"); + int count = 0; + for (int r = 0; r < 255; r += 15) { + for (int g = 0; g < 255; g += 15) { + count = 0; + for (int b = 0; b < 255; b += 15) { + if (count++ % 5 == 0) { + input.append("\n"); + } + input.append("\033[38:2:3:"); + input.append(r); + input.append(":"); + input.append(g); + input.append(":"); + input.append(b); + input.append(":"); + input.append("m"); + input.append(String.format(" (%02x%02x%02x) ", r, g, b)); + input.append("\033[0m"); + + } + } + } + input.append("\n"); + input.append(" 24-bit (RGB incremented by 15)"); + count = 0; + for (int r = 0; r < 255; r += 15) { + for (int g = 0; g < 255; g += 15) { + count = 0; + for (int b = 0; b < 255; b += 15) { + if (count++ % 5 == 0) { + input.append("\n"); + } + input.append("\033[48:2:3:"); + input.append(r); + input.append(":"); + input.append(g); + input.append(":"); + input.append(b); + input.append(":"); + input.append("m"); + input.append(String.format(" (%02x%02x%02x) ", r, g, b)); + input.append("\033[0m"); + + } + } + } + + input.append("\n"); + + fANSIEscapeColors = input.toString(); } class InfiniteFileInputStream extends InputStream { public InfiniteFileInputStream() { - try { - fInputStream = new FileInputStream(fFile); - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + startInputStream(); + + } + + private void startInputStream() { + if (fFile == null) { + fInputStream = new ByteArrayInputStream(fANSIEscapeColors.getBytes(StandardCharsets.ISO_8859_1)); + } else { + try { + fInputStream = new FileInputStream(fFile); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } } @@ -80,7 +174,7 @@ final class VT100DataSource implements IDataSource { int n = fInputStream.read(b, off, len); if (n <= 0) { fInputStream.close(); - fInputStream = new FileInputStream(fFile); + startInputStream(); n = fInputStream.read(b, off, len); } fAvailable -= n; diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/StyleTest.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/StyleTest.java index 9520e614ab4..cdc11a8ac13 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/StyleTest.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/StyleTest.java @@ -13,9 +13,9 @@ package org.eclipse.tm.terminal.model; import junit.framework.TestCase; public class StyleTest extends TestCase { - final TerminalColor c1 = TerminalColor.getForTest(1); - final TerminalColor c2 = TerminalColor.getForTest(2); - final TerminalColor c3 = TerminalColor.getForTest(3); + final TerminalColor c1 = TerminalColor.getIndexedTerminalColor(1); + final TerminalColor c2 = TerminalColor.getIndexedTerminalColor(2); + final TerminalColor c3 = TerminalColor.getIndexedTerminalColor(3); public void testGetStyle() { TerminalStyle s1 = TerminalStyle.getStyle(c1, c2, true, false, true, false); diff --git a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/TerminalColorUITest.java b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/TerminalColorUITest.java index 588bcf63f4e..cf0bf3781ce 100644 --- a/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/TerminalColorUITest.java +++ b/terminal/plugins/org.eclipse.tm.terminal.test/src/org/eclipse/tm/terminal/model/TerminalColorUITest.java @@ -17,6 +17,7 @@ import static org.eclipse.tm.terminal.model.TerminalColor.WHITE; import static org.eclipse.tm.terminal.model.TerminalColor.YELLOW; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import org.eclipse.swt.widgets.Display; import org.junit.AfterClass; @@ -79,4 +80,75 @@ public class TerminalColorUITest { assertEquals(WHITE.convertColor(false, true), BLACK.convertColor(true, true)); assertNotEquals(WHITE.convertColor(false, true), BLACK.convertColor(false, true)); } + + @Test + public void testIndexesResolveToStandardColors() { + // check explicit colors + assertEquals(TerminalColor.BLACK.convertColor(false, false), + TerminalColor.getIndexedTerminalColor(0).convertColor(false, false)); + assertEquals(TerminalColor.RED.convertColor(false, false), + TerminalColor.getIndexedTerminalColor(1).convertColor(false, false)); + + // Now check all colors + for (int i = 0; i < 8; i++) { + assertEquals(TerminalColor.values()[i].convertColor(false, false), + TerminalColor.getIndexedTerminalColor(i).convertColor(false, false)); + } + } + + @Test + public void testIndexesResolveToBrightColors() { + // check explicit colors + assertEquals(TerminalColor.BLACK.convertColor(false, true), + TerminalColor.getIndexedTerminalColor(8).convertColor(false, false)); + assertEquals(TerminalColor.RED.convertColor(false, true), + TerminalColor.getIndexedTerminalColor(9).convertColor(false, false)); + + // Now check all colors + for (int i = 0; i < 8; i++) { + assertEquals(TerminalColor.values()[i].convertColor(false, true), + TerminalColor.getIndexedTerminalColor(i + 8).convertColor(false, false)); + } + } + + @Test + public void testIndexesInRange() { + for (int i = 0; i < 16; i++) { + assertNotNull(TerminalColor.getIndexedTerminalColor(i)); + } + for (int i = 16; i < 256; i++) { + assertNotNull(TerminalColor.getIndexedRGBColor(i)); + } + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexesOutOfRange_m1TerminalColor() { + assertNotNull(TerminalColor.getIndexedTerminalColor(-1)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexesOutOfRange_m1RGBColor() { + assertNotNull(TerminalColor.getIndexedRGBColor(-1)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexesOutOfRange_16() { + assertNotNull(TerminalColor.getIndexedTerminalColor(16)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexesOutOfRange_15() { + assertNotNull(TerminalColor.getIndexedRGBColor(15)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexesOutOfRange_256TerminalColor() { + assertNotNull(TerminalColor.getIndexedTerminalColor(256)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIndexesOutOfRange_256RGBColor() { + assertNotNull(TerminalColor.getIndexedRGBColor(256)); + } + }