#5 append RGB colors when -Djexer.ECMA48.rgbColor=true
[fanfix.git] / src / jexer / io / ECMA48Screen.java
CommitLineData
daa4106c 1/*
217c6107
KL
2 * Jexer - Java Text User Interface
3 *
e16dda65 4 * The MIT License (MIT)
217c6107 5 *
a2018e99 6 * Copyright (C) 2017 Kevin Lamonte
217c6107 7 *
e16dda65
KL
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
217c6107 14 *
e16dda65
KL
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
217c6107 17 *
e16dda65
KL
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
7b5261bc
KL
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
217c6107
KL
28 */
29package jexer.io;
30
31import jexer.bits.Cell;
32import jexer.bits.CellAttributes;
33
34/**
2b9c27db
KL
35 * This Screen implementation draws to an xterm/ANSI X3.64/ECMA-48 type
36 * terminal.
217c6107 37 */
2b9c27db 38public final class ECMA48Screen extends Screen {
217c6107
KL
39
40 /**
7b5261bc
KL
41 * Emit debugging to stderr.
42 */
43 private boolean debugToStderr;
44
45 /**
46 * We call terminal.cursor() so need the instance.
217c6107
KL
47 */
48 private ECMA48Terminal terminal;
49
50 /**
7b5261bc 51 * Public constructor.
217c6107
KL
52 *
53 * @param terminal ECMA48Terminal to use
54 */
7b5261bc
KL
55 public ECMA48Screen(final ECMA48Terminal terminal) {
56 debugToStderr = false;
57
58 this.terminal = terminal;
217c6107 59
7b5261bc
KL
60 // Query the screen size
61 setDimensions(terminal.getSessionInfo().getWindowWidth(),
62 terminal.getSessionInfo().getWindowHeight());
217c6107
KL
63 }
64
65 /**
7b5261bc 66 * Perform a somewhat-optimal rendering of a line.
217c6107
KL
67 *
68 * @param y row coordinate. 0 is the top-most row.
69 * @param sb StringBuilder to write escape sequences to
70 * @param lastAttr cell attributes from the last call to flushLine
71 */
7b5261bc
KL
72 private void flushLine(final int y, final StringBuilder sb,
73 CellAttributes lastAttr) {
74
75 int lastX = -1;
76 int textEnd = 0;
77 for (int x = 0; x < width; x++) {
78 Cell lCell = logical[x][y];
79 if (!lCell.isBlank()) {
80 textEnd = x;
81 }
82 }
83 // Push textEnd to first column beyond the text area
84 textEnd++;
85
86 // DEBUG
87 // reallyCleared = true;
88
89 for (int x = 0; x < width; x++) {
90 Cell lCell = logical[x][y];
91 Cell pCell = physical[x][y];
92
8dc20d38 93 if (!lCell.equals(pCell) || reallyCleared) {
7b5261bc
KL
94
95 if (debugToStderr) {
96 System.err.printf("\n--\n");
97 System.err.printf(" Y: %d X: %d\n", y, x);
98 System.err.printf(" lCell: %s\n", lCell);
99 System.err.printf(" pCell: %s\n", pCell);
100 System.err.printf(" ==== \n");
101 }
102
103 if (lastAttr == null) {
104 lastAttr = new CellAttributes();
105 sb.append(terminal.normal());
106 }
107
108 // Place the cell
109 if ((lastX != (x - 1)) || (lastX == -1)) {
110 // Advancing at least one cell, or the first gotoXY
111 sb.append(terminal.gotoXY(x, y));
112 }
113
114 assert (lastAttr != null);
115
116 if ((x == textEnd) && (textEnd < width - 1)) {
117 assert (lCell.isBlank());
118
119 for (int i = x; i < width; i++) {
120 assert (logical[i][y].isBlank());
5dfd1c11 121 // Physical is always updated
7b5261bc
KL
122 physical[i][y].reset();
123 }
124
125 // Clear remaining line
126 sb.append(terminal.clearRemainingLine());
127 lastAttr.reset();
128 return;
129 }
130
131 // Now emit only the modified attributes
132 if ((lCell.getForeColor() != lastAttr.getForeColor())
133 && (lCell.getBackColor() != lastAttr.getBackColor())
7c870d89
KL
134 && (lCell.isBold() == lastAttr.isBold())
135 && (lCell.isReverse() == lastAttr.isReverse())
136 && (lCell.isUnderline() == lastAttr.isUnderline())
137 && (lCell.isBlink() == lastAttr.isBlink())
7b5261bc
KL
138 ) {
139 // Both colors changed, attributes the same
15ea4d73
KL
140 sb.append(terminal.color(lCell.isBold(),
141 lCell.getForeColor(), lCell.getBackColor()));
7b5261bc
KL
142
143 if (debugToStderr) {
144 System.err.printf("1 Change only fore/back colors\n");
145 }
146 } else if ((lCell.getForeColor() != lastAttr.getForeColor())
147 && (lCell.getBackColor() != lastAttr.getBackColor())
7c870d89
KL
148 && (lCell.isBold() != lastAttr.isBold())
149 && (lCell.isReverse() != lastAttr.isReverse())
150 && (lCell.isUnderline() != lastAttr.isUnderline())
151 && (lCell.isBlink() != lastAttr.isBlink())
7b5261bc
KL
152 ) {
153 // Everything is different
154 sb.append(terminal.color(lCell.getForeColor(),
155 lCell.getBackColor(),
7c870d89
KL
156 lCell.isBold(), lCell.isReverse(),
157 lCell.isBlink(),
158 lCell.isUnderline()));
7b5261bc
KL
159
160 if (debugToStderr) {
161 System.err.printf("2 Set all attributes\n");
162 }
163 } else if ((lCell.getForeColor() != lastAttr.getForeColor())
164 && (lCell.getBackColor() == lastAttr.getBackColor())
7c870d89
KL
165 && (lCell.isBold() == lastAttr.isBold())
166 && (lCell.isReverse() == lastAttr.isReverse())
167 && (lCell.isUnderline() == lastAttr.isUnderline())
168 && (lCell.isBlink() == lastAttr.isBlink())
7b5261bc
KL
169 ) {
170
171 // Attributes same, foreColor different
15ea4d73
KL
172 sb.append(terminal.color(lCell.isBold(),
173 lCell.getForeColor(), true));
7b5261bc
KL
174
175 if (debugToStderr) {
176 System.err.printf("3 Change foreColor\n");
177 }
178 } else if ((lCell.getForeColor() == lastAttr.getForeColor())
179 && (lCell.getBackColor() != lastAttr.getBackColor())
7c870d89
KL
180 && (lCell.isBold() == lastAttr.isBold())
181 && (lCell.isReverse() == lastAttr.isReverse())
182 && (lCell.isUnderline() == lastAttr.isUnderline())
183 && (lCell.isBlink() == lastAttr.isBlink())
7b5261bc
KL
184 ) {
185 // Attributes same, backColor different
15ea4d73
KL
186 sb.append(terminal.color(lCell.isBold(),
187 lCell.getBackColor(), false));
7b5261bc
KL
188
189 if (debugToStderr) {
190 System.err.printf("4 Change backColor\n");
191 }
192 } else if ((lCell.getForeColor() == lastAttr.getForeColor())
193 && (lCell.getBackColor() == lastAttr.getBackColor())
7c870d89
KL
194 && (lCell.isBold() == lastAttr.isBold())
195 && (lCell.isReverse() == lastAttr.isReverse())
196 && (lCell.isUnderline() == lastAttr.isUnderline())
197 && (lCell.isBlink() == lastAttr.isBlink())
7b5261bc
KL
198 ) {
199
200 // All attributes the same, just print the char
201 // NOP
202
203 if (debugToStderr) {
204 System.err.printf("5 Only emit character\n");
205 }
206 } else {
207 // Just reset everything again
208 sb.append(terminal.color(lCell.getForeColor(),
209 lCell.getBackColor(),
7c870d89
KL
210 lCell.isBold(),
211 lCell.isReverse(),
212 lCell.isBlink(),
213 lCell.isUnderline()));
7b5261bc
KL
214
215 if (debugToStderr) {
216 System.err.printf("6 Change all attributes\n");
217 }
218 }
219 // Emit the character
220 sb.append(lCell.getChar());
221
222 // Save the last rendered cell
223 lastX = x;
224 lastAttr.setTo(lCell);
225
8dc20d38 226 // Physical is always updated
7b5261bc
KL
227 physical[x][y].setTo(lCell);
228
8dc20d38 229 } // if (!lCell.equals(pCell) || (reallyCleared == true))
7b5261bc
KL
230
231 } // for (int x = 0; x < width; x++)
217c6107
KL
232 }
233
234 /**
235 * Render the screen to a string that can be emitted to something that
236 * knows how to process ECMA-48/ANSI X3.64 escape sequences.
237 *
238 * @return escape sequences string that provides the updates to the
239 * physical screen
240 */
1ac2ccb1 241 private String flushString() {
7b5261bc
KL
242 if (!dirty) {
243 assert (!reallyCleared);
244 return "";
245 }
246
247 CellAttributes attr = null;
248
249 StringBuilder sb = new StringBuilder();
250 if (reallyCleared) {
251 attr = new CellAttributes();
252 sb.append(terminal.clearAll());
253 }
254
255 for (int y = 0; y < height; y++) {
256 flushLine(y, sb, attr);
257 }
258
259 dirty = false;
260 reallyCleared = false;
261
262 String result = sb.toString();
263 if (debugToStderr) {
264 System.err.printf("flushString(): %s\n", result);
265 }
266 return result;
217c6107
KL
267 }
268
269 /**
270 * Push the logical screen to the physical device.
271 */
272 @Override
273 public void flushPhysical() {
7b5261bc
KL
274 String result = flushString();
275 if ((cursorVisible)
276 && (cursorY <= height - 1)
277 && (cursorX <= width - 1)
278 ) {
279 result += terminal.cursor(true);
280 result += terminal.gotoXY(cursorX, cursorY);
281 } else {
282 result += terminal.cursor(false);
283 }
284 terminal.getOutput().write(result);
285 terminal.flush();
217c6107 286 }
55d2b2c2
KL
287
288 /**
289 * Set the window title.
290 *
291 * @param title the new title
292 */
293 public void setTitle(final String title) {
294 terminal.getOutput().write(terminal.setTitle(title));
295 terminal.flush();
296 }
297
217c6107 298}