blob: 1afcaca65afda295e153393baab311c4b6d5bb0f [file] [log] [blame]
David 'Digit' Turnerf59442f2010-10-08 16:22:10 +02001/*
2 * QEMU graphical console
3 *
4 * Copyright (c) 2004 Fabrice Bellard
5 *
6 * 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 */
24#include "qemu-common.h"
25#include "console.h"
26#include "qemu-timer.h"
27
28//#define DEBUG_CONSOLE
29#define DEFAULT_BACKSCROLL 512
30#define MAX_CONSOLES 12
31
32#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
33#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
34
35typedef struct TextAttributes {
36 uint8_t fgcol:4;
37 uint8_t bgcol:4;
38 uint8_t bold:1;
39 uint8_t uline:1;
40 uint8_t blink:1;
41 uint8_t invers:1;
42 uint8_t unvisible:1;
43} TextAttributes;
44
45typedef struct TextCell {
46 uint8_t ch;
47 TextAttributes t_attrib;
48} TextCell;
49
50#define MAX_ESC_PARAMS 3
51
52enum TTYState {
53 TTY_STATE_NORM,
54 TTY_STATE_ESC,
55 TTY_STATE_CSI,
56};
57
58typedef struct QEMUFIFO {
59 uint8_t *buf;
60 int buf_size;
61 int count, wptr, rptr;
62} QEMUFIFO;
63
64static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
65{
66 int l, len;
67
68 l = f->buf_size - f->count;
69 if (len1 > l)
70 len1 = l;
71 len = len1;
72 while (len > 0) {
73 l = f->buf_size - f->wptr;
74 if (l > len)
75 l = len;
76 memcpy(f->buf + f->wptr, buf, l);
77 f->wptr += l;
78 if (f->wptr >= f->buf_size)
79 f->wptr = 0;
80 buf += l;
81 len -= l;
82 }
83 f->count += len1;
84 return len1;
85}
86
87static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
88{
89 int l, len;
90
91 if (len1 > f->count)
92 len1 = f->count;
93 len = len1;
94 while (len > 0) {
95 l = f->buf_size - f->rptr;
96 if (l > len)
97 l = len;
98 memcpy(buf, f->buf + f->rptr, l);
99 f->rptr += l;
100 if (f->rptr >= f->buf_size)
101 f->rptr = 0;
102 buf += l;
103 len -= l;
104 }
105 f->count -= len1;
106 return len1;
107}
108
109typedef enum {
110 GRAPHIC_CONSOLE,
111 TEXT_CONSOLE,
112 TEXT_CONSOLE_FIXED_SIZE
113} console_type_t;
114
115/* ??? This is mis-named.
116 It is used for both text and graphical consoles. */
117struct TextConsole {
118 console_type_t console_type;
119 DisplayState *ds;
120 /* Graphic console state. */
121 vga_hw_update_ptr hw_update;
122 vga_hw_invalidate_ptr hw_invalidate;
123 vga_hw_screen_dump_ptr hw_screen_dump;
124 vga_hw_text_update_ptr hw_text_update;
125 void *hw;
126
127 int g_width, g_height;
128 int width;
129 int height;
130 int total_height;
131 int backscroll_height;
132 int x, y;
133 int x_saved, y_saved;
134 int y_displayed;
135 int y_base;
136 TextAttributes t_attrib_default; /* default text attributes */
137 TextAttributes t_attrib; /* currently active text attributes */
138 TextCell *cells;
139 int text_x[2], text_y[2], cursor_invalidate;
140
141 int update_x0;
142 int update_y0;
143 int update_x1;
144 int update_y1;
145
146 enum TTYState state;
147 int esc_params[MAX_ESC_PARAMS];
148 int nb_esc_params;
149
150 /* fifo for key pressed */
151 QEMUFIFO out_fifo;
152 uint8_t out_fifo_buf[16];
153 QEMUTimer *kbd_timer;
154};
155
156static DisplayState *display_state;
157static TextConsole *active_console;
158static TextConsole *consoles[MAX_CONSOLES];
159static int nb_consoles = 0;
160
161#ifdef CONFIG_ANDROID
162/* Graphic console width, height and bits per pixel.
163 * These default values can be changed with the "-android-gui" option.
164 */
165int android_display_width = 640;
166int android_display_height = 480;
167int android_display_bpp = 32;
168#endif
169
170void vga_hw_update(void)
171{
172 if (active_console && active_console->hw_update)
173 active_console->hw_update(active_console->hw);
174}
175
176void vga_hw_invalidate(void)
177{
178 if (active_console && active_console->hw_invalidate)
179 active_console->hw_invalidate(active_console->hw);
180}
181
182void vga_hw_screen_dump(const char *filename)
183{
184 TextConsole *previous_active_console;
185
186 previous_active_console = active_console;
187 active_console = consoles[0];
188 /* There is currently no way of specifying which screen we want to dump,
189 so always dump the first one. */
190 if (consoles[0]->hw_screen_dump)
191 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
192 active_console = previous_active_console;
193}
194
195void vga_hw_text_update(console_ch_t *chardata)
196{
197 if (active_console && active_console->hw_text_update)
198 active_console->hw_text_update(active_console->hw, chardata);
199}
200
201/* convert a RGBA color to a color index usable in graphic primitives */
202static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
203{
204 unsigned int r, g, b, color;
205
206 switch(ds_get_bits_per_pixel(ds)) {
207#if 0
208 case 8:
209 r = (rgba >> 16) & 0xff;
210 g = (rgba >> 8) & 0xff;
211 b = (rgba) & 0xff;
212 color = (rgb_to_index[r] * 6 * 6) +
213 (rgb_to_index[g] * 6) +
214 (rgb_to_index[b]);
215 break;
216#endif
217 case 15:
218 r = (rgba >> 16) & 0xff;
219 g = (rgba >> 8) & 0xff;
220 b = (rgba) & 0xff;
221 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
222 break;
223 case 16:
224 r = (rgba >> 16) & 0xff;
225 g = (rgba >> 8) & 0xff;
226 b = (rgba) & 0xff;
227 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
228 break;
229 case 32:
230 default:
231 color = rgba;
232 break;
233 }
234 return color;
235}
236
237static void vga_fill_rect (DisplayState *ds,
238 int posx, int posy, int width, int height, uint32_t color)
239{
240 uint8_t *d, *d1;
241 int x, y, bpp;
242
243 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
244 d1 = ds_get_data(ds) +
245 ds_get_linesize(ds) * posy + bpp * posx;
246 for (y = 0; y < height; y++) {
247 d = d1;
248 switch(bpp) {
249 case 1:
250 for (x = 0; x < width; x++) {
251 *((uint8_t *)d) = color;
252 d++;
253 }
254 break;
255 case 2:
256 for (x = 0; x < width; x++) {
257 *((uint16_t *)d) = color;
258 d += 2;
259 }
260 break;
261 case 4:
262 for (x = 0; x < width; x++) {
263 *((uint32_t *)d) = color;
264 d += 4;
265 }
266 break;
267 }
268 d1 += ds_get_linesize(ds);
269 }
270}
271
272/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
273static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
274{
275 const uint8_t *s;
276 uint8_t *d;
277 int wb, y, bpp;
278
279 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
280 wb = w * bpp;
281 if (yd <= ys) {
282 s = ds_get_data(ds) +
283 ds_get_linesize(ds) * ys + bpp * xs;
284 d = ds_get_data(ds) +
285 ds_get_linesize(ds) * yd + bpp * xd;
286 for (y = 0; y < h; y++) {
287 memmove(d, s, wb);
288 d += ds_get_linesize(ds);
289 s += ds_get_linesize(ds);
290 }
291 } else {
292 s = ds_get_data(ds) +
293 ds_get_linesize(ds) * (ys + h - 1) + bpp * xs;
294 d = ds_get_data(ds) +
295 ds_get_linesize(ds) * (yd + h - 1) + bpp * xd;
296 for (y = 0; y < h; y++) {
297 memmove(d, s, wb);
298 d -= ds_get_linesize(ds);
299 s -= ds_get_linesize(ds);
300 }
301 }
302}
303
304/***********************************************************/
305/* basic char display */
306
307#define FONT_HEIGHT 16
308#define FONT_WIDTH 8
309
310#include "vgafont.h"
311
312#define cbswap_32(__x) \
313((uint32_t)( \
314 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
315 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
316 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
317 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
318
319#ifdef HOST_WORDS_BIGENDIAN
320#define PAT(x) x
321#else
322#define PAT(x) cbswap_32(x)
323#endif
324
325static const uint32_t dmask16[16] = {
326 PAT(0x00000000),
327 PAT(0x000000ff),
328 PAT(0x0000ff00),
329 PAT(0x0000ffff),
330 PAT(0x00ff0000),
331 PAT(0x00ff00ff),
332 PAT(0x00ffff00),
333 PAT(0x00ffffff),
334 PAT(0xff000000),
335 PAT(0xff0000ff),
336 PAT(0xff00ff00),
337 PAT(0xff00ffff),
338 PAT(0xffff0000),
339 PAT(0xffff00ff),
340 PAT(0xffffff00),
341 PAT(0xffffffff),
342};
343
344static const uint32_t dmask4[4] = {
345 PAT(0x00000000),
346 PAT(0x0000ffff),
347 PAT(0xffff0000),
348 PAT(0xffffffff),
349};
350
351static uint32_t color_table[2][8];
352
353enum color_names {
354 COLOR_BLACK = 0,
355 COLOR_RED = 1,
356 COLOR_GREEN = 2,
357 COLOR_YELLOW = 3,
358 COLOR_BLUE = 4,
359 COLOR_MAGENTA = 5,
360 COLOR_CYAN = 6,
361 COLOR_WHITE = 7
362};
363
364static const uint32_t color_table_rgb[2][8] = {
365 { /* dark */
366 QEMU_RGB(0x00, 0x00, 0x00), /* black */
367 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
368 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
369 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
370 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
371 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
372 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
373 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
374 },
375 { /* bright */
376 QEMU_RGB(0x00, 0x00, 0x00), /* black */
377 QEMU_RGB(0xff, 0x00, 0x00), /* red */
378 QEMU_RGB(0x00, 0xff, 0x00), /* green */
379 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
380 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
381 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
382 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
383 QEMU_RGB(0xff, 0xff, 0xff), /* white */
384 }
385};
386
387static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
388{
389 switch(ds_get_bits_per_pixel(ds)) {
390 case 8:
391 col |= col << 8;
392 col |= col << 16;
393 break;
394 case 15:
395 case 16:
396 col |= col << 16;
397 break;
398 default:
399 break;
400 }
401
402 return col;
403}
404#ifdef DEBUG_CONSOLE
405static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
406{
407 if (t_attrib->bold) {
408 printf("b");
409 } else {
410 printf(" ");
411 }
412 if (t_attrib->uline) {
413 printf("u");
414 } else {
415 printf(" ");
416 }
417 if (t_attrib->blink) {
418 printf("l");
419 } else {
420 printf(" ");
421 }
422 if (t_attrib->invers) {
423 printf("i");
424 } else {
425 printf(" ");
426 }
427 if (t_attrib->unvisible) {
428 printf("n");
429 } else {
430 printf(" ");
431 }
432
433 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
434}
435#endif
436
437static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
438 TextAttributes *t_attrib)
439{
440 uint8_t *d;
441 const uint8_t *font_ptr;
442 unsigned int font_data, linesize, xorcol, bpp;
443 int i;
444 unsigned int fgcol, bgcol;
445
446#ifdef DEBUG_CONSOLE
447 printf("x: %2i y: %2i", x, y);
448 console_print_text_attributes(t_attrib, ch);
449#endif
450
451 if (t_attrib->invers) {
452 bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
453 fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
454 } else {
455 fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
456 bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
457 }
458
459 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
460 d = ds_get_data(ds) +
461 ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
462 linesize = ds_get_linesize(ds);
463 font_ptr = vgafont16 + FONT_HEIGHT * ch;
464 xorcol = bgcol ^ fgcol;
465 switch(ds_get_bits_per_pixel(ds)) {
466 case 8:
467 for(i = 0; i < FONT_HEIGHT; i++) {
468 font_data = *font_ptr++;
469 if (t_attrib->uline
470 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
471 font_data = 0xFFFF;
472 }
473 ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
474 ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
475 d += linesize;
476 }
477 break;
478 case 16:
479 case 15:
480 for(i = 0; i < FONT_HEIGHT; i++) {
481 font_data = *font_ptr++;
482 if (t_attrib->uline
483 && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
484 font_data = 0xFFFF;
485 }
486 ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
487 ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
488 ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
489 ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
490 d += linesize;
491 }
492 break;
493 case 32:
494 for(i = 0; i < FONT_HEIGHT; i++) {
495 font_data = *font_ptr++;
496 if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
497 font_data = 0xFFFF;
498 }
499 ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
500 ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
501 ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
502 ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
503 ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
504 ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
505 ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
506 ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
507 d += linesize;
508 }
509 break;
510 }
511}
512
513static void text_console_resize(TextConsole *s)
514{
515 TextCell *cells, *c, *c1;
516 int w1, x, y, last_width;
517
518 last_width = s->width;
519 s->width = s->g_width / FONT_WIDTH;
520 s->height = s->g_height / FONT_HEIGHT;
521
522 w1 = last_width;
523 if (s->width < w1)
524 w1 = s->width;
525
526 cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
527 for(y = 0; y < s->total_height; y++) {
528 c = &cells[y * s->width];
529 if (w1 > 0) {
530 c1 = &s->cells[y * last_width];
531 for(x = 0; x < w1; x++) {
532 *c++ = *c1++;
533 }
534 }
535 for(x = w1; x < s->width; x++) {
536 c->ch = ' ';
537 c->t_attrib = s->t_attrib_default;
538 c++;
539 }
540 }
541 qemu_free(s->cells);
542 s->cells = cells;
543}
544
545static inline void text_update_xy(TextConsole *s, int x, int y)
546{
547 s->text_x[0] = MIN(s->text_x[0], x);
548 s->text_x[1] = MAX(s->text_x[1], x);
549 s->text_y[0] = MIN(s->text_y[0], y);
550 s->text_y[1] = MAX(s->text_y[1], y);
551}
552
553static void invalidate_xy(TextConsole *s, int x, int y)
554{
555 if (s->update_x0 > x * FONT_WIDTH)
556 s->update_x0 = x * FONT_WIDTH;
557 if (s->update_y0 > y * FONT_HEIGHT)
558 s->update_y0 = y * FONT_HEIGHT;
559 if (s->update_x1 < (x + 1) * FONT_WIDTH)
560 s->update_x1 = (x + 1) * FONT_WIDTH;
561 if (s->update_y1 < (y + 1) * FONT_HEIGHT)
562 s->update_y1 = (y + 1) * FONT_HEIGHT;
563}
564
565static void update_xy(TextConsole *s, int x, int y)
566{
567 TextCell *c;
568 int y1, y2;
569
570 if (s == active_console) {
571 if (!ds_get_bits_per_pixel(s->ds)) {
572 text_update_xy(s, x, y);
573 return;
574 }
575
576 y1 = (s->y_base + y) % s->total_height;
577 y2 = y1 - s->y_displayed;
578 if (y2 < 0)
579 y2 += s->total_height;
580 if (y2 < s->height) {
581 c = &s->cells[y1 * s->width + x];
582 vga_putcharxy(s->ds, x, y2, c->ch,
583 &(c->t_attrib));
584 invalidate_xy(s, x, y2);
585 }
586 }
587}
588
589static void console_show_cursor(TextConsole *s, int show)
590{
591 TextCell *c;
592 int y, y1;
593
594 if (s == active_console) {
595 int x = s->x;
596
597 if (!ds_get_bits_per_pixel(s->ds)) {
598 s->cursor_invalidate = 1;
599 return;
600 }
601
602 if (x >= s->width) {
603 x = s->width - 1;
604 }
605 y1 = (s->y_base + s->y) % s->total_height;
606 y = y1 - s->y_displayed;
607 if (y < 0)
608 y += s->total_height;
609 if (y < s->height) {
610 c = &s->cells[y1 * s->width + x];
611 if (show) {
612 TextAttributes t_attrib = s->t_attrib_default;
613 t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
614 vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
615 } else {
616 vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
617 }
618 invalidate_xy(s, x, y);
619 }
620 }
621}
622
623static void console_refresh(TextConsole *s)
624{
625 TextCell *c;
626 int x, y, y1;
627
628 if (s != active_console)
629 return;
630 if (!ds_get_bits_per_pixel(s->ds)) {
631 s->text_x[0] = 0;
632 s->text_y[0] = 0;
633 s->text_x[1] = s->width - 1;
634 s->text_y[1] = s->height - 1;
635 s->cursor_invalidate = 1;
636 return;
637 }
638
639 vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
640 color_table[0][COLOR_BLACK]);
641 y1 = s->y_displayed;
642 for(y = 0; y < s->height; y++) {
643 c = s->cells + y1 * s->width;
644 for(x = 0; x < s->width; x++) {
645 vga_putcharxy(s->ds, x, y, c->ch,
646 &(c->t_attrib));
647 c++;
648 }
649 if (++y1 == s->total_height)
650 y1 = 0;
651 }
652 console_show_cursor(s, 1);
653 dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
654}
655
656static void console_scroll(int ydelta)
657{
658 TextConsole *s;
659 int i, y1;
660
661 s = active_console;
662 if (!s || (s->console_type == GRAPHIC_CONSOLE))
663 return;
664
665 if (ydelta > 0) {
666 for(i = 0; i < ydelta; i++) {
667 if (s->y_displayed == s->y_base)
668 break;
669 if (++s->y_displayed == s->total_height)
670 s->y_displayed = 0;
671 }
672 } else {
673 ydelta = -ydelta;
674 i = s->backscroll_height;
675 if (i > s->total_height - s->height)
676 i = s->total_height - s->height;
677 y1 = s->y_base - i;
678 if (y1 < 0)
679 y1 += s->total_height;
680 for(i = 0; i < ydelta; i++) {
681 if (s->y_displayed == y1)
682 break;
683 if (--s->y_displayed < 0)
684 s->y_displayed = s->total_height - 1;
685 }
686 }
687 console_refresh(s);
688}
689
690static void console_put_lf(TextConsole *s)
691{
692 TextCell *c;
693 int x, y1;
694
695 s->y++;
696 if (s->y >= s->height) {
697 s->y = s->height - 1;
698
699 if (s->y_displayed == s->y_base) {
700 if (++s->y_displayed == s->total_height)
701 s->y_displayed = 0;
702 }
703 if (++s->y_base == s->total_height)
704 s->y_base = 0;
705 if (s->backscroll_height < s->total_height)
706 s->backscroll_height++;
707 y1 = (s->y_base + s->height - 1) % s->total_height;
708 c = &s->cells[y1 * s->width];
709 for(x = 0; x < s->width; x++) {
710 c->ch = ' ';
711 c->t_attrib = s->t_attrib_default;
712 c++;
713 }
714 if (s == active_console && s->y_displayed == s->y_base) {
715 if (!ds_get_bits_per_pixel(s->ds)) {
716 s->text_x[0] = 0;
717 s->text_y[0] = 0;
718 s->text_x[1] = s->width - 1;
719 s->text_y[1] = s->height - 1;
720 return;
721 }
722
723 vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
724 s->width * FONT_WIDTH,
725 (s->height - 1) * FONT_HEIGHT);
726 vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
727 s->width * FONT_WIDTH, FONT_HEIGHT,
728 color_table[0][s->t_attrib_default.bgcol]);
729 s->update_x0 = 0;
730 s->update_y0 = 0;
731 s->update_x1 = s->width * FONT_WIDTH;
732 s->update_y1 = s->height * FONT_HEIGHT;
733 }
734 }
735}
736
737/* Set console attributes depending on the current escape codes.
738 * NOTE: I know this code is not very efficient (checking every color for it
739 * self) but it is more readable and better maintainable.
740 */
741static void console_handle_escape(TextConsole *s)
742{
743 int i;
744
745 for (i=0; i<s->nb_esc_params; i++) {
746 switch (s->esc_params[i]) {
747 case 0: /* reset all console attributes to default */
748 s->t_attrib = s->t_attrib_default;
749 break;
750 case 1:
751 s->t_attrib.bold = 1;
752 break;
753 case 4:
754 s->t_attrib.uline = 1;
755 break;
756 case 5:
757 s->t_attrib.blink = 1;
758 break;
759 case 7:
760 s->t_attrib.invers = 1;
761 break;
762 case 8:
763 s->t_attrib.unvisible = 1;
764 break;
765 case 22:
766 s->t_attrib.bold = 0;
767 break;
768 case 24:
769 s->t_attrib.uline = 0;
770 break;
771 case 25:
772 s->t_attrib.blink = 0;
773 break;
774 case 27:
775 s->t_attrib.invers = 0;
776 break;
777 case 28:
778 s->t_attrib.unvisible = 0;
779 break;
780 /* set foreground color */
781 case 30:
782 s->t_attrib.fgcol=COLOR_BLACK;
783 break;
784 case 31:
785 s->t_attrib.fgcol=COLOR_RED;
786 break;
787 case 32:
788 s->t_attrib.fgcol=COLOR_GREEN;
789 break;
790 case 33:
791 s->t_attrib.fgcol=COLOR_YELLOW;
792 break;
793 case 34:
794 s->t_attrib.fgcol=COLOR_BLUE;
795 break;
796 case 35:
797 s->t_attrib.fgcol=COLOR_MAGENTA;
798 break;
799 case 36:
800 s->t_attrib.fgcol=COLOR_CYAN;
801 break;
802 case 37:
803 s->t_attrib.fgcol=COLOR_WHITE;
804 break;
805 /* set background color */
806 case 40:
807 s->t_attrib.bgcol=COLOR_BLACK;
808 break;
809 case 41:
810 s->t_attrib.bgcol=COLOR_RED;
811 break;
812 case 42:
813 s->t_attrib.bgcol=COLOR_GREEN;
814 break;
815 case 43:
816 s->t_attrib.bgcol=COLOR_YELLOW;
817 break;
818 case 44:
819 s->t_attrib.bgcol=COLOR_BLUE;
820 break;
821 case 45:
822 s->t_attrib.bgcol=COLOR_MAGENTA;
823 break;
824 case 46:
825 s->t_attrib.bgcol=COLOR_CYAN;
826 break;
827 case 47:
828 s->t_attrib.bgcol=COLOR_WHITE;
829 break;
830 }
831 }
832}
833
834static void console_clear_xy(TextConsole *s, int x, int y)
835{
836 int y1 = (s->y_base + y) % s->total_height;
837 TextCell *c = &s->cells[y1 * s->width + x];
838 c->ch = ' ';
839 c->t_attrib = s->t_attrib_default;
840 update_xy(s, x, y);
841}
842
843static void console_putchar(TextConsole *s, int ch)
844{
845 TextCell *c;
846 int y1, i;
847 int x, y;
848
849 switch(s->state) {
850 case TTY_STATE_NORM:
851 switch(ch) {
852 case '\r': /* carriage return */
853 s->x = 0;
854 break;
855 case '\n': /* newline */
856 console_put_lf(s);
857 break;
858 case '\b': /* backspace */
859 if (s->x > 0)
860 s->x--;
861 break;
862 case '\t': /* tabspace */
863 if (s->x + (8 - (s->x % 8)) > s->width) {
864 s->x = 0;
865 console_put_lf(s);
866 } else {
867 s->x = s->x + (8 - (s->x % 8));
868 }
869 break;
870 case '\a': /* alert aka. bell */
871 /* TODO: has to be implemented */
872 break;
873 case 14:
874 /* SI (shift in), character set 0 (ignored) */
875 break;
876 case 15:
877 /* SO (shift out), character set 1 (ignored) */
878 break;
879 case 27: /* esc (introducing an escape sequence) */
880 s->state = TTY_STATE_ESC;
881 break;
882 default:
883 if (s->x >= s->width) {
884 /* line wrap */
885 s->x = 0;
886 console_put_lf(s);
887 }
888 y1 = (s->y_base + s->y) % s->total_height;
889 c = &s->cells[y1 * s->width + s->x];
890 c->ch = ch;
891 c->t_attrib = s->t_attrib;
892 update_xy(s, s->x, s->y);
893 s->x++;
894 break;
895 }
896 break;
897 case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
898 if (ch == '[') {
899 for(i=0;i<MAX_ESC_PARAMS;i++)
900 s->esc_params[i] = 0;
901 s->nb_esc_params = 0;
902 s->state = TTY_STATE_CSI;
903 } else {
904 s->state = TTY_STATE_NORM;
905 }
906 break;
907 case TTY_STATE_CSI: /* handle escape sequence parameters */
908 if (ch >= '0' && ch <= '9') {
909 if (s->nb_esc_params < MAX_ESC_PARAMS) {
910 s->esc_params[s->nb_esc_params] =
911 s->esc_params[s->nb_esc_params] * 10 + ch - '0';
912 }
913 } else {
914 s->nb_esc_params++;
915 if (ch == ';')
916 break;
917#ifdef DEBUG_CONSOLE
918 fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
919 s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
920#endif
921 s->state = TTY_STATE_NORM;
922 switch(ch) {
923 case 'A':
924 /* move cursor up */
925 if (s->esc_params[0] == 0) {
926 s->esc_params[0] = 1;
927 }
928 s->y -= s->esc_params[0];
929 if (s->y < 0) {
930 s->y = 0;
931 }
932 break;
933 case 'B':
934 /* move cursor down */
935 if (s->esc_params[0] == 0) {
936 s->esc_params[0] = 1;
937 }
938 s->y += s->esc_params[0];
939 if (s->y >= s->height) {
940 s->y = s->height - 1;
941 }
942 break;
943 case 'C':
944 /* move cursor right */
945 if (s->esc_params[0] == 0) {
946 s->esc_params[0] = 1;
947 }
948 s->x += s->esc_params[0];
949 if (s->x >= s->width) {
950 s->x = s->width - 1;
951 }
952 break;
953 case 'D':
954 /* move cursor left */
955 if (s->esc_params[0] == 0) {
956 s->esc_params[0] = 1;
957 }
958 s->x -= s->esc_params[0];
959 if (s->x < 0) {
960 s->x = 0;
961 }
962 break;
963 case 'G':
964 /* move cursor to column */
965 s->x = s->esc_params[0] - 1;
966 if (s->x < 0) {
967 s->x = 0;
968 }
969 break;
970 case 'f':
971 case 'H':
972 /* move cursor to row, column */
973 s->x = s->esc_params[1] - 1;
974 if (s->x < 0) {
975 s->x = 0;
976 }
977 s->y = s->esc_params[0] - 1;
978 if (s->y < 0) {
979 s->y = 0;
980 }
981 break;
982 case 'J':
983 switch (s->esc_params[0]) {
984 case 0:
985 /* clear to end of screen */
986 for (y = s->y; y < s->height; y++) {
987 for (x = 0; x < s->width; x++) {
988 if (y == s->y && x < s->x) {
989 continue;
990 }
991 console_clear_xy(s, x, y);
992 }
993 }
994 break;
995 case 1:
996 /* clear from beginning of screen */
997 for (y = 0; y <= s->y; y++) {
998 for (x = 0; x < s->width; x++) {
999 if (y == s->y && x > s->x) {
1000 break;
1001 }
1002 console_clear_xy(s, x, y);
1003 }
1004 }
1005 break;
1006 case 2:
1007 /* clear entire screen */
1008 for (y = 0; y <= s->height; y++) {
1009 for (x = 0; x < s->width; x++) {
1010 console_clear_xy(s, x, y);
1011 }
1012 }
1013 break;
1014 }
1015 case 'K':
1016 switch (s->esc_params[0]) {
1017 case 0:
1018 /* clear to eol */
1019 for(x = s->x; x < s->width; x++) {
1020 console_clear_xy(s, x, s->y);
1021 }
1022 break;
1023 case 1:
1024 /* clear from beginning of line */
1025 for (x = 0; x <= s->x; x++) {
1026 console_clear_xy(s, x, s->y);
1027 }
1028 break;
1029 case 2:
1030 /* clear entire line */
1031 for(x = 0; x < s->width; x++) {
1032 console_clear_xy(s, x, s->y);
1033 }
1034 break;
1035 }
1036 break;
1037 case 'm':
1038 console_handle_escape(s);
1039 break;
1040 case 'n':
1041 /* report cursor position */
1042 /* TODO: send ESC[row;colR */
1043 break;
1044 case 's':
1045 /* save cursor position */
1046 s->x_saved = s->x;
1047 s->y_saved = s->y;
1048 break;
1049 case 'u':
1050 /* restore cursor position */
1051 s->x = s->x_saved;
1052 s->y = s->y_saved;
1053 break;
1054 default:
1055#ifdef DEBUG_CONSOLE
1056 fprintf(stderr, "unhandled escape character '%c'\n", ch);
1057#endif
1058 break;
1059 }
1060 break;
1061 }
1062 }
1063}
1064
1065void console_select(unsigned int index)
1066{
1067 TextConsole *s;
1068
1069 if (index >= MAX_CONSOLES)
1070 return;
1071 active_console->g_width = ds_get_width(active_console->ds);
1072 active_console->g_height = ds_get_height(active_console->ds);
1073 s = consoles[index];
1074 if (s) {
1075 DisplayState *ds = s->ds;
1076 active_console = s;
1077 if (ds_get_bits_per_pixel(s->ds)) {
1078 ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
1079 } else {
1080 s->ds->surface->width = s->width;
1081 s->ds->surface->height = s->height;
1082 }
1083 dpy_resize(s->ds);
1084 vga_hw_invalidate();
1085 }
1086}
1087
1088static void text_console_invalidate(void *opaque)
1089{
1090 TextConsole *s = (TextConsole *) opaque;
1091 if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
1092 s->g_width = ds_get_width(s->ds);
1093 s->g_height = ds_get_height(s->ds);
1094 text_console_resize(s);
1095 }
1096 console_refresh(s);
1097}
1098
1099static TextConsole *get_graphic_console(DisplayState *ds)
1100{
1101 int i;
1102 TextConsole *s;
1103 for (i = 0; i < nb_consoles; i++) {
1104 s = consoles[i];
1105 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
1106 return s;
1107 }
1108 return NULL;
1109}
1110
1111static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
1112{
1113 TextConsole *s;
1114 int i;
1115
1116 if (nb_consoles >= MAX_CONSOLES)
1117 return NULL;
1118 s = qemu_mallocz(sizeof(TextConsole));
1119 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
1120 (console_type == GRAPHIC_CONSOLE))) {
1121 active_console = s;
1122 }
1123 s->ds = ds;
1124 s->console_type = console_type;
1125 if (console_type != GRAPHIC_CONSOLE) {
1126 consoles[nb_consoles++] = s;
1127 } else {
1128 /* HACK: Put graphical consoles before text consoles. */
1129 for (i = nb_consoles; i > 0; i--) {
1130 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
1131 break;
1132 consoles[i] = consoles[i - 1];
1133 }
1134 consoles[i] = s;
1135 nb_consoles++;
1136 }
1137 return s;
1138}
1139
1140static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
1141{
1142 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1143
1144 surface->width = width;
1145 surface->height = height;
1146 surface->linesize = width * 4;
1147 surface->pf = qemu_default_pixelformat(32);
1148#ifdef HOST_WORDS_BIGENDIAN
1149 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1150#else
1151 surface->flags = QEMU_ALLOCATED_FLAG;
1152#endif
1153 surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
1154
1155 return surface;
1156}
1157
1158static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
1159 int width, int height)
1160{
1161 surface->width = width;
1162 surface->height = height;
1163 surface->linesize = width * 4;
1164 surface->pf = qemu_default_pixelformat(32);
1165 if (surface->flags & QEMU_ALLOCATED_FLAG)
1166 surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
1167 else
1168 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
1169#ifdef HOST_WORDS_BIGENDIAN
1170 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
1171#else
1172 surface->flags = QEMU_ALLOCATED_FLAG;
1173#endif
1174
1175 return surface;
1176}
1177
1178DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
1179 int linesize, uint8_t *data)
1180{
1181 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
1182
1183 surface->width = width;
1184 surface->height = height;
1185 surface->linesize = linesize;
1186 surface->pf = qemu_default_pixelformat(bpp);
1187#ifdef HOST_WORDS_BIGENDIAN
1188 surface->flags = QEMU_BIG_ENDIAN_FLAG;
1189#endif
1190 surface->data = data;
1191
1192 return surface;
1193}
1194
1195static void defaultallocator_free_displaysurface(DisplaySurface *surface)
1196{
1197 if (surface == NULL)
1198 return;
1199 if (surface->flags & QEMU_ALLOCATED_FLAG)
1200 qemu_free(surface->data);
1201 qemu_free(surface);
1202}
1203
1204static struct DisplayAllocator default_allocator = {
1205 defaultallocator_create_displaysurface,
1206 defaultallocator_resize_displaysurface,
1207 defaultallocator_free_displaysurface
1208};
1209
1210static void dumb_display_init(void)
1211{
1212 DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
1213 ds->allocator = &default_allocator;
1214 ds->surface = qemu_create_displaysurface(ds, 640, 480);
1215 register_displaystate(ds);
1216}
1217
1218/***********************************************************/
1219/* register display */
1220
1221void register_displaystate(DisplayState *ds)
1222{
1223 DisplayState **s;
1224 s = &display_state;
1225 while (*s != NULL)
1226 s = &(*s)->next;
1227 ds->next = NULL;
1228 *s = ds;
1229}
1230
1231DisplayState *get_displaystate(void)
1232{
1233 if (!display_state) {
1234 dumb_display_init ();
1235 }
1236 return display_state;
1237}
1238
1239DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
1240{
1241 if(ds->allocator == &default_allocator) {
1242 DisplaySurface *surf;
1243 surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
1244 defaultallocator_free_displaysurface(ds->surface);
1245 ds->surface = surf;
1246 ds->allocator = da;
1247 }
1248 return ds->allocator;
1249}
1250
1251DisplayState *graphic_console_init(vga_hw_update_ptr update,
1252 vga_hw_invalidate_ptr invalidate,
1253 vga_hw_screen_dump_ptr screen_dump,
1254 vga_hw_text_update_ptr text_update,
1255 void *opaque)
1256{
1257 TextConsole *s;
1258 DisplayState *ds;
1259
1260 ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
1261 ds->allocator = &default_allocator;
1262#ifdef CONFIG_ANDROID
1263 ds->surface = qemu_create_displaysurface(ds, android_display_width, android_display_height);
1264#else
1265 ds->surface = qemu_create_displaysurface(ds, 640, 480);
1266#endif
1267
1268 s = new_console(ds, GRAPHIC_CONSOLE);
1269 if (s == NULL) {
1270 qemu_free_displaysurface(ds);
1271 qemu_free(ds);
1272 return NULL;
1273 }
1274 s->hw_update = update;
1275 s->hw_invalidate = invalidate;
1276 s->hw_screen_dump = screen_dump;
1277 s->hw_text_update = text_update;
1278 s->hw = opaque;
1279
1280 register_displaystate(ds);
1281 return ds;
1282}
1283
1284int is_graphic_console(void)
1285{
1286 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
1287}
1288
1289int is_fixedsize_console(void)
1290{
1291 return active_console && active_console->console_type != TEXT_CONSOLE;
1292}
1293
1294void console_color_init(DisplayState *ds)
1295{
1296 int i, j;
1297 for (j = 0; j < 2; j++) {
1298 for (i = 0; i < 8; i++) {
1299 color_table[j][i] = col_expand(ds,
1300 vga_get_color(ds, color_table_rgb[j][i]));
1301 }
1302 }
1303}
1304
1305void qemu_console_resize(DisplayState *ds, int width, int height)
1306{
1307 TextConsole *s = get_graphic_console(ds);
1308 if (!s) return;
1309
1310 s->g_width = width;
1311 s->g_height = height;
1312 if (is_graphic_console()) {
1313 ds->surface = qemu_resize_displaysurface(ds, width, height);
1314 dpy_resize(ds);
1315 }
1316}
1317
1318void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
1319 int dst_x, int dst_y, int w, int h)
1320{
1321 if (is_graphic_console()) {
1322 dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
1323 }
1324}
1325
1326PixelFormat qemu_different_endianness_pixelformat(int bpp)
1327{
1328 PixelFormat pf;
1329
1330 memset(&pf, 0x00, sizeof(PixelFormat));
1331
1332 pf.bits_per_pixel = bpp;
1333 pf.bytes_per_pixel = bpp / 8;
1334 pf.depth = bpp == 32 ? 24 : bpp;
1335
1336 switch (bpp) {
1337 case 24:
1338 pf.rmask = 0x000000FF;
1339 pf.gmask = 0x0000FF00;
1340 pf.bmask = 0x00FF0000;
1341 pf.rmax = 255;
1342 pf.gmax = 255;
1343 pf.bmax = 255;
1344 pf.rshift = 0;
1345 pf.gshift = 8;
1346 pf.bshift = 16;
1347 pf.rbits = 8;
1348 pf.gbits = 8;
1349 pf.bbits = 8;
1350 break;
1351 case 32:
1352 pf.rmask = 0x0000FF00;
1353 pf.gmask = 0x00FF0000;
1354 pf.bmask = 0xFF000000;
1355 pf.amask = 0x00000000;
1356 pf.amax = 255;
1357 pf.rmax = 255;
1358 pf.gmax = 255;
1359 pf.bmax = 255;
1360 pf.ashift = 0;
1361 pf.rshift = 8;
1362 pf.gshift = 16;
1363 pf.bshift = 24;
1364 pf.rbits = 8;
1365 pf.gbits = 8;
1366 pf.bbits = 8;
1367 pf.abits = 8;
1368 break;
1369 default:
1370 break;
1371 }
1372 return pf;
1373}
1374
1375PixelFormat qemu_default_pixelformat(int bpp)
1376{
1377 PixelFormat pf;
1378
1379 memset(&pf, 0x00, sizeof(PixelFormat));
1380
1381 pf.bits_per_pixel = bpp;
1382 pf.bytes_per_pixel = bpp / 8;
1383 pf.depth = bpp == 32 ? 24 : bpp;
1384
1385 switch (bpp) {
1386 case 15:
1387 pf.bits_per_pixel = 16;
1388 pf.bytes_per_pixel = 2;
1389 pf.rmask = 0x00007c00;
1390 pf.gmask = 0x000003E0;
1391 pf.bmask = 0x0000001F;
1392 pf.rmax = 31;
1393 pf.gmax = 31;
1394 pf.bmax = 31;
1395 pf.rshift = 10;
1396 pf.gshift = 5;
1397 pf.bshift = 0;
1398 pf.rbits = 5;
1399 pf.gbits = 5;
1400 pf.bbits = 5;
1401 break;
1402 case 16:
1403 pf.rmask = 0x0000F800;
1404 pf.gmask = 0x000007E0;
1405 pf.bmask = 0x0000001F;
1406 pf.rmax = 31;
1407 pf.gmax = 63;
1408 pf.bmax = 31;
1409 pf.rshift = 11;
1410 pf.gshift = 5;
1411 pf.bshift = 0;
1412 pf.rbits = 5;
1413 pf.gbits = 6;
1414 pf.bbits = 5;
1415 break;
1416 case 24:
1417 pf.rmask = 0x00FF0000;
1418 pf.gmask = 0x0000FF00;
1419 pf.bmask = 0x000000FF;
1420 pf.rmax = 255;
1421 pf.gmax = 255;
1422 pf.bmax = 255;
1423 pf.rshift = 16;
1424 pf.gshift = 8;
1425 pf.bshift = 0;
1426 pf.rbits = 8;
1427 pf.gbits = 8;
1428 pf.bbits = 8;
1429 case 32:
1430 pf.rmask = 0x00FF0000;
1431 pf.gmask = 0x0000FF00;
1432 pf.bmask = 0x000000FF;
1433 pf.amax = 255;
1434 pf.rmax = 255;
1435 pf.gmax = 255;
1436 pf.bmax = 255;
1437 pf.ashift = 24;
1438 pf.rshift = 16;
1439 pf.gshift = 8;
1440 pf.bshift = 0;
1441 pf.rbits = 8;
1442 pf.gbits = 8;
1443 pf.bbits = 8;
1444 pf.abits = 8;
1445 break;
1446 default:
1447 break;
1448 }
1449 return pf;
1450}
1451
1452#ifdef CONFIG_ANDROID
1453void
1454android_display_init_from(int width, int height, int rotation, int bpp)
1455{
1456 DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
1457 ds->allocator = &default_allocator;
1458 ds->surface = qemu_create_displaysurface(ds, width, height);
1459 register_displaystate(ds);
1460}
1461#endif