[Spice-devel] [PATCH spice-gtk 3/3] spicy: demo SpicePort usage

Marc-André Lureau marcandre.lureau at gmail.com
Fri Nov 30 04:43:46 PST 2012


spicy has been modified to recognized 2 different port types to play
with:

* org.spice.spicy: will connect the port to the current stdin/stdout,
  and can be used as a chardev for the qemu monitor

* org.spice.spicy.break: will send a break event on connect and
  disconnect immediately (exercice the port event and flush)
---
 configure.ac |   1 +
 gtk/spicy.c  | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 150 insertions(+)

diff --git a/configure.ac b/configure.ac
index 7c59575..629fd5a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,7 @@ AM_CONDITIONAL([OS_WIN32],[test "$os_win32" = "yes"])
 
 AC_CHECK_HEADERS([sys/ipc.h sys/shm.h])
 AC_CHECK_HEADERS([sys/socket.h netinet/in.h arpa/inet.h])
+AC_CHECK_HEADERS([termios.h])
 
 AC_CHECK_LIBM
 AC_SUBST(LIBM)
diff --git a/gtk/spicy.c b/gtk/spicy.c
index 5758cc4..dd8c970 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -22,6 +22,9 @@
 #include <glib/gi18n.h>
 
 #include <sys/stat.h>
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
 
 #define GNOME_DESKTOP_USE_UNSTABLE_API 2
 #include "display/gnome-rr.h"
@@ -128,6 +131,7 @@ static char *spicy_title = NULL;
 static GMainLoop     *mainloop = NULL;
 static int           connections = 0;
 static GKeyFile      *keyfile = NULL;
+static SpicePortChannel*stdin_port = NULL;
 static GnomeRRScreen *rrscreen = NULL;
 static GnomeRRConfig *rrsaved = NULL;
 static GnomeRRConfig *rrcurrent = NULL;
@@ -1612,6 +1616,101 @@ static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
     g_clear_pointer(&monitors, g_array_unref);
 }
 
+static void port_write_cb(GObject *source_object,
+                          GAsyncResult *res,
+                          gpointer user_data)
+{
+    SpicePortChannel *port = SPICE_PORT_CHANNEL(source_object);
+    GError *error = NULL;
+
+    spice_port_write_finish(port, res, &error);
+    if (error != NULL)
+        g_warning("%s", error->message);
+    g_clear_error(&error);
+}
+
+static void port_flushed_cb(GObject *source_object,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+    SpiceChannel *channel = SPICE_CHANNEL(source_object);
+    GError *error = NULL;
+
+    spice_channel_flush_finish(channel, res, &error);
+    if (error != NULL)
+        g_warning("%s", error->message);
+    g_clear_error(&error);
+
+    spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED);
+}
+
+static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
+{
+    char buf[4096];
+    gsize bytes_read;
+    GIOStatus status;
+
+    if (!(condition & G_IO_IN))
+        return FALSE;
+
+    status = g_io_channel_read_chars(gin, buf, sizeof(buf), &bytes_read, NULL);
+    if (status != G_IO_STATUS_NORMAL)
+        return FALSE;
+
+    if (stdin_port != NULL)
+        spice_port_write_async(stdin_port, buf, bytes_read, NULL, port_write_cb, NULL);
+
+    return TRUE;
+}
+
+static void port_opened(SpiceChannel *channel, GParamSpec *pspec,
+                        spice_connection *conn)
+{
+    SpicePortChannel *port = SPICE_PORT_CHANNEL(channel);
+    gchar *name = NULL;
+    gboolean opened = FALSE;
+
+    g_object_get(channel,
+                 "port-name", &name,
+                 "port-opened", &opened,
+                 NULL);
+
+    g_printerr("port %p %s: %s\n", channel, name, opened ? "opened" : "closed");
+
+    if (opened) {
+        /* only send a break event and disconnect */
+        if (g_strcmp0(name, "org.spice.spicy.break") == 0) {
+            spice_port_event(port, SPICE_PORT_EVENT_BREAK);
+        }
+
+        /* handle the first spicy port and connect it to stdin/out */
+        if (g_strcmp0(name, "org.spice.spicy") == 0 && stdin_port == NULL) {
+            stdin_port = port;
+            goto end;
+        }
+
+        if (port == stdin_port)
+            goto end;
+
+        spice_channel_flush_async(channel, NULL, port_flushed_cb, conn);
+    } else {
+        if (port == stdin_port)
+            stdin_port = NULL;
+    }
+
+end:
+    g_free(name);
+}
+
+static void port_data(SpicePortChannel *port,
+                      gpointer data, int size, spice_connection *conn)
+{
+    if (port != stdin_port)
+        return;
+
+    write(fileno(stdout), data, size);
+}
+
 static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
 {
     spice_connection *conn = data;
@@ -1659,6 +1758,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
     if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
         update_auto_usbredir_sensitive(conn);
     }
+
+    if (SPICE_IS_PORT_CHANNEL(channel)) {
+        g_signal_connect(channel, "notify::port-opened",
+                         G_CALLBACK(port_opened), conn);
+        g_signal_connect(channel, "port-data",
+                         G_CALLBACK(port_data), conn);
+        spice_channel_connect(channel);
+    }
 }
 
 static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data)
@@ -1687,6 +1794,11 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
         update_auto_usbredir_sensitive(conn);
     }
 
+    if (SPICE_IS_PORT_CHANNEL(channel)) {
+        if (SPICE_PORT_CHANNEL(channel) == stdin_port)
+            stdin_port = NULL;
+    }
+
     conn->channels--;
     if (conn->channels > 0) {
         return;
@@ -1839,6 +1951,40 @@ static void usb_connect_failed(GObject               *object,
     gtk_widget_destroy(dialog);
 }
 
+static void setup_terminal(gboolean reset)
+{
+    int stdinfd = fileno(stdin);
+
+    if (!isatty(stdinfd))
+        return;
+
+#ifdef HAVE_TERMIOS_H
+    static struct termios saved_tios;
+    struct termios tios;
+
+    if (reset)
+        tios = saved_tios;
+    else {
+        tcgetattr(stdinfd, &tios);
+        saved_tios = tios;
+        tios.c_lflag &= ~(ICANON | ECHO);
+    }
+
+    tcsetattr(stdinfd, TCSANOW, &tios);
+#endif
+}
+
+static void watch_stdin(void)
+{
+    int stdinfd = fileno(stdin);
+    GIOChannel *gin;
+
+    setup_terminal(false);
+    gin = g_io_channel_unix_new(stdinfd);
+    g_io_channel_set_flags(gin, G_IO_FLAG_NONBLOCK, NULL);
+    g_io_add_watch(gin, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, NULL);
+}
+
 int main(int argc, char *argv[])
 {
     GError *error = NULL;
@@ -1929,6 +2075,8 @@ int main(int argc, char *argv[])
     g_free(port);
     g_free(tls_port);
 
+    watch_stdin();
+
     connection_connect(conn);
     if (connections > 0)
         g_main_loop_run(mainloop);
@@ -1950,5 +2098,6 @@ int main(int argc, char *argv[])
 
     g_free(spicy_title);
 
+    setup_terminal(true);
     return 0;
 }
-- 
1.7.11.7



More information about the Spice-devel mailing list