2 * Jexer - Java Text User Interface
4 * License: LGPLv3 or later
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
10 * Copyright (C) 2015 Kevin Lamonte
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
33 import jexer
.bits
.Cell
;
34 import jexer
.bits
.CellAttributes
;
37 * This Screen implementation draws to an xterm/ANSI X3.64/ECMA-48 type
40 public final class ECMA48Screen
extends Screen
{
43 * Emit debugging to stderr.
45 private boolean debugToStderr
;
48 * We call terminal.cursor() so need the instance.
50 private ECMA48Terminal terminal
;
55 * @param terminal ECMA48Terminal to use
57 public ECMA48Screen(final ECMA48Terminal terminal
) {
58 debugToStderr
= false;
60 this.terminal
= terminal
;
62 // Query the screen size
63 setDimensions(terminal
.getSessionInfo().getWindowWidth(),
64 terminal
.getSessionInfo().getWindowHeight());
68 * Perform a somewhat-optimal rendering of a line.
70 * @param y row coordinate. 0 is the top-most row.
71 * @param sb StringBuilder to write escape sequences to
72 * @param lastAttr cell attributes from the last call to flushLine
74 private void flushLine(final int y
, final StringBuilder sb
,
75 CellAttributes lastAttr
) {
79 for (int x
= 0; x
< width
; x
++) {
80 Cell lCell
= logical
[x
][y
];
81 if (!lCell
.isBlank()) {
85 // Push textEnd to first column beyond the text area
89 // reallyCleared = true;
91 for (int x
= 0; x
< width
; x
++) {
92 Cell lCell
= logical
[x
][y
];
93 Cell pCell
= physical
[x
][y
];
95 if (!lCell
.equals(pCell
) || reallyCleared
) {
98 System
.err
.printf("\n--\n");
99 System
.err
.printf(" Y: %d X: %d\n", y
, x
);
100 System
.err
.printf(" lCell: %s\n", lCell
);
101 System
.err
.printf(" pCell: %s\n", pCell
);
102 System
.err
.printf(" ==== \n");
105 if (lastAttr
== null) {
106 lastAttr
= new CellAttributes();
107 sb
.append(terminal
.normal());
111 if ((lastX
!= (x
- 1)) || (lastX
== -1)) {
112 // Advancing at least one cell, or the first gotoXY
113 sb
.append(terminal
.gotoXY(x
, y
));
116 assert (lastAttr
!= null);
118 if ((x
== textEnd
) && (textEnd
< width
- 1)) {
119 assert (lCell
.isBlank());
121 for (int i
= x
; i
< width
; i
++) {
122 assert (logical
[i
][y
].isBlank());
123 // Physical is always updatesd
124 physical
[i
][y
].reset();
127 // Clear remaining line
128 sb
.append(terminal
.clearRemainingLine());
133 // Now emit only the modified attributes
134 if ((lCell
.getForeColor() != lastAttr
.getForeColor())
135 && (lCell
.getBackColor() != lastAttr
.getBackColor())
136 && (lCell
.getBold() == lastAttr
.getBold())
137 && (lCell
.getReverse() == lastAttr
.getReverse())
138 && (lCell
.getUnderline() == lastAttr
.getUnderline())
139 && (lCell
.getBlink() == lastAttr
.getBlink())
141 // Both colors changed, attributes the same
142 sb
.append(terminal
.color(lCell
.getForeColor(),
143 lCell
.getBackColor()));
146 System
.err
.printf("1 Change only fore/back colors\n");
148 } else if ((lCell
.getForeColor() != lastAttr
.getForeColor())
149 && (lCell
.getBackColor() != lastAttr
.getBackColor())
150 && (lCell
.getBold() != lastAttr
.getBold())
151 && (lCell
.getReverse() != lastAttr
.getReverse())
152 && (lCell
.getUnderline() != lastAttr
.getUnderline())
153 && (lCell
.getBlink() != lastAttr
.getBlink())
155 // Everything is different
156 sb
.append(terminal
.color(lCell
.getForeColor(),
157 lCell
.getBackColor(),
158 lCell
.getBold(), lCell
.getReverse(),
160 lCell
.getUnderline()));
163 System
.err
.printf("2 Set all attributes\n");
165 } else if ((lCell
.getForeColor() != lastAttr
.getForeColor())
166 && (lCell
.getBackColor() == lastAttr
.getBackColor())
167 && (lCell
.getBold() == lastAttr
.getBold())
168 && (lCell
.getReverse() == lastAttr
.getReverse())
169 && (lCell
.getUnderline() == lastAttr
.getUnderline())
170 && (lCell
.getBlink() == lastAttr
.getBlink())
173 // Attributes same, foreColor different
174 sb
.append(terminal
.color(lCell
.getForeColor(), true));
177 System
.err
.printf("3 Change foreColor\n");
179 } else if ((lCell
.getForeColor() == lastAttr
.getForeColor())
180 && (lCell
.getBackColor() != lastAttr
.getBackColor())
181 && (lCell
.getBold() == lastAttr
.getBold())
182 && (lCell
.getReverse() == lastAttr
.getReverse())
183 && (lCell
.getUnderline() == lastAttr
.getUnderline())
184 && (lCell
.getBlink() == lastAttr
.getBlink())
186 // Attributes same, backColor different
187 sb
.append(terminal
.color(lCell
.getBackColor(), false));
190 System
.err
.printf("4 Change backColor\n");
192 } else if ((lCell
.getForeColor() == lastAttr
.getForeColor())
193 && (lCell
.getBackColor() == lastAttr
.getBackColor())
194 && (lCell
.getBold() == lastAttr
.getBold())
195 && (lCell
.getReverse() == lastAttr
.getReverse())
196 && (lCell
.getUnderline() == lastAttr
.getUnderline())
197 && (lCell
.getBlink() == lastAttr
.getBlink())
200 // All attributes the same, just print the char
204 System
.err
.printf("5 Only emit character\n");
207 // Just reset everything again
208 sb
.append(terminal
.color(lCell
.getForeColor(),
209 lCell
.getBackColor(),
213 lCell
.getUnderline()));
216 System
.err
.printf("6 Change all attributes\n");
219 // Emit the character
220 sb
.append(lCell
.getChar());
222 // Save the last rendered cell
224 lastAttr
.setTo(lCell
);
226 // Physical is always updated
227 physical
[x
][y
].setTo(lCell
);
229 } // if (!lCell.equals(pCell) || (reallyCleared == true))
231 } // for (int x = 0; x < width; x++)
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.
238 * @return escape sequences string that provides the updates to the
241 private String
flushString() {
243 assert (!reallyCleared
);
247 CellAttributes attr
= null;
249 StringBuilder sb
= new StringBuilder();
251 attr
= new CellAttributes();
252 sb
.append(terminal
.clearAll());
255 for (int y
= 0; y
< height
; y
++) {
256 flushLine(y
, sb
, attr
);
260 reallyCleared
= false;
262 String result
= sb
.toString();
264 System
.err
.printf("flushString(): %s\n", result
);
270 * Push the logical screen to the physical device.
273 public void flushPhysical() {
274 String result
= flushString();
276 && (cursorY
<= height
- 1)
277 && (cursorX
<= width
- 1)
279 result
+= terminal
.cursor(true);
280 result
+= terminal
.gotoXY(cursorX
, cursorY
);
282 result
+= terminal
.cursor(false);
284 terminal
.getOutput().write(result
);