2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 Kevin Lamonte
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import jexer
.bits
.Cell
;
32 import jexer
.bits
.CellAttributes
;
35 * This Screen implementation draws to an xterm/ANSI X3.64/ECMA-48 type
38 public final class ECMA48Screen
extends Screen
{
41 * Emit debugging to stderr.
43 private boolean debugToStderr
= false;
46 * We call terminal.cursor() so need the instance.
48 private ECMA48Terminal terminal
;
53 * @param terminal ECMA48Terminal to use
55 public ECMA48Screen(final ECMA48Terminal terminal
) {
56 this.terminal
= terminal
;
58 // Query the screen size
59 setDimensions(terminal
.getSessionInfo().getWindowWidth(),
60 terminal
.getSessionInfo().getWindowHeight());
64 * Perform a somewhat-optimal rendering of a line.
66 * @param y row coordinate. 0 is the top-most row.
67 * @param sb StringBuilder to write escape sequences to
68 * @param lastAttr cell attributes from the last call to flushLine
70 private void flushLine(final int y
, final StringBuilder sb
,
71 CellAttributes lastAttr
) {
75 for (int x
= 0; x
< width
; x
++) {
76 Cell lCell
= logical
[x
][y
];
77 if (!lCell
.isBlank()) {
81 // Push textEnd to first column beyond the text area
85 // reallyCleared = true;
87 for (int x
= 0; x
< width
; x
++) {
88 Cell lCell
= logical
[x
][y
];
89 Cell pCell
= physical
[x
][y
];
91 if (!lCell
.equals(pCell
) || reallyCleared
) {
94 System
.err
.printf("\n--\n");
95 System
.err
.printf(" Y: %d X: %d\n", y
, x
);
96 System
.err
.printf(" lCell: %s\n", lCell
);
97 System
.err
.printf(" pCell: %s\n", pCell
);
98 System
.err
.printf(" ==== \n");
101 if (lastAttr
== null) {
102 lastAttr
= new CellAttributes();
103 sb
.append(terminal
.normal());
107 if ((lastX
!= (x
- 1)) || (lastX
== -1)) {
108 // Advancing at least one cell, or the first gotoXY
109 sb
.append(terminal
.gotoXY(x
, y
));
112 assert (lastAttr
!= null);
114 if ((x
== textEnd
) && (textEnd
< width
- 1)) {
115 assert (lCell
.isBlank());
117 for (int i
= x
; i
< width
; i
++) {
118 assert (logical
[i
][y
].isBlank());
119 // Physical is always updated
120 physical
[i
][y
].reset();
123 // Clear remaining line
124 sb
.append(terminal
.clearRemainingLine());
129 // Now emit only the modified attributes
130 if ((lCell
.getForeColor() != lastAttr
.getForeColor())
131 && (lCell
.getBackColor() != lastAttr
.getBackColor())
132 && (lCell
.isBold() == lastAttr
.isBold())
133 && (lCell
.isReverse() == lastAttr
.isReverse())
134 && (lCell
.isUnderline() == lastAttr
.isUnderline())
135 && (lCell
.isBlink() == lastAttr
.isBlink())
137 // Both colors changed, attributes the same
138 sb
.append(terminal
.color(lCell
.isBold(),
139 lCell
.getForeColor(), lCell
.getBackColor()));
142 System
.err
.printf("1 Change only fore/back colors\n");
144 } else if ((lCell
.getForeColor() != lastAttr
.getForeColor())
145 && (lCell
.getBackColor() != lastAttr
.getBackColor())
146 && (lCell
.isBold() != lastAttr
.isBold())
147 && (lCell
.isReverse() != lastAttr
.isReverse())
148 && (lCell
.isUnderline() != lastAttr
.isUnderline())
149 && (lCell
.isBlink() != lastAttr
.isBlink())
151 // Everything is different
152 sb
.append(terminal
.color(lCell
.getForeColor(),
153 lCell
.getBackColor(),
154 lCell
.isBold(), lCell
.isReverse(),
156 lCell
.isUnderline()));
159 System
.err
.printf("2 Set all attributes\n");
161 } else if ((lCell
.getForeColor() != lastAttr
.getForeColor())
162 && (lCell
.getBackColor() == lastAttr
.getBackColor())
163 && (lCell
.isBold() == lastAttr
.isBold())
164 && (lCell
.isReverse() == lastAttr
.isReverse())
165 && (lCell
.isUnderline() == lastAttr
.isUnderline())
166 && (lCell
.isBlink() == lastAttr
.isBlink())
169 // Attributes same, foreColor different
170 sb
.append(terminal
.color(lCell
.isBold(),
171 lCell
.getForeColor(), true));
174 System
.err
.printf("3 Change foreColor\n");
176 } else if ((lCell
.getForeColor() == lastAttr
.getForeColor())
177 && (lCell
.getBackColor() != lastAttr
.getBackColor())
178 && (lCell
.isBold() == lastAttr
.isBold())
179 && (lCell
.isReverse() == lastAttr
.isReverse())
180 && (lCell
.isUnderline() == lastAttr
.isUnderline())
181 && (lCell
.isBlink() == lastAttr
.isBlink())
183 // Attributes same, backColor different
184 sb
.append(terminal
.color(lCell
.isBold(),
185 lCell
.getBackColor(), false));
188 System
.err
.printf("4 Change backColor\n");
190 } else if ((lCell
.getForeColor() == lastAttr
.getForeColor())
191 && (lCell
.getBackColor() == lastAttr
.getBackColor())
192 && (lCell
.isBold() == lastAttr
.isBold())
193 && (lCell
.isReverse() == lastAttr
.isReverse())
194 && (lCell
.isUnderline() == lastAttr
.isUnderline())
195 && (lCell
.isBlink() == lastAttr
.isBlink())
198 // All attributes the same, just print the char
202 System
.err
.printf("5 Only emit character\n");
205 // Just reset everything again
206 sb
.append(terminal
.color(lCell
.getForeColor(),
207 lCell
.getBackColor(),
211 lCell
.isUnderline()));
214 System
.err
.printf("6 Change all attributes\n");
217 // Emit the character
218 sb
.append(lCell
.getChar());
220 // Save the last rendered cell
222 lastAttr
.setTo(lCell
);
224 // Physical is always updated
225 physical
[x
][y
].setTo(lCell
);
227 } // if (!lCell.equals(pCell) || (reallyCleared == true))
229 } // for (int x = 0; x < width; x++)
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.
236 * @return escape sequences string that provides the updates to the
239 private String
flushString() {
241 assert (!reallyCleared
);
245 CellAttributes attr
= null;
247 StringBuilder sb
= new StringBuilder();
249 attr
= new CellAttributes();
250 sb
.append(terminal
.clearAll());
253 for (int y
= 0; y
< height
; y
++) {
254 flushLine(y
, sb
, attr
);
258 reallyCleared
= false;
260 String result
= sb
.toString();
262 System
.err
.printf("flushString(): %s\n", result
);
268 * Push the logical screen to the physical device.
271 public void flushPhysical() {
272 String result
= flushString();
274 && (cursorY
<= height
- 1)
275 && (cursorX
<= width
- 1)
277 result
+= terminal
.cursor(true);
278 result
+= terminal
.gotoXY(cursorX
, cursorY
);
280 result
+= terminal
.cursor(false);
282 terminal
.getOutput().write(result
);
287 * Set the window title.
289 * @param title the new title
291 public void setTitle(final String title
) {
292 terminal
.getOutput().write(terminal
.setTitle(title
));