重要的函数:
void spice_main_channel_clipboard_selection_grab(SpiceMainChannel *channel, guint selection,
guint32 *types, int ntypes); 抓取剪切板
void spice_main_channel_clipboard_selection_release(SpiceMainChannel *channel, guint selection); 剪切板释放
void spice_main_channel_clipboard_selection_notify(SpiceMainChannel *channel, guint selection,guint32 type, const guchar *data, size_t size); 通知剪切板内容
void spice_main_channel_clipboard_selection_request(SpiceMainChannel *channel, guint selection,guint32 type); 请求剪切板
guint32 *types, int ntypes); 抓取剪切板
void spice_main_channel_clipboard_selection_release(SpiceMainChannel *channel, guint selection); 剪切板释放
void spice_main_channel_clipboard_selection_notify(SpiceMainChannel *channel, guint selection,guint32 type, const guchar *data, size_t size); 通知剪切板内容
void spice_main_channel_clipboard_selection_request(SpiceMainChannel *channel, guint selection,guint32 type); 请求剪切板
1、host--> guest复制粘贴
1.1、宿主机复制:监听剪切板变化,获取剪切板内容
1.1.1、连接剪切板内容变化的回调函数
void spice_gtk_session_copy_to_guest(SpiceGtkSession *self)
{g_return_if_fail(SPICE_IS_GTK_SESSION(self));g_return_if_fail(read_only(self) == FALSE);SpiceGtkSessionPrivate *s = self->priv;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,get_weak_ref(self)); //剪贴板收到支持类型的内容之后时,callback将被调用。}
}
1.1.2、剪切板内容处理
static void clipboard_get_targets(GtkClipboard *clipboard,GdkAtom *atoms,gint n_atoms, gpointer user_data)
{SpiceGtkSession *self = free_weak_ref(user_data);SPICE_DEBUG("%s:", __FUNCTION__);if (self == NULL)return;g_return_if_fail(SPICE_IS_GTK_SESSION(self));if (atoms == NULL) {SPICE_DEBUG("Retrieving the clipboard data has failed");return;}SpiceGtkSessionPrivate *s = self->priv;guint32 types[SPICE_N_ELEMENTS(atom2agent)] = { 0 };gint num_types;int a;int selection;if (s->main == NULL)return;selection = get_selection_from_clipboard(s, clipboard);g_return_if_fail(selection != -1);/* GTK+ does seem to cache atoms, but not for Wayland */g_free(s->atoms[selection]);s->atoms[selection] = g_memdup(atoms, n_atoms * sizeof(GdkAtom));s->n_atoms[selection] = n_atoms;if (s->clip_grabbed[selection]) {SPICE_DEBUG("Clipboard is already grabbed, re-grab: %d atoms", n_atoms);}/* Set all Atoms that matches our current protocol implementation */num_types = 0;for (a = 0; a < n_atoms; a++) {guint m;gchar *name = gdk_atom_name(atoms[a]);SPICE_DEBUG(" \"%s\"", name);for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {guint t;if (strcasecmp(name, atom2agent[m].xatom) != 0) {continue;}if (atom2agent[m].vdagent == VD_AGENT_CLIPBOARD_FILE_LIST) {
#ifdef HAVE_PHODAV_VIRTUALif (!clipboard_get_open_webdav(s->session)) {SPICE_DEBUG("Received %s target, but the clipboard webdav channel isn't available, skipping", atom2agent[m].xatom);break;}
#elsebreak;
#endif}/* check if type is already in list */for (t = 0; t < num_types; t++) {if (types[t] == atom2agent[m].vdagent) {break;}}if (t == num_types) {/* add type to empty slot */types[t] = atom2agent[m].vdagent;num_types++;}}g_free(name);}if (num_types == 0) {SPICE_DEBUG("No GdkAtoms will be sent from %d", n_atoms);return;}s->clip_grabbed[selection] = TRUE;if (spice_main_channel_agent_test_capability(s->main, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))spice_main_channel_clipboard_selection_grab(s->main, selection, types, num_types);///* Sending a grab causes the agent to do an implicit release */s->nclip_targets[selection] = 0;
}
1.2、guest粘贴:发送request请求获取内容
1.2.1、连接回调函数
static void channel_new(SpiceSession *session, SpiceChannel *channel,gpointer user_data)
{g_return_if_fail(SPICE_IS_GTK_SESSION(user_data));SpiceGtkSession *self = user_data;SpiceGtkSessionPrivate *s = self->priv;if (SPICE_IS_MAIN_CHANNEL(channel)) {SPICE_DEBUG("Changing main channel from %p to %p", s->main, 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_delay), self);}if (SPICE_IS_INPUTS_CHANNEL(channel)) {spice_g_signal_connect_object(channel, "inputs-modifiers",G_CALLBACK(guest_modifiers_changed), self, 0);spice_gtk_session_sync_keyboard_modifiers_for_channel(self, SPICE_INPUTS_CHANNEL(channel), TRUE);}
}
1.2.2、收到服务器的request请求剪切板内容
static gboolean clipboard_request(SpiceMainChannel *main, guint selection,guint type, gpointer user_data)
{g_return_val_if_fail(SPICE_IS_GTK_SESSION(user_data), FALSE);SpiceGtkSession *self = user_data;SpiceGtkSessionPrivate *s = self->priv;GdkAtom atom;GtkClipboard* cb;int m;cb = get_clipboard_from_selection(s, selection);g_return_val_if_fail(cb != NULL, FALSE);g_return_val_if_fail(s->clipboard_by_guest[selection] == FALSE, FALSE);g_return_val_if_fail(s->clip_grabbed[selection], FALSE);if (read_only(self))return FALSE;if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {gtk_clipboard_request_text(cb, clipboard_received_text_cb,get_weak_ref(self)); //发送文本} else if (type == VD_AGENT_CLIPBOARD_FILE_LIST) {
#ifdef HAVE_PHODAV_VIRTUALatom = clipboard_select_uris_atom(s, selection);if (atom == GDK_NONE) {return FALSE;}gtk_clipboard_request_contents(cb, atom, clipboard_received_uri_contents_cb, get_weak_ref(self));//发视其他内容
#elsereturn FALSE;
#endif} else {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,get_weak_ref(self));}return TRUE;
}
static gboolean clipboard_grab(SpiceMainChannel *main, guint selection,guint32* types, guint32 ntypes,gpointer user_data)
{g_return_val_if_fail(SPICE_IS_GTK_SESSION(user_data), FALSE);SpiceGtkSession *self = user_data;SpiceGtkSessionPrivate *s = self->priv;GtkTargetEntry targets[SPICE_N_ELEMENTS(atom2agent)];gboolean target_selected[SPICE_N_ELEMENTS(atom2agent)] = { FALSE, };gboolean found;GtkClipboard* cb;int m, n;int num_targets = 0;clipboard_release_delay_remove(self, selection, false);cb = get_clipboard_from_selection(s, selection);g_return_val_if_fail(cb != NULL, FALSE);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(num_targets < SPICE_N_ELEMENTS(atom2agent), FALSE);targets[num_targets].target = (gchar*)atom2agent[m].xatom;targets[num_targets].info = m;target_selected[m] = TRUE;num_targets++;}}if (!found) {g_warning("clipboard: couldn't find a matching type for: %u",types[n]);}}g_free(s->clip_targets[selection]);s->nclip_targets[selection] = num_targets;s->clip_targets[selection] = g_memdup(targets, sizeof(GtkTargetEntry) * num_targets);/* Receiving a grab implies we've released our own grab */s->clip_grabbed[selection] = FALSE;if (read_only(self) ||!s->auto_clipboard_enable ||s->nclip_targets[selection] == 0) {return TRUE;}if (!gtk_clipboard_set_with_owner(cb,targets,num_targets,clipboard_get,clipboard_clear,G_OBJECT(self))) {g_warning("clipboard grab failed");return FALSE;}s->clipboard_by_guest[selection] = TRUE;s->clip_hasdata[selection] = FALSE;return TRUE;
}
2、guest --> host复制粘贴
2.1、剪切板粘贴时调用函数 clipboard_get
void spice_gtk_session_paste_from_guest(SpiceGtkSession *self)
{g_return_if_fail(SPICE_IS_GTK_SESSION(self));g_return_if_fail(read_only(self) == FALSE);SpiceGtkSessionPrivate *s = self->priv;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_owner(s->clipboard, s->clip_targets[selection], s->nclip_targets[selection], clipboard_get, clipboard_clear, G_OBJECT(self))) {g_warning("Clipboard grab failed");return;}s->clipboard_by_guest[selection] = TRUE;s->clip_hasdata[selection] = FALSE;
}
2.2、连接主通道信号 main-clipboard-selection处理粘贴内容
static void clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer user_data)
{g_return_if_fail(SPICE_IS_GTK_SESSION(user_data));RunInfo ri = { NULL, };SpiceGtkSession *self = user_data;SpiceGtkSessionPrivate *s = self->priv;gboolean agent_connected = FALSE;gulong clipboard_handler;gulong agent_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);if (s->clipboard_release_delay[selection]) {SPICE_DEBUG("not requesting data from guest during delayed release");return;}ri.selection_data = selection_data;ri.info = info;ri.loop = g_main_loop_new(NULL, FALSE);ri.selection = selection;ri.self = self;clipboard_handler = g_signal_connect(s->main, "main-clipboard-selection",G_CALLBACK(clipboard_got_from_guest),&ri);agent_handler = g_signal_connect_swapped(s->main, "notify::agent-connected",G_CALLBACK(clipboard_agent_connected),&ri);spice_main_channel_clipboard_selection_request(s->main, selection,atom2agent[info].vdagent);g_object_get(s->main, "agent-connected", &agent_connected, NULL);if (!agent_connected) {SPICE_DEBUG("canceled clipboard_get, before running loop");goto cleanup;}/* This is modeled on the implementation of gtk_dialog_run() even though* these thread functions are deprecated and appears to be needed to avoid* dead-lock from gtk_dialog_run().*/G_GNUC_BEGIN_IGNORE_DEPRECATIONSgdk_threads_leave();g_main_loop_run(ri.loop);gdk_threads_enter();G_GNUC_END_IGNORE_DEPRECATIONScleanup:g_clear_pointer(&ri.loop, g_main_loop_unref);g_signal_handler_disconnect(s->main, clipboard_handler);g_signal_handler_disconnect(s->main, agent_handler);
}
2.3、将内容设置到剪切板上面
static void clipboard_got_from_guest(SpiceMainChannel *main, guint selection,guint type, const guchar *data, guint size,gpointer user_data)
{RunInfo *ri = user_data;SpiceGtkSessionPrivate *s = ri->self->priv;gchar *conv = NULL;g_return_if_fail(selection == ri->selection);SPICE_DEBUG("clipboard got data");if (atom2agent[ri->info].vdagent == VD_AGENT_CLIPBOARD_UTF8_TEXT) {/* on windows, gtk+ would already convert to LF endings, butnot on unix */if (spice_main_channel_agent_test_capability(s->main, VD_AGENT_CAP_GUEST_LINEEND_CRLF)) {conv = spice_dos2unix((gchar*)data, size);size = strlen(conv);}gtk_selection_data_set_text(ri->selection_data, conv ?: (gchar*)data, size);} else {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);g_free(conv);
}