summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ui/console.c105
-rw-r--r--ui/gtk.c60
-rw-r--r--ui/sdl2.c2
-rw-r--r--ui/vnc-jobs.c4
-rw-r--r--ui/vnc.c10
-rw-r--r--ui/vnc.h3
-rw-r--r--vl.c76
7 files changed, 168 insertions, 92 deletions
diff --git a/ui/console.c b/ui/console.c
index b02510cdca..945f05d728 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -165,6 +165,8 @@ struct QemuConsole {
     QEMUFIFO out_fifo;
     uint8_t out_fifo_buf[16];
     QEMUTimer *kbd_timer;
+
+    QTAILQ_ENTRY(QemuConsole) next;
 };
 
 struct DisplayState {
@@ -180,8 +182,8 @@ struct DisplayState {
 
 static DisplayState *display_state;
 static QemuConsole *active_console;
-static QemuConsole **consoles;
-static int nb_consoles = 0;
+static QTAILQ_HEAD(consoles_head, QemuConsole) consoles =
+    QTAILQ_HEAD_INITIALIZER(consoles);
 static bool cursor_visible_phase;
 static QEMUTimer *cursor_timer;
 
@@ -197,7 +199,7 @@ static void gui_update(void *opaque)
     uint64_t dcl_interval;
     DisplayState *ds = opaque;
     DisplayChangeListener *dcl;
-    int i;
+    QemuConsole *con;
 
     ds->refreshing = true;
     dpy_refresh(ds);
@@ -212,9 +214,9 @@ static void gui_update(void *opaque)
     }
     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);
@@ -1292,10 +1294,13 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
     s->ds = ds;
     s->console_type = console_type;
 
-    consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1));
-    if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) {
-        s->index = nb_consoles;
-        consoles[nb_consoles++] = s;
+    if (QTAILQ_EMPTY(&consoles)) {
+        s->index = 0;
+        QTAILQ_INSERT_TAIL(&consoles, s, next);
+    } else if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) {
+        QemuConsole *last = QTAILQ_LAST(&consoles, consoles_head);
+        s->index = last->index + 1;
+        QTAILQ_INSERT_TAIL(&consoles, s, next);
     } else {
         /*
          * HACK: Put graphical consoles before text consoles.
@@ -1303,15 +1308,24 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
          * Only do that for coldplugged devices.  After initial device
          * initialization we will not renumber the consoles any more.
          */
-        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;
+        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;
 }
@@ -1861,21 +1875,21 @@ static DisplayState *get_alloc_displaystate(void)
 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), &error_abort);
         g_free(name);
     }
 
@@ -1957,33 +1971,34 @@ void graphic_console_close(QemuConsole *con)
 
 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;
 }
