[Spice-devel] [PATCH spice-gtk 3/7] Move clipboard handling to SpiceGtkSession

Marc-André Lureau marcandre.lureau at gmail.com
Thu Oct 6 11:49:23 PDT 2011


On Thu, Oct 6, 2011 at 8:07 PM, Hans de Goede <hdegoede at redhat.com> wrote:
> This fixes copy and paste with multi-monitor guests. There still is
> one small issue left with this patch, changing the setting for auto-clipboard
> in one spicy window, does not get reflected in the Options menu of the
> other spicy windows.
>
> This can be fixed by listening to the notify signal, this also requires
> SpiceDisplay to listen to property changes on its SpiceGtkSession and
> then do a g_object_set on itself to update its own property (and also
> emit its own notify signal.
>
> I'll write a separate patch for this.
>
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> ---
>  doc/reference/spice-gtk-sections.txt |    2 +
>  gtk/map-file                         |    2 +
>  gtk/spice-gtk-session.c              |  593 +++++++++++++++++++++++++++++++++-
>  gtk/spice-gtk-session.h              |    2 +
>  gtk/spice-widget-priv.h              |   15 +-
>  gtk/spice-widget.c                   |  465 ++-------------------------
>  6 files changed, 618 insertions(+), 461 deletions(-)
>
> diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
> index d5e8e70..56ae829 100644
> --- a/doc/reference/spice-gtk-sections.txt
> +++ b/doc/reference/spice-gtk-sections.txt
> @@ -292,6 +292,8 @@ SpiceUsbDeviceManagerPrivate
>  SpiceGtkSession
>  SpiceGtkSessionClass
>  spice_gtk_session_get
> +spice_gtk_session_copy_to_guest
> +spice_gtk_session_paste_from_guest
>  <SUBSECTION Standard>
>  SPICE_GTK_SESSION
>  SPICE_IS_GTK_SESSION
> diff --git a/gtk/map-file b/gtk/map-file
> index 41793a9..82440c0 100644
> --- a/gtk/map-file
> +++ b/gtk/map-file
> @@ -69,6 +69,8 @@ spice_session_open_fd;
>  spice_session_verify_get_type;
>  spice_gtk_session_get;
>  spice_gtk_session_get_type;
> +spice_gtk_session_copy_to_guest;
> +spice_gtk_session_paste_from_guest;
>  spice_set_session_option;
>  spice_smartcard_channel_get_type;
>  spice_smartcard_manager_get;
> diff --git a/gtk/spice-gtk-session.c b/gtk/spice-gtk-session.c
> index 165d91e..d3cdd7d 100644
> --- a/gtk/spice-gtk-session.c
> +++ b/gtk/spice-gtk-session.c
> @@ -16,10 +16,25 @@
>    License along with this library; if not, see <http://www.gnu.org/licenses/>.
>  */
>
> +#include <gtk/gtk.h>
> +#include <spice/vd_agent.h>
> +#include "spice-common.h"
>  #include "spice-gtk-session.h"
>
> +#define CLIPBOARD_LAST (VD_AGENT_CLIPBOARD_SELECTION_SECONDARY + 1)
> +
>  struct _SpiceGtkSessionPrivate {
> -    SpiceSession *session;
> +    SpiceSession            *session;
> +    SpiceMainChannel        *main;
> +    gboolean                auto_clipboard_enable;
> +    GtkClipboard            *clipboard;
> +    GtkClipboard            *clipboard_primary;
> +    GtkTargetEntry          *clip_targets[CLIPBOARD_LAST];
> +    guint                   nclip_targets[CLIPBOARD_LAST];
> +    gboolean                clip_hasdata[CLIPBOARD_LAST];
> +    gboolean                clip_grabbed[CLIPBOARD_LAST];
> +    gboolean                clipboard_by_guest[CLIPBOARD_LAST];
> +    gboolean                clipboard_selfgrab_pending[CLIPBOARD_LAST];
>  };
>
>  /**
> @@ -51,6 +66,16 @@ struct _SpiceGtkSessionPrivate {
>  */
>
>  /* ------------------------------------------------------------------ */
> +/* Prototypes for private functions */
> +static void clipboard_owner_change(GtkClipboard *clipboard,
> +                                   GdkEventOwnerChange *event,
> +                                   gpointer user_data);
> +static void channel_new(SpiceSession *session, SpiceChannel *channel,
> +                        gpointer user_data);
> +static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
> +                            gpointer user_data);
> +
> +/* ------------------------------------------------------------------ */
>  /* gobject glue                                                       */
>
>  #define SPICE_GTK_SESSION_GET_PRIVATE(obj) \
> @@ -62,11 +87,21 @@ G_DEFINE_TYPE (SpiceGtkSession, spice_gtk_session, G_TYPE_OBJECT);
>  enum {
>     PROP_0,
>     PROP_SESSION,
> +    PROP_AUTO_CLIPBOARD,
>  };
>
>  static void spice_gtk_session_init(SpiceGtkSession *self)
>  {
> -    self->priv = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    SpiceGtkSessionPrivate *s;
> +
> +    s = self->priv = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +
> +    s->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
> +    g_signal_connect(G_OBJECT(s->clipboard), "owner-change",
> +                     G_CALLBACK(clipboard_owner_change), self);
> +    s->clipboard_primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
> +    g_signal_connect(G_OBJECT(s->clipboard_primary), "owner-change",
> +                     G_CALLBACK(clipboard_owner_change), self);
>  }
>
>  static GObject *
> @@ -76,6 +111,9 @@ spice_gtk_session_constructor(GType                  gtype,
>  {
>     GObject *obj;
>     SpiceGtkSession *self;
> +    SpiceGtkSessionPrivate *s;
> +    GList *list;
> +    GList *it;
>
>     {
>         /* Always chain up to the parent constructor */
> @@ -85,20 +123,50 @@ spice_gtk_session_constructor(GType                  gtype,
>     }
>
>     self = SPICE_GTK_SESSION(obj);
> -    if (!self->priv->session)
> +    s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    if (!s->session)
>         g_error("SpiceGtKSession constructed without a session");
>
> +    g_signal_connect(s->session, "channel-new",
> +                     G_CALLBACK(channel_new), self);
> +    g_signal_connect(s->session, "channel-destroy",
> +                     G_CALLBACK(channel_destroy), self);
> +    list = spice_session_get_channels(s->session);
> +    for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
> +        channel_new(s->session, it->data, (gpointer*)self);
> +    }
> +    g_list_free(list);
> +
>     return obj;
>  }
>
>  static void spice_gtk_session_dispose(GObject *gobject)
>  {
> -#if 0
>     SpiceGtkSession *self = SPICE_GTK_SESSION(gobject);
>     SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> -#endif
>
>     /* release stuff */
> +    if (s->clipboard) {
> +        g_signal_handlers_disconnect_by_func(s->clipboard,
> +                G_CALLBACK(clipboard_owner_change), self);
> +        s->clipboard = NULL;
> +    }
> +
> +    if (s->clipboard_primary) {
> +        g_signal_handlers_disconnect_by_func(s->clipboard_primary,
> +                G_CALLBACK(clipboard_owner_change), self);
> +        s->clipboard_primary = NULL;
> +    }
> +
> +    if (s->session) {
> +        g_signal_handlers_disconnect_by_func(s->session,
> +                                             G_CALLBACK(channel_new),
> +                                             self);
> +        g_signal_handlers_disconnect_by_func(s->session,
> +                                             G_CALLBACK(channel_destroy),
> +                                             self);
> +        s->session = NULL;
> +    }
>
>     /* Chain up to the parent class */
>     if (G_OBJECT_CLASS(spice_gtk_session_parent_class)->dispose)
> @@ -107,12 +175,15 @@ static void spice_gtk_session_dispose(GObject *gobject)
>
>  static void spice_gtk_session_finalize(GObject *gobject)
>  {
> -#if 0
>     SpiceGtkSession *self = SPICE_GTK_SESSION(gobject);
>     SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> -#endif
> +    int i;
>
>     /* release stuff */
> +    for (i = 0; i < CLIPBOARD_LAST; ++i) {
> +        g_free(s->clip_targets[i]);
> +        s->clip_targets[i] = NULL;
> +    }
>
>     /* Chain up to the parent class */
>     if (G_OBJECT_CLASS(spice_gtk_session_parent_class)->finalize)
> @@ -131,6 +202,9 @@ static void spice_gtk_session_get_property(GObject    *gobject,
>     case PROP_SESSION:
>         g_value_set_object(value, s->session);
>        break;
> +    case PROP_AUTO_CLIPBOARD:
> +        g_value_set_boolean(value, s->auto_clipboard_enable);
> +        break;
>     default:
>        G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
>        break;
> @@ -149,6 +223,9 @@ static void spice_gtk_session_set_property(GObject      *gobject,
>     case PROP_SESSION:
>         s->session = g_value_get_object(value);
>         break;
> +    case PROP_AUTO_CLIPBOARD:
> +        s->auto_clipboard_enable = g_value_get_boolean(value);
> +        break;
>     default:
>         G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
>         break;
> @@ -181,6 +258,23 @@ static void spice_gtk_session_class_init(SpiceGtkSessionClass *klass)
>                              G_PARAM_CONSTRUCT_ONLY |
>                              G_PARAM_STATIC_STRINGS));
>
> +    /**
> +     * SpiceGtkSession:auto-clipboard:
> +     *
> +     * When this is true the clipboard gets automatically shared between host
> +     * and guest.
> +     **/
> +    g_object_class_install_property
> +        (gobject_class, PROP_AUTO_CLIPBOARD,
> +         g_param_spec_boolean("auto-clipboard",
> +                              "Auto clipboard",
> +                              "Automatically relay clipboard changes between "
> +                              "host and guest.",
> +                              TRUE,
> +                              G_PARAM_READWRITE |
> +                              G_PARAM_CONSTRUCT |
> +                              G_PARAM_STATIC_STRINGS));
> +
>     g_type_class_add_private(klass, sizeof(SpiceGtkSessionPrivate));
>  }
>
> @@ -193,6 +287,446 @@ spice_gtk_session_spice_session_destroyed_cb(gpointer user_data,
>     g_object_unref(self);
>  }
>
> +/* ---------------------------------------------------------------- */
> +/* private functions (clipboard related)                            */
> +
> +static GtkClipboard* get_clipboard_from_selection(SpiceGtkSessionPrivate *s,
> +                                                  guint selection)
> +{
> +    if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
> +        return s->clipboard;
> +    } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
> +        return s->clipboard_primary;
> +    } else {
> +        g_warning("Unhandled clipboard selection: %d", selection);
> +        return NULL;
> +    }
> +}
> +
> +static gint get_selection_from_clipboard(SpiceGtkSessionPrivate *s,
> +                                         GtkClipboard* cb)
> +{
> +    if (cb == s->clipboard) {
> +        return VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
> +    } else if (cb == s->clipboard_primary) {
> +        return VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
> +    } else {
> +        g_warning("Unhandled clipboard");
> +        return -1;
> +    }
> +}
> +
> +static const struct {
> +    const char  *xatom;
> +    uint32_t    vdagent;
> +    uint32_t    flags;
> +} atom2agent[] = {
> +    {
> +        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> +        .xatom   = "UTF8_STRING",
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> +        .xatom   = "text/plain;charset=utf-8"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> +        .xatom   = "STRING"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> +        .xatom   = "TEXT"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> +        .xatom   = "text/plain"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_PNG,
> +        .xatom   = "image/png"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> +        .xatom   = "image/bmp"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> +        .xatom   = "image/x-bmp"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> +        .xatom   = "image/x-MS-bmp"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> +        .xatom   = "image/x-win-bitmap"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_TIFF,
> +        .xatom   = "image/tiff"
> +    },{
> +        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_JPG,
> +        .xatom   = "image/jpeg"
> +    }
> +};
> +
> +static void clipboard_get_targets(GtkClipboard *clipboard,
> +                                  GdkAtom *atoms,
> +                                  gint n_atoms,
> +                                  gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    guint32 types[SPICE_N_ELEMENTS(atom2agent)];
> +    char *name;
> +    int a, m, t;
> +    int selection;
> +
> +    selection = get_selection_from_clipboard(s, clipboard);
> +    g_return_if_fail(selection != -1);
> +    g_return_if_fail(s->main != NULL);
> +
> +    SPICE_DEBUG("%s:", __FUNCTION__);
> +    if (spice_util_get_debug()) {
> +        for (a = 0; a < n_atoms; a++) {
> +            name = gdk_atom_name(atoms[a]);
> +            SPICE_DEBUG(" \"%s\"", name);
> +            g_free(name);
> +        }
> +    }
> +
> +    memset(types, 0, sizeof(types));
> +    for (a = 0; a < n_atoms; a++) {
> +        name = gdk_atom_name(atoms[a]);
> +        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> +            if (strcasecmp(name, atom2agent[m].xatom) != 0) {
> +                continue;
> +            }
> +            /* found match */
> +            for (t = 0; t < SPICE_N_ELEMENTS(atom2agent); t++) {
> +                if (types[t] == atom2agent[m].vdagent) {
> +                    /* type already in list */
> +                    break;
> +                }
> +                if (types[t] == 0) {
> +                    /* add type to empty slot */
> +                    types[t] = atom2agent[m].vdagent;
> +                    break;
> +                }
> +            }
> +            break;
> +        }
> +        g_free(name);
> +    }
> +    for (t = 0; t < SPICE_N_ELEMENTS(atom2agent); t++) {
> +        if (types[t] == 0) {
> +            break;
> +        }
> +    }
> +    if (!s->clip_grabbed[selection] && t > 0) {
> +        s->clip_grabbed[selection] = TRUE;
> +        spice_main_clipboard_selection_grab(s->main, selection, types, t);
> +        /* Sending a grab causes the agent to do an impicit release */
> +        s->nclip_targets[selection] = 0;
> +    }
> +}
> +
> +static void clipboard_owner_change(GtkClipboard        *clipboard,
> +                                   GdkEventOwnerChange *event,
> +                                   gpointer            user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    int selection;
> +
> +    selection = get_selection_from_clipboard(s, clipboard);
> +    g_return_if_fail(selection != -1);
> +
> +    if (s->main == NULL)
> +        return;
> +
> +    if (s->clip_grabbed[selection]) {
> +        s->clip_grabbed[selection] = FALSE;
> +        spice_main_clipboard_selection_release(s->main, selection);
> +    }
> +
> +    switch (event->reason) {
> +    case GDK_OWNER_CHANGE_NEW_OWNER:
> +        if (s->clipboard_selfgrab_pending[selection]) {
> +            s->clipboard_selfgrab_pending[selection] = FALSE;
> +            break;
> +        }
> +        s->clipboard_by_guest[selection] = FALSE;
> +        s->clip_hasdata[selection] = TRUE;
> +        if (s->auto_clipboard_enable)
> +            gtk_clipboard_request_targets(clipboard, clipboard_get_targets,
> +                                          self);
> +        break;
> +    default:
> +        s->clip_hasdata[selection] = FALSE;
> +        break;
> +    }
> +}
> +
> +typedef struct
> +{
> +    GMainLoop *loop;
> +    GtkSelectionData *selection_data;
> +    guint info;
> +    gulong timeout_handler;
> +    guint selection;
> +} RunInfo;
> +
> +static void clipboard_got_from_guest(SpiceMainChannel *main, guint selection,
> +                                     guint type, guchar *data, guint size,
> +                                     gpointer user_data)
> +{
> +    RunInfo *ri = user_data;
> +
> +    g_return_if_fail(selection == ri->selection);
> +
> +    SPICE_DEBUG("clipboard got data");
> +
> +    gtk_selection_data_set(ri->selection_data,
> +        gdk_atom_intern_static_string(atom2agent[ri->info].xatom),
> +        8, data, size);
> +
> +    if (g_main_loop_is_running (ri->loop))
> +        g_main_loop_quit (ri->loop);
> +}
> +
> +static gboolean clipboard_timeout(gpointer user_data)
> +{
> +    RunInfo *ri = user_data;
> +
> +    g_warning("clipboard get timed out");
> +    if (g_main_loop_is_running (ri->loop))
> +        g_main_loop_quit (ri->loop);
> +
> +    ri->timeout_handler = 0;
> +    return FALSE;
> +}
> +
> +static void clipboard_get(GtkClipboard *clipboard,
> +                          GtkSelectionData *selection_data,
> +                          guint info, gpointer user_data)
> +{
> +    RunInfo ri = { NULL, };
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    gulong clipboard_handler;
> +    int selection;
> +
> +    SPICE_DEBUG("clipboard get");
> +
> +    selection = get_selection_from_clipboard(s, clipboard);
> +    g_return_if_fail(selection != -1);
> +    g_return_if_fail(info < SPICE_N_ELEMENTS(atom2agent));
> +    g_return_if_fail(s->main != NULL);
> +
> +    ri.selection_data = selection_data;
> +    ri.info = info;
> +    ri.loop = g_main_loop_new(NULL, FALSE);
> +    ri.selection = selection;
> +
> +    clipboard_handler = g_signal_connect(s->main, "main-clipboard-selection",
> +                                         G_CALLBACK(clipboard_got_from_guest),
> +                                         &ri);
> +    ri.timeout_handler = g_timeout_add_seconds(7, clipboard_timeout, &ri);
> +    spice_main_clipboard_selection_request(s->main, selection,
> +                                           atom2agent[info].vdagent);
> +
> +    /* apparently, this is needed to avoid dead-lock, from
> +       gtk_dialog_run */
> +    GDK_THREADS_LEAVE();
> +    g_main_loop_run(ri.loop);
> +    GDK_THREADS_ENTER();
> +
> +    g_main_loop_unref(ri.loop);
> +    ri.loop = NULL;
> +    g_signal_handler_disconnect(s->main, clipboard_handler);
> +    if (ri.timeout_handler != 0)
> +        g_source_remove(ri.timeout_handler);
> +}
> +
> +static void clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
> +{
> +    SPICE_DEBUG("clipboard_clear");
> +    /* We watch for clipboard ownership changes and act on those, so we
> +       don't need to do anything here */
> +}
> +
> +static gboolean clipboard_grab(SpiceMainChannel *main, guint selection,
> +                               guint32* types, guint32 ntypes,
> +                               gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    GtkTargetEntry targets[SPICE_N_ELEMENTS(atom2agent)];
> +    gboolean target_selected[SPICE_N_ELEMENTS(atom2agent)] = { FALSE, };
> +    gboolean found;
> +    GtkClipboard* cb;
> +    int m, n, i;
> +
> +    cb = get_clipboard_from_selection(s, selection);
> +    g_return_val_if_fail(cb != NULL, FALSE);
> +
> +    i = 0;
> +    for (n = 0; n < ntypes; ++n) {
> +        found = FALSE;
> +        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> +            if (atom2agent[m].vdagent == types[n] && !target_selected[m]) {
> +                found = TRUE;
> +                g_return_val_if_fail(i < SPICE_N_ELEMENTS(atom2agent), FALSE);
> +                targets[i].target = (gchar*)atom2agent[m].xatom;
> +                targets[i].flags = 0;
> +                targets[i].info = m;
> +                target_selected[m] = TRUE;
> +                i += 1;
> +            }
> +        }
> +        if (!found) {
> +            g_warning("clipboard: couldn't find a matching type for: %d",
> +                      types[n]);
> +        }
> +    }
> +
> +    g_free(s->clip_targets[selection]);
> +    s->nclip_targets[selection] = i;
> +    s->clip_targets[selection] = g_memdup(targets, sizeof(GtkTargetEntry) * i);
> +    /* Receiving a grab implies we've released our own grab */
> +    s->clip_grabbed[selection] = FALSE;
> +
> +    if (!s->auto_clipboard_enable || s->nclip_targets[selection] == 0)
> +        goto skip_grab_clipboard;
> +
> +    if (!gtk_clipboard_set_with_data(cb, targets, i, clipboard_get,
> +                                     clipboard_clear, self)) {
> +        g_warning("clipboard grab failed");
> +        return FALSE;
> +    }
> +    s->clipboard_selfgrab_pending[selection] = TRUE;
> +    s->clipboard_by_guest[selection] = TRUE;
> +    s->clip_hasdata[selection] = FALSE;
> +
> +skip_grab_clipboard:
> +    return TRUE;
> +}
> +
> +static void clipboard_received_cb(GtkClipboard *clipboard,
> +                                  GtkSelectionData *selection_data,
> +                                  gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    gint len = 0, m;
> +    guint32 type = VD_AGENT_CLIPBOARD_NONE;
> +    gchar* name;
> +    GdkAtom atom;
> +    int selection;
> +
> +    selection = get_selection_from_clipboard(s, clipboard);
> +    g_return_if_fail(selection != -1);
> +
> +    len = gtk_selection_data_get_length(selection_data);
> +    if (len == -1) {
> +        SPICE_DEBUG("empty clipboard");
> +        len = 0;
> +    } else if (len == 0) {
> +        SPICE_DEBUG("TODO: what should be done here?");
> +    } else {
> +        atom = gtk_selection_data_get_data_type(selection_data);
> +        name = gdk_atom_name(atom);
> +        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> +            if (strcasecmp(name, atom2agent[m].xatom) == 0) {
> +                break;
> +            }
> +        }
> +
> +        if (m >= SPICE_N_ELEMENTS(atom2agent)) {
> +            g_warning("clipboard_received for unsupported type: %s", name);
> +        } else {
> +            type = atom2agent[m].vdagent;
> +        }
> +
> +        g_free(name);
> +    }
> +
> +    spice_main_clipboard_selection_notify(s->main, selection, type,
> +        gtk_selection_data_get_data(selection_data), len);
> +}
> +
> +static gboolean clipboard_request(SpiceMainChannel *main, guint selection,
> +                                  guint type, gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    GdkAtom atom;
> +    GtkClipboard* cb;
> +    int m;
> +
> +    cb = get_clipboard_from_selection(s, selection);
> +    g_return_val_if_fail(cb != NULL, FALSE);
> +
> +    for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> +        if (atom2agent[m].vdagent == type)
> +            break;
> +    }
> +
> +    g_return_val_if_fail(m < SPICE_N_ELEMENTS(atom2agent), FALSE);
> +
> +    atom = gdk_atom_intern_static_string(atom2agent[m].xatom);
> +    gtk_clipboard_request_contents(cb, atom, clipboard_received_cb, self);
> +
> +    return TRUE;
> +}
> +
> +static void clipboard_release(SpiceMainChannel *main, guint selection,
> +                              gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    GtkClipboard* clipboard = get_clipboard_from_selection(s, selection);
> +    if (!clipboard)
> +        return;
> +
> +    s->nclip_targets[selection] = 0;
> +
> +    if (!s->clipboard_by_guest[selection])
> +        return;
> +    gtk_clipboard_clear(clipboard);
> +    s->clipboard_by_guest[selection] = FALSE;
> +}
> +
> +static void channel_new(SpiceSession *session, SpiceChannel *channel,
> +                        gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +
> +    if (SPICE_IS_MAIN_CHANNEL(channel)) {
> +        s->main = SPICE_MAIN_CHANNEL(channel);
> +        g_signal_connect(channel, "main-clipboard-selection-grab",
> +                         G_CALLBACK(clipboard_grab), self);
> +        g_signal_connect(channel, "main-clipboard-selection-request",
> +                         G_CALLBACK(clipboard_request), self);
> +        g_signal_connect(channel, "main-clipboard-selection-release",
> +                         G_CALLBACK(clipboard_release), self);
> +    }
> +}
> +
> +static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
> +                            gpointer user_data)
> +{
> +    SpiceGtkSession *self = user_data;
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    guint i;
> +
> +    if (SPICE_IS_MAIN_CHANNEL(channel)) {
> +        s->main = NULL;
> +        for (i = 0; i < CLIPBOARD_LAST; ++i) {
> +            if (s->clipboard_by_guest[i]) {
> +                GtkClipboard *cb = get_clipboard_from_selection(s, i);
> +                if (cb)
> +                    gtk_clipboard_clear(cb);
> +                s->clipboard_by_guest[i] = FALSE;
> +            }
> +            s->clip_grabbed[i] = FALSE;
> +            s->nclip_targets[i] = 0;
> +        }
> +    }
> +}
> +
>  /* ------------------------------------------------------------------ */
>  /* public functions                                                   */
>
> @@ -228,3 +762,48 @@ SpiceGtkSession *spice_gtk_session_get(SpiceSession *session)
>
>     return SPICE_GTK_SESSION(self);
>  }
> +
> +/**
> + * spice_gtk_session_copy_to_guest:
> + * @self:
> + *
> + * Copy client-side clipboard to guest clipboard.
> + **/
> +void spice_gtk_session_copy_to_guest(SpiceGtkSession *self)
> +{
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    int selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
> +
> +    if (s->clip_hasdata[selection] && !s->clip_grabbed[selection]) {
> +        gtk_clipboard_request_targets(s->clipboard, clipboard_get_targets,
> +                                      self);
> +    }
> +}
> +
> +/**
> + * spice_gtk_session_paste_from_guest:
> + * @self:
> + *
> + * Copy guest clipboard to client-side clipboard.
> + **/
> +void spice_gtk_session_paste_from_guest(SpiceGtkSession *self)
> +{
> +    SpiceGtkSessionPrivate *s = SPICE_GTK_SESSION_GET_PRIVATE(self);
> +    int selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
> +
> +    if (s->nclip_targets[selection] == 0) {
> +        g_warning("Guest clipboard is not available.");
> +        return;
> +    }
> +
> +    if (!gtk_clipboard_set_with_data(s->clipboard,
> +                                     s->clip_targets[selection],
> +                                     s->nclip_targets[selection],
> +                                     clipboard_get, clipboard_clear, self)) {
> +        g_warning("Clipboard grab failed");
> +        return;
> +    }
> +    s->clipboard_selfgrab_pending[selection] = TRUE;
> +    s->clipboard_by_guest[selection] = TRUE;
> +    s->clip_hasdata[selection] = FALSE;
> +}
> diff --git a/gtk/spice-gtk-session.h b/gtk/spice-gtk-session.h
> index 9c59fa2..3b4eac6 100644
> --- a/gtk/spice-gtk-session.h
> +++ b/gtk/spice-gtk-session.h
> @@ -57,6 +57,8 @@ struct _SpiceGtkSessionClass
>  GType spice_gtk_session_get_type(void);
>
>  SpiceGtkSession *spice_gtk_session_get(SpiceSession *session);
> +void spice_gtk_session_copy_to_guest(SpiceGtkSession *self);
> +void spice_gtk_session_paste_from_guest(SpiceGtkSession *self);
>
>  G_END_DECLS
>
> diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
> index bd6dedb..f94c8c6 100644
> --- a/gtk/spice-widget-priv.h
> +++ b/gtk/spice-widget-priv.h
> @@ -36,13 +36,11 @@ G_BEGIN_DECLS
>
>  #include "spice-widget.h"
>  #include "spice-common.h"
> -#include <spice/vd_agent.h>
> +#include "spice-gtk-session.h"
>
>  #define SPICE_DISPLAY_GET_PRIVATE(obj)                                  \
>     (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_DISPLAY, SpiceDisplayPrivate))
>
> -#define CLIPBOARD_LAST (VD_AGENT_CLIPBOARD_SELECTION_SECONDARY + 1)
> -
>  struct _SpiceDisplayPrivate {
>     gint                    channel_id;
>
> @@ -50,7 +48,6 @@ struct _SpiceDisplayPrivate {
>     bool                    keyboard_grab_enable;
>     bool                    mouse_grab_enable;
>     bool                    resize_guest_enable;
> -    bool                    auto_clipboard_enable;
>     bool                    auto_usbredir_enable;
>
>     /* state */
> @@ -77,16 +74,8 @@ struct _SpiceDisplayPrivate {
>     cairo_surface_t         *ximage;
>  #endif
>
> -    GtkClipboard            *clipboard;
> -    GtkClipboard            *clipboard_primary;
> -    GtkTargetEntry          *clip_targets[CLIPBOARD_LAST];
> -    guint                   nclip_targets[CLIPBOARD_LAST];
> -    bool                    clip_hasdata[CLIPBOARD_LAST];
> -    bool                    clip_grabbed[CLIPBOARD_LAST];
> -    gboolean                clipboard_by_guest[CLIPBOARD_LAST];
> -    gboolean                clipboard_selfgrab_pending[CLIPBOARD_LAST];
> -
>     SpiceSession            *session;
> +    SpiceGtkSession         *gtk_session;
>     SpiceMainChannel        *main;
>     SpiceChannel            *display;
>     SpiceCursorChannel      *cursor;
> diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
> index 2beea73..8b1c9ad 100644
> --- a/gtk/spice-widget.c
> +++ b/gtk/spice-widget.c
> @@ -110,8 +110,6 @@ static void try_keyboard_ungrab(SpiceDisplay *display);
>  static void try_mouse_grab(GtkWidget *widget);
>  static void try_mouse_ungrab(GtkWidget *widget);
>  static void recalc_geometry(GtkWidget *widget, gboolean set_display);
> -static void clipboard_owner_change(GtkClipboard *clipboard,
> -                                   GdkEventOwnerChange *event, gpointer user_data);
>  static void disconnect_main(SpiceDisplay *display);
>  static void disconnect_cursor(SpiceDisplay *display);
>  static void disconnect_display(SpiceDisplay *display);
> @@ -129,6 +127,7 @@ static void spice_display_get_property(GObject    *object,
>  {
>     SpiceDisplay *display = SPICE_DISPLAY(object);
>     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> +    gboolean boolean;
>
>     switch (prop_id) {
>     case PROP_KEYBOARD_GRAB:
> @@ -141,7 +140,8 @@ static void spice_display_get_property(GObject    *object,
>         g_value_set_boolean(value, d->resize_guest_enable);
>         break;
>     case PROP_AUTO_CLIPBOARD:
> -        g_value_set_boolean(value, d->auto_clipboard_enable);
> +        g_object_get(d->gtk_session, "auto-clipboard", &boolean, NULL);
> +        g_value_set_boolean(value, boolean);
>         break;
>     case PROP_AUTO_USBREDIR:
>         g_value_set_boolean(value, d->auto_usbredir_enable);
> @@ -198,7 +198,8 @@ static void spice_display_set_property(GObject      *object,
>         }
>         break;
>     case PROP_AUTO_CLIPBOARD:
> -        d->auto_clipboard_enable = g_value_get_boolean(value);
> +        g_object_set(d->gtk_session, "auto-clipboard",
> +                     g_value_get_boolean(value), NULL);
>         break;
>     case PROP_AUTO_USBREDIR:
>         d->auto_usbredir_enable = g_value_get_boolean(value);
> @@ -221,17 +222,6 @@ static void spice_display_dispose(GObject *obj)
>     disconnect_display(display);
>     disconnect_cursor(display);
>
> -    if (d->clipboard) {
> -        g_signal_handlers_disconnect_by_func(d->clipboard, G_CALLBACK(clipboard_owner_change),
> -                                             display);
> -        d->clipboard = NULL;
> -    }
> -
> -    if (d->clipboard_primary) {
> -        g_signal_handlers_disconnect_by_func(d->clipboard_primary, G_CALLBACK(clipboard_owner_change),
> -                                             display);
> -        d->clipboard_primary = NULL;
> -    }
>     if (d->session) {
>         g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(channel_new),
>                                              display);
> @@ -239,6 +229,7 @@ static void spice_display_dispose(GObject *obj)
>                                              display);
>         g_object_unref(d->session);
>         d->session = NULL;
> +        d->gtk_session = NULL;
>     }
>  }
>
> @@ -246,7 +237,6 @@ static void spice_display_finalize(GObject *obj)
>  {
>     SpiceDisplay *display = SPICE_DISPLAY(obj);
>     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    int i;
>
>     SPICE_DEBUG("Finalize spice display");
>
> @@ -257,11 +247,6 @@ static void spice_display_finalize(GObject *obj)
>     g_free(d->activeseq);
>     d->activeseq = NULL;
>
> -    for (i = 0; i < CLIPBOARD_LAST; ++i) {
> -        g_free(d->clip_targets[i]);
> -        d->clip_targets[i] = NULL;
> -    }
> -
>     G_OBJECT_CLASS(spice_display_parent_class)->finalize(obj);
>  }
>
> @@ -289,13 +274,6 @@ static void spice_display_init(SpiceDisplay *display)
>     d->grabseq = spice_grab_sequence_new_from_string("Control_L+Alt_L");
>     d->activeseq = g_new0(gboolean, d->grabseq->nkeysyms);
>
> -    d->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
> -    g_signal_connect(G_OBJECT(d->clipboard), "owner-change",
> -                     G_CALLBACK(clipboard_owner_change), display);
> -    d->clipboard_primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
> -    g_signal_connect(G_OBJECT(d->clipboard_primary), "owner-change",
> -                     G_CALLBACK(clipboard_owner_change), display);
> -
>     if (g_getenv("SPICE_DEBUG_CURSOR"))
>         d->mouse_cursor = gdk_cursor_new(GDK_DOT);
>     else
> @@ -1088,174 +1066,6 @@ static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *conf)
>
>  /* ---------------------------------------------------------------- */
>
> -static GtkClipboard* get_clipboard_from_selection(SpiceDisplayPrivate *d, guint selection)
> -{
> -    if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
> -        return d->clipboard;
> -    } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) {
> -        return d->clipboard_primary;
> -    } else {
> -        g_warning("Unhandled clipboard selection: %d", selection);
> -        return NULL;
> -    }
> -}
> -
> -static gint get_selection_from_clipboard(SpiceDisplayPrivate *d, GtkClipboard* cb)
> -{
> -    if (cb == d->clipboard) {
> -        return VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
> -    } else if (cb == d->clipboard_primary) {
> -        return VD_AGENT_CLIPBOARD_SELECTION_PRIMARY;
> -    } else {
> -        g_warning("Unhandled clipboard");
> -        return -1;
> -    }
> -}
> -
> -static const struct {
> -    const char  *xatom;
> -    uint32_t    vdagent;
> -    uint32_t    flags;
> -} atom2agent[] = {
> -    {
> -        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> -        .xatom   = "UTF8_STRING",
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> -        .xatom   = "text/plain;charset=utf-8"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> -        .xatom   = "STRING"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> -        .xatom   = "TEXT"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_UTF8_TEXT,
> -        .xatom   = "text/plain"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_PNG,
> -        .xatom   = "image/png"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> -        .xatom   = "image/bmp"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> -        .xatom   = "image/x-bmp"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> -        .xatom   = "image/x-MS-bmp"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_BMP,
> -        .xatom   = "image/x-win-bitmap"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_TIFF,
> -        .xatom   = "image/tiff"
> -    },{
> -        .vdagent = VD_AGENT_CLIPBOARD_IMAGE_JPG,
> -        .xatom   = "image/jpeg"
> -    }
> -};
> -
> -static void clipboard_get_targets(GtkClipboard *clipboard,
> -                                  GdkAtom *atoms,
> -                                  gint n_atoms,
> -                                  gpointer data)
> -{
> -    SpiceDisplay *display = data;
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    guint32 types[SPICE_N_ELEMENTS(atom2agent)];
> -    char *name;
> -    int a, m, t;
> -    int selection;
> -
> -    selection = get_selection_from_clipboard(d, clipboard);
> -    g_return_if_fail(selection != -1);
> -
> -    SPICE_DEBUG("%s:", __FUNCTION__);
> -    if (spice_util_get_debug()) {
> -        for (a = 0; a < n_atoms; a++) {
> -            name = gdk_atom_name(atoms[a]);
> -            SPICE_DEBUG(" \"%s\"", name);
> -            g_free(name);
> -        }
> -    }
> -
> -    memset(types, 0, sizeof(types));
> -    for (a = 0; a < n_atoms; a++) {
> -        name = gdk_atom_name(atoms[a]);
> -        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> -            if (strcasecmp(name, atom2agent[m].xatom) != 0) {
> -                continue;
> -            }
> -            /* found match */
> -            for (t = 0; t < SPICE_N_ELEMENTS(atom2agent); t++) {
> -                if (types[t] == atom2agent[m].vdagent) {
> -                    /* type already in list */
> -                    break;
> -                }
> -                if (types[t] == 0) {
> -                    /* add type to empty slot */
> -                    types[t] = atom2agent[m].vdagent;
> -                    break;
> -                }
> -            }
> -            break;
> -        }
> -        g_free(name);
> -    }
> -    for (t = 0; t < SPICE_N_ELEMENTS(atom2agent); t++) {
> -        if (types[t] == 0) {
> -            break;
> -        }
> -    }
> -    if (!d->clip_grabbed[selection] && t > 0) {
> -        d->clip_grabbed[selection] = TRUE;
> -        spice_main_clipboard_selection_grab(d->main,
> -            get_selection_from_clipboard(d, clipboard), types, t);
> -        /* Sending a grab causes the agent to do an impicit release */
> -        d->nclip_targets[selection] = 0;
> -    }
> -}
> -
> -static void clipboard_owner_change(GtkClipboard        *clipboard,
> -                                   GdkEventOwnerChange *event,
> -                                   gpointer            data)
> -{
> -    SpiceDisplay *display = data;
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    int selection;
> -
> -    selection = get_selection_from_clipboard(d, clipboard);
> -    g_return_if_fail(selection != -1);
> -
> -    if (d->main == NULL)
> -        return;
> -
> -    if (d->clip_grabbed[selection]) {
> -        d->clip_grabbed[selection] = FALSE;
> -        spice_main_clipboard_selection_release(d->main,
> -            get_selection_from_clipboard(d, clipboard));
> -    }
> -
> -    switch (event->reason) {
> -    case GDK_OWNER_CHANGE_NEW_OWNER:
> -        if (d->clipboard_selfgrab_pending[selection]) {
> -            d->clipboard_selfgrab_pending[selection] = FALSE;
> -            break;
> -        }
> -        d->clipboard_by_guest[selection] = FALSE;
> -        d->clip_hasdata[selection] = TRUE;
> -        if (d->auto_clipboard_enable)
> -            gtk_clipboard_request_targets(clipboard, clipboard_get_targets, data);
> -        break;
> -    default:
> -        d->clip_hasdata[selection] = FALSE;
> -        break;
> -    }
> -}
> -
> -/* ---------------------------------------------------------------- */
> -
>  static void spice_display_class_init(SpiceDisplayClass *klass)
>  {
>     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
> @@ -1314,6 +1124,12 @@ static void spice_display_class_init(SpiceDisplayClass *klass)
>                               G_PARAM_CONSTRUCT |
>                               G_PARAM_STATIC_STRINGS));
>
> +    /**
> +     * SpiceDisplay:auto-clipboard:
> +     *
> +     * When this is true the clipboard gets automatically shared between host
> +     * and guest.
> +     **/

