Skip to content

Commit

Permalink
256 colors and truecolor support
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed Dec 7, 2020
1 parent 7dc9c1b commit 235b653
Show file tree
Hide file tree
Showing 11 changed files with 532 additions and 7 deletions.
40 changes: 40 additions & 0 deletions src/main/java/org/fusesource/jansi/Ansi.java
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,26 @@ public Ansi fg(Color color) {
return this;
}

public Ansi fg(int color) {
attributeOptions.add(38);
attributeOptions.add(5);
attributeOptions.add(color & 0xff);
return this;
}

public Ansi fgRgb(int color) {
return fgRgb(color >> 16, color >> 8, color);
}

public Ansi fgRgb(int r, int g, int b) {
attributeOptions.add(38);
attributeOptions.add(2);
attributeOptions.add(r & 0xff);
attributeOptions.add(g & 0xff);
attributeOptions.add(b & 0xff);
return this;
}

public Ansi fgBlack() {
return this.fg(Color.BLACK);
}
Expand Down Expand Up @@ -415,6 +435,26 @@ public Ansi bg(Color color) {
return this;
}

public Ansi bg(int color) {
attributeOptions.add(48);
attributeOptions.add(5);
attributeOptions.add(color & 0xff);
return this;
}

public Ansi bgRgb(int color) {
return bgRgb(color >> 16, color >> 8, color);
}

public Ansi bgRgb(int r, int g, int b) {
attributeOptions.add(48);
attributeOptions.add(2);
attributeOptions.add(r & 0xff);
attributeOptions.add(g & 0xff);
attributeOptions.add(b & 0xff);
return this;
}

public Ansi bgCyan() {
return this.bg(Color.CYAN);
}
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/fusesource/jansi/AnsiColors.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2009-2017 the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.jansi;

