update roadmap
[nikiroo-utils.git] / src / jexer / io / ECMA48Screen.java
CommitLineData
217c6107
KL
1/**
2 * Jexer - Java Text User Interface
3 *
4 * Version: $Id$
5 *
6 * Author: Kevin Lamonte, <a href="mailto:kevin.lamonte@gmail.com">kevin.lamonte@gmail.com</a>
7 *
8 * License: LGPLv3 or later
9 *
10 * Copyright: This module is licensed under the GNU Lesser General
11 * Public License Version 3. Please see the file "COPYING" in this
12 * directory for more information about the GNU Lesser General Public
13 * License Version 3.
14 *
15 * Copyright (C) 2015 Kevin Lamonte
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Lesser General Public License
19 * as published by the Free Software Foundation; either version 3 of
20 * the License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/licenses/, or write to the Free Software
30 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
31 * 02110-1301 USA
32 */
33package jexer.io;
34
35import jexer.bits.Cell;
36import jexer.bits.CellAttributes;
37
38/**
39 * This Screen class draws to an xterm/ANSI X3.64/ECMA-48 type terminal.
40 */
41public class ECMA48Screen extends Screen {
42
43 /**
44 * We call terminal.cursor() so need the instance
45 */
46 private ECMA48Terminal terminal;
47
48 /**
49 * Public constructor
50 *
51 * @param terminal ECMA48Terminal to use
52 */
53 public ECMA48Screen(ECMA48Terminal terminal) {
54 this.terminal = terminal;
55
56 // Query the screen size
57 setDimensions(terminal.session.getWindowWidth(),
58 terminal.session.getWindowHeight());
59 }
60
61 /**
62 * Perform a somewhat-optimal rendering of a line
63 *
64 * @param y row coordinate. 0 is the top-most row.
65 * @param sb StringBuilder to write escape sequences to
66 * @param lastAttr cell attributes from the last call to flushLine
67 */
68 private void flushLine(int y, StringBuilder sb, CellAttributes lastAttr) {
69
70 int lastX = -1;
71 int textEnd = 0;
72 for (int x = 0; x < width; x++) {
73 Cell lCell = logical[x][y];
74 if (!lCell.isBlank()) {
75 textEnd = x;
76 }
77 }
78 // Push textEnd to first column beyond the text area
79 textEnd++;
80
81 // DEBUG
82 // reallyCleared = true;
83
84 for (int x = 0; x < width; x++) {
85 Cell lCell = logical[x][y];
86 Cell pCell = physical[x][y];
87
88 if ((lCell != pCell) || (reallyCleared == true)) {
89
90 if (debugToStderr) {
91 System.err.printf("\n--\n");
92 System.err.printf(" Y: %d X: %d\n", y, x);
93 System.err.printf(" lCell: %s\n", lCell);
94 System.err.printf(" pCell: %s\n", pCell);
95 System.err.printf(" ==== \n");
96 }
97
98 if (lastAttr == null) {
99 lastAttr = new CellAttributes();
100 sb.append(terminal.normal());
101 }
102
103 // Place the cell
104 if ((lastX != (x - 1)) || (lastX == -1)) {
105 // Advancing at least one cell, or the first gotoXY
106 sb.append(terminal.gotoXY(x, y));
107 }
108
109 assert(lastAttr != null);
110
111 if ((x == textEnd) && (textEnd < width - 1)) {
112 assert(lCell.isBlank());
113
114 for (int i = x; i < width; i++) {
115 assert(logical[i][y].isBlank());
116 // Physical is always updatesd
117 physical[i][y].reset();
118 }
119
120 // Clear remaining line
121 sb.append(terminal.clearRemainingLine());
122 lastAttr.reset();
123 return;
124 }
125
126 // Now emit only the modified attributes
127 if ((lCell.foreColor != lastAttr.foreColor) &&
128 (lCell.backColor != lastAttr.backColor) &&
129 (lCell.bold == lastAttr.bold) &&
130 (lCell.reverse == lastAttr.reverse) &&
131 (lCell.underline == lastAttr.underline) &&
132 (lCell.blink == lastAttr.blink)) {
133
134 // Both colors changed, attributes the same
135 sb.append(terminal.color(lCell.foreColor,
136 lCell.backColor));
137
138 if (debugToStderr) {
139 System.err.printf("1 Change only fore/back colors\n");
140 }
141 } else if ((lCell.foreColor != lastAttr.foreColor) &&
142 (lCell.backColor != lastAttr.backColor) &&
143 (lCell.bold != lastAttr.bold) &&
144 (lCell.reverse != lastAttr.reverse) &&
145 (lCell.underline != lastAttr.underline) &&
146 (lCell.blink != lastAttr.blink)) {
147
148 if (debugToStderr) {
149 System.err.printf("2 Set all attributes\n");
150 }
151
152 // Everything is different
153 sb.append(terminal.color(lCell.foreColor,
154 lCell.backColor,
155 lCell.bold, lCell.reverse, lCell.blink,
156 lCell.underline));
157
158 } else if ((lCell.foreColor != lastAttr.foreColor) &&
159 (lCell.backColor == lastAttr.backColor) &&
160 (lCell.bold == lastAttr.bold) &&
161 (lCell.reverse == lastAttr.reverse) &&
162 (lCell.underline == lastAttr.underline) &&
163 (lCell.blink == lastAttr.blink)) {
164
165 // Attributes same, foreColor different
166 sb.append(terminal.color(lCell.foreColor, true));
167
168 if (debugToStderr) {
169 System.err.printf("3 Change foreColor\n");
170 }
171
172 } else if ((lCell.foreColor == lastAttr.foreColor) &&
173 (lCell.backColor != lastAttr.backColor) &&
174 (lCell.bold == lastAttr.bold) &&
175 (lCell.reverse == lastAttr.reverse) &&
176 (lCell.underline == lastAttr.underline) &&
177 (lCell.blink == lastAttr.blink)) {
178
179 // Attributes same, backColor different
180 sb.append(terminal.color(lCell.backColor, false));
181
182 if (debugToStderr) {
183 System.err.printf("4 Change backColor\n");
184 }
185
186 } else if ((lCell.foreColor == lastAttr.foreColor) &&
187 (lCell.backColor == lastAttr.backColor) &&
188 (lCell.bold == lastAttr.bold) &&
189 (lCell.reverse == lastAttr.reverse) &&
190 (lCell.underline == lastAttr.underline) &&
191 (lCell.blink == lastAttr.blink)) {
192
193 // All attributes the same, just print the char
194 // NOP
195
196 if (debugToStderr) {
197 System.err.printf("5 Only emit character\n");
198 }
199 } else {
200 // Just reset everything again
201 sb.append(terminal.color(lCell.foreColor, lCell.backColor,
202 lCell.bold, lCell.reverse, lCell.blink,
203 lCell.underline));
204
205 if (debugToStderr) {
206 System.err.printf("6 Change all attributes\n");
207 }
208 }
209 // Emit the character
210 sb.append(lCell.ch);
211
212 // Save the last rendered cell
213 lastX = x;
214 lastAttr.setTo(lCell);
215
216 // Physical is always updatesd
217 physical[x][y].setTo(lCell);
218
219 } // if ((lCell != pCell) || (reallyCleared == true))
220
221 } // for (int x = 0; x < width; x++)
222 }
223
224 /**
225 * Render the screen to a string that can be emitted to something that
226 * knows how to process ECMA-48/ANSI X3.64 escape sequences.
227 *
228 * @return escape sequences string that provides the updates to the
229 * physical screen
230 */
231 public String flushString() {
232 if (dirty == false) {
233 assert(reallyCleared == false);
234 return "";
235 }
236
237 CellAttributes attr = null;
238
239 StringBuilder sb = new StringBuilder();
240 if (reallyCleared == true) {
241 attr = new CellAttributes();
242 sb.append(terminal.clearAll());
243 }
244
245 for (int y = 0; y < height; y++) {
246 flushLine(y, sb, attr);
247 }
248
249 dirty = false;
250 reallyCleared = false;
251
252 String result = sb.toString();
253 if (debugToStderr) {
254 System.err.printf("flushString(): %s\n", result);
255 }
256 return result;
257 }
258
259 /**
260 * Push the logical screen to the physical device.
261 */
262 @Override
263 public void flushPhysical() {
264 String result = flushString();
265 if ((cursorVisible) &&
266 (cursorY <= height - 1) &&
267 (cursorX <= width - 1)
268 ) {
269 result += terminal.cursor(true);
270 result += terminal.gotoXY(cursorX, cursorY);
271 } else {
272 result += terminal.cursor(false);
273 }
274 terminal.getOutput().write(result);
275 terminal.flush();
276 }
277}