@@ -2013,22 +2028,19 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
 
 QemuConsole *qemu_console_lookup_unused(void)
 {
+    QemuConsole *con;
     Object *obj;
-    int i;
 
-    for (i = 0; i < nb_consoles; i++) {
-        if (!consoles[i]) {
-            continue;
-        }
-        if (consoles[i]->hw_ops != &unused_ops) {
+    QTAILQ_FOREACH(con, &consoles, next) {
+        if (con->hw_ops != &unused_ops) {
             continue;
         }
-        obj = object_property_get_link(OBJECT(consoles[i]),
+        obj = object_property_get_link(OBJECT(con),
                                        "device", &error_abort);
         if (obj != NULL) {
             continue;
         }
-        return consoles[i];
+        return con;
     }
     return NULL;
 }
@@ -2130,12 +2142,11 @@ static void text_console_update_cursor_timer(void)
 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;
diff --git a/ui/gtk.c b/ui/gtk.c
index bb3214cffb..dbce970dc4 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -145,6 +145,7 @@
 #define GDK_KEY_2 GDK_2
 #define GDK_KEY_f GDK_f
 #define GDK_KEY_g GDK_g
+#define GDK_KEY_m GDK_m
 #define GDK_KEY_q GDK_q
 #define GDK_KEY_plus GDK_plus
 #define GDK_KEY_equal GDK_equal
@@ -208,6 +209,7 @@ struct GtkDisplayState {
 
     GtkWidget *show_tabs_item;
     GtkWidget *untabify_item;
+    GtkWidget *show_menubar_item;
 
     GtkWidget *vbox;
     GtkWidget *notebook;
@@ -1387,6 +1389,30 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
     }
 }
 
+static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
+{
+    GtkDisplayState *s = opaque;
+    VirtualConsole *vc = gd_vc_find_current(s);
+
+    if (s->full_screen) {
+        return;
+    }
+
+    if (gtk_check_menu_item_get_active(
+                GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
+        gtk_widget_show(s->menu_bar);
+    } else {
+        gtk_widget_hide(s->menu_bar);
+    }
+    gd_update_windowsize(vc);
+}
+
+static void gd_accel_show_menubar(void *opaque)
+{
+    GtkDisplayState *s = opaque;
+    gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item));
+}
+
 static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
 {
     GtkDisplayState *s = opaque;
@@ -1403,7 +1429,10 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
     } else {
         gtk_window_unfullscreen(GTK_WINDOW(s->window));
         gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
-        gtk_widget_show(s->menu_bar);
+        if (gtk_check_menu_item_get_active(
+                    GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
+            gtk_widget_show(s->menu_bar);
+        }
         s->full_screen = FALSE;
         if (vc->type == GD_VC_GFX) {
             vc->gfx.scale_x = 1.0;
@@ -2036,6 +2065,8 @@ static void gd_connect_signals(GtkDisplayState *s)
                      G_CALLBACK(gd_menu_show_tabs), s);
     g_signal_connect(s->untabify_item, "activate",
                      G_CALLBACK(gd_menu_untabify), s);
+    g_signal_connect(s->show_menubar_item, "activate",
+                     G_CALLBACK(gd_menu_show_menubar), s);
 
     g_signal_connect(s->window, "delete-event",
                      G_CALLBACK(gd_window_close), s);
@@ -2272,11 +2303,26 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
     s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
     gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
 
+    s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
+            _("Show Menubar"));
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
+                                   TRUE);
+    gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
+            g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
+#if GTK_CHECK_VERSION(3, 8, 0)
+    gtk_accel_label_set_accel(
+            GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))),
+            GDK_KEY_m, HOTKEY_MODIFIERS);
+#endif
+    gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item);
+
     return view_menu;
 }
 
 static void gd_create_menus(GtkDisplayState *s)
 {
+    GtkSettings *settings;
+
     s->accel_group = gtk_accel_group_new();
     s->machine_menu = gd_create_menu_machine(s);
     s->view_menu = gd_create_menu_view(s);
@@ -2292,6 +2338,10 @@ static void gd_create_menus(GtkDisplayState *s)
 
     g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
     gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
+
+    /* Disable the default "F10" menu shortcut. */
+    settings = gtk_widget_get_settings(s->window);
+    g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL);
 }
 
 
@@ -2434,13 +2484,17 @@ static void early_gtk_display_init(DisplayOptions *opts)
     }
 
     assert(opts->type == DISPLAY_TYPE_GTK);
-    if (opts->has_gl && opts->gl) {
+    if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
+        if (opts->gl == DISPLAYGL_MODE_ES) {
+            error_report("gtk: opengl es not supported");
+            return;
+        }
 #if defined(CONFIG_OPENGL)
 #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND)
         if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
             gtk_use_gl_area = true;
             gtk_gl_area_init();
-        }
+        } else
 #endif
         {
             gtk_egl_init();
diff --git a/ui/sdl2.c b/ui/sdl2.c
index da037248c2..a0f9b16bc1 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -804,7 +804,6 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
         return;
     }
     sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs);
-    sdl2_console->opts = o;
     for (i = 0; i < sdl2_num_outputs; i++) {
         QemuConsole *con = qemu_console_lookup_by_index(i);
         assert(con != NULL);
@@ -812,6 +811,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
             sdl2_console[i].hidden = true;
         }
         sdl2_console[i].idx = i;
