71878e968988fa4e9d11d132b7953c15b6f15829
2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2016 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
;
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 debugToStderr
= false;
58 this.terminal
= terminal
;
60 // Query the screen size
61 setDimensions(terminal
.getSessionInfo().getWindowWidth(),
62 terminal
.getSessionInfo().getWindowHeight());
66 * Perform a somewhat-optimal rendering of a line.
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
72 private void flushLine(final int y
, final StringBuilder sb
,
73 CellAttributes lastAttr
) {
77 for (int x
= 0; x
< width
; x
++) {
78 Cell lCell
= logical
[x
][y
];
79 if (!lCell
.isBlank()) {
83 // Push textEnd to first column beyond the text area
87 // reallyCleared = true;
89 for (int x
= 0; x
< width
; x
++) {
90 Cell lCell
= logical
[x
][y
];
91 Cell pCell
= physical
[x
][y
];
93 if (!lCell
.equals(pCell
) || reallyCleared
) {
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");
103 if (lastAttr
== null) {
104 lastAttr
= new CellAttributes();
105 sb
.append(terminal
.normal());
109 if ((lastX
!= (x
- 1)) || (lastX
== -1)) {
110 // Advancing at least one cell, or the first gotoXY
111 sb
.append(terminal
.gotoXY(x
, y
));
114 assert (lastAttr
!= null);
116 if ((x
== textEnd
) && (textEnd
< width
- 1)) {
117 assert (lCell
.isBlank());
119 for (int i
= x
; i
< width
; i
++) {
120 assert (logical
[i
][y
].isBlank());
121 // Physical is always updated
122 physical
[i
][y
].reset();
125 // Clear remaining line
126 sb
.append(terminal
.clearRemainingLine());
131 // Now emit only the modified attributes
132 if ((lCell
.getForeColor() != lastAttr
.getForeColor())
133 && (lCell
.getBackColor() != lastAttr
.getBackColor())
134 && (lCell
.isBold() == lastAttr
.isBold())
135 && (lCell
.isReverse() == lastAttr
.isReverse())
136 && (lCell
.isUnderline() == lastAttr
.isUnderline())
137 && (lCell
.isBlink() == lastAttr
.isBlink())
139 // Both colors changed, attributes the same
140 sb
.append(terminal
.color(lCell
.getForeColor(),
141 lCell
.getBackColor()));
144 System
.err
.printf("1 Change only fore/back colors\n");
146 } else if ((lCell
.getForeColor() != lastAttr
.getForeColor())
147 && (lCell
.getBackColor() != lastAttr
.getBackColor())
148 && (lCell
.isBold() != lastAttr
.isBold())
149 && (lCell
.isReverse() != lastAttr
.isReverse())
150 && (lCell
.isUnderline() != lastAttr
.isUnderline())
151 && (lCell
.isBlink() != lastAttr
.isBlink())
153 // Everything is different
154 sb
.append(terminal
.color(lCell
.getForeColor(),
155 lCell
.getBackColor(),
156 lCell
.isBold(), lCell
.isReverse(),
158 lCell
.isUnderline()));
161 System
.err
.printf("2 Set all attributes\n");
163 } else if ((lCell
.getForeColor() != lastAttr
.getForeColor())
164 && (lCell
.getBackColor() == lastAttr
.getBackColor())
165 && (lCell
.isBold() == lastAttr
.isBold())
166 && (lCell
.isReverse() == lastAttr
.isReverse())
167 && (lCell
.isUnderline() == lastAttr
.isUnderline())
168 && (lCell
.isBlink() == lastAttr
.isBlink())
171 // Attributes same, foreColor different
172 sb
.append(terminal
.color(lCell
.getForeColor(), true));
175 System
.err
.printf("3 Change foreColor\n");
177 } else if ((lCell
.getForeColor() == lastAttr
.getForeColor())
178 && (lCell
.getBackColor() != lastAttr
.getBackColor())
179 && (lCell
.isBold() == lastAttr
.isBold())
180 && (lCell
.isReverse() == lastAttr
.isReverse())
181 && (lCell
.isUnderline() == lastAttr
.isUnderline())
182 && (lCell
.isBlink() == lastAttr
.isBlink())
184 // Attributes same, backColor different
185 sb
.append(terminal
.color(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
));