*
* The MIT License (MIT)
*
- * Copyright (C) 2016 Kevin Lamonte
+ * Copyright (C) 2017 Kevin Lamonte
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*/
public final class ECMA48Terminal implements Runnable {
+ /**
+ * If true, emit T.416-style RGB colors. This is a) expensive in
+ * bandwidth, and b) potentially terrible looking for non-xterms.
+ */
+ private static boolean doRgbColor = false;
+
/**
* The session information.
*/
windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
+ // Permit RGB colors only if externally requested
+ if (System.getProperty("jexer.ECMA48.rgbColor") != null) {
+ if (System.getProperty("jexer.ECMA48.rgbColor").equals("true")) {
+ doRgbColor = true;
+ }
+ }
+
+ // Spin up the input reader
+ eventQueue = new LinkedList<TInputEvent>();
+ readerThread = new Thread(this);
+ readerThread.start();
+ }
+
+ /**
+ * Constructor sets up state for getEvent().
+ *
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
+ * @param input the InputStream underlying 'reader'. Its available()
+ * method is used to determine if reader.read() will block or not.
+ * @param reader a Reader connected to the remote user.
+ * @param writer a PrintWriter connected to the remote user.
+ * @param setRawMode if true, set System.in into raw mode with stty.
+ * This should in general not be used. It is here solely for Demo3,
+ * which uses System.in.
+ * @throws IllegalArgumentException if input, reader, or writer are null.
+ */
+ public ECMA48Terminal(final Object listener, final InputStream input,
+ final Reader reader, final PrintWriter writer,
+ final boolean setRawMode) {
+
+ if (input == null) {
+ throw new IllegalArgumentException("InputStream must be specified");
+ }
+ if (reader == null) {
+ throw new IllegalArgumentException("Reader must be specified");
+ }
+ if (writer == null) {
+ throw new IllegalArgumentException("Writer must be specified");
+ }
+ reset();
+ mouse1 = false;
+ mouse2 = false;
+ mouse3 = false;
+ stopReaderThread = false;
+ this.listener = listener;
+
+ inputStream = input;
+ this.input = reader;
+
+ if (setRawMode == true) {
+ sttyRaw();
+ }
+ this.setRawMode = setRawMode;
+
+ if (input instanceof SessionInfo) {
+ // This is a TelnetInputStream that exposes window size and
+ // environment variables from the telnet layer.
+ sessionInfo = (SessionInfo) input;
+ }
+ if (sessionInfo == null) {
+ if (setRawMode == true) {
+ // Reading right off the tty
+ sessionInfo = new TTYSessionInfo();
+ } else {
+ sessionInfo = new TSessionInfo();
+ }
+ }
+
+ this.output = writer;
+
+ // Enable mouse reporting and metaSendsEscape
+ this.output.printf("%s%s", mouse(true), xtermMetaSendsEscape(true));
+ this.output.flush();
+
+ // Hang onto the window size
+ windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
+ sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
+
+ // Permit RGB colors only if externally requested
+ if (System.getProperty("jexer.ECMA48.rgbColor") != null) {
+ if (System.getProperty("jexer.ECMA48.rgbColor").equals("true")) {
+ doRgbColor = true;
+ }
+ }
+
// Spin up the input reader
eventQueue = new LinkedList<TInputEvent>();
readerThread = new Thread(this);
readerThread.start();
}
+ /**
+ * Constructor sets up state for getEvent().
+ *
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
+ * @param input the InputStream underlying 'reader'. Its available()
+ * method is used to determine if reader.read() will block or not.
+ * @param reader a Reader connected to the remote user.
+ * @param writer a PrintWriter connected to the remote user.
+ * @throws IllegalArgumentException if input, reader, or writer are null.
+ */
+ public ECMA48Terminal(final Object listener, final InputStream input,
+ final Reader reader, final PrintWriter writer) {
+
+ this(listener, input, reader, writer, false);
+ }
+
/**
* Restore terminal to normal state.
*/
return "\033[?1036l";
}
+ /**
+ * Create an xterm OSC sequence to change the window title. Note package
+ * private access.
+ *
+ * @param title the new title
+ * @return the string to emit to xterm
+ */
+ String setTitle(final String title) {
+ return "\033]2;" + title + "\007";
+ }
+
/**
* Create a SGR parameter sequence for a single color change. Note
* package private access.
*
+ * @param bold if true, set bold
* @param color one of the Color.WHITE, Color.BLUE, etc. constants
* @param foreground if true, this is a foreground color
* @return the string to emit to an ANSI / ECMA-style terminal,
* e.g. "\033[42m"
*/
- String color(final Color color, final boolean foreground) {
- return color(color, foreground, true);
+ String color(final boolean bold, final Color color,
+ final boolean foreground) {
+ return color(color, foreground, true) +
+ rgbColor(bold, color, foreground);
+ }
+
+ /**
+ * Create a T.416 RGB parameter sequence for a single color change.
+ *
+ * @param bold if true, set bold
+ * @param color one of the Color.WHITE, Color.BLUE, etc. constants
+ * @param foreground if true, this is a foreground color
+ * @return the string to emit to an xterm terminal with RGB support,
+ * e.g. "\033[38;2;RR;GG;BBm"
+ */
+ private String rgbColor(final boolean bold, final Color color,
+ final boolean foreground) {
+ if (doRgbColor == false) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder("\033[");
+ if (bold) {
+ // Bold implies foreground only
+ sb.append("38;2;");
+ if (color.equals(Color.BLACK)) {
+ sb.append("84;84;84");
+ } else if (color.equals(Color.RED)) {
+ sb.append("252;84;84");
+ } else if (color.equals(Color.GREEN)) {
+ sb.append("84;252;84");
+ } else if (color.equals(Color.YELLOW)) {
+ sb.append("252;252;84");
+ } else if (color.equals(Color.BLUE)) {
+ sb.append("84;84;252");
+ } else if (color.equals(Color.MAGENTA)) {
+ sb.append("252;84;252");
+ } else if (color.equals(Color.CYAN)) {
+ sb.append("84;252;252");
+ } else if (color.equals(Color.WHITE)) {
+ sb.append("252;252;252");
+ }
+ } else {
+ if (foreground) {
+ sb.append("38;2;");
+ } else {
+ sb.append("48;2;");
+ }
+ if (color.equals(Color.BLACK)) {
+ sb.append("0;0;0");
+ } else if (color.equals(Color.RED)) {
+ sb.append("168;0;0");
+ } else if (color.equals(Color.GREEN)) {
+ sb.append("0;168;0");
+ } else if (color.equals(Color.YELLOW)) {
+ sb.append("168;84;0");
+ } else if (color.equals(Color.BLUE)) {
+ sb.append("0;0;168");
+ } else if (color.equals(Color.MAGENTA)) {
+ sb.append("168;0;168");
+ } else if (color.equals(Color.CYAN)) {
+ sb.append("0;168;168");
+ } else if (color.equals(Color.WHITE)) {
+ sb.append("168;168;168");
+ }
+ }
+ sb.append("m");
+ return sb.toString();
+ }
+
+ /**
+ * Create a T.416 RGB parameter sequence for both foreground and
+ * background color change.
+ *
+ * @param bold if true, set bold
+ * @param foreColor one of the Color.WHITE, Color.BLUE, etc. constants
+ * @param backColor one of the Color.WHITE, Color.BLUE, etc. constants
+ * @return the string to emit to an xterm terminal with RGB support,
+ * e.g. "\033[38;2;RR;GG;BB;48;2;RR;GG;BBm"
+ */
+ private String rgbColor(final boolean bold, final Color foreColor,
+ final Color backColor) {
+ if (doRgbColor == false) {
+ return "";
+ }
+
+ return rgbColor(bold, foreColor, true) +
+ rgbColor(false, backColor, false);
}
/**
* Create a SGR parameter sequence for both foreground and background
* color change. Note package private access.
*
+ * @param bold if true, set bold
* @param foreColor one of the Color.WHITE, Color.BLUE, etc. constants
* @param backColor one of the Color.WHITE, Color.BLUE, etc. constants
* @return the string to emit to an ANSI / ECMA-style terminal,
* e.g. "\033[31;42m"
*/
- String color(final Color foreColor, final Color backColor) {
- return color(foreColor, backColor, true);
+ String color(final boolean bold, final Color foreColor,
+ final Color backColor) {
+ return color(foreColor, backColor, true) +
+ rgbColor(bold, foreColor, backColor);
}
/**
sb.append("\033[0;");
}
sb.append(String.format("%d;%dm", ecmaForeColor, ecmaBackColor));
+ sb.append(rgbColor(bold, foreColor, backColor));
return sb.toString();
}
* e.g. "\033[0m"
*/
String normal() {
- return normal(true);
+ return normal(true) + rgbColor(false, Color.WHITE, Color.BLACK);
}
/**