From 0281da658abce29810d1a565cf9e3f55919e1bca Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Mon, 8 May 2023 14:41:48 -0500 Subject: [PATCH] Add API for console feature detection --- .../formatters/ColorPatternFormatter.java | 26 +++-------- .../logmanager/formatters/ColorPrintf.java | 9 ++-- .../logmanager/handlers/ConsoleHandler.java | 46 +++++++++++++++++++ 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/jboss/logmanager/formatters/ColorPatternFormatter.java b/src/main/java/org/jboss/logmanager/formatters/ColorPatternFormatter.java index 54362896..acf69188 100644 --- a/src/main/java/org/jboss/logmanager/formatters/ColorPatternFormatter.java +++ b/src/main/java/org/jboss/logmanager/formatters/ColorPatternFormatter.java @@ -21,6 +21,7 @@ import org.jboss.logmanager.ExtLogRecord; import org.jboss.logmanager.Level; +import org.jboss.logmanager.handlers.ConsoleHandler; import java.util.logging.Formatter; import java.util.logging.LogRecord; @@ -53,17 +54,6 @@ public ColorPatternFormatter(int darken, final String pattern) { setPattern(pattern); } - static final boolean trueColor = determineTrueColor(); - - static boolean determineTrueColor() { - final String colorterm = System.getenv("COLORTERM"); - return (colorterm != null && (colorterm.contains("truecolor") || colorterm.contains("24bit"))); - } - - static boolean isTrueColor() { - return trueColor; - } - public void setSteps(final FormatStep[] steps) { FormatStep[] colorSteps = new FormatStep[steps.length]; for (int i = 0; i < steps.length; i++) { @@ -146,6 +136,8 @@ public String formatMessage(final LogRecord logRecord) { static final class ColorStep implements FormatStep { private final int r, g, b; private final FormatStep delegate; + // capture current console state + private final boolean trueColor = ConsoleHandler.isTrueColor(); ColorStep(final FormatStep delegate, final int r, final int g, final int b, final int darken) { this.r = r >>> darken; @@ -155,7 +147,7 @@ static final class ColorStep implements FormatStep { } public void render(final Formatter formatter, final StringBuilder builder, final ExtLogRecord record) { - ColorUtil.startFgColor(builder, isTrueColor(), r, g, b); + ColorUtil.startFgColor(builder, trueColor, r, g, b); delegate.render(formatter, builder, record); ColorUtil.endFgColor(builder); } @@ -183,6 +175,8 @@ static final class LevelColorStep implements FormatStep { private static final int SATURATION = 66; private final FormatStep delegate; private final int darken; + // capture current console state + private final boolean trueColor = ConsoleHandler.isTrueColor(); LevelColorStep(final FormatStep delegate, final int darken) { this.delegate = delegate; @@ -195,7 +189,7 @@ public void render(final Formatter formatter, final StringBuilder builder, final int r = ((level < 300 ? 0 : (level - 300) * (255 - SATURATION) / 300) + SATURATION) >>> darken; int g = ((300 - abs(level - 300)) * (255 - SATURATION) / 300 + SATURATION) >>> darken; int b = ((level > 300 ? 0 : level * (255 - SATURATION) / 300) + SATURATION) >>> darken; - ColorUtil.startFgColor(builder, isTrueColor(), r, g, b); + ColorUtil.startFgColor(builder, trueColor, r, g, b); delegate.render(formatter, builder, record); ColorUtil.endFgColor(builder); } @@ -212,10 +206,4 @@ public boolean isCallerInformationRequired() { return false; } } - - static final class TrueColorHolder { - private TrueColorHolder() {} - - static final boolean trueColor = ColorPatternFormatter.determineTrueColor(); - } } diff --git a/src/main/java/org/jboss/logmanager/formatters/ColorPrintf.java b/src/main/java/org/jboss/logmanager/formatters/ColorPrintf.java index a986caf3..71ec0e5c 100644 --- a/src/main/java/org/jboss/logmanager/formatters/ColorPrintf.java +++ b/src/main/java/org/jboss/logmanager/formatters/ColorPrintf.java @@ -27,8 +27,11 @@ import java.util.Locale; import java.util.UUID; +import org.jboss.logmanager.handlers.ConsoleHandler; + class ColorPrintf extends Printf { private final int darken; + private final boolean trueColor = ConsoleHandler.isTrueColor(); ColorPrintf(final int darken) { super(Locale.getDefault()); @@ -72,11 +75,11 @@ protected void formatFormattableString(final StringBuilder target, final Formatt protected void formatPlainString(final StringBuilder target, final Object item, final GeneralFlags genFlags, final int width, final int precision) { if (item instanceof Class || item instanceof Executable || item instanceof Field) { - ColorUtil.startFgColor(target, ColorPatternFormatter.trueColor, 0xff >>> darken, 0xff >>> darken, 0xdd >>> darken); + ColorUtil.startFgColor(target, trueColor, 0xff >>> darken, 0xff >>> darken, 0xdd >>> darken); } else if (item instanceof UUID) { - ColorUtil.startFgColor(target, ColorPatternFormatter.trueColor, 0xdd >>> darken, 0xff >>> darken, 0xdd >>> darken); + ColorUtil.startFgColor(target, trueColor, 0xdd >>> darken, 0xff >>> darken, 0xdd >>> darken); } else { - ColorUtil.startFgColor(target, ColorPatternFormatter.trueColor, 0xdd >>> darken, 0xdd >>> darken, 0xdd >>> darken); + ColorUtil.startFgColor(target, trueColor, 0xdd >>> darken, 0xdd >>> darken, 0xdd >>> darken); } super.formatPlainString(target, item, genFlags, width, precision); ColorUtil.endFgColor(target); diff --git a/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java b/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java index ecc83990..651e1699 100644 --- a/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java +++ b/src/main/java/org/jboss/logmanager/handlers/ConsoleHandler.java @@ -160,4 +160,50 @@ private static Writer wrap(final Writer writer) { public void setOutputStream(final OutputStream outputStream) { super.setOutputStream(wrap(outputStream)); } + + /** + * Determine whether the console exists. + * If the console does not exist, then the standard output stream will be used when {@link Target#CONSOLE} is + * selected as {@linkplain #setTarget(Target) the output target}. + * + * @return {@code true} if there is a console, {@code false} otherwise + */ + public static boolean hasConsole() { + return console != null; + } + + /** + * Determine whether the console supports truecolor output. + * This call may be expensive, so the result should be captured for the lifetime of any formatter making use of + * this information. + * + * @return {@code true} if the console exists and supports truecolor output; {@code false} otherwise + */ + public static boolean isTrueColor() { + if (! hasConsole()) { + return false; + } + final String colorterm = System.getenv("COLORTERM"); + return colorterm != null && (colorterm.contains("truecolor") || colorterm.contains("24bit")); + } + + /** + * Determine whether the console can be passively detected to support graphical output. + * This call may be expensive, so the result should be captured for the lifetime of any formatter making use of + * this information. + * + * @return {@code true} if the console exists and supports graphical output; {@code false} otherwise or if + * graphical support cannot be passively detected + */ + public static boolean isGraphicsSupportPassivelyDetected() { + if (! hasConsole()) { + return false; + } + final String term = System.getenv("TERM"); + final String termProgram = System.getenv("TERM_PROGRAM"); + return term != null && (term.equalsIgnoreCase("kitty") + || term.equalsIgnoreCase("wezterm") + || term.equalsIgnoreCase("konsole") + ) || termProgram != null && termProgram.equalsIgnoreCase("wezterm"); + } }