*/
#include "qemu-common.h"
#include "ui/console.h"
+#include "hw/qdev-core.h"
#include "qemu/timer.h"
#include "qmp-commands.h"
#include "sysemu/char.h"
+#include "trace.h"
-//#define DEBUG_CONSOLE
#define DEFAULT_BACKSCROLL 512
#define MAX_CONSOLES 12
#define CONSOLE_CURSOR_PERIOD 500
} console_type_t;
struct QemuConsole {
+ Object parent;
+
int index;
console_type_t console_type;
DisplayState *ds;
DisplaySurface *surface;
+ int dcls;
/* Graphic console state. */
+ Object *device;
+ uint32_t head;
const GraphicHwOps *hw_ops;
void *hw;
};
struct DisplayState {
- struct QEMUTimer *gui_timer;
+ QEMUTimer *gui_timer;
+ uint64_t last_update;
+ uint64_t update_interval;
+ bool refreshing;
bool have_gfx;
bool have_text;
static int nb_consoles = 0;
static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
-static void dpy_gfx_switch_surface(DisplayState *ds,
- DisplaySurface *surface);
+static void dpy_refresh(DisplayState *s);
+static DisplayState *get_alloc_displaystate(void);
static void gui_update(void *opaque)
{
- uint64_t interval = GUI_REFRESH_INTERVAL;
+ uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
+ uint64_t dcl_interval;
DisplayState *ds = opaque;
DisplayChangeListener *dcl;
+ int i;
+ ds->refreshing = true;
dpy_refresh(ds);
+ ds->refreshing = false;
QLIST_FOREACH(dcl, &ds->listeners, next) {
- if (dcl->gui_timer_interval &&
- dcl->gui_timer_interval < interval) {
- interval = dcl->gui_timer_interval;
+ dcl_interval = dcl->update_interval ?
+ dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
+ if (interval > dcl_interval) {
+ interval = dcl_interval;
+ }
+ }
+ if (ds->update_interval != interval) {
+ ds->update_interval = interval;
+ for (i = 0; i < nb_consoles; i++) {
+ if (consoles[i]->hw_ops->update_interval) {
+ consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval);
+ }
}
+ trace_console_refresh(interval);
}
- qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
+ ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ timer_mod(ds->gui_timer, ds->last_update + interval);
}
static void gui_setup_refresh(DisplayState *ds)
}
if (need_timer && ds->gui_timer == NULL) {
- ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
- qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
+ ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
+ timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
}
if (!need_timer && ds->gui_timer != NULL) {
- qemu_del_timer(ds->gui_timer);
- qemu_free_timer(ds->gui_timer);
+ timer_del(ds->gui_timer);
+ timer_free(ds->gui_timer);
ds->gui_timer = NULL;
}
{
int width = pixman_image_get_width(ds->image);
int height = pixman_image_get_height(ds->image);
+ int fd;
FILE *f;
int y;
int ret;
pixman_image_t *linebuf;
trace_ppm_save(filename, ds);
- f = fopen(filename, "wb");
- if (!f) {
+ fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
+ if (fd == -1) {
error_setg(errp, "failed to open file '%s': %s", filename,
strerror(errno));
return;
}
+ f = fdopen(fd, "wb");
ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
if (ret < 0) {
linebuf = NULL;
void qmp_screendump(const char *filename, Error **errp)
{
- QemuConsole *con = consoles[0];
+ QemuConsole *con = qemu_console_lookup_by_index(0);
DisplaySurface *surface;
if (con == NULL) {
}
};
-#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(QemuConsole *s, int x, int y, int ch,
TextAttributes *t_attrib)
{
TextCell *c;
int y1, y2;
- if (s != active_console) {
+ if (!qemu_console_is_visible(s)) {
return;
}
int y, y1;
int x = s->x;
- if (s != active_console) {
+ if (!qemu_console_is_visible(s)) {
return;
}
TextCell *c;
int x, y, y1;
- if (s != active_console)
+ if (!qemu_console_is_visible(s)) {
return;
+ }
if (s->ds->have_text) {
s->text_x[0] = 0;
}
}
-static void console_scroll(int ydelta)
+static void console_scroll(QemuConsole *s, int ydelta)
{
- QemuConsole *s;
int i, y1;
- s = active_console;
- if (!s || (s->console_type == GRAPHIC_CONSOLE))
- return;
-
if (ydelta > 0) {
for(i = 0; i < ydelta; i++) {
if (s->y_displayed == s->y_base)
c->t_attrib = s->t_attrib_default;
c++;
}
- if (s == active_console && s->y_displayed == s->y_base) {
+ if (qemu_console_is_visible(s) && s->y_displayed == s->y_base) {
if (s->ds->have_text) {
s->text_x[0] = 0;
s->text_y[0] = 0;
s->nb_esc_params++;
if (ch == ';')
break;
-#ifdef DEBUG_CONSOLE
- fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
- s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
-#endif
+ trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
+ ch, s->nb_esc_params);
s->state = TTY_STATE_NORM;
switch(ch) {
case 'A':
s->y = s->y_saved;
break;
default:
-#ifdef DEBUG_CONSOLE
- fprintf(stderr, "unhandled escape character '%c'\n", ch);
-#endif
+ trace_console_putchar_unhandled(ch);
break;
}
break;
void console_select(unsigned int index)
{
+ DisplayChangeListener *dcl;
QemuConsole *s;
if (index >= MAX_CONSOLES)
return;
trace_console_select(index);
- s = consoles[index];
+ s = qemu_console_lookup_by_index(index);
if (s) {
DisplayState *ds = s->ds;
if (active_console && active_console->cursor_timer) {
- qemu_del_timer(active_console->cursor_timer);
+ timer_del(active_console->cursor_timer);
}
active_console = s;
if (ds->have_gfx) {
- dpy_gfx_switch_surface(ds, s->surface);
+ QLIST_FOREACH(dcl, &ds->listeners, next) {
+ if (dcl->con != NULL) {
+ continue;
+ }
+ if (dcl->ops->dpy_gfx_switch) {
+ dcl->ops->dpy_gfx_switch(dcl, s->surface);
+ }
+ }
dpy_gfx_update(s, 0, 0, surface_width(s->surface),
surface_height(s->surface));
}
dpy_text_resize(s, s->width, s->height);
}
if (s->cursor_timer) {
- qemu_mod_timer(s->cursor_timer,
- qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
+ timer_mod(s->cursor_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
}
}
}
/* characters are pending: we send them a bit later (XXX:
horrible, should change char device API) */
if (s->out_fifo.count > 0) {
- qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
+ timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
}
}
switch(keysym) {
case QEMU_KEY_CTRL_UP:
- console_scroll(-1);
+ console_scroll(s, -1);
break;
case QEMU_KEY_CTRL_DOWN:
- console_scroll(1);
+ console_scroll(s, 1);
break;
case QEMU_KEY_CTRL_PAGEUP:
- console_scroll(-10);
+ console_scroll(s, -10);
break;
case QEMU_KEY_CTRL_PAGEDOWN:
- console_scroll(10);
+ console_scroll(s, 10);
break;
default:
/* convert the QEMU keysym to VT100 key string */
static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
{
+ Error *local_err = NULL;
+ Object *obj;
QemuConsole *s;
int i;
if (nb_consoles >= MAX_CONSOLES)
return NULL;
- s = g_malloc0(sizeof(QemuConsole));
+
+ obj = object_new(TYPE_QEMU_CONSOLE);
+ s = QEMU_CONSOLE(obj);
+ object_property_add_link(obj, "device", TYPE_DEVICE,
+ (Object **)&s->device, &local_err);
+ object_property_add_uint32_ptr(obj, "head",
+ &s->head, &local_err);
+
if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
(console_type == GRAPHIC_CONSOLE))) {
active_console = s;
return surface;
}
+static DisplaySurface *qemu_create_dummy_surface(void)
+{
+ static const char msg[] =
+ "This VM has no graphic display device.";
+ DisplaySurface *surface = qemu_create_displaysurface(640, 480);
+ pixman_color_t bg = color_table_rgb[0][COLOR_BLACK];
+ pixman_color_t fg = color_table_rgb[0][COLOR_WHITE];
+ pixman_image_t *glyph;
+ int len, x, y, i;
+
+ len = strlen(msg);
+ x = (640/FONT_WIDTH - len) / 2;
+ y = (480/FONT_HEIGHT - 1) / 2;
+ for (i = 0; i < len; i++) {
+ glyph = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, msg[i]);
+ qemu_pixman_glyph_render(glyph, surface->image, &fg, &bg,
+ x+i, y, FONT_WIDTH, FONT_HEIGHT);
+ qemu_pixman_image_unref(glyph);
+ }
+ return surface;
+}
+
void qemu_free_displaysurface(DisplaySurface *surface)
{
if (surface == NULL) {
g_free(surface);
}
-void register_displaychangelistener(DisplayState *ds,
- DisplayChangeListener *dcl)
+void register_displaychangelistener(DisplayChangeListener *dcl)
{
+ static DisplaySurface *dummy;
+ QemuConsole *con;
+
trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
- dcl->ds = ds;
- QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
- gui_setup_refresh(ds);
- if (dcl->ops->dpy_gfx_switch && active_console) {
- dcl->ops->dpy_gfx_switch(dcl, active_console->surface);
+ dcl->ds = get_alloc_displaystate();
+ QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
+ gui_setup_refresh(dcl->ds);
+ if (dcl->con) {
+ dcl->con->dcls++;
+ con = dcl->con;
+ } else {
+ con = active_console;
+ }
+ if (dcl->ops->dpy_gfx_switch) {
+ if (con) {
+ dcl->ops->dpy_gfx_switch(dcl, con->surface);
+ } else {
+ if (!dummy) {
+ dummy = qemu_create_dummy_surface();
+ }
+ dcl->ops->dpy_gfx_switch(dcl, dummy);
+ }
+ }
+}
+
+void update_displaychangelistener(DisplayChangeListener *dcl,
+ uint64_t interval)
+{
+ DisplayState *ds = dcl->ds;
+
+ dcl->update_interval = interval;
+ if (!ds->refreshing && ds->update_interval > interval) {
+ timer_mod(ds->gui_timer, ds->last_update + interval);
}
}
{
DisplayState *ds = dcl->ds;
trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
+ if (dcl->con) {
+ dcl->con->dcls--;
+ }
QLIST_REMOVE(dcl, next);
gui_setup_refresh(ds);
}
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
int width = surface_width(con->surface);
int height = surface_height(con->surface);
w = MIN(w, width - x);
h = MIN(h, height - y);
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_gfx_update) {
dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
}
}
}
-static void dpy_gfx_switch_surface(DisplayState *ds,
- DisplaySurface *surface)
-{
- struct DisplayChangeListener *dcl;
-
- QLIST_FOREACH(dcl, &ds->listeners, next) {
- if (dcl->ops->dpy_gfx_switch) {
- dcl->ops->dpy_gfx_switch(dcl, surface);
- }
- }
-}
-
void dpy_gfx_replace_surface(QemuConsole *con,
DisplaySurface *surface)
{
DisplayState *s = con->ds;
DisplaySurface *old_surface = con->surface;
+ DisplayChangeListener *dcl;
con->surface = surface;
- if (con == active_console) {
- dpy_gfx_switch_surface(s, surface);
+ QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
+ if (dcl->ops->dpy_gfx_switch) {
+ dcl->ops->dpy_gfx_switch(dcl, surface);
+ }
}
qemu_free_displaysurface(old_surface);
}
void dpy_refresh(DisplayState *s)
{
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
+
QLIST_FOREACH(dcl, &s->listeners, next) {
if (dcl->ops->dpy_refresh) {
dcl->ops->dpy_refresh(dcl);
int dst_x, int dst_y, int w, int h)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_gfx_copy) {
dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
} else { /* TODO */
void dpy_text_cursor(QemuConsole *con, int x, int y)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_text_cursor) {
dcl->ops->dpy_text_cursor(dcl, x, y);
}
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_text_update) {
dcl->ops->dpy_text_update(dcl, x, y, w, h);
}
DisplayState *s = con->ds;
struct DisplayChangeListener *dcl;
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_text_resize) {
dcl->ops->dpy_text_resize(dcl, w, h);
}
void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_mouse_set) {
dcl->ops->dpy_mouse_set(dcl, x, y, on);
}
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
- if (con != active_console) {
+ if (!qemu_console_is_visible(con)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
+ if (con != (dcl->con ? dcl->con : active_console)) {
+ continue;
+ }
if (dcl->ops->dpy_cursor_define) {
dcl->ops->dpy_cursor_define(dcl, cursor);
}
bool dpy_cursor_define_supported(QemuConsole *con)
{
DisplayState *s = con->ds;
- struct DisplayChangeListener *dcl;
+ DisplayChangeListener *dcl;
+
QLIST_FOREACH(dcl, &s->listeners, next) {
if (dcl->ops->dpy_cursor_define) {
return true;
*/
DisplayState *init_displaystate(void)
{
+ Error *local_err = NULL;
+ gchar *name;
int i;
if (!display_state) {
consoles[i]->ds == NULL) {
text_console_do_init(consoles[i]->chr, display_state);
}
+
+ /* Hook up into the qom tree here (not in new_console()), once
+ * all QemuConsoles are created and the order / numbering
+ * doesn't change any more */
+ name = g_strdup_printf("console[%d]", i);
+ object_property_add_child(container_get(object_get_root(), "/backend"),
+ name, OBJECT(consoles[i]), &local_err);
+ g_free(name);
}
return display_state;
}
-QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops,
+QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
+ const GraphicHwOps *hw_ops,
void *opaque)
{
+ Error *local_err = NULL;
int width = 640;
int height = 480;
QemuConsole *s;
s = new_console(ds, GRAPHIC_CONSOLE);
s->hw_ops = hw_ops;
s->hw = opaque;
+ if (dev) {
+ object_property_set_link(OBJECT(s), OBJECT(dev),
+ "device", &local_err);
+ object_property_set_int(OBJECT(s), head,
+ "head", &local_err);
+ }
s->surface = qemu_create_displaysurface(width, height);
return s;
}
-int is_graphic_console(void)
+QemuConsole *qemu_console_lookup_by_index(unsigned int index)
{
- return active_console && active_console->console_type == GRAPHIC_CONSOLE;
+ if (index >= MAX_CONSOLES) {
+ return NULL;
+ }
+ return consoles[index];
}
-int is_fixedsize_console(void)
+QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
{
- return active_console && active_console->console_type != TEXT_CONSOLE;
+ Error *local_err = NULL;
+ Object *obj;
+ uint32_t h;
+ int i;
+
+ for (i = 0; i < nb_consoles; i++) {
+ if (!consoles[i]) {
+ continue;
+ }
+ obj = object_property_get_link(OBJECT(consoles[i]),
+ "device", &local_err);
+ if (DEVICE(obj) != dev) {
+ continue;
+ }
+ h = object_property_get_int(OBJECT(consoles[i]),
+ "head", &local_err);
+ if (h != head) {
+ continue;
+ }
+ return consoles[i];
+ }
+ return NULL;
+}
+
+bool qemu_console_is_visible(QemuConsole *con)
+{
+ return (con == active_console) || (con->dcls > 0);
+}
+
+bool qemu_console_is_graphic(QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con && (con->console_type == GRAPHIC_CONSOLE);
+}
+
+bool qemu_console_is_fixedsize(QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con && (con->console_type != TEXT_CONSOLE);
+}
+
+int qemu_console_get_index(QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? con->index : -1;
+}
+
+uint32_t qemu_console_get_head(QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? con->head : -1;
+}
+
+int qemu_console_get_width(QemuConsole *con, int fallback)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? surface_width(con->surface) : fallback;
+}
+
+int qemu_console_get_height(QemuConsole *con, int fallback)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? surface_height(con->surface) : fallback;
}
static void text_console_set_echo(CharDriverState *chr, bool echo)
s->cursor_visible_phase = !s->cursor_visible_phase;
graphic_hw_invalidate(s);
- qemu_mod_timer(s->cursor_timer,
- qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
+ timer_mod(s->cursor_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
}
static const GraphicHwOps text_console_ops = {
s->out_fifo.buf = s->out_fifo_buf;
s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
- s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
+ s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
s->ds = ds;
s->y_displayed = 0;
}
s->cursor_timer =
- qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
+ timer_new_ms(QEMU_CLOCK_REALTIME, text_console_update_cursor, s);
s->hw_ops = &text_console_ops;
s->hw = s;
s->chr = chr;
chr->opaque = s;
chr->chr_set_echo = text_console_set_echo;
+ /* console/chardev init sometimes completes elsewhere in a 2nd
+ * stage, so defer OPENED events until they are fully initialized
+ */
+ chr->explicit_be_open = true;
if (display_state) {
text_console_do_init(chr, display_state);
}
}
+static const TypeInfo qemu_console_info = {
+ .name = TYPE_QEMU_CONSOLE,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(QemuConsole),
+ .class_size = sizeof(QemuConsoleClass),
+};
+
+
static void register_types(void)
{
+ type_register_static(&qemu_console_info);
register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC,
qemu_chr_parse_vc);
}