X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ui%2Fconsole.c;h=199ba69101d532c4a6658b5c3007d9c4caff1a30;hb=60aad298cb6de52f2716b2e82e1353ea9de95fd6;hp=214cdba4895fb2f08bae90a2faafa9d8a4f5813e;hpb=81c0d5a66295024d0a42e3d28efcd102a32f93c3;p=qemu.git diff --git a/ui/console.c b/ui/console.c index 214cdba48..199ba6910 100644 --- a/ui/console.c +++ b/ui/console.c @@ -23,6 +23,7 @@ */ #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" @@ -113,12 +114,16 @@ typedef enum { } 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; const GraphicHwOps *hw_ops; void *hw; @@ -172,9 +177,8 @@ static QemuConsole *consoles[MAX_CONSOLES]; 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) { @@ -204,8 +208,8 @@ static void gui_update(void *opaque) } trace_console_refresh(interval); } - ds->last_update = qemu_get_clock_ms(rt_clock); - qemu_mod_timer(ds->gui_timer, ds->last_update + interval); + 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) @@ -228,12 +232,12 @@ 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; } @@ -266,18 +270,20 @@ static void ppm_save(const char *filename, struct DisplaySurface *ds, { 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; @@ -309,7 +315,7 @@ write_err: 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) { @@ -403,39 +409,6 @@ static const pixman_color_t color_table_rgb[2][8] = { } }; -#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) { @@ -1022,22 +995,30 @@ static void console_putchar(QemuConsole *s, int ch) 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)); } @@ -1045,8 +1026,8 @@ void console_select(unsigned int index) 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); } } } @@ -1091,7 +1072,7 @@ static void kbd_send_chars(void *opaque) /* 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); } } @@ -1190,12 +1171,19 @@ static void text_console_update(void *opaque, console_ch_t *chardata) 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); + if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { active_console = s; @@ -1279,6 +1267,28 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp, 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) { @@ -1289,15 +1299,30 @@ void qemu_free_displaysurface(DisplaySurface *surface) 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); + } } } @@ -1308,7 +1333,7 @@ void update_displaychangelistener(DisplayChangeListener *dcl, dcl->update_interval = interval; if (!ds->refreshing && ds->update_interval > interval) { - qemu_mod_timer(ds->gui_timer, ds->last_update + interval); + timer_mod(ds->gui_timer, ds->last_update + interval); } } @@ -1316,6 +1341,9 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl) { 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); } @@ -1323,7 +1351,7 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl) 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); @@ -1338,40 +1366,38 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) 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 (qemu_console_is_visible(con)) { - 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); @@ -1383,12 +1409,15 @@ void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y, int dst_x, int dst_y, int w, int h) { DisplayState *s = con->ds; - struct DisplayChangeListener *dcl; + DisplayChangeListener *dcl; 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 */ @@ -1400,12 +1429,15 @@ void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y, void dpy_text_cursor(QemuConsole *con, int x, int y) { DisplayState *s = con->ds; - struct DisplayChangeListener *dcl; + DisplayChangeListener *dcl; 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); } @@ -1415,12 +1447,15 @@ void dpy_text_cursor(QemuConsole *con, int x, int 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 (!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); } @@ -1436,6 +1471,9 @@ void dpy_text_resize(QemuConsole *con, int w, int h) 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); } @@ -1445,12 +1483,15 @@ void dpy_text_resize(QemuConsole *con, int w, int h) void dpy_mouse_set(QemuConsole *con, int x, int y, int on) { DisplayState *s = con->ds; - struct DisplayChangeListener *dcl; + DisplayChangeListener *dcl; 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); } @@ -1460,12 +1501,15 @@ void dpy_mouse_set(QemuConsole *con, int x, int y, int on) void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) { DisplayState *s = con->ds; - struct DisplayChangeListener *dcl; + DisplayChangeListener *dcl; 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); } @@ -1475,7 +1519,8 @@ void dpy_cursor_define(QemuConsole *con, QEMUCursor *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; @@ -1502,6 +1547,8 @@ static DisplayState *get_alloc_displaystate(void) */ DisplayState *init_displaystate(void) { + Error *local_err = NULL; + gchar *name; int i; if (!display_state) { @@ -1513,14 +1560,24 @@ DisplayState *init_displaystate(void) 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, + const GraphicHwOps *hw_ops, void *opaque) { + Error *local_err = NULL; int width = 640; int height = 480; QemuConsole *s; @@ -1531,14 +1588,45 @@ QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops, 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); + } s->surface = qemu_create_displaysurface(width, height); return s; } +QemuConsole *qemu_console_lookup_by_index(unsigned int index) +{ + if (index >= MAX_CONSOLES) { + return NULL; + } + return consoles[index]; +} + +QemuConsole *qemu_console_lookup_by_device(DeviceState *dev) +{ + Error *local_err = NULL; + Object *obj; + 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) { + return consoles[i]; + } + } + return NULL; +} + bool qemu_console_is_visible(QemuConsole *con) { - return con == active_console; + return (con == active_console) || (con->dcls > 0); } bool qemu_console_is_graphic(QemuConsole *con) @@ -1570,8 +1658,8 @@ static void text_console_update_cursor(void *opaque) 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 = { @@ -1591,7 +1679,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) 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; @@ -1608,7 +1696,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) } 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; @@ -1677,6 +1765,10 @@ static CharDriverState *text_console_init(ChardevVC *vc) 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); @@ -1877,8 +1969,17 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, } } +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); }