LICENSE CHANGED TO MIT
[nikiroo-utils.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 *
e16dda65 6 * Copyright (C) 2016 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());
121 // Physical is always updatesd
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
140 sb.append(terminal.color(lCell.getForeColor(),
141 lCell.getBackColor()));
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
172 sb.append(terminal.color(lCell.getForeColor(), true));
173
174 if (debugToStderr) {
175 System.err.printf("3 Change foreColor\n");
176 }
177 } else if ((lCell.getForeColor() == lastAttr.getForeColor())
178 && (lCell.getBackColor() != lastAttr.getBackColor())
7c870d89
KL
179 && (lCell.isBold() == lastAttr.isBold())
180 && (lCell.isReverse() == lastAttr.isReverse())
181 && (lCell.isUnderline() == lastAttr.isUnderline())
182 && (lCell.isBlink() == lastAttr.isBlink())
7b5261bc
KL
183 ) {
184 // Attributes same, backColor different
185 sb.append(terminal.color(lCell.getBackColor(), false));
186
187 if (debugToStderr) {
188 System.err.printf("4 Change backColor\n");
189 }
190 } else if ((lCell.getForeColor() == lastAttr.getForeColor())
191 && (lCell.getBackColor() == lastAttr.getBackColor())
7c870d89
KL
192 && (lCell.isBold() == lastAttr.isBold())
193 && (lCell.isReverse() == lastAttr.isReverse())
194 && (lCell.isUnderline() == lastAttr.isUnderline())
195 && (lCell.isBlink() == lastAttr.isBlink())
7b5261bc
KL
196 ) {
197
198 // All attributes the same, just print the char
199 // NOP
200
201 if (debugToStderr) {
202 System.err.printf("5 Only emit character\n");
203 }
204 } else {
205 // Just reset everything again
206 sb.append(terminal.color(lCell.getForeColor(),
207 lCell.getBackColor(),
7c870d89
KL
208 lCell.isBold(),
209 lCell.isReverse(),
210 lCell.isBlink(),
211 lCell.isUnderline()));
7b5261bc
KL
212
213 if (debugToStderr) {
214 System.err.printf("6 Change all attributes\n");
215 }
216 }
217 // Emit the character
218 sb.append(lCell.getChar());
219
220 // Save the last rendered cell
221 lastX = x;
222 lastAttr.setTo(lCell);
223
8dc20d38 224 // Physical is always updated
7b5261bc
KL
225 physical[x][y].setTo(lCell);
226
8dc20d38 227 } // if (!lCell.equals(pCell) || (reallyCleared == true))
7b5261bc
KL
228
229 } // for (int x = 0; x < width; x++)
217c6107
KL
230 }
231
232 /**
233 * Render the screen to a string that can be emitted to something that
234 * knows how to process ECMA-48/ANSI X3.64 escape sequences.
235 *
236 * @return escape sequences string that provides the updates to the
237 * physical screen
238 */
1ac2ccb1 239 private String flushString() {
7b5261bc
KL
240 if (!dirty) {
241 assert (!reallyCleared);
242 return "";
243 }
244
245 CellAttributes attr = null;
246
247 StringBuilder sb = new StringBuilder();
248 if (reallyCleared) {
249 attr = new CellAttributes();
250 sb.append(terminal.clearAll());
251 }
252
253 for (int y = 0; y < height; y++) {
254 flushLine(y, sb, attr);
255 }
256
257 dirty = false;
258 reallyCleared = false;
259
260 String result = sb.toString();
261 if (debugToStderr) {
262 System.err.printf("flushString(): %s\n", result);
263 }
264 return result;
217c6107
KL
265 }
266
267 /**
268 * Push the logical screen to the physical device.
269 */
270 @Override
271 public void flushPhysical() {
7b5261bc
KL
272 String result = flushString();
273 if ((cursorVisible)
274 && (cursorY <= height - 1)
275 && (cursorX <= width - 1)
276 ) {
277 result += terminal.cursor(true);
278 result += terminal.gotoXY(cursorX, cursorY);
279 } else {
280 result += terminal.cursor(false);
281 }
282 terminal.getOutput().write(result);
283 terminal.flush();
217c6107
KL
284 }
285}