#include "qemu-common.h"
#include "console.h"
#include "qemu-timer.h"
+#include "qmp-commands.h"
//#define DEBUG_CONSOLE
#define DEFAULT_BACKSCROLL 512
#define MAX_CONSOLES 12
+#define CONSOLE_CURSOR_PERIOD 500
#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)
TEXT_CONSOLE_FIXED_SIZE
} console_type_t;
-/* ??? This is mis-named.
- It is used for both text and graphical consoles. */
-struct TextConsole {
+struct QemuConsole {
int index;
console_type_t console_type;
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;
vga_hw_text_update_ptr hw_text_update;
void *hw;
-
int g_width, g_height;
+
+ /* Text console state */
int width;
int height;
int total_height;
TextCell *cells;
int text_x[2], text_y[2], cursor_invalidate;
int echo;
+ bool cursor_visible_phase;
+ QEMUTimer *cursor_timer;
int update_x0;
int update_y0;
};
static DisplayState *display_state;
-static TextConsole *active_console;
-static TextConsole *consoles[MAX_CONSOLES];
+static QemuConsole *active_console;
+static QemuConsole *consoles[MAX_CONSOLES];
static int nb_consoles = 0;
void vga_hw_update(void)
active_console->hw_invalidate(active_console->hw);
}
-void vga_hw_screen_dump(const char *filename)
+void qmp_screendump(const char *filename, Error **errp)
{
- TextConsole *previous_active_console;
+ QemuConsole *previous_active_console;
+ bool cswitch;
previous_active_console = active_console;
+ cswitch = previous_active_console && previous_active_console->index != 0;
/* There is currently no way of specifying which screen we want to dump,
so always dump the first one. */
- console_select(0);
+ if (cswitch) {
+ console_select(0);
+ }
if (consoles[0] && consoles[0]->hw_screen_dump) {
- consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
+ consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
+ } else {
+ error_setg(errp, "device doesn't support screendump\n");
}
- if (previous_active_console) {
+ if (cswitch) {
console_select(previous_active_console->index);
}
}
}
}
-static void text_console_resize(TextConsole *s)
+static void text_console_resize(QemuConsole *s)
{
TextCell *cells, *c, *c1;
int w1, x, y, last_width;
s->cells = cells;
}
-static inline void text_update_xy(TextConsole *s, int x, int y)
+static inline void text_update_xy(QemuConsole *s, int x, int y)
{
s->text_x[0] = MIN(s->text_x[0], x);
s->text_x[1] = MAX(s->text_x[1], x);
s->text_y[1] = MAX(s->text_y[1], y);
}
-static void invalidate_xy(TextConsole *s, int x, int y)
+static void invalidate_xy(QemuConsole *s, int x, int y)
{
if (s->update_x0 > x * FONT_WIDTH)
s->update_x0 = x * FONT_WIDTH;
s->update_y1 = (y + 1) * FONT_HEIGHT;
}
-static void update_xy(TextConsole *s, int x, int y)
+static void update_xy(QemuConsole *s, int x, int y)
{
TextCell *c;
int y1, y2;
}
}
-static void console_show_cursor(TextConsole *s, int show)
+static void console_show_cursor(QemuConsole *s, int show)
{
TextCell *c;
int y, y1;
y += s->total_height;
if (y < s->height) {
c = &s->cells[y1 * s->width + x];
- if (show) {
+ if (show && s->cursor_visible_phase) {
TextAttributes t_attrib = s->t_attrib_default;
t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
}
}
-static void console_refresh(TextConsole *s)
+static void console_refresh(QemuConsole *s)
{
TextCell *c;
int x, y, y1;
if (s != active_console)
return;
- if (!ds_get_bits_per_pixel(s->ds)) {
+
+ if (s->ds->have_text) {
s->text_x[0] = 0;
s->text_y[0] = 0;
s->text_x[1] = s->width - 1;
s->text_y[1] = s->height - 1;
s->cursor_invalidate = 1;
- return;
}
- vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
- color_table[0][COLOR_BLACK]);
- y1 = s->y_displayed;
- for(y = 0; y < s->height; y++) {
- c = s->cells + y1 * s->width;
- for(x = 0; x < s->width; x++) {
- vga_putcharxy(s->ds, x, y, c->ch,
- &(c->t_attrib));
- c++;
+ if (s->ds->have_gfx) {
+ vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
+ color_table[0][COLOR_BLACK]);
+ y1 = s->y_displayed;
+ for (y = 0; y < s->height; y++) {
+ c = s->cells + y1 * s->width;
+ for (x = 0; x < s->width; x++) {
+ vga_putcharxy(s->ds, x, y, c->ch,
+ &(c->t_attrib));
+ c++;
+ }
+ if (++y1 == s->total_height) {
+ y1 = 0;
+ }
}
- if (++y1 == s->total_height)
- y1 = 0;
+ console_show_cursor(s, 1);
+ dpy_gfx_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
}
- console_show_cursor(s, 1);
- dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
}
static void console_scroll(int ydelta)
{
- TextConsole *s;
+ QemuConsole *s;
int i, y1;
s = active_console;
console_refresh(s);
}
-static void console_put_lf(TextConsole *s)
+static void console_put_lf(QemuConsole *s)
{
TextCell *c;
int x, y1;
* NOTE: I know this code is not very efficient (checking every color for it
* self) but it is more readable and better maintainable.
*/
-static void console_handle_escape(TextConsole *s)
+static void console_handle_escape(QemuConsole *s)
{
int i;
}
}
-static void console_clear_xy(TextConsole *s, int x, int y)
+static void console_clear_xy(QemuConsole *s, int x, int y)
{
int y1 = (s->y_base + y) % s->total_height;
TextCell *c = &s->cells[y1 * s->width + x];
update_xy(s, x, y);
}
-static void console_putchar(TextConsole *s, int ch)
+/* set cursor, checking bounds */
+static void set_cursor(QemuConsole *s, int x, int y)
+{
+ if (x < 0) {
+ x = 0;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ if (y >= s->height) {
+ y = s->height - 1;
+ }
+ if (x >= s->width) {
+ x = s->width - 1;
+ }
+
+ s->x = x;
+ s->y = y;
+}
+
+static void console_putchar(QemuConsole *s, int ch)
{
TextCell *c;
int y1, i;
case TTY_STATE_CSI: /* handle escape sequence parameters */
if (ch >= '0' && ch <= '9') {
if (s->nb_esc_params < MAX_ESC_PARAMS) {
- s->esc_params[s->nb_esc_params] =
- s->esc_params[s->nb_esc_params] * 10 + ch - '0';
+ int *param = &s->esc_params[s->nb_esc_params];
+ int digit = (ch - '0');
+
+ *param = (*param <= (INT_MAX - digit) / 10) ?
+ *param * 10 + digit : INT_MAX;
}
} else {
- s->nb_esc_params++;
+ if (s->nb_esc_params < MAX_ESC_PARAMS)
+ s->nb_esc_params++;
if (ch == ';')
break;
#ifdef DEBUG_CONSOLE
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->y -= s->esc_params[0];
- if (s->y < 0) {
- s->y = 0;
- }
+ set_cursor(s, s->x, s->y - s->esc_params[0]);
break;
case 'B':
/* move cursor down */
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->y += s->esc_params[0];
- if (s->y >= s->height) {
- s->y = s->height - 1;
- }
+ set_cursor(s, s->x, s->y + s->esc_params[0]);
break;
case 'C':
/* move cursor right */
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->x += s->esc_params[0];
- if (s->x >= s->width) {
- s->x = s->width - 1;
- }
+ set_cursor(s, s->x + s->esc_params[0], s->y);
break;
case 'D':
/* move cursor left */
if (s->esc_params[0] == 0) {
s->esc_params[0] = 1;
}
- s->x -= s->esc_params[0];
- if (s->x < 0) {
- s->x = 0;
- }
+ set_cursor(s, s->x - s->esc_params[0], s->y);
break;
case 'G':
/* move cursor to column */
- s->x = s->esc_params[0] - 1;
- if (s->x < 0) {
- s->x = 0;
- }
+ set_cursor(s, s->esc_params[0] - 1, s->y);
break;
case 'f':
case 'H':
/* move cursor to row, column */
- s->x = s->esc_params[1] - 1;
- if (s->x < 0) {
- s->x = 0;
- }
- s->y = s->esc_params[0] - 1;
- if (s->y < 0) {
- s->y = 0;
- }
+ set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
break;
case 'J':
switch (s->esc_params[0]) {
void console_select(unsigned int index)
{
- TextConsole *s;
+ QemuConsole *s;
if (index >= MAX_CONSOLES)
return;
s = consoles[index];
if (s) {
DisplayState *ds = s->ds;
+
+ if (active_console && active_console->cursor_timer) {
+ qemu_del_timer(active_console->cursor_timer);
+ }
active_console = s;
- if (ds_get_bits_per_pixel(s->ds)) {
+ if (ds->have_gfx) {
ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
- } else {
- s->ds->surface->width = s->width;
- s->ds->surface->height = s->height;
+ dpy_gfx_resize(ds);
+ }
+ if (ds->have_text) {
+ dpy_text_resize(ds, s->width, s->height);
+ }
+ if (s->cursor_timer) {
+ qemu_mod_timer(s->cursor_timer,
+ qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
}
- dpy_resize(s->ds);
vga_hw_invalidate();
}
}
static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
{
- TextConsole *s = chr->opaque;
+ QemuConsole *s = chr->opaque;
int i;
s->update_x0 = s->width * FONT_WIDTH;
console_putchar(s, buf[i]);
}
console_show_cursor(s, 1);
- if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
- dpy_update(s->ds, s->update_x0, s->update_y0,
- s->update_x1 - s->update_x0,
- s->update_y1 - s->update_y0);
+ if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
+ dpy_gfx_update(s->ds, s->update_x0, s->update_y0,
+ s->update_x1 - s->update_x0,
+ s->update_y1 - s->update_y0);
}
return len;
}
static void kbd_send_chars(void *opaque)
{
- TextConsole *s = opaque;
+ QemuConsole *s = opaque;
int len;
uint8_t buf[16];
/* called when an ascii key is pressed */
void kbd_put_keysym(int keysym)
{
- TextConsole *s;
+ QemuConsole *s;
uint8_t buf[16], *q;
int c;
static void text_console_invalidate(void *opaque)
{
- TextConsole *s = (TextConsole *) opaque;
+ QemuConsole *s = (QemuConsole *) opaque;
if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
s->g_width = ds_get_width(s->ds);
s->g_height = ds_get_height(s->ds);
static void text_console_update(void *opaque, console_ch_t *chardata)
{
- TextConsole *s = (TextConsole *) opaque;
+ QemuConsole *s = (QemuConsole *) opaque;
int i, j, src;
if (s->text_x[0] <= s->text_x[1]) {
(s->cells[src].t_attrib.fgcol << 12) |
(s->cells[src].t_attrib.bgcol << 8) |
(s->cells[src].t_attrib.bold << 21));
- dpy_update(s->ds, s->text_x[0], s->text_y[0],
- s->text_x[1] - s->text_x[0], i - s->text_y[0]);
+ dpy_text_update(s->ds, s->text_x[0], s->text_y[0],
+ s->text_x[1] - s->text_x[0], i - s->text_y[0]);
s->text_x[0] = s->width;
s->text_y[0] = s->height;
s->text_x[1] = 0;
s->text_y[1] = 0;
}
if (s->cursor_invalidate) {
- dpy_cursor(s->ds, s->x, s->y);
+ dpy_text_cursor(s->ds, s->x, s->y);
s->cursor_invalidate = 0;
}
}
-static TextConsole *get_graphic_console(DisplayState *ds)
+static QemuConsole *get_graphic_console(DisplayState *ds)
{
int i;
- TextConsole *s;
+ QemuConsole *s;
for (i = 0; i < nb_consoles; i++) {
s = consoles[i];
if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
return NULL;
}
-static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
+static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
{
- TextConsole *s;
+ QemuConsole *s;
int i;
if (nb_consoles >= MAX_CONSOLES)
return NULL;
- s = g_malloc0(sizeof(TextConsole));
+ s = g_malloc0(sizeof(QemuConsole));
if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
(console_type == GRAPHIC_CONSOLE))) {
active_console = s;
return s;
}
-static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
+static void qemu_alloc_display(DisplaySurface *surface, int width, int height,
+ int linesize, PixelFormat pf, int newflags)
{
- DisplaySurface *surface = (DisplaySurface*) g_malloc0(sizeof(DisplaySurface));
+ surface->pf = pf;
- int linesize = width * 4;
- qemu_alloc_display(surface, width, height, linesize,
- qemu_default_pixelformat(32), 0);
- return surface;
+ qemu_pixman_image_unref(surface->image);
+ surface->image = NULL;
+
+ surface->format = qemu_pixman_get_format(&pf);
+ assert(surface->format != 0);
+ surface->image = pixman_image_create_bits(surface->format,
+ width, height,
+ NULL, linesize);
+ assert(surface->image != NULL);
+
+ surface->flags = newflags | QEMU_ALLOCATED_FLAG;
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags |= QEMU_BIG_ENDIAN_FLAG;
+#endif
}
-static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
- int width, int height)
+DisplaySurface *qemu_create_displaysurface(DisplayState *ds,
+ int width, int height)
{
+ DisplaySurface *surface = g_new0(DisplaySurface, 1);
+
int linesize = width * 4;
qemu_alloc_display(surface, width, height, linesize,
qemu_default_pixelformat(32), 0);
return surface;
}
-void qemu_alloc_display(DisplaySurface *surface, int width, int height,
- int linesize, PixelFormat pf, int newflags)
+DisplaySurface *qemu_resize_displaysurface(DisplayState *ds,
+ int width, int height)
{
- void *data;
- surface->width = width;
- surface->height = height;
- surface->linesize = linesize;
- surface->pf = pf;
- if (surface->flags & QEMU_ALLOCATED_FLAG) {
- data = g_realloc(surface->data,
- surface->linesize * surface->height);
- } else {
- data = g_malloc(surface->linesize * surface->height);
- }
- surface->data = (uint8_t *)data;
- surface->flags = newflags | QEMU_ALLOCATED_FLAG;
-#ifdef HOST_WORDS_BIGENDIAN
- surface->flags |= QEMU_BIG_ENDIAN_FLAG;
-#endif
+ int linesize = width * 4;
+
+ trace_displaysurface_resize(ds, ds->surface, width, height);
+ qemu_alloc_display(ds->surface, width, height, linesize,
+ qemu_default_pixelformat(32), 0);
+ return ds->surface;
}
-DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
- int linesize, uint8_t *data)
+DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
+ int linesize, uint8_t *data)
{
- DisplaySurface *surface = (DisplaySurface*) g_malloc0(sizeof(DisplaySurface));
+ DisplaySurface *surface = g_new0(DisplaySurface, 1);
- surface->width = width;
- surface->height = height;
- surface->linesize = linesize;
surface->pf = qemu_default_pixelformat(bpp);
+
+ surface->format = qemu_pixman_get_format(&surface->pf);
+ assert(surface->format != 0);
+ surface->image = pixman_image_create_bits(surface->format,
+ width, height,
+ (void *)data, linesize);
+ assert(surface->image != NULL);
+
#ifdef HOST_WORDS_BIGENDIAN
surface->flags = QEMU_BIG_ENDIAN_FLAG;
#endif
- surface->data = data;
return surface;
}
-static void defaultallocator_free_displaysurface(DisplaySurface *surface)
+void qemu_free_displaysurface(DisplayState *ds)
{
- if (surface == NULL)
+ trace_displaysurface_free(ds, ds->surface);
+ if (ds->surface == NULL) {
return;
- if (surface->flags & QEMU_ALLOCATED_FLAG)
- g_free(surface->data);
- g_free(surface);
+ }
+ qemu_pixman_image_unref(ds->surface->image);
+ g_free(ds->surface);
}
-static struct DisplayAllocator default_allocator = {
- defaultallocator_create_displaysurface,
- defaultallocator_resize_displaysurface,
- defaultallocator_free_displaysurface
-};
-
static void dumb_display_init(void)
{
DisplayState *ds = g_malloc0(sizeof(DisplayState));
int width = 640;
int height = 480;
- ds->allocator = &default_allocator;
if (is_fixedsize_console()) {
width = active_console->g_width;
height = active_console->g_height;
return display_state;
}
-DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
-{
- if(ds->allocator == &default_allocator) {
- DisplaySurface *surf;
- surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
- defaultallocator_free_displaysurface(ds->surface);
- ds->surface = surf;
- ds->allocator = da;
- }
- return ds->allocator;
-}
-
DisplayState *graphic_console_init(vga_hw_update_ptr update,
vga_hw_invalidate_ptr invalidate,
vga_hw_screen_dump_ptr screen_dump,
vga_hw_text_update_ptr text_update,
void *opaque)
{
- TextConsole *s;
+ QemuConsole *s;
DisplayState *ds;
ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
- ds->allocator = &default_allocator;
ds->surface = qemu_create_displaysurface(ds, 640, 480);
s = new_console(ds, GRAPHIC_CONSOLE);
static void text_console_set_echo(CharDriverState *chr, bool echo)
{
- TextConsole *s = chr->opaque;
+ QemuConsole *s = chr->opaque;
s->echo = echo;
}
+static void text_console_update_cursor(void *opaque)
+{
+ QemuConsole *s = opaque;
+
+ s->cursor_visible_phase = !s->cursor_visible_phase;
+ vga_hw_invalidate();
+ qemu_mod_timer(s->cursor_timer,
+ qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
+}
+
static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
{
- TextConsole *s;
+ QemuConsole *s;
static int color_inited;
s = chr->opaque;
s->g_height = ds_get_height(s->ds);
}
+ s->cursor_timer =
+ qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
+
s->hw_invalidate = text_console_invalidate;
s->hw_text_update = text_console_update;
s->hw = s;
CharDriverState *text_console_init(QemuOpts *opts)
{
CharDriverState *chr;
- TextConsole *s;
+ QemuConsole *s;
unsigned width;
unsigned height;
void qemu_console_resize(DisplayState *ds, int width, int height)
{
- TextConsole *s = get_graphic_console(ds);
+ QemuConsole *s = get_graphic_console(ds);
if (!s) return;
s->g_width = width;
s->g_height = height;
if (is_graphic_console()) {
ds->surface = qemu_resize_displaysurface(ds, width, height);
- dpy_resize(ds);
+ dpy_gfx_resize(ds);
}
}
int dst_x, int dst_y, int w, int h)
{
if (is_graphic_console()) {
- dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
+ dpy_gfx_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
}
}
memset(&pf, 0x00, sizeof(PixelFormat));
pf.bits_per_pixel = bpp;
- pf.bytes_per_pixel = bpp / 8;
+ pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
pf.depth = bpp == 32 ? 24 : bpp;
switch (bpp) {
memset(&pf, 0x00, sizeof(PixelFormat));
pf.bits_per_pixel = bpp;
- pf.bytes_per_pixel = bpp / 8;
+ pf.bytes_per_pixel = DIV_ROUND_UP(bpp, 8);
pf.depth = bpp == 32 ? 24 : bpp;
switch (bpp) {
case 15:
pf.bits_per_pixel = 16;
- pf.bytes_per_pixel = 2;
pf.rmask = 0x00007c00;
pf.gmask = 0x000003E0;
pf.bmask = 0x0000001F;
pf.rmask = 0x00FF0000;
pf.gmask = 0x0000FF00;
pf.bmask = 0x000000FF;
- pf.amax = 255;
pf.rmax = 255;
pf.gmax = 255;
pf.bmax = 255;
- pf.ashift = 24;
pf.rshift = 16;
pf.gshift = 8;
pf.bshift = 0;
pf.rbits = 8;
pf.gbits = 8;
pf.bbits = 8;
- pf.abits = 8;
break;
default:
break;