AWT keyboard barely working
[nikiroo-utils.git] / src / jexer / io / AWTScreen.java
1 /**
2 * Jexer - Java Text User Interface
3 *
4 * License: LGPLv3 or later
5 *
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.
9 *
10 * Copyright (C) 2015 Kevin Lamonte
11 *
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.
16 *
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.
21 *
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
26 * 02110-1301 USA
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
30 */
31 package jexer.io;
32
33 import jexer.bits.Cell;
34 import jexer.bits.CellAttributes;
35
36 import java.awt.Color;
37 import java.awt.Cursor;
38 import java.awt.Font;
39 import java.awt.FontMetrics;
40 import java.awt.Frame;
41 import java.awt.Graphics;
42 import java.awt.Insets;
43 import java.awt.geom.Rectangle2D;
44 import java.io.InputStream;
45
46 /**
47 * This Screen implementation draws to a Java AWT Frame.
48 */
49 public final class AWTScreen extends Screen {
50
51 private static Color MYBLACK;
52 private static Color MYRED;
53 private static Color MYGREEN;
54 private static Color MYYELLOW;
55 private static Color MYBLUE;
56 private static Color MYMAGENTA;
57 private static Color MYCYAN;
58 private static Color MYWHITE;
59
60 private static Color MYBOLD_BLACK;
61 private static Color MYBOLD_RED;
62 private static Color MYBOLD_GREEN;
63 private static Color MYBOLD_YELLOW;
64 private static Color MYBOLD_BLUE;
65 private static Color MYBOLD_MAGENTA;
66 private static Color MYBOLD_CYAN;
67 private static Color MYBOLD_WHITE;
68
69 private static boolean dosColors = false;
70
71 /**
72 * Setup AWT colors to match DOS color palette.
73 */
74 private static void setDOSColors() {
75 if (dosColors) {
76 return;
77 }
78 MYBLACK = new Color(0x00, 0x00, 0x00);
79 MYRED = new Color(0xa8, 0x00, 0x00);
80 MYGREEN = new Color(0x00, 0xa8, 0x00);
81 MYYELLOW = new Color(0xa8, 0x54, 0x00);
82 MYBLUE = new Color(0x00, 0x00, 0xa8);
83 MYMAGENTA = new Color(0xa8, 0x00, 0xa8);
84 MYCYAN = new Color(0x00, 0xa8, 0xa8);
85 MYWHITE = new Color(0xa8, 0xa8, 0xa8);
86 MYBOLD_BLACK = new Color(0x54, 0x54, 0x54);
87 MYBOLD_RED = new Color(0xfc, 0x54, 0x54);
88 MYBOLD_GREEN = new Color(0x54, 0xfc, 0x54);
89 MYBOLD_YELLOW = new Color(0xfc, 0xfc, 0x54);
90 MYBOLD_BLUE = new Color(0x54, 0x54, 0xfc);
91 MYBOLD_MAGENTA = new Color(0xfc, 0x54, 0xfc);
92 MYBOLD_CYAN = new Color(0x54, 0xfc, 0xfc);
93 MYBOLD_WHITE = new Color(0xfc, 0xfc, 0xfc);
94
95 dosColors = true;
96 }
97
98 /**
99 * AWTFrame is our top-level hook into the AWT system.
100 */
101 class AWTFrame extends Frame {
102
103 /**
104 * The terminus font resource filename.
105 */
106 private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
107
108 /**
109 * The TUI Screen data.
110 */
111 AWTScreen screen;
112
113 /**
114 * Width of a character cell.
115 */
116 private int textWidth = 1;
117
118 /**
119 * Height of a character cell.
120 */
121 private int textHeight = 1;
122
123 /**
124 * Descent of a character cell.
125 */
126 private int maxDescent = 0;
127
128 /**
129 * Top pixel value.
130 */
131 private int top = 30;
132
133 /**
134 * Left pixel value.
135 */
136 private int left = 30;
137
138 /**
139 * Convert a CellAttributes foreground color to an AWT Color.
140 *
141 * @param attr the text attributes
142 * @return the AWT Color
143 */
144 private Color attrToForegroundColor(final CellAttributes attr) {
145 /*
146 * TODO:
147 * reverse
148 * blink
149 * underline
150 */
151 if (attr.getBold()) {
152 if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
153 return MYBOLD_BLACK;
154 } else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
155 return MYBOLD_RED;
156 } else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
157 return MYBOLD_BLUE;
158 } else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
159 return MYBOLD_GREEN;
160 } else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
161 return MYBOLD_YELLOW;
162 } else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
163 return MYBOLD_CYAN;
164 } else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
165 return MYBOLD_MAGENTA;
166 } else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
167 return MYBOLD_WHITE;
168 }
169 } else {
170 if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
171 return MYBLACK;
172 } else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
173 return MYRED;
174 } else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
175 return MYBLUE;
176 } else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
177 return MYGREEN;
178 } else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
179 return MYYELLOW;
180 } else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
181 return MYCYAN;
182 } else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
183 return MYMAGENTA;
184 } else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
185 return MYWHITE;
186 }
187 }
188 throw new IllegalArgumentException("Invalid color: " + attr.getForeColor().getValue());
189 }
190
191 /**
192 * Convert a CellAttributes background color to an AWT Color.
193 *
194 * @param attr the text attributes
195 * @return the AWT Color
196 */
197 private Color attrToBackgroundColor(final CellAttributes attr) {
198 /*
199 * TODO:
200 * reverse
201 * blink
202 * underline
203 */
204 if (attr.getBackColor().equals(jexer.bits.Color.BLACK)) {
205 return MYBLACK;
206 } else if (attr.getBackColor().equals(jexer.bits.Color.RED)) {
207 return MYRED;
208 } else if (attr.getBackColor().equals(jexer.bits.Color.BLUE)) {
209 return MYBLUE;
210 } else if (attr.getBackColor().equals(jexer.bits.Color.GREEN)) {
211 return MYGREEN;
212 } else if (attr.getBackColor().equals(jexer.bits.Color.YELLOW)) {
213 return MYYELLOW;
214 } else if (attr.getBackColor().equals(jexer.bits.Color.CYAN)) {
215 return MYCYAN;
216 } else if (attr.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
217 return MYMAGENTA;
218 } else if (attr.getBackColor().equals(jexer.bits.Color.WHITE)) {
219 return MYWHITE;
220 }
221 throw new IllegalArgumentException("Invalid color: " + attr.getBackColor().getValue());
222 }
223
224 /**
225 * Public constructor.
226 *
227 * @param screen the Screen that Backend talks to
228 */
229 public AWTFrame(final AWTScreen screen) {
230 this.screen = screen;
231 setDOSColors();
232
233 setTitle("Jexer Application");
234 setBackground(java.awt.Color.black);
235 setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
236 // setFont(new Font("Liberation Mono", Font.BOLD, 16));
237 // setFont(new Font(Font.MONOSPACED, Font.PLAIN, 16));
238
239 try {
240 ClassLoader loader = Thread.currentThread().getContextClassLoader();
241 InputStream in = loader.getResourceAsStream(FONTFILE);
242 Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
243 Font terminus = terminusRoot.deriveFont(Font.PLAIN, 22);
244 setFont(terminus);
245 } catch (Exception e) {
246 e.printStackTrace();
247 // setFont(new Font("Liberation Mono", Font.PLAIN, 24));
248 setFont(new Font(Font.MONOSPACED, Font.PLAIN, 24));
249 }
250 setVisible(true);
251 resizeToScreen();
252 }
253
254 /**
255 * Resize to font dimensions.
256 */
257 public void resizeToScreen() {
258 Graphics gr = getGraphics();
259 FontMetrics fm = gr.getFontMetrics();
260 maxDescent = fm.getMaxDescent();
261 Rectangle2D bounds = fm.getMaxCharBounds(gr);
262 int leading = fm.getLeading();
263 textWidth = (int)Math.round(bounds.getWidth());
264 textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
265 // This also produces the same number, but works better for ugly
266 // monospace.
267 textHeight = fm.getMaxAscent() + maxDescent - leading;
268
269 // Figure out the thickness of borders and use that to set the
270 // final size.
271 Insets insets = getInsets();
272 left = insets.left;
273 top = insets.top;
274
275 setSize(textWidth * screen.width + insets.left + insets.right,
276 textHeight * screen.height + insets.top + insets.bottom);
277
278 /*
279 System.err.printf("W: %d H: %d MD: %d L: %d\n", textWidth,
280 textHeight, maxDescent, leading);
281 */
282 }
283
284 /**
285 * Paint redraws the whole screen.
286 *
287 * @param gr the AWT Graphics context
288 */
289 @Override
290 public void paint(final Graphics gr) {
291
292 for (int y = 0; y < screen.height; y++) {
293 for (int x = 0; x < screen.width; x++) {
294 Cell lCell = screen.logical[x][y];
295 Cell pCell = screen.physical[x][y];
296
297 int xPixel = x * textWidth + left;
298 int yPixel = y * textHeight + top;
299
300 if (!lCell.equals(pCell)) {
301
302 // Draw the background rectangle, then the foreground
303 // character.
304 gr.setColor(attrToBackgroundColor(lCell));
305 gr.fillRect(xPixel, yPixel, textWidth, textHeight);
306 gr.setColor(attrToForegroundColor(lCell));
307 char [] chars = new char[1];
308 chars[0] = lCell.getChar();
309 gr.drawChars(chars, 0, 1, xPixel,
310 yPixel + textHeight - maxDescent);
311
312 // Physical is always updated
313 physical[x][y].setTo(lCell);
314 }
315 }
316 }
317 }
318 }
319
320 /**
321 * The raw AWT Frame. Note package private access.
322 */
323 AWTFrame frame;
324
325 /**
326 * Public constructor.
327 */
328 public AWTScreen() {
329 frame = new AWTFrame(this);
330 }
331
332 /**
333 * Push the logical screen to the physical device.
334 */
335 @Override
336 public void flushPhysical() {
337 Graphics gr = frame.getGraphics();
338 frame.paint(gr);
339 }
340 }