blob: 8fd7e7d97b96aba626cdaa220e02e50d10b3d405 [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
David 'Digit' Turnerf59442f2010-10-08 16:22:10 +020064typedef enum {
65 GRAPHIC_CONSOLE,
66 TEXT_CONSOLE,
67 TEXT_CONSOLE_FIXED_SIZE
68} console_type_t;
69
70/* ??? This is mis-named.
71 It is used for both text and graphical consoles. */
72struct TextConsole {
73 console_type_t console_type;
74 DisplayState *ds;
75 /* Graphic console state. */
76 vga_hw_update_ptr hw_update;
77 vga_hw_invalidate_ptr hw_invalidate;
78 vga_hw_screen_dump_ptr hw_screen_dump;
79 vga_hw_text_update_ptr hw_text_update;
80 void *hw;
81
82 int g_width, g_height;
83 int width;
84 int height;
85 int total_height;
86 int backscroll_height;
87 int x, y;
88 int x_saved, y_saved;
89 int y_displayed;
90 int y_base;
91 TextAttributes t_attrib_default; /* default text attributes */
92 TextAttributes t_attrib; /* currently active text attributes */
93 TextCell *cells;
94 int text_x[2], text_y[2], cursor_invalidate;
95
96 int update_x0;
97 int update_y0;
98 int update_x1;
99 int update_y1;
100
101 enum TTYState state;
102 int esc_params[MAX_ESC_PARAMS];
103 int nb_esc_params;
104
105 /* fifo for key pressed */
106 QEMUFIFO out_fifo;
107 uint8_t out_fifo_buf[16];
108 QEMUTimer *kbd_timer;
109};
110
111static DisplayState *display_state;
112static TextConsole *active_console;
113static TextConsole *consoles[MAX_CONSOLES];
114static int nb_consoles = 0;
115
116#ifdef CONFIG_ANDROID
117/* Graphic console width, height and bits per pixel.
118 * These default values can be changed with the "-android-gui" option.
119 */
120int android_display_width = 640;
121int android_display_height = 480;
122int android_display_bpp = 32;
123#endif
124
125void vga_hw_update(void)
126{
127 if (active_console && active_console->hw_update)
128 active_console->hw_update(active_console->hw);
129}
130
131void vga_hw_invalidate(void)
132{
133 if (active_console && active_console->hw_invalidate)
134 active_console->hw_invalidate(active_console->hw);
135}
136
137void vga_hw_screen_dump(const char *filename)
138{
139 TextConsole *previous_active_console;
140
141 previous_active_console = active_console;
142 active_console = consoles[0];
143 /* There is currently no way of specifying which screen we want to dump,
144 so always dump the first one. */
145 if (consoles[0]->hw_screen_dump)
146 consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
147 active_console = previous_active_console;
148}
149
150void vga_hw_text_update(console_ch_t *chardata)
151{
152 if (active_console && active_console->hw_text_update)
153 active_console->hw_text_update(active_console->hw, chardata);
154}
155
156/* convert a RGBA color to a color index usable in graphic primitives */
157static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
158{
159 unsigned int r, g, b, color;
160
161 switch(ds_get_bits_per_pixel(ds)) {
162#if 0
163 case 8:
164 r = (rgba >> 16) & 0xff;
165 g = (rgba >> 8) & 0xff;
166 b = (rgba) & 0xff;
167 color = (rgb_to_index[r] * 6 * 6) +
168 (rgb_to_index[g] * 6) +
169 (rgb_to_index[b]);
170 break;
171#endif
172 case 15:
173 r = (rgba >> 16) & 0xff;
174 g = (rgba >> 8) & 0xff;
175 b = (rgba) & 0xff;
176 color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
177 break;
178 case 16:
179 r = (rgba >> 16) & 0xff;
180 g = (rgba >> 8) & 0xff;
181 b = (rgba) & 0xff;
182 color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
183 break;
184 case 32:
185 default:
186 color = rgba;
187 break;
188 }
189 return color;
190}
191
David 'Digit' Turnerf59442f2010-10-08 16:22:10 +0200192/***********************************************************/
193/* basic char display */
194
195#define FONT_HEIGHT 16
196#define FONT_WIDTH 8
197
198#include "vgafont.h"
199
200#define cbswap_32(__x) \
201((uint32_t)( \
202 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
203 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
204 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
205 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
206
207#ifdef HOST_WORDS_BIGENDIAN
208#define PAT(x) x
209#else
210#define PAT(x) cbswap_32(x)
211#endif
212
213static const uint32_t dmask16[16] = {
214 PAT(0x00000000),
215 PAT(0x000000ff),
216 PAT(0x0000ff00),
217 PAT(0x0000ffff),
218 PAT(0x00ff0000),
219 PAT(0x00ff00ff),
220 PAT(0x00ffff00),
221 PAT(0x00ffffff),
222 PAT(0xff000000),
223 PAT(0xff0000ff),
224 PAT(0xff00ff00),
225 PAT(0xff00ffff),
226 PAT(0xffff0000),
227 PAT(0xffff00ff),
228 PAT(0xffffff00),
229 PAT(0xffffffff),
230};
231
232static const uint32_t dmask4[4] = {
233 PAT(0x00000000),
234 PAT(0x0000ffff),
235 PAT(0xffff0000),
236 PAT(0xffffffff),
237};
238
239static uint32_t color_table[2][8];
240
241enum color_names {
242 COLOR_BLACK = 0,
243 COLOR_RED = 1,
244 COLOR_GREEN = 2,
245 COLOR_YELLOW = 3,
246 COLOR_BLUE = 4,
247 COLOR_MAGENTA = 5,
248 COLOR_CYAN = 6,
249 COLOR_WHITE = 7
250};
251
252static const uint32_t color_table_rgb[2][8] = {
253 { /* dark */
254 QEMU_RGB(0x00, 0x00, 0x00), /* black */
255 QEMU_RGB(0xaa, 0x00, 0x00), /* red */
256 QEMU_RGB(0x00, 0xaa, 0x00), /* green */
257 QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
258 QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
259 QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
260 QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
261 QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
262 },
263 { /* bright */
264 QEMU_RGB(0x00, 0x00, 0x00), /* black */
265 QEMU_RGB(0xff, 0x00, 0x00), /* red */
266 QEMU_RGB(0x00, 0xff, 0x00), /* green */
267 QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
268 QEMU_RGB(0x00, 0x00, 0xff), /* blue */
269 QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
270 QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
271 QEMU_RGB(0xff, 0xff, 0xff), /* white */
272 }
273};
274
275static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
276{
277 switch(ds_get_bits_per_pixel(ds)) {
278 case 8:
279 col |= col << 8;
280 col |= col << 16;
281 break;
282 case 15:
283 case 16:
284 col |= col << 16;
285 break;
286 default:
287 break;
288 }
289
290 return col;
291}
292#ifdef DEBUG_CONSOLE
293static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
294{
295 if (t_attrib->bold) {
296 printf("b");
297 } else {
298 printf(" ");
299 }
300 if (t_attrib->uline) {
301 printf("u");
302 } else {
303 printf(" ");
304 }
305 if (t_attrib->blink) {
306 printf("l");
307 } else {
308 printf(" ");
309 }
310 if (t_attrib->invers) {
311 printf("i");
312 } else {
313 printf(" ");
314 }
315 if (t_attrib->unvisible) {
316 printf("n");
317 } else {
318 printf(" ");
319 }
320
321 printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
322}
323#endif
324
David 'Digit' Turnerf59442f2010-10-08 16:22:10 +0200325void console_select(unsigned int index)
326{
327 TextConsole *s;
328
329 if (index >= MAX_CONSOLES)
330 return;
331 active_console->g_width = ds_get_width(active_console->ds);
332 active_console->g_height = ds_get_height(active_console->ds);
333 s = consoles[index];
334 if (s) {
335 DisplayState *ds = s->ds;
336 active_console = s;
337 if (ds_get_bits_per_pixel(s->ds)) {
338 ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
339 } else {
340 s->ds->surface->width = s->width;
341 s->ds->surface->height = s->height;
342 }
343 dpy_resize(s->ds);
344 vga_hw_invalidate();
345 }
346}
347
David 'Digit' Turnerf59442f2010-10-08 16:22:10 +0200348static TextConsole *get_graphic_console(DisplayState *ds)
349{
350 int i;
351 TextConsole *s;
352 for (i = 0; i < nb_consoles; i++) {
353 s = consoles[i];
354 if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
355 return s;
356 }
357 return NULL;
358}
359
360static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
361{
362 TextConsole *s;
363 int i;
364
365 if (nb_consoles >= MAX_CONSOLES)
366 return NULL;
367 s = qemu_mallocz(sizeof(TextConsole));
368 if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
369 (console_type == GRAPHIC_CONSOLE))) {
370 active_console = s;
371 }
372 s->ds = ds;
373 s->console_type = console_type;
374 if (console_type != GRAPHIC_CONSOLE) {
375 consoles[nb_consoles++] = s;
376 } else {
377 /* HACK: Put graphical consoles before text consoles. */
378 for (i = nb_consoles; i > 0; i--) {
379 if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
380 break;
381 consoles[i] = consoles[i - 1];
382 }
383 consoles[i] = s;
384 nb_consoles++;
385 }
386 return s;
387}
388
389static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
390{
391 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
392
393 surface->width = width;
394 surface->height = height;
395 surface->linesize = width * 4;
396 surface->pf = qemu_default_pixelformat(32);
397#ifdef HOST_WORDS_BIGENDIAN
398 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
399#else
400 surface->flags = QEMU_ALLOCATED_FLAG;
401#endif
402 surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
403
404 return surface;
405}
406
407static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
408 int width, int height)
409{
410 surface->width = width;
411 surface->height = height;
412 surface->linesize = width * 4;
413 surface->pf = qemu_default_pixelformat(32);
414 if (surface->flags & QEMU_ALLOCATED_FLAG)
415 surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
416 else
417 surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
418#ifdef HOST_WORDS_BIGENDIAN
419 surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
420#else
421 surface->flags = QEMU_ALLOCATED_FLAG;
422#endif
423
424 return surface;
425}
426
427DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
428 int linesize, uint8_t *data)
429{
430 DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
431
432 surface->width = width;
433 surface->height = height;
434 surface->linesize = linesize;
435 surface->pf = qemu_default_pixelformat(bpp);
436#ifdef HOST_WORDS_BIGENDIAN
437 surface->flags = QEMU_BIG_ENDIAN_FLAG;
438#endif
439 surface->data = data;
440
441 return surface;
442}
443
444static void defaultallocator_free_displaysurface(DisplaySurface *surface)
445{
446 if (surface == NULL)
447 return;
448 if (surface->flags & QEMU_ALLOCATED_FLAG)
449 qemu_free(surface->data);
450 qemu_free(surface);
451}
452
453static struct DisplayAllocator default_allocator = {
454 defaultallocator_create_displaysurface,
455 defaultallocator_resize_displaysurface,
456 defaultallocator_free_displaysurface
457};
458
459static void dumb_display_init(void)
460{
461 DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
462 ds->allocator = &default_allocator;
463 ds->surface = qemu_create_displaysurface(ds, 640, 480);
464 register_displaystate(ds);
465}
466
467/***********************************************************/
468/* register display */
469
470void register_displaystate(DisplayState *ds)
471{
472 DisplayState **s;
473 s = &display_state;
474 while (*s != NULL)
475 s = &(*s)->next;
476 ds->next = NULL;
477 *s = ds;
478}
479
480DisplayState *get_displaystate(void)
481{
482 if (!display_state) {
483 dumb_display_init ();
484 }
485 return display_state;
486}
487
488DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
489{
490 if(ds->allocator == &default_allocator) {
491 DisplaySurface *surf;
492 surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
493 defaultallocator_free_displaysurface(ds->surface);
494 ds->surface = surf;
495 ds->allocator = da;
496 }
497 return ds->allocator;
498}
499
500DisplayState *graphic_console_init(vga_hw_update_ptr update,
501 vga_hw_invalidate_ptr invalidate,
502 vga_hw_screen_dump_ptr screen_dump,
503 vga_hw_text_update_ptr text_update,
504 void *opaque)
505{
506 TextConsole *s;
507 DisplayState *ds;
508
509 ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
510 ds->allocator = &default_allocator;
511#ifdef CONFIG_ANDROID
512 ds->surface = qemu_create_displaysurface(ds, android_display_width, android_display_height);
513#else
514 ds->surface = qemu_create_displaysurface(ds, 640, 480);
515#endif
516
517 s = new_console(ds, GRAPHIC_CONSOLE);
518 if (s == NULL) {
519 qemu_free_displaysurface(ds);
520 qemu_free(ds);
521 return NULL;
522 }
523 s->hw_update = update;
524 s->hw_invalidate = invalidate;
525 s->hw_screen_dump = screen_dump;
526 s->hw_text_update = text_update;
527 s->hw = opaque;
528
529 register_displaystate(ds);
530 return ds;
531}
532
533int is_graphic_console(void)
534{
535 return active_console && active_console->console_type == GRAPHIC_CONSOLE;
536}
537
538int is_fixedsize_console(void)
539{
540 return active_console && active_console->console_type != TEXT_CONSOLE;
541}
542
543void console_color_init(DisplayState *ds)
544{
545 int i, j;
546 for (j = 0; j < 2; j++) {
547 for (i = 0; i < 8; i++) {
548 color_table[j][i] = col_expand(ds,
549 vga_get_color(ds, color_table_rgb[j][i]));
550 }
551 }
552}
553
554void qemu_console_resize(DisplayState *ds, int width, int height)
555{
556 TextConsole *s = get_graphic_console(ds);
557 if (!s) return;
558
559 s->g_width = width;
560 s->g_height = height;
561 if (is_graphic_console()) {
562 ds->surface = qemu_resize_displaysurface(ds, width, height);
563 dpy_resize(ds);
564 }
565}
566
567void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
568 int dst_x, int dst_y, int w, int h)
569{
570 if (is_graphic_console()) {
571 dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
572 }
573}
574
575PixelFormat qemu_different_endianness_pixelformat(int bpp)
576{
577 PixelFormat pf;
578
579 memset(&pf, 0x00, sizeof(PixelFormat));
580
581 pf.bits_per_pixel = bpp;
582 pf.bytes_per_pixel = bpp / 8;
583 pf.depth = bpp == 32 ? 24 : bpp;
584
585 switch (bpp) {
586 case 24:
587 pf.rmask = 0x000000FF;
588 pf.gmask = 0x0000FF00;
589 pf.bmask = 0x00FF0000;
590 pf.rmax = 255;
591 pf.gmax = 255;
592 pf.bmax = 255;
593 pf.rshift = 0;
594 pf.gshift = 8;
595 pf.bshift = 16;
596 pf.rbits = 8;
597 pf.gbits = 8;
598 pf.bbits = 8;
599 break;
600 case 32:
601 pf.rmask = 0x0000FF00;
602 pf.gmask = 0x00FF0000;
603 pf.bmask = 0xFF000000;
604 pf.amask = 0x00000000;
605 pf.amax = 255;
606 pf.rmax = 255;
607 pf.gmax = 255;
608 pf.bmax = 255;
609 pf.ashift = 0;
610 pf.rshift = 8;
611 pf.gshift = 16;
612 pf.bshift = 24;
613 pf.rbits = 8;
614 pf.gbits = 8;
615 pf.bbits = 8;
616 pf.abits = 8;
617 break;
618 default:
619 break;
620 }
621 return pf;
622}
623
624PixelFormat qemu_default_pixelformat(int bpp)
625{
626 PixelFormat pf;
627
628 memset(&pf, 0x00, sizeof(PixelFormat));
629
630 pf.bits_per_pixel = bpp;
631 pf.bytes_per_pixel = bpp / 8;
632 pf.depth = bpp == 32 ? 24 : bpp;
633
634 switch (bpp) {
635 case 15:
636 pf.bits_per_pixel = 16;
637 pf.bytes_per_pixel = 2;
638 pf.rmask = 0x00007c00;
639 pf.gmask = 0x000003E0;
640 pf.bmask = 0x0000001F;
641 pf.rmax = 31;
642 pf.gmax = 31;
643 pf.bmax = 31;
644 pf.rshift = 10;
645 pf.gshift = 5;
646 pf.bshift = 0;
647 pf.rbits = 5;
648 pf.gbits = 5;
649 pf.bbits = 5;
650 break;
651 case 16:
652 pf.rmask = 0x0000F800;
653 pf.gmask = 0x000007E0;
654 pf.bmask = 0x0000001F;
655 pf.rmax = 31;
656 pf.gmax = 63;
657 pf.bmax = 31;
658 pf.rshift = 11;
659 pf.gshift = 5;
660 pf.bshift = 0;
661 pf.rbits = 5;
662 pf.gbits = 6;
663 pf.bbits = 5;
664 break;
665 case 24:
666 pf.rmask = 0x00FF0000;
667 pf.gmask = 0x0000FF00;
668 pf.bmask = 0x000000FF;
669 pf.rmax = 255;
670 pf.gmax = 255;
671 pf.bmax = 255;
672 pf.rshift = 16;
673 pf.gshift = 8;
674 pf.bshift = 0;
675 pf.rbits = 8;
676 pf.gbits = 8;
677 pf.bbits = 8;
678 case 32:
679 pf.rmask = 0x00FF0000;
680 pf.gmask = 0x0000FF00;
681 pf.bmask = 0x000000FF;
682 pf.amax = 255;
683 pf.rmax = 255;
684 pf.gmax = 255;
685 pf.bmax = 255;
686 pf.ashift = 24;
687 pf.rshift = 16;
688 pf.gshift = 8;
689 pf.bshift = 0;
690 pf.rbits = 8;
691 pf.gbits = 8;
692 pf.bbits = 8;
693 pf.abits = 8;
694 break;
695 default:
696 break;
697 }
698 return pf;
699}
700
701#ifdef CONFIG_ANDROID
702void
703android_display_init_from(int width, int height, int rotation, int bpp)
704{
705 DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
706 ds->allocator = &default_allocator;
707 ds->surface = qemu_create_displaysurface(ds, width, height);
708 register_displaystate(ds);
709}
710#endif