Register a console notifier so the GTK display dynamically creates and
destroys VirtualConsole tabs when graphic consoles are added or removed
at runtime (e.g. vfio-pci with display=on hotplug).

Add skips consoles that already have a VC binding, remove skips unknown
consoles.

Signed-off-by: Marc-André Lureau <[email protected]>
---
 include/ui/gtk.h |  1 +
 ui/gtk.c         | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index b0be5070795..6cb611ce474 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -140,6 +140,7 @@ struct GtkDisplayState {
 
     GdkCursor *null_cursor;
     Notifier mouse_mode_notifier;
+    Notifier console_notifier;
     gboolean free_scale;
     gboolean keep_aspect_ratio;
 
diff --git a/ui/gtk.c b/ui/gtk.c
index 6325704c397..6ef58d1367c 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -2332,7 +2332,8 @@ static bool gd_scale_valid(double scale)
     return scale >= VC_SCALE_MIN && scale <= VC_SCALE_MAX;
 }
 
-static void add_gfx_console(GtkDisplayState *s, QemuConsole *con)
+static VirtualConsole *
+add_gfx_console(GtkDisplayState *s, QemuConsole *con)
 {
     VirtualConsole *vc = g_new0(VirtualConsole, 1);
     const DisplayChangeListenerOps *ops = &dcl_ops;
@@ -2414,6 +2415,76 @@ static void add_gfx_console(GtkDisplayState *s, 
QemuConsole *con)
     qemu_console_register_listener(con, &vc->gfx.dcl, ops);
 
     gd_connect_vc_gfx_signals(vc);
+    return vc;
+}
+
+static void gd_vc_add_gfx(GtkDisplayState *s, QemuConsole *con)
+{
+    VirtualConsole *vc;
+    int i;
+
+    for (i = 0; i < (int)s->vcs->len; i++) {
+        VirtualConsole *v = g_ptr_array_index(s->vcs, i);
+        if (v->type == GD_VC_GFX && v->gfx.dcl.con == con) {
+            return;
+        }
+    }
+
+    vc = add_gfx_console(s, con);
+    gtk_widget_show_all(vc->tab_item);
+    gtk_widget_realize(vc->gfx.drawing_area);
+
+    if (s->free_scale) {
+        gd_update_windowsize(vc);
+    }
+
+    gd_update_caption(s);
+    gd_rebuild_vc_menu(s);
+
+    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), true);
+}
+
+static void gd_vc_remove_gfx(GtkDisplayState *s, QemuConsole *con)
+{
+    VirtualConsole *vc = NULL;
+    guint idx;
+
+    for (idx = 0; idx < s->vcs->len; idx++) {
+        VirtualConsole *v = g_ptr_array_index(s->vcs, idx);
+        if (v->type == GD_VC_GFX && v->gfx.dcl.con == con) {
+            vc = v;
+            break;
+        }
+    }
+    if (!vc) {
+        return;
+    }
+
+    if (s->kbd_owner == vc) {
+        gd_ungrab_keyboard(s);
+    }
+    if (s->ptr_owner == vc) {
+        gd_ungrab_pointer(s);
+    }
+
+    g_ptr_array_remove_index(s->vcs, idx);
+    gd_rebuild_vc_menu(s);
+    gd_update_caption(s);
+}
+
+static void gd_console_notify(Notifier *n, void *data)
+{
+    GtkDisplayState *s = container_of(n, GtkDisplayState, console_notifier);
+    QemuConsoleEvent *event = data;
+
+    switch (event->type) {
+    case QEMU_CONSOLE_ADDED:
+        gd_vc_add_gfx(s, event->con);
+        break;
+    case QEMU_CONSOLE_REMOVED:
+        gd_vc_remove_gfx(s, event->con);
+        break;
+    }
 }
 
 static void gd_create_menu_view(GtkDisplayState *s, DisplayOptions *opts)
@@ -2688,6 +2759,9 @@ static void gtk_display_init(DisplayState *ds, 
DisplayOptions *opts)
 
     gd_create_menus(s, opts);
 
+    s->console_notifier.notify = gd_console_notify;
+    qemu_console_add_notifier(&s->console_notifier);
+
     gd_connect_signals(s);
 
     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
@@ -2808,6 +2882,7 @@ static void gtk_display_cleanup(void)
         return;
     }
 
+    qemu_console_remove_notifier(&s->console_notifier);
     qemu_remove_mouse_mode_change_notifier(&s->mouse_mode_notifier);
     gd_clipboard_cleanup(s);
     g_signal_handlers_disconnect_by_func(s->notebook,

-- 
2.54.0


Reply via email to