[Spice-devel] [PATCH spice-gtk 1/2] Add controller foreign menu support

Christophe Fergeau cfergeau at redhat.com
Wed Feb 29 08:43:59 PST 2012


I'm not fluent in vala, but this looks sane after a quick glance.

Christophe

On Wed, Feb 29, 2012 at 03:04:17PM +0100, Marc-André Lureau wrote:
> ---
>  data/spice-protocol.vapi                     |   91 +++++++++++
>  gtk/controller/Makefile.am                   |   10 +-
>  gtk/controller/custom.vapi                   |    9 +
>  gtk/controller/foreign-menu.vala             |  207 ++++++++++++++++++++++++++
>  gtk/controller/menu.vala                     |    6 +
>  gtk/controller/spice-foreign-menu-listener.c |  159 ++++++++++++++++++++
>  gtk/controller/spice-foreign-menu-listener.h |   47 ++++++
>  spice-protocol                               |    2 +-
>  8 files changed, 529 insertions(+), 2 deletions(-)
>  create mode 100644 gtk/controller/foreign-menu.vala
>  create mode 100644 gtk/controller/spice-foreign-menu-listener.c
>  create mode 100644 gtk/controller/spice-foreign-menu-listener.h
> 
> diff --git a/data/spice-protocol.vapi b/data/spice-protocol.vapi
> index 4cb1a2f..01b1a81 100644
> --- a/data/spice-protocol.vapi
> +++ b/data/spice-protocol.vapi
> @@ -107,4 +107,95 @@ namespace SpiceProtocol {
>  			GRAYED,
>  		}
>  	}
> +
> +    [CCode (cprefix = "FrgMenu", cheader_filename = "spice/foreign_menu_prot.h")]
> +    namespace ForeignMenu {
> +        [CCode (cname = "FOREIGN_MENU_MAGIC")]
> +        public const uint32 MAGIC;
> +        [CCode (cname = "FOREIGN_MENU_VERSION")]
> +        public const int VERSION;
> +
> +        [Compact]
> +        public struct InitHeader {
> +            uint32 magic;
> +            uint32 version;
> +            uint32 size;
> +        }
> +
> +        [Compact]
> +        [CCode (has_destroy_function = false)]
> +        public struct Init {
> +            InitHeader base;
> +            uint64 credentials;
> +            string title; // utf8
> +        }
> +
> +        [Compact]
> +        public struct Msg {
> +            uint32 id;
> +            uint32 size;
> +        }
> +
> +        [CCode (cprefix = "FOREIGN_MENU_", cname = "int")]
> +        public enum MsgId {
> +            //extrenal app -> spice client
> +            SET_TITLE,
> +            ADD_ITEM,
> +            MODIFY_ITEM,
> +            REMOVE_ITEM,
> +            CLEAR,
> +
> +            //spice client -> external app
> +            ITEM_EVENT,
> +            APP_ACTIVATED,
> +            APP_DEACTIVATED,
> +        }
> +
> +        [Compact]
> +        [CCode (cname = "FrgMenuSetTitle")]
> +        public struct SetTitle {
> +            Msg base;
> +            string string; // utf8
> +        }
> +
> +        [CCode (cprefix = "FOREIGN_MENU_ITEM_TYPE_", cname = "unsigned int", has_type_id = false)]
> +        [Flags]
> +        public enum MenuFlags {
> +            CHECKED,
> +            DIM,
> +            SEPARATOR
> +        }
> +
> +        [Compact]
> +        [CCode (cname = "FrgMenuAddItem")]
> +        public struct AddItem {
> +            Msg base;
> +            uint32 id;
> +            uint32 type;
> +            uint32 position;
> +            string string; // utf8
> +        }
> +
> +        [Compact]
> +        [CCode (cname = "FrgMenuRmItem")]
> +        public struct RmItem {
> +            Msg base;
> +            uint32 id;
> +        }
> +
> +        [CCode (cprefix = "FOREIGN_MENU_EVENT_", cname = "int")]
> +        public enum EventType {
> +            CLICK,
> +            CHECKED,
> +            UNCHECKED,
> +        }
> +
> +        [Compact]
> +        [CCode (cname = "FrgMenuEvent")]
> +        public struct Event {
> +            Msg base;
> +            uint32 id;
> +            uint32 action;
> +        }
> +    }
>  }
> diff --git a/gtk/controller/Makefile.am b/gtk/controller/Makefile.am
> index 522e014..20f1a48 100644
> --- a/gtk/controller/Makefile.am
> +++ b/gtk/controller/Makefile.am
> @@ -1,6 +1,11 @@
>  NULL =
>  
> -AM_CPPFLAGS = $(GIO_CFLAGS) $(PROTOCOL_CFLAGS)
> +AM_CPPFLAGS =					\
> +	-DG_LOG_DOMAIN=\"GSpiceController\"	\
> +	$(GIO_CFLAGS)				\
> +	$(PROTOCOL_CFLAGS)			\
> +	$(NULL)
> +
>  # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
>  AM_LDFLAGS =					\
>  	-no-undefined				\
> @@ -22,11 +27,14 @@ BUILT_SOURCES = controller.vala.stamp
>  libspice_controller_la_VALASOURCES =		\
>  	menu.vala				\
>  	controller.vala				\
> +	foreign-menu.vala			\
>  	$(NULL)
>  libspice_controller_la_SOURCES =		\
>  	custom.h				\
>  	spice-controller-listener.c		\
>  	spice-controller-listener.h		\
> +	spice-foreign-menu-listener.c		\
> +	spice-foreign-menu-listener.h		\
>  	$(libspice_controller_la_VALASOURCES:.vala=.c) \
>  	$(NULL)
>  
> diff --git a/gtk/controller/custom.vapi b/gtk/controller/custom.vapi
> index 7a94b82..a12fdec 100644
> --- a/gtk/controller/custom.vapi
> +++ b/gtk/controller/custom.vapi
> @@ -16,4 +16,13 @@ namespace Spice {
>  		[CCode (cname = "spice_controller_listener_accept_async", cheader_filename = "spice-controller-listener.h")]
>  		public async unowned GLib.IOStream accept_async (GLib.Cancellable? cancellable = null, out GLib.Object? source_object = null) throws GLib.Error;
>  	}
> +
> +	[CCode (cname = "GObject", ref_function = "g_object_ref", unref_function = "g_object_unref", free_function = "")]
> +	class ForeignMenuListener {
> +		[CCode (cname = "spice_foreign_menu_listener_new", cheader_filename = "spice-foreign-menu-listener.h")]
> +		public static ForeignMenuListener new_listener (string addr) throws GLib.Error;
> +
> +		[CCode (cname = "spice_foreign_menu_listener_accept_async", cheader_filename = "spice-foreign-menu-listener.h")]
> +		public async unowned GLib.IOStream accept_async (GLib.Cancellable? cancellable = null, out GLib.Object? source_object = null) throws GLib.Error;
> +	}
>  }
> diff --git a/gtk/controller/foreign-menu.vala b/gtk/controller/foreign-menu.vala
> new file mode 100644
> index 0000000..677e2ad
> --- /dev/null
> +++ b/gtk/controller/foreign-menu.vala
> @@ -0,0 +1,207 @@
> +// Copyright (C) 2012 Red Hat, Inc.
> +
> +// This library is free software; you can redistribute it and/or
> +// modify it under the terms of the GNU Lesser General Public
> +// License as published by the Free Software Foundation; either
> +// version 2.1 of the License, or (at your option) any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +// Lesser General Public License for more details.
> +
> +// You should have received a copy of the GNU Lesser General Public
> +// License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +
> +using Custom;
> +
> +namespace SpiceCtrl {
> +
> +public class ForeignMenu: Object {
> +
> +	public Menu menu { get; private set; }
> +    public string title { get; private set; }
> +
> +	private int nclients;
> +	private List<IOStream> clients;
> +
> +	public ForeignMenu() {
> +		menu = new Menu ();
> +	}
> +
> +	public void menu_item_click_msg (int32 item_id) {
> +		debug ("clicked id: %d".printf (item_id));
> +
> +		var msg = SpiceProtocol.ForeignMenu.Event ();
> +		msg.base.size = (uint32)sizeof (SpiceProtocol.ForeignMenu.Event);
> +		msg.base.id = SpiceProtocol.ForeignMenu.MsgId.ITEM_EVENT;
> +		msg.id = item_id;
> +		msg.action = SpiceProtocol.ForeignMenu.EventType.CLICK;
> +
> +		unowned uint8[] p = ((uint8[])(&msg))[0:msg.base.size];
> +		send_msg (p);
> +	}
> +
> +	public void menu_item_checked_msg (int32 item_id, bool checked = true) {
> +		debug ("%schecked id: %d".printf (checked ? "" : "un", item_id));
> +
> +		var msg = SpiceProtocol.ForeignMenu.Event ();
> +		msg.base.size = (uint32)sizeof (SpiceProtocol.ForeignMenu.Event);
> +		msg.base.id = SpiceProtocol.ForeignMenu.MsgId.ITEM_EVENT;
> +		msg.id = item_id;
> +		msg.action = checked ?
> +			SpiceProtocol.ForeignMenu.EventType.CHECKED :
> +			SpiceProtocol.ForeignMenu.EventType.UNCHECKED;
> +
> +		unowned uint8[] p = ((uint8[])(&msg))[0:msg.base.size];
> +		send_msg (p);
> +	}
> +
> +	public void app_activated_msg (bool activated = true) {
> +		var msg = SpiceProtocol.ForeignMenu.Msg ();
> +		msg.size = (uint32)sizeof (SpiceProtocol.ForeignMenu.Event);
> +		msg.id = activated ?
> +			SpiceProtocol.ForeignMenu.MsgId.APP_ACTIVATED :
> +			SpiceProtocol.ForeignMenu.MsgId.APP_DEACTIVATED;
> +
> +		unowned uint8[] p = ((uint8[])(&msg))[0:msg.size];
> +		send_msg (p);
> +	}
> +
> +	public async bool send_msg (uint8[] p) throws GLib.Error {
> +		// vala FIXME: pass Controller.Msg instead
> +		// vala doesn't keep reference on the struct in async methods
> +		// it copies only base, which is not enough to transmit the whole
> +		// message.
> +		try {
> +			foreach (var c in clients)
> +				yield c.output_stream.write_async (p);
> +		} catch (GLib.Error e) {
> +			warning (e.message);
> +		}
> +
> +		return true;
> +	}
> +
> +	SpiceProtocol.Controller.MenuFlags get_menu_flags (uint32 type) {
> +		SpiceProtocol.Controller.MenuFlags flags = 0;
> +
> +		if ((SpiceProtocol.ForeignMenu.MenuFlags.CHECKED & type) != 0)
> +			flags |= SpiceProtocol.Controller.MenuFlags.CHECKED;
> +		if ((SpiceProtocol.ForeignMenu.MenuFlags.DIM & type) != 0)
> +			flags |= SpiceProtocol.Controller.MenuFlags.GRAYED;
> +
> +		return flags;
> +	}
> +
> +	private bool handle_message (SpiceProtocol.ForeignMenu.Msg* msg) {
> +		switch (msg.id) {
> +		case SpiceProtocol.ForeignMenu.MsgId.SET_TITLE:
> +			var t = (SpiceProtocol.ForeignMenu.SetTitle*)(msg);
> +			title = t.string;
> +			break;
> +		case SpiceProtocol.ForeignMenu.MsgId.ADD_ITEM:
> +			var i = (SpiceProtocol.ForeignMenu.AddItem*)(msg);
> +			debug ("add id:%u type:%u position:%u title:%s", i.id, i.type, i.position, i.string);
> +			menu.items.append (new MenuItem ((int)i.id, i.string, get_menu_flags (i.type)));
> +			notify_property ("menu");
> +			break;
> +		case SpiceProtocol.ForeignMenu.MsgId.MODIFY_ITEM:
> +			debug ("deprecated: modify item");
> +			break;
> +		case SpiceProtocol.ForeignMenu.MsgId.REMOVE_ITEM:
> +			var i = (SpiceProtocol.ForeignMenu.RmItem*)(msg);
> +			debug ("not implemented: remove id:%u".printf (i.id));
> +			break;
> +		case SpiceProtocol.ForeignMenu.MsgId.CLEAR:
> +			menu = new Menu ();
> +			break;
> +		default:
> +			warn_if_reached ();
> +			return false;
> +		}
> +		return true;
> +	}
> +
> +	private async void handle_client (IOStream c) throws GLib.Error {
> +		var header = SpiceProtocol.ForeignMenu.InitHeader ();
> +		unowned uint8[] p = null;
> +
> +		debug ("new socket client, reading init header");
> +
> +		p = ((uint8[])(&header))[0:sizeof(SpiceProtocol.ForeignMenu.InitHeader)]; // FIXME vala
> +		var read = yield c.input_stream.read_async (p);
> +		if (warn_if (read != sizeof (SpiceProtocol.ForeignMenu.InitHeader)))
> +			return;
> +		if (warn_if (header.magic != SpiceProtocol.ForeignMenu.MAGIC))
> +			return;
> +		if (warn_if (header.version != SpiceProtocol.ForeignMenu.VERSION))
> +			return;
> +		if (warn_if (header.size < sizeof (SpiceProtocol.ForeignMenu.Init)))
> +			return;
> +
> +		uint64 credentials = 0;
> +		p = ((uint8[])(&credentials))[0:sizeof(uint64)];
> +		read = yield c.input_stream.read_async (p);
> +		if (warn_if (read != sizeof(uint64)))
> +			return;
> +		if (warn_if (credentials != 0))
> +			return;
> +
> +		var title_size = header.size - sizeof(SpiceProtocol.ForeignMenu.Init);
> +		var title = new uint8[title_size + 1];
> +		read = yield c.input_stream.read_async (title[0:title_size]);
> +		this.title = (string)title;
> +
> +		var t = new uint8[sizeof(SpiceProtocol.ForeignMenu.Msg)];
> +		for (;;) {
> +			read = yield c.input_stream.read_async (t[0:sizeof(SpiceProtocol.ForeignMenu.Msg)]);
> +			if (read == 0)
> +				break;
> +
> +			if (warn_if (read != sizeof (SpiceProtocol.ForeignMenu.Msg))) {
> +				warning ("read only: " + read.to_string ());
> +				break;
> +			}
> +
> +			var msg = (SpiceProtocol.ForeignMenu.Msg*)t;
> +			if (warn_if (msg.size < sizeof (SpiceProtocol.ForeignMenu.Msg)))
> +				break;
> +
> +			if (msg.size > sizeof (SpiceProtocol.ForeignMenu.Msg)) {
> +				t.resize ((int)msg.size);
> +				msg = (SpiceProtocol.ForeignMenu.Msg*)t;
> +				read = yield c.input_stream.read_async (t[sizeof(SpiceProtocol.ForeignMenu.Msg):msg.size]);
> +				if (read == 0)
> +					break;
> +				if (warn_if (read != msg.size - sizeof(SpiceProtocol.ForeignMenu.Msg)))
> +					break;
> +			}
> +
> +			handle_message (msg);
> +		}
> +
> +	}
> +
> +	public async void listen (string? addr = null) throws GLib.Error, SpiceCtrl.Error
> +	{
> +		var listener = Spice.ForeignMenuListener.new_listener (addr);
> +
> +		for (;;) {
> +			var c = yield listener.accept_async ();
> +			nclients += 1;
> +			clients.append (c);
> +			try {
> +				yield handle_client (c);
> +			} catch (GLib.Error e) {
> +				warning (e.message);
> +			}
> +			c.close ();
> +			clients.remove (c);
> +			nclients -= 1;
> +		}
> +	}
> +
> +}
> +
> +} // SpiceCtrl
> diff --git a/gtk/controller/menu.vala b/gtk/controller/menu.vala
> index 7f2f42a..7e8fc16 100644
> --- a/gtk/controller/menu.vala
> +++ b/gtk/controller/menu.vala
> @@ -28,6 +28,12 @@ public class MenuItem: Object {
>  	public string accel;
>  	public SpiceProtocol.Controller.MenuFlags flags;
>  
> +	public MenuItem (int id, string text, SpiceProtocol.Controller.MenuFlags flags) {
> +		this.id = id;
> +		this.text = text;
> +		this.flags = flags;
> +	}
> +
>  	public MenuItem.from_string (string str) throws SpiceCtrl.Error {
>  		var params = str.split (SpiceProtocol.Controller.MENU_PARAM_DELIMITER);
>  		if (warn_if (params.length != 5))
> diff --git a/gtk/controller/spice-foreign-menu-listener.c b/gtk/controller/spice-foreign-menu-listener.c
> new file mode 100644
> index 0000000..8322a13
> --- /dev/null
> +++ b/gtk/controller/spice-foreign-menu-listener.c
> @@ -0,0 +1,159 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2012 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +
> +#include <glib.h>
> +#include <glib/gstdio.h>
> +
> +#include "spice-foreign-menu-listener.h"
> +
> +#ifdef G_OS_WIN32
> +#include <windows.h>
> +#include "namedpipe.h"
> +#include "namedpipelistener.h"
> +#endif
> +
> +#ifdef G_OS_UNIX
> +#include <gio/gunixsocketaddress.h>
> +#endif
> +
> +/**
> + * SpiceForeignMenuListenerError:
> + * @SPICE_FOREIGN_MENU_LISTENER_ERROR_VALUE: invalid value.
> + *
> + * Possible errors of foreign menu listener related functions.
> + **/
> +
> +/**
> + * SPICE_FOREIGN_MENU_LISTENER_ERROR:
> + *
> + * The error domain of the foreign menu listener subsystem.
> + **/
> +GQuark
> +spice_foreign_menu_listener_error_quark (void)
> +{
> +  return g_quark_from_static_string ("spice-foreign-menu-listener-error");
> +}
> +
> +GObject*
> +spice_foreign_menu_listener_new (const gchar *address, GError **error)
> +{
> +    GObject *listener = NULL;
> +    gchar *addr = NULL;
> +
> +    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
> +
> +    addr = g_strdup (address);
> +
> +#ifdef G_OS_WIN32
> +    if (addr == NULL)
> +        addr = g_strdup (g_getenv ("SPICE_FOREIGN_MENU_NAMEDPIPE"));
> +    if (addr == NULL)
> +        addr = g_strdup_printf ("\\\\.\\pipe\\SpiceForeignMenu-%" G_GUINT64_FORMAT, (guint64)GetCurrentProcessId ());
> +#else
> +    if (addr == NULL)
> +        addr = g_strdup (g_getenv ("SPICE_FOREIGN_MENU_SOCKET"));
> +    if (addr == NULL)
> +        addr = g_strdup_printf ("/tmp/SpiceForeignMenu-%" G_GUINT64_FORMAT ".uds", (guint64)getpid ());
> +#endif
> +    if (addr == NULL) {
> +        g_set_error (error,
> +                     SPICE_FOREIGN_MENU_LISTENER_ERROR,
> +                     SPICE_FOREIGN_MENU_LISTENER_ERROR_VALUE,
> +#ifdef G_OS_WIN32
> +                     "Missing namedpipe address"
> +#else
> +                     "Missing socket address"
> +#endif
> +                     );
> +        goto end;
> +    }
> +
> +    g_unlink (addr);
> +
> +#ifdef G_OS_WIN32
> +    {
> +        SpiceNamedPipe *np;
> +
> +        listener = G_OBJECT (spice_named_pipe_listener_new ());
> +
> +        np = spice_named_pipe_new (addr, error);
> +        if (!np) {
> +            g_object_unref (listener);
> +            listener = NULL;
> +            goto end;
> +        }
> +
> +        spice_named_pipe_listener_add_named_pipe (SPICE_NAMED_PIPE_LISTENER (listener), np);
> +    }
> +#else
> +    {
> +        listener = G_OBJECT (g_socket_listener_new ());
> +
> +        if (!g_socket_listener_add_address (G_SOCKET_LISTENER (listener),
> +                                            G_SOCKET_ADDRESS (g_unix_socket_address_new (addr)),
> +                                            G_SOCKET_TYPE_STREAM,
> +                                            G_SOCKET_PROTOCOL_DEFAULT,
> +                                            NULL,
> +                                            NULL,
> +                                            error))
> +            g_warning ("failed to add address");
> +    }
> +#endif
> +
> +end:
> +    g_free (addr);
> +    return listener;
> +}
> +
> +void
> +spice_foreign_menu_listener_accept_async (GObject *listener,
> +                                          GCancellable *cancellable,
> +                                          GAsyncReadyCallback callback,
> +                                          gpointer user_data)
> +{
> +    g_return_if_fail(G_IS_OBJECT(listener));
> +
> +#ifdef G_OS_WIN32
> +    spice_named_pipe_listener_accept_async (SPICE_NAMED_PIPE_LISTENER (listener), cancellable, callback, user_data);
> +#else
> +    g_socket_listener_accept_async (G_SOCKET_LISTENER (listener), cancellable, callback, user_data);
> +#endif
> +}
> +
> +GIOStream*
> +spice_foreign_menu_listener_accept_finish (GObject *listener,
> +                                           GAsyncResult *result,
> +                                           GObject **source_object,
> +                                           GError **error)
> +{
> +    g_return_val_if_fail(G_IS_OBJECT(listener), NULL);
> +
> +#ifdef G_OS_WIN32
> +    SpiceNamedPipeConnection *np;
> +    np = spice_named_pipe_listener_accept_finish (SPICE_NAMED_PIPE_LISTENER (listener), result, source_object, error);
> +    if (np)
> +        return G_IO_STREAM (np);
> +#else
> +    GSocketConnection *socket;
> +    socket = g_socket_listener_accept_finish (G_SOCKET_LISTENER (listener), result, source_object, error);
> +    if (socket)
> +        return G_IO_STREAM (socket);
> +#endif
> +
> +    return NULL;
> +}
> diff --git a/gtk/controller/spice-foreign-menu-listener.h b/gtk/controller/spice-foreign-menu-listener.h
> new file mode 100644
> index 0000000..1071528
> --- /dev/null
> +++ b/gtk/controller/spice-foreign-menu-listener.h
> @@ -0,0 +1,47 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2012 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see <http://www.gnu.org/licenses/>.
> +*/
> +#ifndef __SPICE_FOREIGN_MENU_LISTENER_H__
> +#define __SPICE_FOREIGN_MENU_LISTENER_H__
> +
> +#include <gio/gio.h>
> +
> +G_BEGIN_DECLS
> +
> +#define SPICE_FOREIGN_MENU_LISTENER_ERROR spice_foreign_menu_listener_error_quark ()
> +GQuark spice_foreign_menu_listener_error_quark (void);
> +
> +typedef enum
> +{
> +    SPICE_FOREIGN_MENU_LISTENER_ERROR_VALUE /* incorrect value */
> +} SpiceForeignMenuListenerError;
> +
> +
> +GObject* spice_foreign_menu_listener_new (const gchar *address, GError **error);
> +
> +void spice_foreign_menu_listener_accept_async (GObject *listener,
> +                                             GCancellable *cancellable,
> +                                             GAsyncReadyCallback callback,
> +                                             gpointer user_data);
> +
> +GIOStream* spice_foreign_menu_listener_accept_finish (GObject *listener,
> +                                                    GAsyncResult *result,
> +                                                    GObject **source_object,
> +                                                    GError **error);
> +G_END_DECLS
> +
> +#endif /* __SPICE_FOREIGN_MENU_LISTENER_H__ */
> diff --git a/spice-protocol b/spice-protocol
> index cda8862..d5edafd 160000
> --- a/spice-protocol
> +++ b/spice-protocol
> @@ -1 +1 @@
> -Subproject commit cda88623d0754aeeda005ddc048dd113d279845b
> +Subproject commit d5edafd28ab762b1b5f663aec449d3e3743f1184
> -- 
> 1.7.7.6
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20120229/4a9aa8ab/attachment-0001.pgp>


More information about the Spice-devel mailing list