blob: 6d0c1b3b6cc9cc4ef9f0fdeea866302e24b9bf02 [file] [log] [blame]
bellarde7f0ad52004-07-14 17:28:59 +00001/*
2 * QEMU graphical console
ths5fafdf22007-09-16 21:08:06 +00003 *
bellarde7f0ad52004-07-14 17:28:59 +00004 * Copyright (c) 2004 Fabrice Bellard
ths5fafdf22007-09-16 21:08:06 +00005 *
bellarde7f0ad52004-07-14 17:28:59 +00006 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
pbrook87ecb682007-11-17 17:14:51 +000024#include "qemu-common.h"
25#include "console.h"
26#include "qemu-timer.h"
bellarde7f0ad52004-07-14 17:28:59 +000027
pbrook6d6f7c22006-03-11 15:35:30 +000028//#define DEBUG_CONSOLE
bellarde7f0ad52004-07-14 17:28:59 +000029#define DEFAULT_BACKSCROLL 512
30#define MAX_CONSOLES 12
pbrookc60e08d2008-07-01 16:24:38 +000031#define DEFAULT_MONITOR_SIZE "800x600"
bellarde7f0ad52004-07-14 17:28:59 +000032
bellard26489842006-06-25 17:37:36 +000033#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
34#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
bellarde7f0ad52004-07-14 17:28:59 +000035
pbrook6d6f7c22006-03-11 15:35:30 +000036typedef struct TextAttributes {
37 uint8_t fgcol:4;
38 uint8_t bgcol:4;
39 uint8_t bold:1;
40 uint8_t uline:1;
41 uint8_t blink:1;
42 uint8_t invers:1;
43 uint8_t unvisible:1;
44} TextAttributes;
45
bellarde7f0ad52004-07-14 17:28:59 +000046typedef struct TextCell {
47 uint8_t ch;
pbrook6d6f7c22006-03-11 15:35:30 +000048 TextAttributes t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +000049} TextCell;
50
51#define MAX_ESC_PARAMS 3
52
53enum TTYState {
54 TTY_STATE_NORM,
55 TTY_STATE_ESC,
56 TTY_STATE_CSI,
57};
58
bellarde15d7372006-06-25 16:26:29 +000059typedef struct QEMUFIFO {
60 uint8_t *buf;
61 int buf_size;
62 int count, wptr, rptr;
63} QEMUFIFO;
64
pbrook9596ebb2007-11-18 01:44:38 +000065static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000066{
67 int l, len;
68
69 l = f->buf_size - f->count;
70 if (len1 > l)
71 len1 = l;
72 len = len1;
73 while (len > 0) {
74 l = f->buf_size - f->wptr;
75 if (l > len)
76 l = len;
77 memcpy(f->buf + f->wptr, buf, l);
78 f->wptr += l;
79 if (f->wptr >= f->buf_size)
80 f->wptr = 0;
81 buf += l;
82 len -= l;
83 }
84 f->count += len1;
85 return len1;
86}
87
pbrook9596ebb2007-11-18 01:44:38 +000088static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
bellarde15d7372006-06-25 16:26:29 +000089{
90 int l, len;
91
92 if (len1 > f->count)
93 len1 = f->count;
94 len = len1;
95 while (len > 0) {
96 l = f->buf_size - f->rptr;
97 if (l > len)
98 l = len;
99 memcpy(buf, f->buf + f->rptr, l);
100 f->rptr += l;
101 if (f->rptr >= f->buf_size)
102 f->rptr = 0;
103 buf += l;
104 len -= l;
105 }
106 f->count -= len1;
107 return len1;
108}
109
thsaf3a9032007-07-11 23:14:59 +0000110typedef enum {
111 GRAPHIC_CONSOLE,
balrogc21bbcf2008-09-24 03:32:33 +0000112 TEXT_CONSOLE,
113 TEXT_CONSOLE_FIXED_SIZE
thsaf3a9032007-07-11 23:14:59 +0000114} console_type_t;
115
pbrook95219892006-04-09 01:06:34 +0000116/* ??? This is mis-named.
117 It is used for both text and graphical consoles. */
bellarde7f0ad52004-07-14 17:28:59 +0000118struct TextConsole {
thsaf3a9032007-07-11 23:14:59 +0000119 console_type_t console_type;
bellarde7f0ad52004-07-14 17:28:59 +0000120 DisplayState *ds;
pbrook95219892006-04-09 01:06:34 +0000121 /* Graphic console state. */
122 vga_hw_update_ptr hw_update;
123 vga_hw_invalidate_ptr hw_invalidate;
124 vga_hw_screen_dump_ptr hw_screen_dump;
balrog4d3b6f62008-02-10 16:33:14 +0000125 vga_hw_text_update_ptr hw_text_update;
pbrook95219892006-04-09 01:06:34 +0000126 void *hw;
127
bellarde7f0ad52004-07-14 17:28:59 +0000128 int g_width, g_height;
129 int width;
130 int height;
131 int total_height;
132 int backscroll_height;
bellarde7f0ad52004-07-14 17:28:59 +0000133 int x, y;
thsadb47962007-01-16 23:02:36 +0000134 int x_saved, y_saved;
bellarde7f0ad52004-07-14 17:28:59 +0000135 int y_displayed;
136 int y_base;
pbrook6d6f7c22006-03-11 15:35:30 +0000137 TextAttributes t_attrib_default; /* default text attributes */
138 TextAttributes t_attrib; /* currently active text attributes */
bellarde7f0ad52004-07-14 17:28:59 +0000139 TextCell *cells;
balrog4d3b6f62008-02-10 16:33:14 +0000140 int text_x[2], text_y[2], cursor_invalidate;
bellarde7f0ad52004-07-14 17:28:59 +0000141
pbrook14778c22009-01-21 03:02:52 +0000142 int update_x0;
143 int update_y0;
144 int update_x1;
145 int update_y1;
146
bellarde7f0ad52004-07-14 17:28:59 +0000147 enum TTYState state;
148 int esc_params[MAX_ESC_PARAMS];
149 int nb_esc_params;
150
pbrooke5b0bc42007-01-27 23:46:43 +0000151 CharDriverState *chr;
bellarde15d7372006-06-25 16:26:29 +0000152 /* fifo for key pressed */
153 QEMUFIFO out_fifo;
154 uint8_t out_fifo_buf[16];
155 QEMUTimer *kbd_timer;
bellarde7f0ad52004-07-14 17:28:59 +0000156};
157
158static TextConsole *active_console;
159static TextConsole *consoles[MAX_CONSOLES];
160static int nb_consoles = 0;
161
pbrook95219892006-04-09 01:06:34 +0000162void vga_hw_update(void)
163{
thsadb47962007-01-16 23:02:36 +0000164 if (active_console && active_console->hw_update)
pbrook95219892006-04-09 01:06:34 +0000165 active_console->hw_update(active_console->hw);
166}
167
168void vga_hw_invalidate(void)
169{
170 if (active_console->hw_invalidate)
171 active_console->hw_invalidate(active_console->hw);
172}
173
174void vga_hw_screen_dump(const char *filename)
175{
balrog8571c052008-07-19 13:04:26 +0000176 TextConsole *previous_active_console;
177
178 previous_active_console = active_console;
179 active_console = consoles[0];
180 /* There is currently no way of specifying which screen we want to dump,
aurel327b455222008-09-02 00:09:16 +0000181 so always dump the first one. */
pbrook95219892006-04-09 01:06:34 +0000182 if (consoles[0]->hw_screen_dump)
183 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
balrog8571c052008-07-19 13:04:26 +0000184 active_console = previous_active_console;
pbrook95219892006-04-09 01:06:34 +0000185}
186
balrog4d3b6f62008-02-10 16:33:14 +0000187void vga_hw_text_update(console_ch_t *chardata)
188{
189 if (active_console && active_console->hw_text_update)
190 active_console->hw_text_update(active_console->hw, chardata);
191}
192
bellarde7f0ad52004-07-14 17:28:59 +0000193/* convert a RGBA color to a color index usable in graphic primitives */
194static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
195{
196 unsigned int r, g, b, color;
197
aliguori0e1f5a02008-11-24 19:29:13 +0000198 switch(ds_get_bits_per_pixel(ds)) {
bellarde7f0ad52004-07-14 17:28:59 +0000199#if 0
200 case 8:
201 r = (rgba >> 16) & 0xff;
202 g = (rgba >> 8) & 0xff;
203 b = (rgba) & 0xff;
ths5fafdf22007-09-16 21:08:06 +0000204 color = (rgb_to_index[r] * 6 * 6) +
205 (rgb_to_index[g] * 6) +
bellarde7f0ad52004-07-14 17:28:59 +0000206 (rgb_to_index[b]);
207 break;
208#endif
209 case 15:
210 r = (rgba >> 16) & 0xff;
211 g = (rgba >> 8) & 0xff;
212 b = (rgba) & 0xff;
213 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
214 break;
215 case 16:
216 r = (rgba >> 16) & 0xff;
217 g = (rgba >> 8) & 0xff;
218 b = (rgba) & 0xff;
219 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
220 break;
221 case 32:
222 default:
223 color = rgba;
224 break;
225 }
226 return color;
227}
228
ths5fafdf22007-09-16 21:08:06 +0000229static void vga_fill_rect (DisplayState *ds,
bellarde7f0ad52004-07-14 17:28:59 +0000230 int posx, int posy, int width, int height, uint32_t color)
231{
232 uint8_t *d, *d1;
233 int x, y, bpp;
ths3b46e622007-09-17 08:09:54 +0000234
aliguori0e1f5a02008-11-24 19:29:13 +0000235 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
236 d1 = ds_get_data(ds) +
237 ds_get_linesize(ds) * posy + bpp * posx;
bellarde7f0ad52004-07-14 17:28:59 +0000238 for (y = 0; y < height; y++) {
239 d = d1;
240 switch(bpp) {
241 case 1:
242 for (x = 0; x < width; x++) {
243 *((uint8_t *)d) = color;
244 d++;
245 }
246 break;
247 case 2:
248 for (x = 0; x < width; x++) {
249 *((uint16_t *)d) = color;
250 d += 2;
251 }
252 break;
253 case 4:
254 for (x = 0; x < width; x++) {
255 *((uint32_t *)d) = color;
256 d += 4;
257 }
258 break;
259 }
aliguori0e1f5a02008-11-24 19:29:13 +0000260 d1 += ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000261 }
262}
263
264/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
265static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
266{
267 const uint8_t *s;
268 uint8_t *d;
269 int wb, y, bpp;
270
aliguori0e1f5a02008-11-24 19:29:13 +0000271 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
bellarde7f0ad52004-07-14 17:28:59 +0000272 wb = w * bpp;
273 if (yd <= ys) {
aliguori0e1f5a02008-11-24 19:29:13 +0000274 s = ds_get_data(ds) +
275 ds_get_linesize(ds) * ys + bpp * xs;
276 d = ds_get_data(ds) +
277 ds_get_linesize(ds) * yd + bpp * xd;
bellarde7f0ad52004-07-14 17:28:59 +0000278 for (y = 0; y < h; y++) {
279 memmove(d, s, wb);
aliguori0e1f5a02008-11-24 19:29:13 +0000280 d += ds_get_linesize(ds);
281 s += ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000282 }
283 } else {
aliguori0e1f5a02008-11-24 19:29:13 +0000284 s = ds_get_data(ds) +
285 ds_get_linesize(ds) * (ys + h - 1) + bpp * xs;
286 d = ds_get_data(ds) +
287 ds_get_linesize(ds) * (yd + h - 1) + bpp * xd;
bellarde7f0ad52004-07-14 17:28:59 +0000288 for (y = 0; y < h; y++) {
289 memmove(d, s, wb);
aliguori0e1f5a02008-11-24 19:29:13 +0000290 d -= ds_get_linesize(ds);
291 s -= ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000292 }
293 }
294}
295
296/***********************************************************/
297/* basic char display */
298
299#define FONT_HEIGHT 16
300#define FONT_WIDTH 8
301
302#include "vgafont.h"
303
304#define cbswap_32(__x) \
305((uint32_t)( \
306 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
307 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
308 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
309 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
310
311#ifdef WORDS_BIGENDIAN
312#define PAT(x) x
313#else
314#define PAT(x) cbswap_32(x)
315#endif
316
317static const uint32_t dmask16[16] = {
318 PAT(0x00000000),
319 PAT(0x000000ff),
320 PAT(0x0000ff00),
321 PAT(0x0000ffff),
322 PAT(0x00ff0000),
323 PAT(0x00ff00ff),
324 PAT(0x00ffff00),
325 PAT(0x00ffffff),
326 PAT(0xff000000),
327 PAT(0xff0000ff),
328 PAT(0xff00ff00),
329 PAT(0xff00ffff),
330 PAT(0xffff0000),
331 PAT(0xffff00ff),
332 PAT(0xffffff00),
333 PAT(0xffffffff),
334};
335
336static const uint32_t dmask4[4] = {
337 PAT(0x00000000),
338 PAT(0x0000ffff),
339 PAT(0xffff0000),
340 PAT(0xffffffff),
341};
342
pbrook6d6f7c22006-03-11 15:35:30 +0000343static uint32_t color_table[2][8];
bellarde7f0ad52004-07-14 17:28:59 +0000344
pbrook6d6f7c22006-03-11 15:35:30 +0000345enum color_names {
346 COLOR_BLACK = 0,
347 COLOR_RED = 1,
348 COLOR_GREEN = 2,
349 COLOR_YELLOW = 3,
350 COLOR_BLUE = 4,
351 COLOR_MAGENTA = 5,
352 COLOR_CYAN = 6,
353 COLOR_WHITE = 7
354};
355
356static const uint32_t color_table_rgb[2][8] = {
357 { /* dark */
bellard26489842006-06-25 17:37:36 +0000358 QEMU_RGB(0x00, 0x00, 0x00), /* black */
359 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
360 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
361 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
362 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
363 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
364 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
365 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000366 },
367 { /* bright */
bellard26489842006-06-25 17:37:36 +0000368 QEMU_RGB(0x00, 0x00, 0x00), /* black */
369 QEMU_RGB(0xff, 0x00, 0x00), /* red */
370 QEMU_RGB(0x00, 0xff, 0x00), /* green */
371 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
372 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
373 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
374 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
375 QEMU_RGB(0xff, 0xff, 0xff), /* white */
pbrook6d6f7c22006-03-11 15:35:30 +0000376 }
bellarde7f0ad52004-07-14 17:28:59 +0000377};
378
379static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
380{
aliguori0e1f5a02008-11-24 19:29:13 +0000381 switch(ds_get_bits_per_pixel(ds)) {
bellarde7f0ad52004-07-14 17:28:59 +0000382 case 8:
383 col |= col << 8;
384 col |= col << 16;
385 break;
386 case 15:
387 case 16:
388 col |= col << 16;
389 break;
390 default:
391 break;
392 }
393
394 return col;
395}
pbrook6d6f7c22006-03-11 15:35:30 +0000396#ifdef DEBUG_CONSOLE
397static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
398{
399 if (t_attrib->bold) {
400 printf("b");
401 } else {
402 printf(" ");
403 }
404 if (t_attrib->uline) {
405 printf("u");
406 } else {
407 printf(" ");
408 }
409 if (t_attrib->blink) {
410 printf("l");
411 } else {
412 printf(" ");
413 }
414 if (t_attrib->invers) {
415 printf("i");
416 } else {
417 printf(" ");
418 }
419 if (t_attrib->unvisible) {
420 printf("n");
421 } else {
422 printf(" ");
423 }
424
425 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
426}
427#endif
bellarde7f0ad52004-07-14 17:28:59 +0000428
ths5fafdf22007-09-16 21:08:06 +0000429static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000430 TextAttributes *t_attrib)
bellarde7f0ad52004-07-14 17:28:59 +0000431{
432 uint8_t *d;
433 const uint8_t *font_ptr;
434 unsigned int font_data, linesize, xorcol, bpp;
435 int i;
pbrook6d6f7c22006-03-11 15:35:30 +0000436 unsigned int fgcol, bgcol;
437
438#ifdef DEBUG_CONSOLE
439 printf("x: %2i y: %2i", x, y);
440 console_print_text_attributes(t_attrib, ch);
441#endif
442
443 if (t_attrib->invers) {
444 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
445 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
446 } else {
447 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
448 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
449 }
bellarde7f0ad52004-07-14 17:28:59 +0000450
aliguori0e1f5a02008-11-24 19:29:13 +0000451 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
452 d = ds_get_data(ds) +
453 ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
454 linesize = ds_get_linesize(ds);
bellarde7f0ad52004-07-14 17:28:59 +0000455 font_ptr = vgafont16 + FONT_HEIGHT * ch;
456 xorcol = bgcol ^ fgcol;
aliguori0e1f5a02008-11-24 19:29:13 +0000457 switch(ds_get_bits_per_pixel(ds)) {
bellarde7f0ad52004-07-14 17:28:59 +0000458 case 8:
459 for(i = 0; i < FONT_HEIGHT; i++) {
460 font_data = *font_ptr++;
pbrook6d6f7c22006-03-11 15:35:30 +0000461 if (t_attrib->uline
462 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
463 font_data = 0xFFFF;
464 }
bellarde7f0ad52004-07-14 17:28:59 +0000465 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
466 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
467 d += linesize;
468 }
469 break;
470 case 16:
471 case 15:
472 for(i = 0; i < FONT_HEIGHT; i++) {
473 font_data = *font_ptr++;
pbrook6d6f7c22006-03-11 15:35:30 +0000474 if (t_attrib->uline
475 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
476 font_data = 0xFFFF;
477 }
bellarde7f0ad52004-07-14 17:28:59 +0000478 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
479 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
480 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
481 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
482 d += linesize;
483 }
484 break;
485 case 32:
486 for(i = 0; i < FONT_HEIGHT; i++) {
487 font_data = *font_ptr++;
pbrook6d6f7c22006-03-11 15:35:30 +0000488 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
489 font_data = 0xFFFF;
490 }
bellarde7f0ad52004-07-14 17:28:59 +0000491 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
492 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
493 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
494 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
495 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
496 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
497 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
498 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
499 d += linesize;
500 }
501 break;
502 }
503}
504
505static void text_console_resize(TextConsole *s)
506{
507 TextCell *cells, *c, *c1;
508 int w1, x, y, last_width;
509
510 last_width = s->width;
511 s->width = s->g_width / FONT_WIDTH;
512 s->height = s->g_height / FONT_HEIGHT;
513
514 w1 = last_width;
515 if (s->width < w1)
516 w1 = s->width;
517
518 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
519 for(y = 0; y < s->total_height; y++) {
520 c = &cells[y * s->width];
521 if (w1 > 0) {
522 c1 = &s->cells[y * last_width];
523 for(x = 0; x < w1; x++) {
524 *c++ = *c1++;
525 }
526 }
527 for(x = w1; x < s->width; x++) {
528 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000529 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000530 c++;
531 }
532 }
balroga528b802007-10-30 22:38:53 +0000533 qemu_free(s->cells);
bellarde7f0ad52004-07-14 17:28:59 +0000534 s->cells = cells;
535}
536
balrog4d3b6f62008-02-10 16:33:14 +0000537static inline void text_update_xy(TextConsole *s, int x, int y)
538{
539 s->text_x[0] = MIN(s->text_x[0], x);
540 s->text_x[1] = MAX(s->text_x[1], x);
541 s->text_y[0] = MIN(s->text_y[0], y);
542 s->text_y[1] = MAX(s->text_y[1], y);
543}
544
pbrook14778c22009-01-21 03:02:52 +0000545static void invalidate_xy(TextConsole *s, int x, int y)
546{
547 if (s->update_x0 > x * FONT_WIDTH)
548 s->update_x0 = x * FONT_WIDTH;
549 if (s->update_y0 > y * FONT_HEIGHT)
550 s->update_y0 = y * FONT_HEIGHT;
551 if (s->update_x1 < (x + 1) * FONT_WIDTH)
552 s->update_x1 = (x + 1) * FONT_WIDTH;
553 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
554 s->update_y1 = (y + 1) * FONT_HEIGHT;
555}
556
bellarde7f0ad52004-07-14 17:28:59 +0000557static void update_xy(TextConsole *s, int x, int y)
558{
559 TextCell *c;
560 int y1, y2;
561
562 if (s == active_console) {
aliguori0e1f5a02008-11-24 19:29:13 +0000563 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000564 text_update_xy(s, x, y);
565 return;
566 }
567
bellarde7f0ad52004-07-14 17:28:59 +0000568 y1 = (s->y_base + y) % s->total_height;
569 y2 = y1 - s->y_displayed;
570 if (y2 < 0)
571 y2 += s->total_height;
572 if (y2 < s->height) {
573 c = &s->cells[y1 * s->width + x];
ths5fafdf22007-09-16 21:08:06 +0000574 vga_putcharxy(s->ds, x, y2, c->ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000575 &(c->t_attrib));
pbrook14778c22009-01-21 03:02:52 +0000576 invalidate_xy(s, x, y2);
bellarde7f0ad52004-07-14 17:28:59 +0000577 }
578 }
579}
580
581static void console_show_cursor(TextConsole *s, int show)
582{
583 TextCell *c;
584 int y, y1;
585
586 if (s == active_console) {
thsed8276a2007-02-10 22:37:56 +0000587 int x = s->x;
balrog4d3b6f62008-02-10 16:33:14 +0000588
aliguori0e1f5a02008-11-24 19:29:13 +0000589 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000590 s->cursor_invalidate = 1;
591 return;
592 }
593
thsed8276a2007-02-10 22:37:56 +0000594 if (x >= s->width) {
595 x = s->width - 1;
596 }
bellarde7f0ad52004-07-14 17:28:59 +0000597 y1 = (s->y_base + s->y) % s->total_height;
598 y = y1 - s->y_displayed;
599 if (y < 0)
600 y += s->total_height;
601 if (y < s->height) {
thsed8276a2007-02-10 22:37:56 +0000602 c = &s->cells[y1 * s->width + x];
bellarde7f0ad52004-07-14 17:28:59 +0000603 if (show) {
pbrook6d6f7c22006-03-11 15:35:30 +0000604 TextAttributes t_attrib = s->t_attrib_default;
605 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
thsed8276a2007-02-10 22:37:56 +0000606 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
bellarde7f0ad52004-07-14 17:28:59 +0000607 } else {
thsed8276a2007-02-10 22:37:56 +0000608 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
bellarde7f0ad52004-07-14 17:28:59 +0000609 }
pbrook14778c22009-01-21 03:02:52 +0000610 invalidate_xy(s, x, y);
bellarde7f0ad52004-07-14 17:28:59 +0000611 }
612 }
613}
614
615static void console_refresh(TextConsole *s)
616{
617 TextCell *c;
618 int x, y, y1;
619
ths5fafdf22007-09-16 21:08:06 +0000620 if (s != active_console)
bellarde7f0ad52004-07-14 17:28:59 +0000621 return;
aliguori0e1f5a02008-11-24 19:29:13 +0000622 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000623 s->text_x[0] = 0;
624 s->text_y[0] = 0;
625 s->text_x[1] = s->width - 1;
626 s->text_y[1] = s->height - 1;
627 s->cursor_invalidate = 1;
628 return;
629 }
bellarde7f0ad52004-07-14 17:28:59 +0000630
aliguori0e1f5a02008-11-24 19:29:13 +0000631 vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
pbrook6d6f7c22006-03-11 15:35:30 +0000632 color_table[0][COLOR_BLACK]);
bellarde7f0ad52004-07-14 17:28:59 +0000633 y1 = s->y_displayed;
634 for(y = 0; y < s->height; y++) {
635 c = s->cells + y1 * s->width;
636 for(x = 0; x < s->width; x++) {
ths5fafdf22007-09-16 21:08:06 +0000637 vga_putcharxy(s->ds, x, y, c->ch,
pbrook6d6f7c22006-03-11 15:35:30 +0000638 &(c->t_attrib));
bellarde7f0ad52004-07-14 17:28:59 +0000639 c++;
640 }
641 if (++y1 == s->total_height)
642 y1 = 0;
643 }
bellarde7f0ad52004-07-14 17:28:59 +0000644 console_show_cursor(s, 1);
pbrook14778c22009-01-21 03:02:52 +0000645 dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
bellarde7f0ad52004-07-14 17:28:59 +0000646}
647
648static void console_scroll(int ydelta)
649{
650 TextConsole *s;
651 int i, y1;
ths3b46e622007-09-17 08:09:54 +0000652
bellarde7f0ad52004-07-14 17:28:59 +0000653 s = active_console;
thsaf3a9032007-07-11 23:14:59 +0000654 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +0000655 return;
656
657 if (ydelta > 0) {
658 for(i = 0; i < ydelta; i++) {
659 if (s->y_displayed == s->y_base)
660 break;
661 if (++s->y_displayed == s->total_height)
662 s->y_displayed = 0;
663 }
664 } else {
665 ydelta = -ydelta;
666 i = s->backscroll_height;
667 if (i > s->total_height - s->height)
668 i = s->total_height - s->height;
669 y1 = s->y_base - i;
670 if (y1 < 0)
671 y1 += s->total_height;
672 for(i = 0; i < ydelta; i++) {
673 if (s->y_displayed == y1)
674 break;
675 if (--s->y_displayed < 0)
676 s->y_displayed = s->total_height - 1;
677 }
678 }
679 console_refresh(s);
680}
681
682static void console_put_lf(TextConsole *s)
683{
684 TextCell *c;
685 int x, y1;
686
bellarde7f0ad52004-07-14 17:28:59 +0000687 s->y++;
688 if (s->y >= s->height) {
689 s->y = s->height - 1;
pbrook6d6f7c22006-03-11 15:35:30 +0000690
bellarde7f0ad52004-07-14 17:28:59 +0000691 if (s->y_displayed == s->y_base) {
692 if (++s->y_displayed == s->total_height)
693 s->y_displayed = 0;
694 }
695 if (++s->y_base == s->total_height)
696 s->y_base = 0;
697 if (s->backscroll_height < s->total_height)
698 s->backscroll_height++;
699 y1 = (s->y_base + s->height - 1) % s->total_height;
700 c = &s->cells[y1 * s->width];
701 for(x = 0; x < s->width; x++) {
702 c->ch = ' ';
pbrook6d6f7c22006-03-11 15:35:30 +0000703 c->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +0000704 c++;
705 }
706 if (s == active_console && s->y_displayed == s->y_base) {
aliguori0e1f5a02008-11-24 19:29:13 +0000707 if (!ds_get_bits_per_pixel(s->ds)) {
balrog4d3b6f62008-02-10 16:33:14 +0000708 s->text_x[0] = 0;
709 s->text_y[0] = 0;
710 s->text_x[1] = s->width - 1;
711 s->text_y[1] = s->height - 1;
712 return;
713 }
714
ths5fafdf22007-09-16 21:08:06 +0000715 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
716 s->width * FONT_WIDTH,
bellarde7f0ad52004-07-14 17:28:59 +0000717 (s->height - 1) * FONT_HEIGHT);
718 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
ths5fafdf22007-09-16 21:08:06 +0000719 s->width * FONT_WIDTH, FONT_HEIGHT,
pbrook6d6f7c22006-03-11 15:35:30 +0000720 color_table[0][s->t_attrib_default.bgcol]);
pbrook14778c22009-01-21 03:02:52 +0000721 s->update_x0 = 0;
722 s->update_y0 = 0;
723 s->update_x1 = s->width * FONT_WIDTH;
724 s->update_y1 = s->height * FONT_HEIGHT;
bellarde7f0ad52004-07-14 17:28:59 +0000725 }
726 }
727}
728
pbrook6d6f7c22006-03-11 15:35:30 +0000729/* Set console attributes depending on the current escape codes.
730 * NOTE: I know this code is not very efficient (checking every color for it
731 * self) but it is more readable and better maintainable.
732 */
733static void console_handle_escape(TextConsole *s)
734{
735 int i;
736
pbrook6d6f7c22006-03-11 15:35:30 +0000737 for (i=0; i<s->nb_esc_params; i++) {
738 switch (s->esc_params[i]) {
739 case 0: /* reset all console attributes to default */
740 s->t_attrib = s->t_attrib_default;
741 break;
742 case 1:
743 s->t_attrib.bold = 1;
744 break;
745 case 4:
746 s->t_attrib.uline = 1;
747 break;
748 case 5:
749 s->t_attrib.blink = 1;
750 break;
751 case 7:
752 s->t_attrib.invers = 1;
753 break;
754 case 8:
755 s->t_attrib.unvisible = 1;
756 break;
757 case 22:
758 s->t_attrib.bold = 0;
759 break;
760 case 24:
761 s->t_attrib.uline = 0;
762 break;
763 case 25:
764 s->t_attrib.blink = 0;
765 break;
766 case 27:
767 s->t_attrib.invers = 0;
768 break;
769 case 28:
770 s->t_attrib.unvisible = 0;
771 break;
772 /* set foreground color */
773 case 30:
774 s->t_attrib.fgcol=COLOR_BLACK;
775 break;
776 case 31:
777 s->t_attrib.fgcol=COLOR_RED;
778 break;
779 case 32:
780 s->t_attrib.fgcol=COLOR_GREEN;
781 break;
782 case 33:
783 s->t_attrib.fgcol=COLOR_YELLOW;
784 break;
785 case 34:
786 s->t_attrib.fgcol=COLOR_BLUE;
787 break;
788 case 35:
789 s->t_attrib.fgcol=COLOR_MAGENTA;
790 break;
791 case 36:
792 s->t_attrib.fgcol=COLOR_CYAN;
793 break;
794 case 37:
795 s->t_attrib.fgcol=COLOR_WHITE;
796 break;
797 /* set background color */
798 case 40:
799 s->t_attrib.bgcol=COLOR_BLACK;
800 break;
801 case 41:
802 s->t_attrib.bgcol=COLOR_RED;
803 break;
804 case 42:
805 s->t_attrib.bgcol=COLOR_GREEN;
806 break;
807 case 43:
808 s->t_attrib.bgcol=COLOR_YELLOW;
809 break;
810 case 44:
811 s->t_attrib.bgcol=COLOR_BLUE;
812 break;
813 case 45:
814 s->t_attrib.bgcol=COLOR_MAGENTA;
815 break;
816 case 46:
817 s->t_attrib.bgcol=COLOR_CYAN;
818 break;
819 case 47:
820 s->t_attrib.bgcol=COLOR_WHITE;
821 break;
822 }
823 }
824}
825
thsadb47962007-01-16 23:02:36 +0000826static void console_clear_xy(TextConsole *s, int x, int y)
827{
828 int y1 = (s->y_base + y) % s->total_height;
829 TextCell *c = &s->cells[y1 * s->width + x];
830 c->ch = ' ';
831 c->t_attrib = s->t_attrib_default;
832 c++;
833 update_xy(s, x, y);
834}
835
bellarde7f0ad52004-07-14 17:28:59 +0000836static void console_putchar(TextConsole *s, int ch)
837{
838 TextCell *c;
thsadb47962007-01-16 23:02:36 +0000839 int y1, i;
840 int x, y;
bellarde7f0ad52004-07-14 17:28:59 +0000841
842 switch(s->state) {
843 case TTY_STATE_NORM:
844 switch(ch) {
pbrook6d6f7c22006-03-11 15:35:30 +0000845 case '\r': /* carriage return */
bellarde7f0ad52004-07-14 17:28:59 +0000846 s->x = 0;
847 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000848 case '\n': /* newline */
bellarde7f0ad52004-07-14 17:28:59 +0000849 console_put_lf(s);
850 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000851 case '\b': /* backspace */
ths5fafdf22007-09-16 21:08:06 +0000852 if (s->x > 0)
bellarde15d7372006-06-25 16:26:29 +0000853 s->x--;
pbrook6d6f7c22006-03-11 15:35:30 +0000854 break;
855 case '\t': /* tabspace */
856 if (s->x + (8 - (s->x % 8)) > s->width) {
bellardbd468842006-07-14 20:24:31 +0000857 s->x = 0;
pbrook6d6f7c22006-03-11 15:35:30 +0000858 console_put_lf(s);
859 } else {
860 s->x = s->x + (8 - (s->x % 8));
861 }
862 break;
863 case '\a': /* alert aka. bell */
864 /* TODO: has to be implemented */
865 break;
thsadb47962007-01-16 23:02:36 +0000866 case 14:
867 /* SI (shift in), character set 0 (ignored) */
868 break;
869 case 15:
870 /* SO (shift out), character set 1 (ignored) */
871 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000872 case 27: /* esc (introducing an escape sequence) */
bellarde7f0ad52004-07-14 17:28:59 +0000873 s->state = TTY_STATE_ESC;
874 break;
875 default:
thsed8276a2007-02-10 22:37:56 +0000876 if (s->x >= s->width) {
877 /* line wrap */
878 s->x = 0;
879 console_put_lf(s);
thsadb47962007-01-16 23:02:36 +0000880 }
bellarde7f0ad52004-07-14 17:28:59 +0000881 y1 = (s->y_base + s->y) % s->total_height;
882 c = &s->cells[y1 * s->width + s->x];
883 c->ch = ch;
pbrook6d6f7c22006-03-11 15:35:30 +0000884 c->t_attrib = s->t_attrib;
bellarde7f0ad52004-07-14 17:28:59 +0000885 update_xy(s, s->x, s->y);
886 s->x++;
bellarde7f0ad52004-07-14 17:28:59 +0000887 break;
888 }
889 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000890 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
bellarde7f0ad52004-07-14 17:28:59 +0000891 if (ch == '[') {
892 for(i=0;i<MAX_ESC_PARAMS;i++)
893 s->esc_params[i] = 0;
894 s->nb_esc_params = 0;
895 s->state = TTY_STATE_CSI;
896 } else {
897 s->state = TTY_STATE_NORM;
898 }
899 break;
pbrook6d6f7c22006-03-11 15:35:30 +0000900 case TTY_STATE_CSI: /* handle escape sequence parameters */
bellarde7f0ad52004-07-14 17:28:59 +0000901 if (ch >= '0' && ch <= '9') {
902 if (s->nb_esc_params < MAX_ESC_PARAMS) {
ths5fafdf22007-09-16 21:08:06 +0000903 s->esc_params[s->nb_esc_params] =
bellarde7f0ad52004-07-14 17:28:59 +0000904 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
905 }
906 } else {
907 s->nb_esc_params++;
908 if (ch == ';')
909 break;
thsadb47962007-01-16 23:02:36 +0000910#ifdef DEBUG_CONSOLE
911 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
912 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
913#endif
bellarde7f0ad52004-07-14 17:28:59 +0000914 s->state = TTY_STATE_NORM;
915 switch(ch) {
thsadb47962007-01-16 23:02:36 +0000916 case 'A':
917 /* move cursor up */
918 if (s->esc_params[0] == 0) {
919 s->esc_params[0] = 1;
920 }
921 s->y -= s->esc_params[0];
922 if (s->y < 0) {
923 s->y = 0;
bellarde7f0ad52004-07-14 17:28:59 +0000924 }
925 break;
thsadb47962007-01-16 23:02:36 +0000926 case 'B':
927 /* move cursor down */
928 if (s->esc_params[0] == 0) {
929 s->esc_params[0] = 1;
930 }
931 s->y += s->esc_params[0];
932 if (s->y >= s->height) {
933 s->y = s->height - 1;
934 }
935 break;
936 case 'C':
937 /* move cursor right */
938 if (s->esc_params[0] == 0) {
939 s->esc_params[0] = 1;
940 }
941 s->x += s->esc_params[0];
942 if (s->x >= s->width) {
943 s->x = s->width - 1;
944 }
945 break;
946 case 'D':
947 /* move cursor left */
948 if (s->esc_params[0] == 0) {
949 s->esc_params[0] = 1;
950 }
951 s->x -= s->esc_params[0];
952 if (s->x < 0) {
953 s->x = 0;
954 }
955 break;
956 case 'G':
957 /* move cursor to column */
958 s->x = s->esc_params[0] - 1;
959 if (s->x < 0) {
960 s->x = 0;
961 }
962 break;
963 case 'f':
964 case 'H':
965 /* move cursor to row, column */
966 s->x = s->esc_params[1] - 1;
967 if (s->x < 0) {
968 s->x = 0;
969 }
970 s->y = s->esc_params[0] - 1;
971 if (s->y < 0) {
972 s->y = 0;
973 }
974 break;
975 case 'J':
976 switch (s->esc_params[0]) {
977 case 0:
978 /* clear to end of screen */
979 for (y = s->y; y < s->height; y++) {
980 for (x = 0; x < s->width; x++) {
981 if (y == s->y && x < s->x) {
982 continue;
983 }
984 console_clear_xy(s, x, y);
985 }
986 }
987 break;
988 case 1:
989 /* clear from beginning of screen */
990 for (y = 0; y <= s->y; y++) {
991 for (x = 0; x < s->width; x++) {
992 if (y == s->y && x > s->x) {
993 break;
994 }
995 console_clear_xy(s, x, y);
996 }
997 }
998 break;
999 case 2:
1000 /* clear entire screen */
1001 for (y = 0; y <= s->height; y++) {
1002 for (x = 0; x < s->width; x++) {
1003 console_clear_xy(s, x, y);
1004 }
1005 }
1006 break;
1007 }
1008 case 'K':
1009 switch (s->esc_params[0]) {
1010 case 0:
1011 /* clear to eol */
1012 for(x = s->x; x < s->width; x++) {
1013 console_clear_xy(s, x, s->y);
1014 }
1015 break;
1016 case 1:
1017 /* clear from beginning of line */
1018 for (x = 0; x <= s->x; x++) {
1019 console_clear_xy(s, x, s->y);
1020 }
1021 break;
1022 case 2:
1023 /* clear entire line */
1024 for(x = 0; x < s->width; x++) {
1025 console_clear_xy(s, x, s->y);
1026 }
bellarde7f0ad52004-07-14 17:28:59 +00001027 break;
1028 }
thsadb47962007-01-16 23:02:36 +00001029 break;
1030 case 'm':
pbrook6d6f7c22006-03-11 15:35:30 +00001031 console_handle_escape(s);
bellarde7f0ad52004-07-14 17:28:59 +00001032 break;
thsadb47962007-01-16 23:02:36 +00001033 case 'n':
1034 /* report cursor position */
1035 /* TODO: send ESC[row;colR */
1036 break;
1037 case 's':
1038 /* save cursor position */
1039 s->x_saved = s->x;
1040 s->y_saved = s->y;
1041 break;
1042 case 'u':
1043 /* restore cursor position */
1044 s->x = s->x_saved;
1045 s->y = s->y_saved;
1046 break;
1047 default:
1048#ifdef DEBUG_CONSOLE
1049 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1050#endif
1051 break;
1052 }
1053 break;
bellarde7f0ad52004-07-14 17:28:59 +00001054 }
1055 }
1056}
1057
1058void console_select(unsigned int index)
1059{
1060 TextConsole *s;
pbrook6d6f7c22006-03-11 15:35:30 +00001061
bellarde7f0ad52004-07-14 17:28:59 +00001062 if (index >= MAX_CONSOLES)
1063 return;
aliguori7d957bd2009-01-15 22:14:11 +00001064 active_console->g_width = ds_get_width(active_console->ds);
1065 active_console->g_height = ds_get_height(active_console->ds);
bellarde7f0ad52004-07-14 17:28:59 +00001066 s = consoles[index];
1067 if (s) {
aliguori7d957bd2009-01-15 22:14:11 +00001068 DisplayState *ds = s->ds;
bellarde7f0ad52004-07-14 17:28:59 +00001069 active_console = s;
aliguori7d957bd2009-01-15 22:14:11 +00001070 ds->surface = qemu_resize_displaysurface(ds->surface, s->g_width,
1071 s->g_height, 32, 4 * s->g_width);
1072 dpy_resize(s->ds);
balrog4d3b6f62008-02-10 16:33:14 +00001073 vga_hw_invalidate();
bellarde7f0ad52004-07-14 17:28:59 +00001074 }
1075}
1076
1077static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
1078{
1079 TextConsole *s = chr->opaque;
1080 int i;
1081
pbrook14778c22009-01-21 03:02:52 +00001082 s->update_x0 = s->width * FONT_WIDTH;
1083 s->update_y0 = s->height * FONT_HEIGHT;
1084 s->update_x1 = 0;
1085 s->update_y1 = 0;
bellarde7f0ad52004-07-14 17:28:59 +00001086 console_show_cursor(s, 0);
1087 for(i = 0; i < len; i++) {
1088 console_putchar(s, buf[i]);
1089 }
1090 console_show_cursor(s, 1);
pbrook14778c22009-01-21 03:02:52 +00001091 if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
1092 dpy_update(s->ds, s->update_x0, s->update_y0,
1093 s->update_x1 - s->update_x0,
1094 s->update_y1 - s->update_y0);
1095 }
bellarde7f0ad52004-07-14 17:28:59 +00001096 return len;
1097}
1098
bellard6fcfafb2004-08-01 21:48:30 +00001099static void console_send_event(CharDriverState *chr, int event)
1100{
1101 TextConsole *s = chr->opaque;
1102 int i;
1103
1104 if (event == CHR_EVENT_FOCUS) {
1105 for(i = 0; i < nb_consoles; i++) {
1106 if (consoles[i] == s) {
1107 console_select(i);
1108 break;
1109 }
1110 }
1111 }
1112}
1113
bellarde15d7372006-06-25 16:26:29 +00001114static void kbd_send_chars(void *opaque)
1115{
1116 TextConsole *s = opaque;
1117 int len;
1118 uint8_t buf[16];
ths3b46e622007-09-17 08:09:54 +00001119
pbrooke5b0bc42007-01-27 23:46:43 +00001120 len = qemu_chr_can_read(s->chr);
bellarde15d7372006-06-25 16:26:29 +00001121 if (len > s->out_fifo.count)
1122 len = s->out_fifo.count;
1123 if (len > 0) {
1124 if (len > sizeof(buf))
1125 len = sizeof(buf);
1126 qemu_fifo_read(&s->out_fifo, buf, len);
pbrooke5b0bc42007-01-27 23:46:43 +00001127 qemu_chr_read(s->chr, buf, len);
bellarde15d7372006-06-25 16:26:29 +00001128 }
1129 /* characters are pending: we send them a bit later (XXX:
1130 horrible, should change char device API) */
1131 if (s->out_fifo.count > 0) {
1132 qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
1133 }
1134}
1135
bellarde7f0ad52004-07-14 17:28:59 +00001136/* called when an ascii key is pressed */
1137void kbd_put_keysym(int keysym)
1138{
1139 TextConsole *s;
1140 uint8_t buf[16], *q;
1141 int c;
1142
1143 s = active_console;
thsaf3a9032007-07-11 23:14:59 +00001144 if (!s || (s->console_type == GRAPHIC_CONSOLE))
bellarde7f0ad52004-07-14 17:28:59 +00001145 return;
1146
1147 switch(keysym) {
1148 case QEMU_KEY_CTRL_UP:
1149 console_scroll(-1);
1150 break;
1151 case QEMU_KEY_CTRL_DOWN:
1152 console_scroll(1);
1153 break;
1154 case QEMU_KEY_CTRL_PAGEUP:
1155 console_scroll(-10);
1156 break;
1157 case QEMU_KEY_CTRL_PAGEDOWN:
1158 console_scroll(10);
1159 break;
1160 default:
bellarde15d7372006-06-25 16:26:29 +00001161 /* convert the QEMU keysym to VT100 key string */
1162 q = buf;
1163 if (keysym >= 0xe100 && keysym <= 0xe11f) {
1164 *q++ = '\033';
1165 *q++ = '[';
1166 c = keysym - 0xe100;
1167 if (c >= 10)
1168 *q++ = '0' + (c / 10);
1169 *q++ = '0' + (c % 10);
1170 *q++ = '~';
1171 } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
1172 *q++ = '\033';
1173 *q++ = '[';
1174 *q++ = keysym & 0xff;
1175 } else {
bellarde7f0ad52004-07-14 17:28:59 +00001176 *q++ = keysym;
bellarde15d7372006-06-25 16:26:29 +00001177 }
pbrooke5b0bc42007-01-27 23:46:43 +00001178 if (s->chr->chr_read) {
bellarde15d7372006-06-25 16:26:29 +00001179 qemu_fifo_write(&s->out_fifo, buf, q - buf);
1180 kbd_send_chars(s);
bellarde7f0ad52004-07-14 17:28:59 +00001181 }
1182 break;
1183 }
1184}
1185
balrog4d3b6f62008-02-10 16:33:14 +00001186static void text_console_invalidate(void *opaque)
1187{
1188 TextConsole *s = (TextConsole *) opaque;
balrog4d3b6f62008-02-10 16:33:14 +00001189 console_refresh(s);
1190}
1191
1192static void text_console_update(void *opaque, console_ch_t *chardata)
1193{
1194 TextConsole *s = (TextConsole *) opaque;
1195 int i, j, src;
1196
1197 if (s->text_x[0] <= s->text_x[1]) {
1198 src = (s->y_base + s->text_y[0]) * s->width;
1199 chardata += s->text_y[0] * s->width;
1200 for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
1201 for (j = 0; j < s->width; j ++, src ++)
1202 console_write_ch(chardata ++, s->cells[src].ch |
1203 (s->cells[src].t_attrib.fgcol << 12) |
1204 (s->cells[src].t_attrib.bgcol << 8) |
1205 (s->cells[src].t_attrib.bold << 21));
1206 dpy_update(s->ds, s->text_x[0], s->text_y[0],
1207 s->text_x[1] - s->text_x[0], i - s->text_y[0]);
1208 s->text_x[0] = s->width;
1209 s->text_y[0] = s->height;
1210 s->text_x[1] = 0;
1211 s->text_y[1] = 0;
1212 }
1213 if (s->cursor_invalidate) {
1214 dpy_cursor(s->ds, s->x, s->y);
1215 s->cursor_invalidate = 0;
1216 }
1217}
1218
aliguori42aa98e2009-01-16 21:01:48 +00001219static TextConsole *get_graphic_console(DisplayState *ds)
blueswir1a147d622009-01-16 19:41:04 +00001220{
aliguori3023f332009-01-16 19:04:14 +00001221 int i;
1222 TextConsole *s;
1223 for (i = 0; i < nb_consoles; i++) {
1224 s = consoles[i];
aliguori42aa98e2009-01-16 21:01:48 +00001225 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
aliguori3023f332009-01-16 19:04:14 +00001226 return s;
1227 }
1228 return NULL;
1229}
1230
thsaf3a9032007-07-11 23:14:59 +00001231static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
bellarde7f0ad52004-07-14 17:28:59 +00001232{
1233 TextConsole *s;
pbrook95219892006-04-09 01:06:34 +00001234 int i;
bellarde7f0ad52004-07-14 17:28:59 +00001235
1236 if (nb_consoles >= MAX_CONSOLES)
1237 return NULL;
1238 s = qemu_mallocz(sizeof(TextConsole));
1239 if (!s) {
1240 return NULL;
1241 }
thsaf3a9032007-07-11 23:14:59 +00001242 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1243 (console_type == GRAPHIC_CONSOLE))) {
bellarde7f0ad52004-07-14 17:28:59 +00001244 active_console = s;
thsaf3a9032007-07-11 23:14:59 +00001245 }
bellarde7f0ad52004-07-14 17:28:59 +00001246 s->ds = ds;
thsaf3a9032007-07-11 23:14:59 +00001247 s->console_type = console_type;
1248 if (console_type != GRAPHIC_CONSOLE) {
pbrook95219892006-04-09 01:06:34 +00001249 consoles[nb_consoles++] = s;
1250 } else {
1251 /* HACK: Put graphical consoles before text consoles. */
1252 for (i = nb_consoles; i > 0; i--) {
thsaf3a9032007-07-11 23:14:59 +00001253 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
pbrook95219892006-04-09 01:06:34 +00001254 break;
1255 consoles[i] = consoles[i - 1];
1256 }
1257 consoles[i] = s;
aliguori3023f332009-01-16 19:04:14 +00001258 nb_consoles++;
pbrook95219892006-04-09 01:06:34 +00001259 }
bellarde7f0ad52004-07-14 17:28:59 +00001260 return s;
1261}
1262
aliguori3023f332009-01-16 19:04:14 +00001263DisplayState *graphic_console_init(vga_hw_update_ptr update,
1264 vga_hw_invalidate_ptr invalidate,
1265 vga_hw_screen_dump_ptr screen_dump,
1266 vga_hw_text_update_ptr text_update,
1267 void *opaque)
bellarde7f0ad52004-07-14 17:28:59 +00001268{
pbrook95219892006-04-09 01:06:34 +00001269 TextConsole *s;
aliguori3023f332009-01-16 19:04:14 +00001270 DisplayState *ds;
aurel32f0f2f972009-01-16 21:13:49 +00001271
aliguori3023f332009-01-16 19:04:14 +00001272 ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
1273 if (ds == NULL)
1274 return NULL;
1275 ds->surface = qemu_create_displaysurface(640, 480, 32, 640 * 4);
pbrook95219892006-04-09 01:06:34 +00001276
thsaf3a9032007-07-11 23:14:59 +00001277 s = new_console(ds, GRAPHIC_CONSOLE);
aliguori3023f332009-01-16 19:04:14 +00001278 if (s == NULL) {
1279 qemu_free_displaysurface(ds->surface);
1280 qemu_free(ds);
1281 return NULL;
1282 }
pbrook95219892006-04-09 01:06:34 +00001283 s->hw_update = update;
1284 s->hw_invalidate = invalidate;
1285 s->hw_screen_dump = screen_dump;
balrog4d3b6f62008-02-10 16:33:14 +00001286 s->hw_text_update = text_update;
pbrook95219892006-04-09 01:06:34 +00001287 s->hw = opaque;
aliguori3023f332009-01-16 19:04:14 +00001288
aurel32f0f2f972009-01-16 21:13:49 +00001289 register_displaystate(ds);
aliguori3023f332009-01-16 19:04:14 +00001290 return ds;
pbrook95219892006-04-09 01:06:34 +00001291}
1292
1293int is_graphic_console(void)
1294{
balrog4d3b6f62008-02-10 16:33:14 +00001295 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
bellarde7f0ad52004-07-14 17:28:59 +00001296}
1297
balrogc21bbcf2008-09-24 03:32:33 +00001298int is_fixedsize_console(void)
1299{
1300 return active_console && active_console->console_type != TEXT_CONSOLE;
1301}
1302
balroga528b802007-10-30 22:38:53 +00001303void console_color_init(DisplayState *ds)
1304{
1305 int i, j;
1306 for (j = 0; j < 2; j++) {
1307 for (i = 0; i < 8; i++) {
aurel32f0f2f972009-01-16 21:13:49 +00001308 color_table[j][i] = col_expand(ds,
balroga528b802007-10-30 22:38:53 +00001309 vga_get_color(ds, color_table_rgb[j][i]));
1310 }
1311 }
1312}
1313
aliguori2796dae2009-01-16 20:23:27 +00001314static int n_text_consoles;
1315static CharDriverState *text_consoles[128];
1316static char *text_console_strs[128];
1317
1318static void text_console_do_init(CharDriverState *chr, DisplayState *ds, const char *p)
bellarde7f0ad52004-07-14 17:28:59 +00001319{
bellarde7f0ad52004-07-14 17:28:59 +00001320 TextConsole *s;
thsaf3a9032007-07-11 23:14:59 +00001321 unsigned width;
1322 unsigned height;
bellarde7f0ad52004-07-14 17:28:59 +00001323 static int color_inited;
pbrook6d6f7c22006-03-11 15:35:30 +00001324
balrogc21bbcf2008-09-24 03:32:33 +00001325 s = new_console(ds, (p == 0) ? TEXT_CONSOLE : TEXT_CONSOLE_FIXED_SIZE);
bellarde7f0ad52004-07-14 17:28:59 +00001326 if (!s) {
1327 free(chr);
aliguorifdb868e2009-01-16 21:06:20 +00001328 return;
bellarde7f0ad52004-07-14 17:28:59 +00001329 }
bellarde7f0ad52004-07-14 17:28:59 +00001330 chr->opaque = s;
1331 chr->chr_write = console_puts;
bellard6fcfafb2004-08-01 21:48:30 +00001332 chr->chr_send_event = console_send_event;
1333
pbrooke5b0bc42007-01-27 23:46:43 +00001334 s->chr = chr;
bellarde15d7372006-06-25 16:26:29 +00001335 s->out_fifo.buf = s->out_fifo_buf;
1336 s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
1337 s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
aliguori3023f332009-01-16 19:04:14 +00001338 s->ds = ds;
ths3b46e622007-09-17 08:09:54 +00001339
bellarde7f0ad52004-07-14 17:28:59 +00001340 if (!color_inited) {
1341 color_inited = 1;
balroga528b802007-10-30 22:38:53 +00001342 console_color_init(s->ds);
bellarde7f0ad52004-07-14 17:28:59 +00001343 }
1344 s->y_displayed = 0;
1345 s->y_base = 0;
1346 s->total_height = DEFAULT_BACKSCROLL;
1347 s->x = 0;
1348 s->y = 0;
aliguori0e1f5a02008-11-24 19:29:13 +00001349 width = ds_get_width(s->ds);
1350 height = ds_get_height(s->ds);
thsaf3a9032007-07-11 23:14:59 +00001351 if (p != 0) {
1352 width = strtoul(p, (char **)&p, 10);
1353 if (*p == 'C') {
1354 p++;
1355 width *= FONT_WIDTH;
1356 }
1357 if (*p == 'x') {
1358 p++;
1359 height = strtoul(p, (char **)&p, 10);
1360 if (*p == 'C') {
1361 p++;
1362 height *= FONT_HEIGHT;
1363 }
1364 }
1365 }
1366 s->g_width = width;
1367 s->g_height = height;
pbrook6d6f7c22006-03-11 15:35:30 +00001368
balrog4d3b6f62008-02-10 16:33:14 +00001369 s->hw_invalidate = text_console_invalidate;
1370 s->hw_text_update = text_console_update;
1371 s->hw = s;
1372
pbrook6d6f7c22006-03-11 15:35:30 +00001373 /* Set text attribute defaults */
1374 s->t_attrib_default.bold = 0;
1375 s->t_attrib_default.uline = 0;
1376 s->t_attrib_default.blink = 0;
1377 s->t_attrib_default.invers = 0;
1378 s->t_attrib_default.unvisible = 0;
1379 s->t_attrib_default.fgcol = COLOR_WHITE;
1380 s->t_attrib_default.bgcol = COLOR_BLACK;
pbrook6d6f7c22006-03-11 15:35:30 +00001381 /* set current text attributes to default */
1382 s->t_attrib = s->t_attrib_default;
bellarde7f0ad52004-07-14 17:28:59 +00001383 text_console_resize(s);
1384
ths86e94de2007-01-05 22:01:59 +00001385 qemu_chr_reset(chr);
aurel32ceecf1d2009-01-18 14:08:04 +00001386 if (chr->init)
1387 chr->init(chr);
bellarde7f0ad52004-07-14 17:28:59 +00001388}
pbrookc60e08d2008-07-01 16:24:38 +00001389
aliguori2796dae2009-01-16 20:23:27 +00001390CharDriverState *text_console_init(const char *p)
1391{
1392 CharDriverState *chr;
1393
1394 chr = qemu_mallocz(sizeof(CharDriverState));
1395 if (!chr)
1396 return NULL;
1397
1398 if (n_text_consoles == 128) {
1399 fprintf(stderr, "Too many text consoles\n");
1400 exit(1);
1401 }
1402 text_consoles[n_text_consoles] = chr;
1403 text_console_strs[n_text_consoles] = p ? qemu_strdup(p) : NULL;
1404 n_text_consoles++;
1405
1406 return chr;
1407}
1408
1409void text_consoles_set_display(DisplayState *ds)
1410{
1411 int i;
1412
1413 for (i = 0; i < n_text_consoles; i++) {
1414 text_console_do_init(text_consoles[i], ds, text_console_strs[i]);
1415 qemu_free(text_console_strs[i]);
1416 }
1417
1418 n_text_consoles = 0;
1419}
1420
aliguori3023f332009-01-16 19:04:14 +00001421void qemu_console_resize(DisplayState *ds, int width, int height)
pbrookc60e08d2008-07-01 16:24:38 +00001422{
aliguori42aa98e2009-01-16 21:01:48 +00001423 TextConsole *s = get_graphic_console(ds);
aliguori3023f332009-01-16 19:04:14 +00001424 s->g_width = width;
1425 s->g_height = height;
1426 if (is_graphic_console()) {
aliguori7d957bd2009-01-15 22:14:11 +00001427 ds->surface = qemu_resize_displaysurface(ds->surface, width, height, 32, 4 * width);
aliguori3023f332009-01-16 19:04:14 +00001428 dpy_resize(ds);
pbrookc60e08d2008-07-01 16:24:38 +00001429 }
1430}
balrog38334f72008-09-24 02:21:24 +00001431
aliguori3023f332009-01-16 19:04:14 +00001432void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
1433 int dst_x, int dst_y, int w, int h)
balrogc21bbcf2008-09-24 03:32:33 +00001434{
aliguori3023f332009-01-16 19:04:14 +00001435 if (is_graphic_console()) {
1436 dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
balrog38334f72008-09-24 02:21:24 +00001437 }
1438}
aliguori7d957bd2009-01-15 22:14:11 +00001439
1440static PixelFormat qemu_default_pixelformat(int bpp)
1441{
1442 PixelFormat pf;
1443
1444 memset(&pf, 0x00, sizeof(PixelFormat));
1445
1446 pf.bits_per_pixel = bpp;
1447 pf.bytes_per_pixel = bpp / 8;
1448 pf.depth = bpp == 32 ? 24 : bpp;
1449
1450 switch (bpp) {
1451 case 8:
1452 pf.rmask = 0x000000E0;
1453 pf.gmask = 0x0000001C;
1454 pf.bmask = 0x00000003;
1455 pf.rmax = 7;
1456 pf.gmax = 7;
1457 pf.bmax = 3;
1458 pf.rshift = 5;
1459 pf.gshift = 2;
1460 pf.bshift = 0;
1461 break;
1462 case 16:
1463 pf.rmask = 0x0000F800;
1464 pf.gmask = 0x000007E0;
1465 pf.bmask = 0x0000001F;
1466 pf.rmax = 31;
1467 pf.gmax = 63;
1468 pf.bmax = 31;
1469 pf.rshift = 11;
1470 pf.gshift = 5;
1471 pf.bshift = 0;
1472 break;
1473 case 24:
1474 case 32:
1475 pf.rmask = 0x00FF0000;
1476 pf.gmask = 0x0000FF00;
1477 pf.bmask = 0x000000FF;
1478 pf.rmax = 255;
1479 pf.gmax = 255;
1480 pf.bmax = 255;
1481 pf.rshift = 16;
1482 pf.gshift = 8;
1483 pf.bshift = 0;
1484 break;
1485 default:
1486 break;
1487 }
1488 return pf;
1489}
1490
1491DisplaySurface* qemu_create_displaysurface(int width, int height, int bpp, int linesize)
1492{
1493 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1494 if (surface == NULL) {
1495 fprintf(stderr, "qemu_create_displaysurface: malloc failed\n");
1496 exit(1);
1497 }
1498
1499 surface->width = width;
1500 surface->height = height;
1501 surface->linesize = linesize;
1502 surface->pf = qemu_default_pixelformat(bpp);
1503#ifdef WORDS_BIGENDIAN
1504 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1505#else
1506 surface->flags = QEMU_ALLOCATED_FLAG;
1507#endif
1508 surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
1509 if (surface->data == NULL) {
1510 fprintf(stderr, "qemu_create_displaysurface: malloc failed\n");
1511 exit(1);
1512 }
1513
1514 return surface;
1515}
1516
1517DisplaySurface* qemu_resize_displaysurface(DisplaySurface *surface,
1518 int width, int height, int bpp, int linesize)
1519{
1520 surface->width = width;
1521 surface->height = height;
1522 surface->linesize = linesize;
1523 surface->pf = qemu_default_pixelformat(bpp);
1524 if (surface->flags & QEMU_ALLOCATED_FLAG)
1525 surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
1526 else
1527 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
1528 if (surface->data == NULL) {
1529 fprintf(stderr, "qemu_resize_displaysurface: malloc failed\n");
1530 exit(1);
1531 }
1532#ifdef WORDS_BIGENDIAN
1533 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1534#else
1535 surface->flags = QEMU_ALLOCATED_FLAG;
1536#endif
1537
1538 return surface;
1539}
1540
1541DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
1542 int linesize, uint8_t *data)
1543{
1544 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1545 if (surface == NULL) {
1546 fprintf(stderr, "qemu_create_displaysurface_from: malloc failed\n");
1547 exit(1);
1548 }
1549
1550 surface->width = width;
1551 surface->height = height;
1552 surface->linesize = linesize;
1553 surface->pf = qemu_default_pixelformat(bpp);
1554#ifdef WORDS_BIGENDIAN
1555 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1556#endif
1557 surface->data = data;
1558
1559 return surface;
1560}
1561
1562void qemu_free_displaysurface(DisplaySurface *surface)
1563{
1564 if (surface == NULL)
1565 return;
1566 if (surface->flags & QEMU_ALLOCATED_FLAG)
1567 qemu_free(surface->data);
1568 qemu_free(surface);
1569}