#include "ui/console.h"
#include "hw/qdev-core.h"
#include "qapi/error.h"
+#include "qapi/qapi-commands-ui.h"
+#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/timer.h"
-#include "qmp-commands.h"
#include "chardev/char-fe.h"
#include "trace.h"
#include "exec/memory.h"
+#include "io/channel-file.h"
+#include "qom/object.h"
#define DEFAULT_BACKSCROLL 512
#define CONSOLE_CURSOR_PERIOD 500
QEMUFIFO out_fifo;
uint8_t out_fifo_buf[16];
QEMUTimer *kbd_timer;
+ CoQueue dump_queue;
+
+ QTAILQ_ENTRY(QemuConsole) next;
};
struct DisplayState {
static DisplayState *display_state;
static QemuConsole *active_console;
-static QemuConsole **consoles;
-static int nb_consoles = 0;
+static QTAILQ_HEAD(, QemuConsole) consoles =
+ QTAILQ_HEAD_INITIALIZER(consoles);
static bool cursor_visible_phase;
static QEMUTimer *cursor_timer;
uint64_t dcl_interval;
DisplayState *ds = opaque;
DisplayChangeListener *dcl;
- int i;
+ QemuConsole *con;
ds->refreshing = true;
dpy_refresh(ds);
}
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);
+ QTAILQ_FOREACH(con, &consoles, next) {
+ if (con->hw_ops->update_interval) {
+ con->hw_ops->update_interval(con->hw, interval);
}
}
trace_console_refresh(interval);
timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
}
if (!need_timer && ds->gui_timer != NULL) {
- timer_del(ds->gui_timer);
timer_free(ds->gui_timer);
ds->gui_timer = NULL;
}
ds->have_text = have_text;
}
+void graphic_hw_update_done(QemuConsole *con)
+{
+ if (con) {
+ qemu_co_queue_restart_all(&con->dump_queue);
+ }
+}
+
void graphic_hw_update(QemuConsole *con)
{
+ bool async = false;
+ con = con ? con : active_console;
if (!con) {
- con = active_console;
+ return;
}
- if (con && con->hw_ops->gfx_update) {
+ if (con->hw_ops->gfx_update) {
con->hw_ops->gfx_update(con->hw);
+ async = con->hw_ops->gfx_update_async;
+ }
+ if (!async) {
+ graphic_hw_update_done(con);
}
}
}
}
+void graphic_hw_gl_flushed(QemuConsole *con)
+{
+ assert(con != NULL);
+
+ if (con->hw_ops->gl_flushed) {
+ con->hw_ops->gl_flushed(con->hw);
+ }
+}
+
int qemu_console_get_window_id(QemuConsole *con)
{
return con->window_id;
}
}
-static void ppm_save(const char *filename, DisplaySurface *ds,
- Error **errp)
+static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
{
- int width = pixman_image_get_width(ds->image);
- int height = pixman_image_get_height(ds->image);
- int fd;
- FILE *f;
+ int width = pixman_image_get_width(image);
+ int height = pixman_image_get_height(image);
+ g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd));
+ g_autofree char *header = NULL;
+ g_autoptr(pixman_image_t) linebuf = NULL;
int y;
- int ret;
- pixman_image_t *linebuf;
- trace_ppm_save(filename, ds);
- 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;
- goto write_err;
+ trace_ppm_save(fd, image);
+
+ header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255);
+ if (qio_channel_write_all(QIO_CHANNEL(ioc),
+ header, strlen(header), errp) < 0) {
+ return false;
}
+
linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
for (y = 0; y < height; y++) {
- qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
- clearerr(f);
- ret = fwrite(pixman_image_get_data(linebuf), 1,
- pixman_image_get_stride(linebuf), f);
- (void)ret;
- if (ferror(f)) {
- goto write_err;
+ qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
+ if (qio_channel_write_all(QIO_CHANNEL(ioc),
+ (char *)pixman_image_get_data(linebuf),
+ pixman_image_get_stride(linebuf), errp) < 0) {
+ return false;
}
}
-out:
- qemu_pixman_image_unref(linebuf);
- fclose(f);
- return;
+ return true;
+}
-write_err:
- error_setg(errp, "failed to write to file '%s': %s", filename,
- strerror(errno));
- unlink(filename);
- goto out;
+static void graphic_hw_update_bh(void *con)
+{
+ graphic_hw_update(con);
}
-void qmp_screendump(const char *filename, Error **errp)
+/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
+void coroutine_fn
+qmp_screendump(const char *filename, bool has_device, const char *device,
+ bool has_head, int64_t head, Error **errp)
{
- QemuConsole *con = qemu_console_lookup_by_index(0);
+ g_autoptr(pixman_image_t) image = NULL;
+ QemuConsole *con;
DisplaySurface *surface;
+ int fd;
- if (con == NULL) {
- error_setg(errp, "There is no QemuConsole I can screendump from.");
- return;
+ if (has_device) {
+ con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
+ errp);
+ if (!con) {
+ return;
+ }
+ } else {
+ if (has_head) {
+ error_setg(errp, "'head' must be specified together with 'device'");
+ return;
+ }
+ con = qemu_console_lookup_by_index(0);
+ if (!con) {
+ error_setg(errp, "There is no console to take a screendump from");
+ return;
+ }
}
- graphic_hw_update(con);
+ if (qemu_co_queue_empty(&con->dump_queue)) {
+ /* Defer the update, it will restart the pending coroutines */
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ graphic_hw_update_bh, con);
+ }
+ qemu_co_queue_wait(&con->dump_queue, NULL);
+
+ /*
+ * All pending coroutines are woken up, while the BQL is held. No
+ * further graphic update are possible until it is released. Take
+ * an image ref before that.
+ */
surface = qemu_console_surface(con);
- ppm_save(filename, surface, errp);
+ if (!surface) {
+ error_setg(errp, "no surface");
+ return;
+ }
+ image = pixman_image_ref(surface->image);
+
+ fd = qemu_open_old(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;
+ }
+
+ /*
+ * The image content could potentially be updated as the coroutine
+ * yields and releases the BQL. It could produce corrupted dump, but
+ * it should be otherwise safe.
+ */
+ if (!ppm_save(fd, image, errp)) {
+ qemu_unlink(filename);
+ }
}
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
if (s->width < w1)
w1 = s->width;
- cells = g_new(TextCell, s->width * s->total_height);
+ cells = g_new(TextCell, s->width * s->total_height + 1);
for(y = 0; y < s->total_height; y++) {
c = &cells[y * s->width];
if (w1 > 0) {
y2 += s->total_height;
}
if (y2 < s->height) {
+ if (x >= s->width) {
+ x = s->width - 1;
+ }
c = &s->cells[y1 * s->width + x];
vga_putcharxy(s, x, y2, c->ch,
&(c->t_attrib));
static void console_clear_xy(QemuConsole *s, int x, int y)
{
int y1 = (s->y_base + y) % s->total_height;
+ if (x >= s->width) {
+ x = s->width - 1;
+ }
TextCell *c = &s->cells[y1 * s->width + x];
c->ch = ' ';
c->t_attrib = s->t_attrib_default;
break;
case 1:
/* clear from beginning of line */
- for (x = 0; x <= s->x; x++) {
+ for (x = 0; x <= s->x && x < s->width; x++) {
console_clear_xy(s, x, s->y);
}
break;
dcl->ops->dpy_gfx_switch(dcl, s->surface);
}
}
- dpy_gfx_update(s, 0, 0, surface_width(s->surface),
- surface_height(s->surface));
+ if (s->surface) {
+ dpy_gfx_update(s, 0, 0, surface_width(s->surface),
+ surface_height(s->surface));
+ }
}
if (ds->have_text) {
dpy_text_resize(s, s->width, s->height);
}
}
-typedef struct VCChardev {
+struct VCChardev {
Chardev parent;
QemuConsole *console;
-} VCChardev;
+};
+typedef struct VCChardev VCChardev;
#define TYPE_CHARDEV_VC "chardev-vc"
-#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
+ TYPE_CHARDEV_VC)
static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
[Q_KEY_CODE_BACKSPACE] = QEMU_KEY_BACKSPACE,
};
-bool kbd_put_qcode_console(QemuConsole *s, int qcode)
+static const int ctrl_qcode_to_keysym[Q_KEY_CODE__MAX] = {
+ [Q_KEY_CODE_UP] = QEMU_KEY_CTRL_UP,
+ [Q_KEY_CODE_DOWN] = QEMU_KEY_CTRL_DOWN,
+ [Q_KEY_CODE_RIGHT] = QEMU_KEY_CTRL_RIGHT,
+ [Q_KEY_CODE_LEFT] = QEMU_KEY_CTRL_LEFT,
+ [Q_KEY_CODE_HOME] = QEMU_KEY_CTRL_HOME,
+ [Q_KEY_CODE_END] = QEMU_KEY_CTRL_END,
+ [Q_KEY_CODE_PGUP] = QEMU_KEY_CTRL_PAGEUP,
+ [Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
+};
+
+bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
{
int keysym;
- keysym = qcode_to_keysym[qcode];
+ keysym = ctrl ? ctrl_qcode_to_keysym[qcode] : qcode_to_keysym[qcode];
if (keysym == 0) {
return false;
}
obj = object_new(TYPE_QEMU_CONSOLE);
s = QEMU_CONSOLE(obj);
+ qemu_co_queue_init(&s->dump_queue);
s->head = head;
object_property_add_link(obj, "device", TYPE_DEVICE,
(Object **)&s->device,
object_property_allow_set_link,
- OBJ_PROP_LINK_UNREF_ON_RELEASE,
- &error_abort);
- object_property_add_uint32_ptr(obj, "head",
- &s->head, &error_abort);
+ OBJ_PROP_LINK_STRONG);
+ object_property_add_uint32_ptr(obj, "head", &s->head,
+ OBJ_PROP_FLAG_READ);
if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
(console_type == GRAPHIC_CONSOLE))) {
}
s->ds = ds;
s->console_type = console_type;
-
- consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1));
- if (console_type != GRAPHIC_CONSOLE) {
- s->index = nb_consoles;
- consoles[nb_consoles++] = s;
+ s->window_id = -1;
+
+ if (QTAILQ_EMPTY(&consoles)) {
+ s->index = 0;
+ QTAILQ_INSERT_TAIL(&consoles, s, next);
+ } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) {
+ QemuConsole *last = QTAILQ_LAST(&consoles);
+ s->index = last->index + 1;
+ QTAILQ_INSERT_TAIL(&consoles, s, next);
} else {
- /* HACK: Put graphical consoles before text consoles. */
- for (i = nb_consoles; i > 0; i--) {
- if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
- break;
- consoles[i] = consoles[i - 1];
- consoles[i]->index = i;
+ /*
+ * HACK: Put graphical consoles before text consoles.
+ *
+ * Only do that for coldplugged devices. After initial device
+ * initialization we will not renumber the consoles any more.
+ */
+ QemuConsole *c = QTAILQ_FIRST(&consoles);
+
+ while (QTAILQ_NEXT(c, next) != NULL &&
+ c->console_type == GRAPHIC_CONSOLE) {
+ c = QTAILQ_NEXT(c, next);
+ }
+ if (c->console_type == GRAPHIC_CONSOLE) {
+ /* have no text consoles */
+ s->index = c->index + 1;
+ QTAILQ_INSERT_AFTER(&consoles, c, s, next);
+ } else {
+ s->index = c->index;
+ QTAILQ_INSERT_BEFORE(c, s, next);
+ /* renumber text consoles */
+ for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) {
+ c->index = i;
+ }
}
- s->index = i;
- consoles[i] = s;
- nb_consoles++;
}
return s;
}
-static void qemu_alloc_display(DisplaySurface *surface, int width, int height)
+DisplaySurface *qemu_create_displaysurface(int width, int height)
{
- qemu_pixman_image_unref(surface->image);
- surface->image = NULL;
+ DisplaySurface *surface = g_new0(DisplaySurface, 1);
+ trace_displaysurface_create(surface, width, height);
surface->format = PIXMAN_x8r8g8b8;
surface->image = pixman_image_create_bits(surface->format,
width, height,
NULL, width * 4);
assert(surface->image != NULL);
-
surface->flags = QEMU_ALLOCATED_FLAG;
-}
-DisplaySurface *qemu_create_displaysurface(int width, int height)
-{
- DisplaySurface *surface = g_new0(DisplaySurface, 1);
-
- trace_displaysurface_create(surface, width, height);
- qemu_alloc_display(surface, width, height);
return surface;
}
return surface;
}
-static void qemu_unmap_displaysurface_guestmem(pixman_image_t *image,
- void *unused)
-{
- void *data = pixman_image_get_data(image);
- uint32_t size = pixman_image_get_stride(image) *
- pixman_image_get_height(image);
- cpu_physical_memory_unmap(data, size, 0, 0);
-}
-
-DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height,
- pixman_format_code_t format,
- int linesize, uint64_t addr)
-{
- DisplaySurface *surface;
- hwaddr size;
- void *data;
-
- if (linesize == 0) {
- linesize = width * PIXMAN_FORMAT_BPP(format) / 8;
- }
-
- size = (hwaddr)linesize * height;
- data = cpu_physical_memory_map(addr, &size, 0);
- if (size != (hwaddr)linesize * height) {
- cpu_physical_memory_unmap(data, size, 0, 0);
- return NULL;
- }
-
- surface = qemu_create_displaysurface_from
- (width, height, format, linesize, data);
- pixman_image_set_destroy_function
- (surface->image, qemu_unmap_displaysurface_guestmem, NULL);
-
- return surface;
-}
-
-static DisplaySurface *qemu_create_message_surface(int w, int h,
- const char *msg)
+DisplaySurface *qemu_create_placeholder_surface(int w, int h,
+ const char *msg)
{
DisplaySurface *surface = qemu_create_displaysurface(w, h);
pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
x+i, y, FONT_WIDTH, FONT_HEIGHT);
qemu_pixman_image_unref(glyph);
}
+ surface->flags |= QEMU_PLACEHOLDER_FLAG;
return surface;
}
return con->gl != NULL;
}
-bool console_has_gl_dmabuf(QemuConsole *con)
+static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl)
{
- return con->gl != NULL && con->gl->ops->dpy_gl_scanout_dmabuf != NULL;
+ if (dcl->ops->dpy_has_dmabuf) {
+ return dcl->ops->dpy_has_dmabuf(dcl);
+ }
+
+ if (dcl->ops->dpy_gl_scanout_dmabuf) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool dpy_compatible_with(QemuConsole *con,
+ DisplayChangeListener *dcl, Error **errp)
+{
+ ERRP_GUARD();
+ int flags;
+
+ flags = con->hw_ops->get_flags ? con->hw_ops->get_flags(con->hw) : 0;
+
+ if (flags & GRAPHIC_FLAGS_GL &&
+ !console_has_gl(con)) {
+ error_setg(errp, "The console requires a GL context.");
+ return false;
+
+ }
+
+ if (flags & GRAPHIC_FLAGS_DMABUF &&
+ !displaychangelistener_has_dmabuf(dcl)) {
+ error_setg(errp, "The console requires display DMABUF support.");
+ return false;
+ }
+
+ return true;
}
void register_displaychangelistener(DisplayChangeListener *dcl)
"This VM has no graphic display device.";
static DisplaySurface *dummy;
QemuConsole *con;
+ Error *err = NULL;
assert(!dcl->ds);
dcl->con->gl = dcl;
}
+ if (dcl->con && !dpy_compatible_with(dcl->con, dcl, &err)) {
+ error_report_err(err);
+ exit(1);
+ }
+
trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
dcl->ds = get_alloc_displaystate();
QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
dcl->ops->dpy_gfx_switch(dcl, con->surface);
} else {
if (!dummy) {
- dummy = qemu_create_message_surface(640, 480, nodev);
+ dummy = qemu_create_placeholder_surface(640, 480, nodev);
}
dcl->ops->dpy_gfx_switch(dcl, dummy);
}
bool dpy_ui_info_supported(QemuConsole *con)
{
+ if (con == NULL) {
+ con = active_console;
+ }
+
return con->hw_ops->ui_info != NULL;
}
+const QemuUIInfo *dpy_get_ui_info(const QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+
+ return &con->ui_info;
+}
+
int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info)
{
- assert(con != NULL);
+ if (con == NULL) {
+ con = active_console;
+ }
if (!dpy_ui_info_supported(con)) {
return -1;
}
}
+void dpy_gfx_update_full(QemuConsole *con)
+{
+ if (!con->surface) {
+ return;
+ }
+ dpy_gfx_update(con, 0, 0,
+ surface_width(con->surface),
+ surface_height(con->surface));
+}
+
void dpy_gfx_replace_surface(QemuConsole *con,
DisplaySurface *surface)
{
+ static const char placeholder_msg[] = "Display output is not active.";
DisplayState *s = con->ds;
DisplaySurface *old_surface = con->surface;
DisplayChangeListener *dcl;
+ int width;
+ int height;
+
+ if (!surface) {
+ if (old_surface) {
+ width = surface_width(old_surface);
+ height = surface_height(old_surface);
+ } else {
+ width = 640;
+ height = 480;
+ }
- assert(old_surface != surface || surface == NULL);
+ surface = qemu_create_placeholder_surface(width, height, placeholder_msg);
+ }
+
+ assert(old_surface != surface);
con->surface = surface;
QLIST_FOREACH(dcl, &s->listeners, next) {
return false;
}
} else {
- /* default is to whitelist native 32 bpp only */
+ /* default is to allow native 32 bpp only */
if (format != qemu_default_pixman_format(32, true)) {
return false;
}
return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
}
-QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
-{
- assert(con->gl);
- return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
-}
-
void dpy_gl_scanout_disable(QemuConsole *con)
{
assert(con->gl);
- if (con->gl->ops->dpy_gl_scanout_disable) {
- con->gl->ops->dpy_gl_scanout_disable(con->gl);
- } else {
- con->gl->ops->dpy_gl_scanout_texture(con->gl, 0, false, 0, 0,
- 0, 0, 0, 0);
- }
+ con->gl->ops->dpy_gl_scanout_disable(con->gl);
}
void dpy_gl_scanout_texture(QemuConsole *con,
DisplayState *init_displaystate(void)
{
gchar *name;
- int i;
+ QemuConsole *con;
get_alloc_displaystate();
- for (i = 0; i < nb_consoles; i++) {
- if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
- consoles[i]->ds == NULL) {
- text_console_do_init(consoles[i]->chr, display_state);
+ QTAILQ_FOREACH(con, &consoles, next) {
+ if (con->console_type != GRAPHIC_CONSOLE &&
+ con->ds == NULL) {
+ text_console_do_init(con->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);
+ name = g_strdup_printf("console[%d]", con->index);
object_property_add_child(container_get(object_get_root(), "/backend"),
- name, OBJECT(consoles[i]), &error_abort);
+ name, OBJECT(con));
g_free(name);
}
int height = 480;
QemuConsole *s;
DisplayState *ds;
+ DisplaySurface *surface;
ds = get_alloc_displaystate();
- trace_console_gfx_new();
- s = new_console(ds, GRAPHIC_CONSOLE, head);
- s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, dpy_set_ui_info_timer, s);
+ s = qemu_console_lookup_unused();
+ if (s) {
+ trace_console_gfx_reuse(s->index);
+ if (s->surface) {
+ width = surface_width(s->surface);
+ height = surface_height(s->surface);
+ }
+ } else {
+ trace_console_gfx_new();
+ s = new_console(ds, GRAPHIC_CONSOLE, head);
+ s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ dpy_set_ui_info_timer, s);
+ }
graphic_console_set_hwops(s, hw_ops, opaque);
if (dev) {
- object_property_set_link(OBJECT(s), OBJECT(dev), "device",
+ object_property_set_link(OBJECT(s), "device", OBJECT(dev),
&error_abort);
}
- s->surface = qemu_create_message_surface(width, height, noinit);
+ surface = qemu_create_placeholder_surface(width, height, noinit);
+ dpy_gfx_replace_surface(s, surface);
return s;
}
+static const GraphicHwOps unused_ops = {
+ /* no callbacks */
+};
+
+void graphic_console_close(QemuConsole *con)
+{
+ static const char unplugged[] =
+ "Guest display has been unplugged";
+ DisplaySurface *surface;
+ int width = 640;
+ int height = 480;
+
+ if (con->surface) {
+ width = surface_width(con->surface);
+ height = surface_height(con->surface);
+ }
+
+ trace_console_gfx_close(con->index);
+ object_property_set_link(OBJECT(con), "device", NULL, &error_abort);
+ graphic_console_set_hwops(con, &unused_ops, NULL);
+
+ if (con->gl) {
+ dpy_gl_scanout_disable(con);
+ }
+ surface = qemu_create_placeholder_surface(width, height, unplugged);
+ dpy_gfx_replace_surface(con, surface);
+}
+
QemuConsole *qemu_console_lookup_by_index(unsigned int index)
{
- if (index >= nb_consoles) {
- return NULL;
+ QemuConsole *con;
+
+ QTAILQ_FOREACH(con, &consoles, next) {
+ if (con->index == index) {
+ return con;
+ }
}
- return consoles[index];
+ return NULL;
}
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
{
+ QemuConsole *con;
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]),
+ QTAILQ_FOREACH(con, &consoles, next) {
+ obj = object_property_get_link(OBJECT(con),
"device", &error_abort);
if (DEVICE(obj) != dev) {
continue;
}
- h = object_property_get_uint(OBJECT(consoles[i]),
+ h = object_property_get_uint(OBJECT(con),
"head", &error_abort);
if (h != head) {
continue;
}
- return consoles[i];
+ return con;
}
return NULL;
}
return con;
}
+QemuConsole *qemu_console_lookup_unused(void)
+{
+ QemuConsole *con;
+ Object *obj;
+
+ QTAILQ_FOREACH(con, &consoles, next) {
+ if (con->hw_ops != &unused_ops) {
+ continue;
+ }
+ obj = object_property_get_link(OBJECT(con),
+ "device", &error_abort);
+ if (obj != NULL) {
+ continue;
+ }
+ return con;
+ }
+ return NULL;
+}
+
bool qemu_console_is_visible(QemuConsole *con)
{
return (con == active_console) || (con->dcls > 0);
return con ? con->head : -1;
}
-QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con)
-{
- assert(con != NULL);
- return &con->ui_info;
-}
-
int qemu_console_get_width(QemuConsole *con, int fallback)
{
if (con == NULL) {
static void text_console_update_cursor(void *opaque)
{
QemuConsole *s;
- int i, count = 0;
+ int count = 0;
cursor_visible_phase = !cursor_visible_phase;
- for (i = 0; i < nb_consoles; i++) {
- s = consoles[i];
+ QTAILQ_FOREACH(s, &consoles, next) {
if (qemu_console_is_graphic(s) ||
!qemu_console_is_visible(s)) {
continue;
text_console_resize(s);
if (chr->label) {
- char msg[128];
- int len;
+ char *msg;
s->t_attrib.bgcol = QEMU_COLOR_BLUE;
- len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
- vc_chr_write(chr, (uint8_t *)msg, len);
+ msg = g_strdup_printf("%s console\r\n", chr->label);
+ vc_chr_write(chr, (uint8_t *)msg, strlen(msg));
+ g_free(msg);
s->t_attrib = s->t_attrib_default;
}
return pf;
}
+static QemuDisplay *dpys[DISPLAY_TYPE__MAX];
+
+void qemu_display_register(QemuDisplay *ui)
+{
+ assert(ui->type < DISPLAY_TYPE__MAX);
+ dpys[ui->type] = ui;
+}
+
+bool qemu_display_find_default(DisplayOptions *opts)
+{
+ static DisplayType prio[] = {
+ DISPLAY_TYPE_GTK,
+ DISPLAY_TYPE_SDL,
+ DISPLAY_TYPE_COCOA
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(prio); i++) {
+ if (dpys[prio[i]] == NULL) {
+ ui_module_load_one(DisplayType_str(prio[i]));
+ }
+ if (dpys[prio[i]] == NULL) {
+ continue;
+ }
+ opts->type = prio[i];
+ return true;
+ }
+ return false;
+}
+
+void qemu_display_early_init(DisplayOptions *opts)
+{
+ assert(opts->type < DISPLAY_TYPE__MAX);
+ if (opts->type == DISPLAY_TYPE_NONE) {
+ return;
+ }
+ if (dpys[opts->type] == NULL) {
+ ui_module_load_one(DisplayType_str(opts->type));
+ }
+ if (dpys[opts->type] == NULL) {
+ error_report("Display '%s' is not available.",
+ DisplayType_str(opts->type));
+ exit(1);
+ }
+ if (dpys[opts->type]->early_init) {
+ dpys[opts->type]->early_init(opts);
+ }
+}
+
+void qemu_display_init(DisplayState *ds, DisplayOptions *opts)
+{
+ assert(opts->type < DISPLAY_TYPE__MAX);
+ if (opts->type == DISPLAY_TYPE_NONE) {
+ return;
+ }
+ assert(dpys[opts->type] != NULL);
+ dpys[opts->type]->init(ds, opts);
+}
+
+void qemu_display_help(void)
+{
+ int idx;
+
+ printf("Available display backend types:\n");
+ printf("none\n");
+ for (idx = DISPLAY_TYPE_NONE; idx < DISPLAY_TYPE__MAX; idx++) {
+ if (!dpys[idx]) {
+ ui_module_load_one(DisplayType_str(idx));
+ }
+ if (dpys[idx]) {
+ printf("%s\n", DisplayType_str(dpys[idx]->type));
+ }
+ }
+}
+
void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
{
int val;