[virt-tools-list] [PATCH 19/22] spice: hook into port channel to create VTE terminals

marcandre.lureau at redhat.com marcandre.lureau at redhat.com
Tue Jul 31 13:41:22 UTC 2018


From: Marc-André Lureau <marcandre.lureau at redhat.com>

QEMU defines a few Spice port channel names in
docs/spice-port-fqdn.txt that can be interacted with a terminal.

Create VirtViewerDisplayVte display for all known terminal channel,
and redirect read/write signals.

Note that if VTE support is disabled, or if the VTE console isn't
shown, spice-gtk will still process those port channels (discarding
the read if VTE is disabled).

Signed-off-by: Marc-André Lureau <marcandre.lureau at redhat.com>
---
 src/virt-viewer-session-spice.c | 118 +++++++++++++++++++++++++++++++-
 1 file changed, 116 insertions(+), 2 deletions(-)

diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer-session-spice.c
index fdc7004..1c622e6 100644
--- a/src/virt-viewer-session-spice.c
+++ b/src/virt-viewer-session-spice.c
@@ -34,6 +34,7 @@
 #include "virt-viewer-util.h"
 #include "virt-viewer-session-spice.h"
 #include "virt-viewer-display-spice.h"
+#include "virt-viewer-display-vte.h"
 #include "virt-viewer-auth.h"
 
 G_DEFINE_TYPE (VirtViewerSessionSpice, virt_viewer_session_spice, VIRT_VIEWER_TYPE_SESSION)
@@ -950,20 +951,116 @@ on_new_file_transfer(SpiceMainChannel *channel G_GNUC_UNUSED,
                                               task);
 }
 
+static void
+spice_port_write_finished(GObject *source_object,
+                          GAsyncResult *res,
+                          gpointer dup)
+{
+    SpicePortChannel *port = SPICE_PORT_CHANNEL(source_object);
+    GError *err = NULL;
+
+    spice_port_channel_write_finish(port, res, &err);
+    if (err) {
+        g_warning("Spice port write failed: %s", err->message);
+        g_error_free(err);
+    }
+    g_free(dup);
+}
+
+static void
+spice_vte_commit(SpicePortChannel *port, const char *text,
+                 guint size, gpointer user_data G_GNUC_UNUSED)
+{
+    void *dup = g_memdup(text, size);
+
+    /* note: spice-gtk queues write */
+    spice_port_channel_write_async(port, dup, size,
+                                   NULL, spice_port_write_finished, dup);
+}
+
+static void
+spice_port_data(VirtViewerDisplayVte *vte, gpointer data, int size,
+                SpicePortChannel *port G_GNUC_UNUSED)
+{
+    virt_viewer_display_vte_feed(vte, data, size);
+}
+
+static const char *
+port_name_to_vte_name(const char *name)
+{
+    if (g_str_equal(name, "org.qemu.console.serial.0"))
+        return _("Serial");
+    else if (g_str_equal(name, "org.qemu.monitor.hmp.0"))
+        return _("QEMU human monitor");
+    else if (g_str_equal(name, "org.qemu.console.debug.0"))
+        return _("QEMU debug console");
+
+    return NULL;
+}
+
+static void
+spice_port_opened(SpiceChannel *channel, GParamSpec *pspec G_GNUC_UNUSED,
+                  VirtViewerSessionSpice *self)
+{
+    SpicePortChannel *port = SPICE_PORT_CHANNEL(channel);
+    int id;
+    gchar *name = NULL;
+    gboolean opened = FALSE;
+    const char *vte_name;
+    GtkWidget *vte;
+
+    g_object_get(G_OBJECT(port),
+                 "channel-id", &id,
+                 "port-name", &name,
+                 "port-opened", &opened,
+                 NULL);
+
+    g_return_if_fail(name != NULL);
+    g_debug("port#%d %s: %s", id, name, opened ? "opened" : "closed");
+
+    vte = g_object_get_data(G_OBJECT(port), "virt-viewer-vte");
+    if (vte) {
+        if (opened)
+            goto end;
+
+        g_object_set_data(G_OBJECT(port), "virt-viewer-vte", NULL);
+        virt_viewer_session_remove_display(VIRT_VIEWER_SESSION(self), VIRT_VIEWER_DISPLAY(vte));
+
+    } else if (opened) {
+        vte_name = port_name_to_vte_name(name);
+        if (!vte_name)
+            goto end;
+
+        vte = virt_viewer_display_vte_new(VIRT_VIEWER_SESSION(self), vte_name);
+        g_object_set_data(G_OBJECT(port), "virt-viewer-vte", g_object_ref_sink(vte));
+        virt_viewer_session_add_display(VIRT_VIEWER_SESSION(self), VIRT_VIEWER_DISPLAY(vte));
+        virt_viewer_signal_connect_object(vte, "commit",
+                                          G_CALLBACK(spice_vte_commit), port, G_CONNECT_SWAPPED);
+        virt_viewer_signal_connect_object(port, "port-data",
+                                          G_CALLBACK(spice_port_data), vte, G_CONNECT_SWAPPED);
+    }
+
+end:
+    g_free(name);
+}
+
 static void
 virt_viewer_session_spice_channel_new(SpiceSession *s,
                                       SpiceChannel *channel,
                                       VirtViewerSession *session)
 {
     VirtViewerSessionSpice *self = VIRT_VIEWER_SESSION_SPICE(session);
-    int id;
+    int id, type;
 
     g_return_if_fail(self != NULL);
 
     virt_viewer_signal_connect_object(channel, "open-fd",
                                       G_CALLBACK(virt_viewer_session_spice_channel_open_fd_request), self, 0);
 
-    g_object_get(channel, "channel-id", &id, NULL);
+    g_object_get(channel,
+                 "channel-id", &id,
+                 "channel-type", &type,
+                 NULL);
 
     g_debug("New spice channel %p %s %d", channel, g_type_name(G_OBJECT_TYPE(channel)), id);
 
@@ -1012,6 +1109,13 @@ virt_viewer_session_spice_channel_new(SpiceSession *s,
             virt_viewer_session_set_has_usbredir(session, TRUE);
     }
 
+    /* the port channel object is also sub-classed for webdav... */
+    if (SPICE_IS_PORT_CHANNEL(channel) && type == SPICE_CHANNEL_PORT) {
+        virt_viewer_signal_connect_object(channel, "notify::port-opened",
+                                          G_CALLBACK(spice_port_opened), self, 0);
+        spice_channel_connect(channel);
+    }
+
     self->priv->channel_count++;
 }
 
@@ -1118,6 +1222,16 @@ virt_viewer_session_spice_channel_destroy(G_GNUC_UNUSED SpiceSession *s,
         g_object_set_data(G_OBJECT(channel), "virt-viewer-displays", NULL);
     }
 
+    if (SPICE_IS_PORT_CHANNEL(channel)) {
+        VirtViewerDisplayVte *vte = g_object_get_data(G_OBJECT(channel), "virt-viewer-vte");
+        g_debug("zap port channel (#%d)", id);
+        if (vte) {
+            g_object_set_data(G_OBJECT(channel), "virt-viewer-vte", NULL);
+            virt_viewer_session_remove_display(VIRT_VIEWER_SESSION(self), VIRT_VIEWER_DISPLAY(vte));
+        }
+
+    }
+
     if (SPICE_IS_PLAYBACK_CHANNEL(channel) && self->priv->audio) {
         g_debug("zap audio channel");
         self->priv->audio = NULL;
-- 
2.18.0.321.gffc6fa0e39




More information about the virt-tools-list mailing list