/* * QEMU graphical console * * Copyright (c) 2004 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "vl.h" //#define DEBUG_CONSOLE #define DEFAULT_BACKSCROLL 512 #define MAX_CONSOLES 12 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff) typedef struct TextAttributes { uint8_t fgcol:4; uint8_t bgcol:4; uint8_t bold:1; uint8_t uline:1; uint8_t blink:1; uint8_t invers:1; uint8_t unvisible:1; } TextAttributes; typedef struct TextCell { uint8_t ch; TextAttributes t_attrib; } TextCell; #define MAX_ESC_PARAMS 3 enum TTYState { TTY_STATE_NORM, TTY_STATE_ESC, TTY_STATE_CSI, }; typedef struct QEMUFIFO { uint8_t *buf; int buf_size; int count, wptr, rptr; } QEMUFIFO; int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1) { int l, len; l = f->buf_size - f->count; if (len1 > l) len1 = l; len = len1; while (len > 0) { l = f->buf_size - f->wptr; if (l > len) l = len; memcpy(f->buf + f->wptr, buf, l); f->wptr += l; if (f->wptr >= f->buf_size) f->wptr = 0; buf += l; len -= l; } f->count += len1; return len1; } int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1) { int l, len; if (len1 > f->count) len1 = f->count; len = len1; while (len > 0) { l = f->buf_size - f->rptr; if (l > len) l = len; memcpy(buf, f->buf + f->rptr, l); f->rptr += l; if (f->rptr >= f->buf_size) f->rptr = 0; buf += l; len -= l; } f->count -= len1; return len1; } /* ??? This is mis-named. It is used for both text and graphical consoles. */ struct TextConsole { int text_console; /* true if text console */ DisplayState *ds; /* Graphic console state. */ vga_hw_update_ptr hw_update; vga_hw_invalidate_ptr hw_invalidate; vga_hw_screen_dump_ptr hw_screen_dump; void *hw; int g_width, g_height; int width; int height; int total_height; int backscroll_height; int x, y; int x_saved, y_saved; int y_displayed; int y_base; TextAttributes t_attrib_default; /* default text attributes */ TextAttributes t_attrib; /* currently active text attributes */ TextCell *cells; enum TTYState state; int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; CharDriverState *chr; /* fifo for key pressed */ QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; }; static TextConsole *active_console; static TextConsole *consoles[MAX_CONSOLES]; static int nb_consoles = 0; void vga_hw_update(void) { if (active_console && active_console->hw_update) active_console->hw_update(active_console->hw); } void vga_hw_invalidate(void) { if (active_console->hw_invalidate) active_console->hw_invalidate(active_console->hw); } void vga_hw_screen_dump(const char *filename) { /* There is currently no was of specifying which screen we want to dump, so always dump the dirst one. */ if (consoles[0]->hw_screen_dump) consoles[0]->hw_screen_dump(consoles[0]->hw, filename); } /* convert a RGBA color to a color index usable in graphic primitives */ static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) { unsigned int r, g, b, color; switch(ds->depth) { #if 0 case 8: r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba) & 0xff; color = (rgb_to_index[r] * 6 * 6) + (rgb_to_index[g] * 6) + (rgb_to_index[b]); break; #endif case 15: r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba) & 0xff; color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); break; case 16: r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba) & 0xff; color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); break; case 32: default: color = rgba; break; } return color; } static void vga_fill_rect (DisplayState *ds, int posx, int posy, int width, int height, uint32_t color) { uint8_t *d, *d1; int x, y, bpp; bpp = (ds->depth + 7) >> 3; d1 = ds->data + ds->linesize * posy + bpp * posx; for (y = 0; y < height; y++) { d = d1; switch(bpp) { case 1: for (x = 0; x < width; x++) { *((uint8_t *)d) = color; d++; } break; case 2: for (x = 0; x < width; x++) { *((uint16_t *)d) = color; d += 2; } break; case 4: for (x = 0; x < width; x++) { *((uint32_t *)d) = color; d += 4; } break; } d1 += ds->linesize; } } /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h) { const uint8_t *s; uint8_t *d; int wb, y, bpp; bpp = (ds->depth + 7) >> 3; wb = w * bpp; if (yd <= ys) { s = ds->data + ds->linesize * ys + bpp * xs; d = ds->data + ds->linesize * yd + bpp * xd; for (y = 0; y < h; y++) { memmove(d, s, wb); d += ds->linesize; s += ds->linesize; } } else { s = ds->data + ds->linesize * (ys + h - 1) + bpp * xs; d = ds->data + ds->linesize * (yd + h - 1) + bpp * xd; for (y = 0; y < h; y++) { memmove(d, s, wb); d -= ds->linesize; s -= ds->linesize; } } } /***********************************************************/ /* basic char display */ #define FONT_HEIGHT 16 #define FONT_WIDTH 8 #include "vgafont.h" #define cbswap_32(__x) \ ((uint32_t)( \ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) #ifdef WORDS_BIGENDIAN #define PAT(x) x #else #define PAT(x) cbswap_32(x) #endif static const uint32_t dmask16[16] = { PAT(0x00000000), PAT(0x000000ff), PAT(0x0000ff00), PAT(0x0000ffff), PAT(0x00ff0000), PAT(0x00ff00ff), PAT(0x00ffff00), PAT(0x00ffffff), PAT(0xff000000), PAT(0xff0000ff), PAT(0xff00ff00), PAT(0xff00ffff), PAT(0xffff0000), PAT(0xffff00ff), PAT(0xffffff00), PAT(0xffffffff), }; static const uint32_t dmask4[4] = { PAT(0x00000000), PAT(0x0000ffff), PAT(0xffff0000), PAT(0xffffffff), }; static uint32_t color_table[2][8]; enum color_names { COLOR_BLACK = 0, COLOR_RED = 1, COLOR_GREEN = 2, COLOR_YELLOW = 3, COLOR_BLUE = 4, COLOR_MAGENTA = 5, COLOR_CYAN = 6, COLOR_WHITE = 7 }; static const uint32_t color_table_rgb[2][8] = { { /* dark */ QEMU_RGB(0x00, 0x00, 0x00), /* black */ QEMU_RGB(0xaa, 0x00, 0x00), /* red */ QEMU_RGB(0x00, 0xaa, 0x00), /* green */ QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */ QEMU_RGB(0x00, 0x00, 0xaa), /* blue */ QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */ QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */ QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */ }, { /* bright */ QEMU_RGB(0x00, 0x00, 0x00), /* black */ QEMU_RGB(0xff, 0x00, 0x00), /* red */ QEMU_RGB(0x00, 0xff, 0x00), /* green */ QEMU_RGB(0xff, 0xff, 0x00), /* yellow */ QEMU_RGB(0x00, 0x00, 0xff), /* blue */ QEMU_RGB(0xff, 0x00, 0xff), /* magenta */ QEMU_RGB(0x00, 0xff, 0xff), /* cyan */ QEMU_RGB(0xff, 0xff, 0xff), /* white */ } }; static inline unsigned int col_expand(DisplayState *ds, unsigned int col) { switch(ds->depth) { case 8: col |= col << 8; col |= col << 16; break; case 15: case 16: col |= col << 16; break; default: break; } return col; } #ifdef DEBUG_CONSOLE static void console_print_text_attributes(TextAttributes *t_attrib, char ch) { if (t_attrib->bold) { printf("b"); } else { printf(" "); } if (t_attrib->uline) { printf("u"); } else { printf(" "); } if (t_attrib->blink) { printf("l"); } else { printf(" "); } if (t_attrib->invers) { printf("i"); } else { printf(" "); } if (t_attrib->unvisible) { printf("n"); } else { printf(" "); } printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch); } #endif static void vga_putcharxy(DisplayState *ds, int x, int y, int ch, TextAttributes *t_attrib) { uint8_t *d; const uint8_t *font_ptr; unsigned int font_data, linesize, xorcol, bpp; int i; unsigned int fgcol, bgcol; #ifdef DEBUG_CONSOLE printf("x: %2i y: %2i", x, y); console_print_text_attributes(t_attrib, ch); #endif if (t_attrib->invers) { bgcol = color_table[t_attrib->bold][t_attrib->fgcol]; fgcol = color_table[t_attrib->bold][t_attrib->bgcol]; } else { fgcol = color_table[t_attrib->bold][t_attrib->fgcol]; bgcol = color_table[t_attrib->bold][t_attrib->bgcol]; } bpp = (ds->depth + 7) >> 3; d = ds->data + ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH; linesize = ds->linesize; font_ptr = vgafont16 + FONT_HEIGHT * ch; xorcol = bgcol ^ fgcol; switch(ds->depth) { case 8: for(i = 0; i < FONT_HEIGHT; i++) { font_data = *font_ptr++; if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) { font_data = 0xFFFF; } ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol; ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;