Can you document this property as deprecated? and suggest using
spice-gtk-session instead? (there are other deprecated gtk-doc
comments) thanks

>     g_object_class_install_property
>         (gobject_class, PROP_AUTO_CLIPBOARD,
>          g_param_spec_boolean("auto-clipboard",
> @@ -1593,18 +1409,12 @@ static void cursor_reset(SpiceCursorChannel *channel, gpointer data)
>  static void disconnect_main(SpiceDisplay *display)
>  {
>     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    gint i;
>
>     if (d->main == NULL)
>         return;
>     g_signal_handlers_disconnect_by_func(d->main, G_CALLBACK(mouse_update),
>                                          display);
>     d->main = NULL;
> -    for (i = 0; i < CLIPBOARD_LAST; ++i) {
> -        d->clipboard_by_guest[i] = FALSE;
> -        d->clip_grabbed[i] = FALSE;
> -        d->nclip_targets[i] = 0;
> -    }
>  }
>
>  static void disconnect_display(SpiceDisplay *display)
> @@ -1639,219 +1449,6 @@ static void disconnect_cursor(SpiceDisplay *display)
>     d->cursor = NULL;
>  }
>
> -typedef struct
> -{
> -    GMainLoop *loop;
> -    SpiceDisplay *display;
> -    GtkSelectionData *selection_data;
> -    guint info;
> -    gulong timeout_handler;
> -    guint selection;
> -} RunInfo;
> -
> -static void clipboard_got_from_guest(SpiceMainChannel *main, guint selection,
> -                                     guint type, guchar *data, guint size,
> -                                     gpointer userdata)
> -{
> -    RunInfo *ri = userdata;
> -
> -    g_return_if_fail(selection == ri->selection);
> -
> -    SPICE_DEBUG("clipboard got data");
> -
> -    gtk_selection_data_set(ri->selection_data,
> -        gdk_atom_intern_static_string(atom2agent[ri->info].xatom),
> -        8, data, size);
> -
> -    if (g_main_loop_is_running (ri->loop))
> -        g_main_loop_quit (ri->loop);
> -}
> -
> -static gboolean clipboard_timeout(gpointer data)
> -{
> -    RunInfo *ri = data;
> -
> -    g_warning("clipboard get timed out");
> -    if (g_main_loop_is_running (ri->loop))
> -        g_main_loop_quit (ri->loop);
> -
> -    ri->timeout_handler = 0;
> -    return FALSE;
> -}
> -
> -static void clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data,
> -                          guint info, gpointer display)
> -{
> -    RunInfo ri = { NULL, };
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    gulong clipboard_handler;
> -
> -    SPICE_DEBUG("clipboard get");
> -
> -    g_return_if_fail(info < SPICE_N_ELEMENTS(atom2agent));
> -    g_return_if_fail(get_selection_from_clipboard(d, clipboard) != -1);
> -
> -    ri.display = display;
> -    ri.selection_data = selection_data;
> -    ri.info = info;
> -    ri.loop = g_main_loop_new(NULL, FALSE);
> -    ri.selection = get_selection_from_clipboard(d, clipboard);
> -
> -    clipboard_handler = g_signal_connect(d->main, "main-clipboard-selection",
> -                                         G_CALLBACK(clipboard_got_from_guest), &ri);
> -    ri.timeout_handler = g_timeout_add_seconds(7, clipboard_timeout, &ri);
> -    spice_main_clipboard_selection_request(d->main, ri.selection, atom2agent[info].vdagent);
> -
> -    /* apparently, this is needed to avoid dead-lock, from
> -       gtk_dialog_run */
> -    GDK_THREADS_LEAVE();
> -    g_main_loop_run(ri.loop);
> -    GDK_THREADS_ENTER();
> -
> -    g_main_loop_unref(ri.loop);
> -    ri.loop = NULL;
> -    g_signal_handler_disconnect(d->main, clipboard_handler);
> -    if (ri.timeout_handler != 0)
> -        g_source_remove(ri.timeout_handler);
> -}
> -
> -static void clipboard_clear(GtkClipboard *clipboard, gpointer display)
> -{
> -    SPICE_DEBUG("clipboard_clear");
> -    /* We watch for clipboard ownership changes and act on those, so we
> -       don't need to do anything here */
> -}
> -
> -static gboolean clipboard_grab(SpiceMainChannel *main, guint selection,
> -                               guint32* types, guint32 ntypes, gpointer display)
> -{
> -    int m, n, i;
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    GtkTargetEntry targets[SPICE_N_ELEMENTS(atom2agent)];
> -    gboolean target_selected[SPICE_N_ELEMENTS(atom2agent)] = { FALSE, };
> -    gboolean found;
> -    GtkClipboard* cb;
> -
> -    cb = get_clipboard_from_selection(d, selection);
> -    g_return_val_if_fail(cb != NULL, FALSE);
> -
> -    i = 0;
> -    for (n = 0; n < ntypes; ++n) {
> -        found = FALSE;
> -        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> -            if (atom2agent[m].vdagent == types[n] && !target_selected[m]) {
> -                found = TRUE;
> -                g_return_val_if_fail(i < SPICE_N_ELEMENTS(atom2agent), FALSE);
> -                targets[i].target = (gchar*)atom2agent[m].xatom;
> -                targets[i].flags = 0;
> -                targets[i].info = m;
> -                target_selected[m] = TRUE;
> -                i += 1;
> -            }
> -        }
> -        if (!found) {
> -            g_warning("clipboard: couldn't find a matching type for: %d", types[n]);
> -        }
> -    }
> -
> -    g_free(d->clip_targets[selection]);
> -    d->nclip_targets[selection] = i;
> -    d->clip_targets[selection] = g_memdup(targets, sizeof(GtkTargetEntry) * i);
> -    /* Receiving a grab implies we've released our own grab */
> -    d->clip_grabbed[selection] = FALSE;
> -
> -    if (!d->auto_clipboard_enable || d->nclip_targets[selection] == 0)
> -        goto skip_grab_clipboard;
> -
> -    if (!gtk_clipboard_set_with_data(cb, targets, i,
> -                                     clipboard_get, clipboard_clear, display)) {
> -        g_warning("clipboard grab failed");
> -        return FALSE;
> -    }
> -    d->clipboard_selfgrab_pending[selection] = TRUE;
> -    d->clipboard_by_guest[selection] = TRUE;
> -    d->clip_hasdata[selection] = FALSE;
> -
> -skip_grab_clipboard:
> -    return TRUE;
> -}
> -
> -static void clipboard_received_cb(GtkClipboard *clipboard,
> -                                  GtkSelectionData *selection_data,
> -                                  gpointer display)
> -{
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    gint len = 0, m;
> -    guint32 type = VD_AGENT_CLIPBOARD_NONE;
> -    gchar* name;
> -    GdkAtom atom;
> -
> -    g_return_if_fail(get_selection_from_clipboard(d, clipboard) != -1);
> -
> -    len = gtk_selection_data_get_length(selection_data);
> -    if (len == -1) {
> -        SPICE_DEBUG("empty clipboard");
> -        len = 0;
> -    } else if (len == 0) {
> -        SPICE_DEBUG("TODO: what should be done here?");
> -    } else {
> -        atom = gtk_selection_data_get_data_type(selection_data);
> -        name = gdk_atom_name(atom);
> -        for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> -            if (strcasecmp(name, atom2agent[m].xatom) == 0) {
> -                break;
> -            }
> -        }
> -
> -        if (m >= SPICE_N_ELEMENTS(atom2agent)) {
> -            g_warning("clipboard_received for unsupported type: %s", name);
> -        } else {
> -            type = atom2agent[m].vdagent;
> -        }
> -
> -        g_free(name);
> -    }
> -
> -    spice_main_clipboard_selection_notify(d->main, get_selection_from_clipboard(d, clipboard),
> -        type, gtk_selection_data_get_data(selection_data), len);
> -}
> -
> -static gboolean clipboard_request(SpiceMainChannel *main, guint selection,
> -                                  guint type, gpointer display)
> -{
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    int m;
> -    GdkAtom atom;
> -
> -    for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {
> -        if (atom2agent[m].vdagent == type)
> -            break;
> -    }
> -
> -    g_return_val_if_fail(m < SPICE_N_ELEMENTS(atom2agent), FALSE);
> -
> -    atom = gdk_atom_intern_static_string(atom2agent[m].xatom);
> -    gtk_clipboard_request_contents(get_clipboard_from_selection(d, selection), atom,
> -                                   clipboard_received_cb, display);
> -
> -    return TRUE;
> -}
> -
> -static void clipboard_release(SpiceMainChannel *main, guint selection, gpointer data)
> -{
> -    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(data);
> -    GtkClipboard* clipboard = get_clipboard_from_selection(d, selection);
> -    if (!clipboard)
> -        return;
> -
> -    d->nclip_targets[selection] = 0;
> -
> -    if (!d->clipboard_by_guest[selection])
> -        return;
> -    gtk_clipboard_clear(clipboard);
> -    d->clipboard_by_guest[selection] = FALSE;
> -}
> -
>  static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
>  {
>     SpiceDisplay *display = data;
> @@ -1864,14 +1461,6 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
>         g_signal_connect(channel, "main-mouse-update",
>                          G_CALLBACK(mouse_update), display);
>         mouse_update(channel, display);
> -        if (id != d->channel_id)
> -            return;
> -        g_signal_connect(channel, "main-clipboard-selection-grab",
> -                         G_CALLBACK(clipboard_grab), display);
> -        g_signal_connect(channel, "main-clipboard-selection-request",
> -                         G_CALLBACK(clipboard_request), display);
> -        g_signal_connect(channel, "main-clipboard-selection-release",
> -                         G_CALLBACK(clipboard_release), display);
>         return;
>     }
>
> @@ -2008,6 +1597,7 @@ SpiceDisplay *spice_display_new(SpiceSession *session, int id)
>     display = g_object_new(SPICE_TYPE_DISPLAY, NULL);
>     d = SPICE_DISPLAY_GET_PRIVATE(display);
>     d->session = g_object_ref(session);
> +    d->gtk_session = spice_gtk_session_get(d->session);
>     d->channel_id = id;
>
>     g_signal_connect(session, "channel-new",
> @@ -2043,32 +1633,25 @@ void spice_display_mouse_ungrab(SpiceDisplay *display)
>  void spice_display_copy_to_guest(SpiceDisplay *display)
>  {
>     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    int selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
>
> -    if (d->clip_hasdata[selection] && !d->clip_grabbed[selection]) {
> -        gtk_clipboard_request_targets(d->clipboard, clipboard_get_targets, display);
> -    }
> +    g_return_if_fail(d->gtk_session != NULL);
> +
> +    spice_gtk_session_copy_to_guest(d->gtk_session);
>  }
>
> +/**
> + * spice_display_paste_from_guest:
> + * @display:
> + *
> + * Copy guest clipboard to client-side clipboard.
> + **/
>  void spice_display_paste_from_guest(SpiceDisplay *display)
>  {
>     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
> -    int selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
>
> -    if (d->nclip_targets[selection] == 0) {
> -        g_warning("Guest clipboard is not available.");
> -        return;
> -    }
> +    g_return_if_fail(d->gtk_session != NULL);
>
> -    if (!gtk_clipboard_set_with_data(d->clipboard,
> -                                     d->clip_targets[selection], d->nclip_targets[selection],
> -                                     clipboard_get, clipboard_clear, display)) {
> -        g_warning("Clipboard grab failed");
> -        return;
> -    }
> -    d->clipboard_selfgrab_pending[selection] = TRUE;
> -    d->clipboard_by_guest[selection] = TRUE;
> -    d->clip_hasdata[selection] = FALSE;
> +    spice_gtk_session_paste_from_guest(d->gtk_session);
>  }
>
>  /**
> --
> 1.7.6.4
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
>


ack

-- 
Marc-André Lureau


More information about the Spice-devel mailing list