/**
* Colors support.
*
* @since 2.1
*/
public enum AnsiColors {

Colors16("16 colors"),
Colors256("256 colors"),
TrueColor("24-bit colors");

private final String description;

AnsiColors(String description) {
this.description = description;
}

String getDescription() {
return description;
}
}
68 changes: 67 additions & 1 deletion src/main/java/org/fusesource/jansi/AnsiConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,37 @@ public class AnsiConsole {
*/
public static final String JANSI_MODE_DEFAULT = "default";

/**
* The default color support that Jansi will use, can be either <code>16</code>,
* <code>256</code> or <code>truecolor</code>. If not set, JANSI will try to
* autodetect the number of colors supported by the terminal by checking the
* <code>COLORTERM</code> and <code>TERM</code> variables.
*/
public static final String JANSI_COLORS = "jansi.colors";
/**
* Jansi colors specific to the standard output stream.
*/
public static final String JANSI_OUT_COLORS = "jansi.out.colors";
/**
* Jansi colors specific to the standard error stream.
*/
public static final String JANSI_ERR_COLORS = "jansi.err.colors";

/**
* Force the use of 16 colors. When using a 256-indexed color, or an RGB
* color, the color will be rounded to the nearest one from the 16 palette.
*/
public static final String JANSI_COLORS_16 = "16";
/**
* Force the use of 256 colors. When using an RGB color, the color will be
* rounded to the nearest one from the standard 256 palette.
*/
public static final String JANSI_COLORS_256 = "256";
/**
* Force the use of 24-bit colors.
*/
public static final String JANSI_COLORS_TRUECOLOR = "truecolor";

/**
* If the <code>jansi.passthrough</code> system property is set to true, will not perform any transformation
* and any ansi sequence will be conveyed without any modification.
Expand Down Expand Up @@ -300,6 +331,41 @@ else if (getBoolean(JANSI_FORCE)) {
mode = isatty ? AnsiMode.Default : AnsiMode.Strip;
}

AnsiColors colors;

String colorterm, term;
// If the jansi.colors property is set, use it
String jansiColors = System.getProperty(stdout ? JANSI_OUT_COLORS : JANSI_ERR_COLORS, System.getProperty(JANSI_COLORS));
if (JANSI_COLORS_TRUECOLOR.equals(jansiColors)) {
colors = AnsiColors.TrueColor;
} else if (JANSI_COLORS_256.equals(jansiColors)) {
colors = AnsiColors.Colors256;
} else if (jansiColors != null) {
colors = AnsiColors.Colors16;
}

// If the COLORTERM env variable contains "truecolor" or "24bit", assume true color support
// see https://gist.github.com/XVilka/8346728#true-color-detection
else if ((colorterm = System.getenv("COLORTERM")) != null
&& (colorterm.contains("truecolor") || colorterm.contains("24bit"))) {
colors = AnsiColors.TrueColor;
}

// check the if TERM contains -direct
else if ((term = System.getenv("TERM")) != null && term.contains("-direct")) {
colors = AnsiColors.TrueColor;
}

// check the if TERM contains -256color
else if (term != null && term.contains("-256color")) {
colors = AnsiColors.Colors256;
}

// else defaults to 16 colors
else {
colors = AnsiColors.Colors16;
}

// If the jansi.noreset property is not set, reset the attributes
// when the stream is closed
boolean resetAtUninstall = !getBoolean(JANSI_NORESET);
Expand All @@ -311,7 +377,7 @@ else if (getBoolean(JANSI_FORCE)) {
} catch (UnsupportedCharsetException e) {
}
}
return newPrintStream(new AnsiOutputStream(out, mode, processor, type, cs,
return newPrintStream(new AnsiOutputStream(out, mode, processor, type, colors, cs,
installer, uninstaller, resetAtUninstall), cs.name());
}

Expand Down
40 changes: 40 additions & 0 deletions src/main/java/org/fusesource/jansi/AnsiMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public static void main(String... args) throws IOException {
System.out.println(AnsiConsole.JANSI_MODE + "= " + System.getProperty(AnsiConsole.JANSI_MODE, ""));
System.out.println(AnsiConsole.JANSI_OUT_MODE + "= " + System.getProperty(AnsiConsole.JANSI_OUT_MODE, ""));
System.out.println(AnsiConsole.JANSI_ERR_MODE + "= " + System.getProperty(AnsiConsole.JANSI_ERR_MODE, ""));
System.out.println(AnsiConsole.JANSI_COLORS + "= " + System.getProperty(AnsiConsole.JANSI_COLORS, ""));
System.out.println(AnsiConsole.JANSI_OUT_COLORS + "= " + System.getProperty(AnsiConsole.JANSI_OUT_COLORS, ""));
System.out.println(AnsiConsole.JANSI_ERR_COLORS + "= " + System.getProperty(AnsiConsole.JANSI_ERR_COLORS, ""));
System.out.println(AnsiConsole.JANSI_PASSTHROUGH + "= " + AnsiConsole.getBoolean(AnsiConsole.JANSI_PASSTHROUGH));
System.out.println(AnsiConsole.JANSI_STRIP + "= " + AnsiConsole.getBoolean(AnsiConsole.JANSI_STRIP));
System.out.println(AnsiConsole.JANSI_FORCE + "= " + AnsiConsole.getBoolean(AnsiConsole.JANSI_FORCE));
Expand Down Expand Up @@ -108,6 +111,10 @@ public static void main(String... args) throws IOException {
for (AnsiType type : AnsiType.values()) {
System.out.println(" - " + type + ": " + type.getDescription());
}
System.out.println("Colors support description:");
for (AnsiColors colors : AnsiColors.values()) {
System.out.println(" - " + colors + ": " + colors.getDescription());
}
System.out.println("Modes description:");
for (AnsiMode mode : AnsiMode.values()) {
System.out.println(" - " + mode + ": " + mode.getDescription());
Expand Down Expand Up @@ -187,6 +194,39 @@ private static void testAnsi(boolean stderr) {
s.print(" " + ansi().bold().fg(c) + c + ansi().reset());
}
s.println();
Ansi ansi = ansi();
ansi.a(" 256 colors: ");
for (int i = 0; i < 6*6*6; i++) {
if (i > 0 && i % 36 == 0) {
ansi.reset();
ansi.newline();
ansi.a(" ");
} else if (i > 0 && i % 6 == 0) {
ansi.reset();
ansi.a(" ");
}
int a0 = i % 6;
int a1 = (i / 6) % 6;
int a2 = i / 36;
ansi.bg(16 + a0 + a2 * 6 + a1 * 36).a(' ');
}
ansi.reset();
s.println(ansi);
ansi = ansi();
ansi.a(" truecolor: ");
for (int i = 0; i < 256; i++) {
if (i > 0 && i % 48 == 0) {
ansi.reset();
ansi.newline();
ansi.a(" ");
}
int r = 255 - i;
int g = i * 2 > 255 ? 255 - 2 * i : 2 * i;
int b = i;
ansi.bgRgb(r, g, b).fgRgb(255 - r, 255 - g, 255 - b).a(i % 2 == 0 ? '/' : '\\');
}
ansi.reset();
s.println(ansi);
}

private static String getPomPropertiesVersion(String path) throws IOException {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/fusesource/jansi/AnsiPrintStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public AnsiType getType() {
return getOut().getType();
}

public AnsiColors getColors() {
return getOut().getColors();
}

public AnsiMode getMode() {
return getOut().getMode();
}
Expand Down Expand Up @@ -71,6 +75,7 @@ public void uninstall() throws IOException {
public String toString() {
return "AnsiPrintStream{"
+ "type=" + getType()
+ ", colors=" + getColors()
+ ", mode=" + getMode()
+ ", resetAtUninstall=" + isResetAtUninstall()
+ "}";
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/org/fusesource/jansi/io/AnsiOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.nio.charset.Charset;
import java.util.ArrayList;

import org.fusesource.jansi.AnsiColors;
import org.fusesource.jansi.AnsiMode;
import org.fusesource.jansi.AnsiType;

Expand Down Expand Up @@ -74,17 +75,19 @@ public interface IoRunnable {

private final AnsiProcessor processor;
private final AnsiType type;
private final AnsiColors colors;
private final IoRunnable installer;
private final IoRunnable uninstaller;
private AnsiMode mode;
private boolean resetAtUninstall;

public AnsiOutputStream(OutputStream os, AnsiMode mode,
AnsiProcessor processor, AnsiType type,
AnsiProcessor processor, AnsiType type, AnsiColors colors,
Charset cs, IoRunnable installer, IoRunnable uninstaller, boolean resetAtUninstall) {
super(os);
this.processor = processor;
this.type = type;
this.colors = colors;
this.installer = installer;
this.uninstaller = uninstaller;
this.resetAtUninstall = resetAtUninstall;
Expand All @@ -96,12 +99,18 @@ public AnsiType getType() {
return type;
}

public AnsiColors getColors() {
return colors;
}

public AnsiMode getMode() {
return mode;
}

public void setMode(AnsiMode mode) {
ap = mode == AnsiMode.Strip ? new AnsiProcessor(out) : mode == AnsiMode.Force ? null : processor;
ap = mode == AnsiMode.Strip
? new AnsiProcessor(out)
: mode == AnsiMode.Force || processor == null ? new ColorsAnsiProcessor(out, colors) : processor;
this.mode = mode;
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/fusesource/jansi/io/AnsiProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.util.ArrayList;
import java.util.Iterator;

import org.fusesource.jansi.AnsiColors;

/**
* ANSI processor providing <code>process*</code> corresponding to ANSI escape codes.
* This class methods implementations are empty: subclasses should actually perform the
Expand All @@ -43,7 +45,7 @@ public AnsiProcessor(OutputStream os) {
* @param optionsIterator the underlying iterator
* @throws IOException if no more non-null values left
*/
private int getNextOptionInt(Iterator<Object> optionsIterator) throws IOException {
protected int getNextOptionInt(Iterator<Object> optionsIterator) throws IOException {
for (;;) {
if (!optionsIterator.hasNext())
throw new IllegalArgumentException();
Expand Down
Loading

0 comments on commit 235b653

Please sign in to comment.