+        sdl2_console[i].opts = o;
 #ifdef CONFIG_OPENGL
         sdl2_console[i].opengl = display_opengl;
         sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops;
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 868dddef4b..b0b15d42a8 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -82,6 +82,7 @@ VncJob *vnc_job_new(VncState *vs)
 {
     VncJob *job = g_new0(VncJob, 1);
 
+    assert(vs->magic == VNC_MAGIC);
     job->vs = vs;
     vnc_lock_queue(queue);
     QLIST_INIT(&job->rectangles);
@@ -214,6 +215,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
     /* Here job can only be NULL if queue->exit is true */
     job = QTAILQ_FIRST(&queue->jobs);
     vnc_unlock_queue(queue);
+    assert(job->vs->magic == VNC_MAGIC);
 
     if (queue->exit) {
         return -1;
@@ -236,6 +238,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
 
     /* Make a local copy of vs and switch output buffers */
     vnc_async_encoding_start(job->vs, &vs);
+    vs.magic = VNC_MAGIC;
 
     /* Start sending rectangles */
     n_rectangles = 0;
@@ -289,6 +292,7 @@ disconnected:
     vnc_unlock_queue(queue);
     qemu_cond_broadcast(&queue->cond);
     g_free(job);
+    vs.magic = 0;
     return 0;
 }
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 5526e54f48..359693238b 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1138,6 +1138,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd)
 {
     VncState *vs = opaque;
 
+    assert(vs->magic == VNC_MAGIC);
     switch (cmd) {
     case AUD_CNOTIFY_DISABLE:
         vnc_lock_output(vs);
@@ -1167,6 +1168,7 @@ static void audio_capture(void *opaque, void *buf, int size)
 {
     VncState *vs = opaque;
 
+    assert(vs->magic == VNC_MAGIC);
     vnc_lock_output(vs);
     if (vs->output.offset < vs->throttle_output_offset) {
         vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
@@ -1275,6 +1277,7 @@ void vnc_disconnect_finish(VncState *vs)
     vs->ioc = NULL;
     object_unref(OBJECT(vs->sioc));
     vs->sioc = NULL;
+    vs->magic = 0;
     g_free(vs);
 }
 
@@ -1414,7 +1417,7 @@ static void vnc_client_write_locked(VncState *vs)
 
 static void vnc_client_write(VncState *vs)
 {
-
+    assert(vs->magic == VNC_MAGIC);
     vnc_lock_output(vs);
     if (vs->output.offset) {
         vnc_client_write_locked(vs);
@@ -1487,6 +1490,7 @@ static void vnc_jobs_bh(void *opaque)
 {
     VncState *vs = opaque;
 
+    assert(vs->magic == VNC_MAGIC);
     vnc_jobs_consume_buffer(vs);
 }
 
@@ -1537,6 +1541,8 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
                        GIOCondition condition, void *opaque)
 {
     VncState *vs = opaque;
+
+    assert(vs->magic == VNC_MAGIC);
     if (condition & G_IO_IN) {
         if (vnc_client_read(vs) < 0) {
             /* vs is free()ed here */
@@ -1568,6 +1574,7 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
 
 void vnc_write(VncState *vs, const void *data, size_t len)
 {
+    assert(vs->magic == VNC_MAGIC);
     if (vs->disconnecting) {
         return;
     }
@@ -3064,6 +3071,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
     int i;
 
     trace_vnc_client_connect(vs, sioc);
+    vs->magic = VNC_MAGIC;
     vs->sioc = sioc;
     object_ref(OBJECT(vs->sioc));
     vs->ioc = QIO_CHANNEL(sioc);
diff --git a/ui/vnc.h b/ui/vnc.h
index 7b29def77d..762632929b 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -255,8 +255,11 @@ typedef enum {
     VNC_STATE_UPDATE_FORCE,
 } VncStateUpdate;
 
+#define VNC_MAGIC ((uint64_t)0x05b3f069b3d204bb)
+
 struct VncState
 {
+    uint64_t magic;
     QIOChannelSocket *sioc; /* The underlying socket */
     QIOChannel *ioc; /* The channel currently used for I/O */
     guint ioc_tag;
diff --git a/vl.c b/vl.c
index b9f6b42779..3b39bbd7a8 100644
--- a/vl.c
+++ b/vl.c
@@ -120,12 +120,14 @@ int main(int argc, char **argv)
 #include "ui/qemu-spice.h"
 #include "qapi/string-input-visitor.h"
 #include "qapi/opts-visitor.h"
+#include "qapi/clone-visitor.h"
 #include "qom/object_interfaces.h"
 #include "exec/semihost.h"
 #include "crypto/init.h"
 #include "sysemu/replay.h"
 #include "qapi/qapi-events-run-state.h"
 #include "qapi/qapi-visit-block-core.h"
+#include "qapi/qapi-visit-ui.h"
 #include "qapi/qapi-commands-block-core.h"
 #include "qapi/qapi-commands-misc.h"
 #include "qapi/qapi-commands-run-state.h"
@@ -2088,11 +2090,40 @@ static void select_vgahw(const char *p)
     }
 }
 
+static void parse_display_qapi(const char *optarg)
+{
+    Error *err = NULL;
+    DisplayOptions *opts;
+    Visitor *v;
+
+    v = qobject_input_visitor_new_str(optarg, "type", &err);
+    if (!v) {
+        error_report_err(err);
+        exit(1);
+    }
+
+    visit_type_DisplayOptions(v, NULL, &opts, &error_fatal);
+    QAPI_CLONE_MEMBERS(DisplayOptions, &dpy, opts);
+
+    qapi_free_DisplayOptions(opts);
+    visit_free(v);
+}
+
 static void parse_display(const char *p)
 {
     const char *opts;
 
     if (strstart(p, "sdl", &opts)) {
+        /*
+         * sdl DisplayType needs hand-crafted parser instead of
+         * parse_display_qapi() due to some options not in
+         * DisplayOptions, specifically:
+         *   - frame
+         *     Already deprecated.
+         *   - ctrl_grab + alt_grab
+         *     Not clear yet what happens to them long-term.  Should
+         *     replaced by something better or deprecated and dropped.
+         */
         dpy.type = DISPLAY_TYPE_SDL;
         while (*opts) {
             const char *nextopt;
@@ -2158,53 +2189,18 @@ static void parse_display(const char *p)
             opts = nextopt;
         }
     } else if (strstart(p, "vnc", &opts)) {
+        /*
+         * vnc isn't a (local) DisplayType but a protocol for remote
+         * display access.
+         */
         if (*opts == '=') {
             vnc_parse(opts + 1, &error_fatal);
         } else {
             error_report("VNC requires a display argument vnc=<display>");
             exit(1);
         }
-    } else if (strstart(p, "egl-headless", &opts)) {
-        dpy.type = DISPLAY_TYPE_EGL_HEADLESS;
-    } else if (strstart(p, "curses", &opts)) {
-        dpy.type = DISPLAY_TYPE_CURSES;
-    } else if (strstart(p, "gtk", &opts)) {
-        dpy.type = DISPLAY_TYPE_GTK;
-        while (*opts) {
-            const char *nextopt;
-
-            if (strstart(opts, ",grab_on_hover=", &nextopt)) {
-                opts = nextopt;
-                dpy.u.gtk.has_grab_on_hover = true;
-                if (strstart(opts, "on", &nextopt)) {
-                    dpy.u.gtk.grab_on_hover = true;
-                } else if (strstart(opts, "off", &nextopt)) {
-                    dpy.u.gtk.grab_on_hover = false;
-                } else {
-                    goto invalid_gtk_args;
-                }
-            } else if (strstart(opts, ",gl=", &nextopt)) {
-                opts = nextopt;
-                dpy.has_gl = true;
-                if (strstart(opts, "on", &nextopt)) {
-                    dpy.gl = DISPLAYGL_MODE_ON;
-                } else if (strstart(opts, "off", &nextopt)) {
-                    dpy.gl = DISPLAYGL_MODE_OFF;
-                } else {
-                    goto invalid_gtk_args;
-                }
-            } else {
-            invalid_gtk_args:
-                error_report("invalid GTK option string");
-                exit(1);
-            }
-            opts = nextopt;
-        }
-    } else if (strstart(p, "none", &opts)) {
-        dpy.type = DISPLAY_TYPE_NONE;
     } else {
-        error_report("unknown display type");
-        exit(1);
+        parse_display_qapi(p);
     }
 }