<div dir="ltr"><div dir="ltr">Hi,<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Sep 9, 2020 at 5:50 PM GitLab Mirror <<a href="mailto:gitlab-mirror@kemper.freedesktop.org">gitlab-mirror@kemper.freedesktop.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> meson.build | 13 -<br>
src/map-file | 1 <br>
src/spice-glib-sym-file | 1 <br>
src/spice-gtk-session.c | 399 +++++++++++++++++++++++++++++++++++++++++++++++<br>
src/spice-session-priv.h | 1 <br>
src/spice-session.c | 30 +++<br>
src/spice-session.h | 5 <br>
7 files changed, 443 insertions(+), 7 deletions(-)<br>
<br>
New commits:<br>
commit f33d589d747f4f7ee6a1241c344ca611a36e9c71<br>
Author: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Date: Fri May 29 17:59:38 2020 +0200<br>
<br>
clipboard: enable copying files to guest using webdav<br>
<br>
When an app advertises the "text/uri-list" target, the user<br>
probably wants to copy/move files. Spice-gtk then sends<br>
a grab message to the vdagent advertising the<br>
VD_AGENT_CLIPBOARD_FILE_LIST type.<br>
<br>
Vdagent can then request clipboard data in this type.<br>
<br>
Spice-gtk tries to talk to the app that owns the clipboard<br>
in its native format in order to determine the preferred<br>
file operation (copy X move).<br>
<br>
For GNOME Nautilus, that's simply "UTF8_TEXT",<br>
for KDE Dolphin, "application/x-kde-cutselection".<br>
<br>
Otherwise the generic "text/uri-list" is used that does not<br>
provide any additional information.<br>
<br>
Once the uri list is obtained from the app, spice-gtk<br>
creates a unique virtual dir in the ".spice-clipboard"<br>
directory that is designated for this purpose.<br>
<br>
Each file is attached inside this virtual dir using<br>
phodav_virtual_dir_attach_real_child(), see phodav API<br>
for details.<br>
<br>
A list of paths in the phodav server is then sent to vdagent,<br>
as specified in the spice-protocol.<br>
Such path can for example look like this:<br>
/.spice-clipboard/b8f0249c-082a-4da9-9a38-2de3237a66f0/file<br>
<br>
It is up to the vdagent to ensure that the spice shared folder<br>
is accessible and to set the clipboard data in a format that<br>
other apps understand.<br>
<br>
This requires new phodav with PhodavVirtualDir API.<br>
<br>
Signed-off-by: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Acked-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>><br>
<br>
diff --git a/meson.build b/meson.build<br>
index 7ade460..e43139e 100644<br>
--- a/meson.build<br>
+++ b/meson.build<br>
@@ -33,7 +33,7 @@ spice_glib_deps = []<br>
spice_gtk_deps = []<br>
spice_wayland_deps = []<br>
spice_acl_deps = []<br>
-spice_protocol_version = '0.14.2'<br>
+spice_protocol_version = '0.14.3'<br>
<br>
#<br>
# Set up subprojects<br>
diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c<br>
index 5e6be4a..dfbd8fa 100644<br>
--- a/src/spice-gtk-session.c<br>
+++ b/src/spice-gtk-session.c<br>
@@ -34,6 +34,10 @@<br>
#endif<br>
#endif<br>
<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+#include <libphodav/phodav.h><br>
+#endif<br>
+<br>
#include <gtk/gtk.h><br>
#include <spice/vd_agent.h><br>
#include "desktop-integration.h"<br>
@@ -61,6 +65,8 @@ struct _SpiceGtkSessionPrivate {<br>
gboolean clip_grabbed[CLIPBOARD_LAST];<br>
gboolean clipboard_by_guest[CLIPBOARD_LAST];<br>
guint clipboard_release_delay[CLIPBOARD_LAST];<br>
+ /* TODO: maybe add a way of restoring this? */<br>
+ GHashTable *cb_shared_files;<br>
/* auto-usbredir related */<br>
gboolean auto_usbredir_enable;<br>
int auto_usbredir_reqs;<br>
@@ -191,6 +197,12 @@ static void spice_gtk_session_init(SpiceGtkSession *self)<br>
<br>
s = self->priv = spice_gtk_session_get_instance_private(self);<br>
<br>
+ s->cb_shared_files =<br>
+ g_hash_table_new_full(g_file_hash,<br>
+ (GEqualFunc)g_file_equal,<br>
+ g_object_unref, /* unref GFile */<br>
+ g_free /* free gchar * */<br>
+ );<br>
s->clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);<br>
g_signal_connect(G_OBJECT(s->clipboard), "owner-change",<br>
G_CALLBACK(clipboard_owner_change), self);<br>
@@ -252,6 +264,7 @@ static void spice_gtk_session_dispose(GObject *gobject)<br>
self);<br>
s->session = NULL;<br>
}<br>
+ g_clear_pointer(&s->cb_shared_files, g_hash_table_destroy);<br>
<br>
/* Chain up to the parent class */<br>
if (G_OBJECT_CLASS(spice_gtk_session_parent_class)->dispose)<br>
@@ -544,6 +557,9 @@ static const struct {<br>
},{<br>
.vdagent = VD_AGENT_CLIPBOARD_IMAGE_JPG,<br>
.xatom = "image/jpeg"<br>
+ },{<br>
+ .vdagent = VD_AGENT_CLIPBOARD_FILE_LIST,<br>
+ .xatom = "text/uri-list"<br>
}<br>
};<br>
<br>
@@ -660,6 +676,18 @@ static void clipboard_get_targets(GtkClipboard *clipboard,<br>
continue;<br>
}<br>
<br>
+ if (atom2agent[m].vdagent == VD_AGENT_CLIPBOARD_FILE_LIST) {<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+ if (!clipboard_get_open_webdav(s->session)) {<br>
+ SPICE_DEBUG("Received %s target, but the clipboard webdav channel "<br>
+ "isn't available, skipping", atom2agent[m].xatom);<br>
+ break;<br>
+ }<br>
+#else<br>
+ break;<br>
+#endif<br>
+ }<br>
+<br>
/* check if type is already in list */<br>
for (t = 0; t < num_types; t++) {<br>
if (types[t] == atom2agent[m].vdagent) {<br>
@@ -1037,6 +1065,318 @@ notify_agent:<br>
g_free(conv);<br>
}<br>
<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+/* returns path to @file under @root in clipboard phodav server, or NULL on error */<br>
+static gchar *clipboard_webdav_share_file(PhodavVirtualDir *root, GFile *file)<br>
+{<br>
+ gchar *uuid;<br>
+ PhodavVirtualDir *dir;<br>
+ GError *err = NULL;<br>
+<br>
+ /* separate directory is created for each file,<br>
+ * as we want to preserve the original filename and avoid conflicts */<br>
+ for (guint i = 0; i < 8; i++) {<br>
+ uuid = g_uuid_string_random();<br>
+ gchar *dir_path = g_strdup_printf(SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH "/%s", uuid);<br>
+ dir = phodav_virtual_dir_new_dir(root, dir_path, &err);<br>
+ g_free(dir_path);<br>
+ if (!err) {<br>
+ break;<br>
+ }<br>
+ g_clear_pointer(&uuid, g_free);<br>
+ if (!g_error_matches(err, G_IO_ERROR, G_IO_ERROR_EXISTS)) {<br>
+ g_warning("failed to create phodav virtual dir: %s", err->message);<br>
+ g_error_free(err);<br>
+ return NULL;<br>
+ }<br>
+ g_clear_error(&err);<br>
+ }<br>
+<br>
+ if (!dir) {<br>
+ g_warning("failed to create phodav virtual dir: all attempts failed");<br>
+ return NULL;<br>
+ }<br>
+<br>
+ phodav_virtual_dir_attach_real_child(dir, file);<br>
+ g_object_unref(dir);<br>
+<br>
+ gchar *base = g_file_get_basename(file);<br>
+ gchar *path = g_strdup_printf(SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH "/%s/%s", uuid, base);<br>
+ g_free(uuid);<br>
+ g_free(base);<br>
+<br>
+ return path;<br>
+}<br>
+<br>
+/* join all strings in @strv into a new char array,<br>
+ * including all terminating NULL-chars */<br>
+static gchar *strv_concat(gchar **strv, gsize *size_out)<br>
+{<br>
+ gchar **str_p, *arr, *curr;<br>
+<br>
+ g_return_val_if_fail(strv && size_out, NULL);<br>
+<br>
+ for (str_p = strv, *size_out = 0; *str_p != NULL; str_p++) {<br>
+ *size_out += strlen(*str_p) + 1;<br>
+ }<br>
+<br>
+ arr = g_malloc(*size_out);<br>
+<br>
+ for (str_p = strv, curr = arr; *str_p != NULL; str_p++) {<br>
+ curr = g_stpcpy(curr, *str_p) + 1;<br>
+ }<br>
+<br>
+ return arr;<br>
+}<br>
+<br>
+/* if not done alreay, share all files in @uris using the webdav server<br>
+ * and return a new buffer with VD_AGENT_CLIPBOARD_FILE_LIST data */<br>
+static gchar *strv_uris_transform_to_data(SpiceGtkSessionPrivate *s,<br>
+ gchar **uris, gsize *size_out, GdkDragAction action)<br>
+{<br>
+ SpiceWebdavChannel *webdav;<br>
+ PhodavServer *phodav;<br>
+ PhodavVirtualDir *root;<br>
+<br>
+ gchar **uri_ptr, *path, **paths, *data;<br>
+ GFile *file;<br>
+ guint n;<br>
+<br>
+ *size_out = 0;<br>
+<br>
+ if (!uris || g_strv_length(uris) < 1) {<br>
+ return NULL;<br>
+ }<br>
+<br>
+ webdav = clipboard_get_open_webdav(s->session);<br>
+ if (!webdav) {<br>
+ SPICE_DEBUG("Received uris, but no webdav channel");<br>
+ return NULL;<br>
+ }<br>
+<br>
+ phodav = spice_session_get_webdav_server(s->session);<br>
+ g_object_get(phodav, "root-file", &root, NULL);<br>
+<br>
+ paths = g_new0(gchar *, g_strv_length(uris) + 2);<br>
+<br>
+ paths[0] = action == GDK_ACTION_MOVE ? "cut" : "copy";<br>
+ n = 1;<br>
+<br>
+ for (uri_ptr = uris; *uri_ptr != NULL; uri_ptr++) {<br>
+ file = g_file_new_for_uri(*uri_ptr);<br>
+<br>
+ /* clipboard data is usually requested multiple times for no obvious reasons<br>
+ * (clipboar managers to blame?), we don't want to create multiple dirs for the same file */<br>
+ path = g_hash_table_lookup(s->cb_shared_files, file);<br>
+ if (path) {<br>
+ SPICE_DEBUG("found %s with path %s", *uri_ptr, path);<br>
+ g_object_unref(file);<br>
+ } else {<br>
+ path = clipboard_webdav_share_file(root, file);<br>
+ g_return_val_if_fail(path != NULL, NULL);<br>
+ SPICE_DEBUG("publishing %s under %s", *uri_ptr, path);<br>
+ /* file and path gets freed once the hash table gets destroyed */<br>
+ g_hash_table_insert(s->cb_shared_files, file, path);<br>
+ }<br>
+ paths[n] = path;<br>
+ n++;<br>
+ }<br>
+<br>
+ g_object_unref(root);<br>
+ data = strv_concat(paths, size_out);<br>
+ g_free(paths);<br>
+<br>
+ return data;<br>
+}<br>
+<br>
+static GdkAtom a_gnome, a_mate, a_nautilus, a_uri_list, a_kde_cut;<br>
+<br>
+static void init_uris_atoms()<br>
+{<br>
+ if (a_gnome != GDK_NONE) {<br>
+ return;<br>
+ }<br>
+ a_gnome = gdk_atom_intern_static_string("x-special/gnome-copied-files");<br>
+ a_mate = gdk_atom_intern_static_string("x-special/mate-copied-files");<br>
+ a_nautilus = gdk_atom_intern_static_string("UTF8_STRING");<br>
+ a_uri_list = gdk_atom_intern_static_string("text/uri-list");<br>
+ a_kde_cut = gdk_atom_intern_static_string("application/x-kde-cutselection");<br>
+}<br>
+<br>
+static GdkAtom clipboard_select_uris_atom(SpiceGtkSessionPrivate *s, guint selection)<br>
+{<br>
+ init_uris_atoms();<br>
+ if (clipboard_find_atom(s, selection, a_gnome)) {<br>
+ return a_gnome;<br>
+ }<br>
+ if (clipboard_find_atom(s, selection, a_mate)) {<br>
+ return a_mate;<br>
+ }<br>
+ if (clipboard_find_atom(s, selection, a_nautilus)) {<br>
+ return a_nautilus;<br>
+ }<br>
+ return clipboard_find_atom(s, selection, a_uri_list);<br>
+}<br>
+<br>
+/* common handler for "x-special/gnome-copied-files" and "x-special/mate-copied-files" */<br>
+static gchar *x_special_copied_files_transform_to_data(SpiceGtkSessionPrivate *s,<br>
+ GtkSelectionData *selection_data, gsize *size_out)<br>
+{<br>
+ const gchar *text;<br>
+ gchar **lines, *data = NULL;<br>
+ GdkDragAction action;<br>
+<br>
+ *size_out = 0;<br>
+<br>
+ text = (gchar *)gtk_selection_data_get_data(selection_data);<br>
+ if (!text) {<br>
+ return NULL;<br>
+ }<br>
+ lines = g_strsplit(text, "\n", -1);<br>
+ if (g_strv_length(lines) < 2) {<br>
+ goto err;<br>
+ }<br>
+<br>
+ if (!g_strcmp0(lines[0], "cut")) {<br>
+ action = GDK_ACTION_MOVE;<br>
+ } else if (!g_strcmp0(lines[0], "copy")) {<br>
+ action = GDK_ACTION_COPY;<br>
+ } else {<br>
+ goto err;<br>
+ }<br>
+<br>
+ data = strv_uris_transform_to_data(s, &lines[1], size_out, action);<br>
+err:<br>
+ g_strfreev(lines);<br>
+ return data;<br>
+}<br>
+<br>
+/* used with newer Nautilus */<br>
+static gchar *nautilus_uris_transform_to_data(SpiceGtkSessionPrivate *s,<br>
+ GtkSelectionData *selection_data, gsize *size_out, gboolean *retry_out)<br>
+{<br>
+ gchar **lines, *text, *data = NULL;<br>
+ guint n_lines;<br>
+ GdkDragAction action;<br>
+<br>
+ *size_out = 0;<br>
+<br>
+ text = (gchar *)gtk_selection_data_get_text(selection_data);<br>
+ if (!text) {<br>
+ return NULL;<br>
+ }<br>
+ lines = g_strsplit(text, "\n", -1);<br>
+ g_free(text);<br>
+ n_lines = g_strv_length(lines);<br>
+<br>
+ if (n_lines < 4) {<br>
+ *retry_out = TRUE;<br>
+ goto err;<br>
+ }<br>
+<br>
+ if (g_strcmp0(lines[0], "x-special/nautilus-clipboard")) {<br>
+ *retry_out = TRUE;<br>
+ goto err;<br>
+ }<br>
+<br>
+ if (!g_strcmp0(lines[1], "cut")) {<br>
+ action = GDK_ACTION_MOVE;<br>
+ } else if (!g_strcmp0(lines[1], "copy")) {<br>
+ action = GDK_ACTION_COPY;<br>
+ } else {<br>
+ goto err;<br>
+ }<br>
+<br>
+ /* the list of uris must end with \n,<br>
+ * so there must be an empty string after the split */<br>
+ if (g_strcmp0(lines[n_lines-1], "")) {<br>
+ goto err;<br>
+ }<br>
+ g_clear_pointer(&lines[n_lines-1], g_free);<br>
+<br>
+ data = strv_uris_transform_to_data(s, &lines[2], size_out, action);<br>
+err:<br>
+ g_strfreev(lines);<br>
+ return data;<br>
+}<br>
+<br>
+static GdkDragAction kde_get_clipboard_action(SpiceGtkSessionPrivate *s, GtkClipboard *clipboard)<br>
+{<br>
+ GtkSelectionData *selection_data;<br>
+ GdkDragAction action;<br>
+ const guchar *data;<br>
+<br>
+ /* this uses another GMainLoop, basically the same mechanism<br>
+ * as we use in clipboard_get(), so it doesn't block */<br>
+ selection_data = gtk_clipboard_wait_for_contents(clipboard, a_kde_cut);<br>
+ data = gtk_selection_data_get_data(selection_data);<br>
+ if (data && data[0] == '1') {<br>
+ action = GDK_ACTION_MOVE;<br>
+ } else {<br>
+ action = GDK_ACTION_COPY;<br>
+ }<br>
+ gtk_selection_data_free(selection_data);<br>
+<br>
+ return action;<br>
+}<br>
+<br>
+static void clipboard_received_uri_contents_cb(GtkClipboard *clipboard,<br>
+ GtkSelectionData *selection_data,<br>
+ gpointer user_data)<br>
+{<br>
+ SpiceGtkSession *self = free_weak_ref(user_data);<br>
+ SpiceGtkSessionPrivate *s;<br>
+ guint selection;<br>
+<br>
+ if (!self) {<br>
+ return;<br>
+ }<br>
+ s = self->priv;<br>
+<br>
+ selection = get_selection_from_clipboard(s, clipboard);<br>
+ g_return_if_fail(selection != -1);<br>
+<br>
+ init_uris_atoms();<br>
+ GdkAtom type = gtk_selection_data_get_data_type(selection_data);<br>
+ gchar *data;<br>
+ gsize len;<br>
+<br>
+ if (type == a_gnome || type == a_mate) {<br>
+ /* used by old Nautilus + many other file managers */<br>
+ data = x_special_copied_files_transform_to_data(s, selection_data, &len);<br>
+ } else if (type == a_nautilus) {<br>
+ gboolean retry = FALSE;<br>
+ data = nautilus_uris_transform_to_data(s, selection_data, &len, &retry);<br>
+<br>
+ if (retry && clipboard_find_atom(s, selection, a_uri_list) != GDK_NONE) {<br>
+ /* it's not Nautilus, so we give it one more try with the generic uri-list target */<br>
+ gtk_clipboard_request_contents(clipboard, a_uri_list,<br>
+ clipboard_received_uri_contents_cb, get_weak_ref(self));<br>
+ return;<br>
+ }<br>
+ } else if (type == a_uri_list) {<br>
+ GdkDragAction action = GDK_ACTION_COPY;<br>
+ gchar **uris = gtk_selection_data_get_uris(selection_data);<br>
+<br>
+ /* KDE uses a separate atom to distinguish between copy and move operation */<br>
+ if (clipboard_find_atom(s, selection, a_kde_cut) != GDK_NONE) {<br>
+ action = kde_get_clipboard_action(s, clipboard);<br>
+ }<br>
+<br>
+ data = strv_uris_transform_to_data(s, uris, &len, action);<br>
+ g_strfreev(uris);<br>
+ } else {<br>
+ g_warning("received uris in unsupported type");<br>
+ data = NULL;<br>
+ len = 0;<br>
+ }<br>
+<br>
+ spice_main_channel_clipboard_selection_notify(s->main, selection,<br>
+ VD_AGENT_CLIPBOARD_FILE_LIST, (guchar *)data, len);<br>
+ g_free(data);<br>
+}<br>
+#endif<br>
+<br>
static void clipboard_received_cb(GtkClipboard *clipboard,<br>
GtkSelectionData *selection_data,<br>
gpointer user_data)<br>
@@ -1111,6 +1451,17 @@ static gboolean clipboard_request(SpiceMainChannel *main, guint selection,<br>
if (type == VD_AGENT_CLIPBOARD_UTF8_TEXT) {<br>
gtk_clipboard_request_text(cb, clipboard_received_text_cb,<br>
get_weak_ref(self));<br>
+ } else if (type == VD_AGENT_CLIPBOARD_FILE_LIST) {<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+ atom = clipboard_select_uris_atom(s, selection);<br>
+ if (atom == GDK_NONE) {<br>
+ return FALSE;<br>
+ }<br>
+ gtk_clipboard_request_contents(cb, atom,<br>
+ clipboard_received_uri_contents_cb, get_weak_ref(self));<br>
+#else<br>
+ return FALSE;<br>
+#endif<br>
} else {<br>
for (m = 0; m < SPICE_N_ELEMENTS(atom2agent); m++) {<br>
if (atom2agent[m].vdagent == type)<br>
diff --git a/src/spice-session.c b/src/spice-session.c<br>
index f0ac891..8831bc5 100644<br>
--- a/src/spice-session.c<br>
+++ b/src/spice-session.c<br>
@@ -2666,6 +2666,17 @@ static void spice_session_set_shared_dir(SpiceSession *session, const gchar *dir<br>
<br>
g_free(s->shared_dir);<br>
s->shared_dir = g_strdup(dir);<br>
+<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+ if (s->webdav == NULL) {<br>
+ return;<br>
+ }<br>
+<br>
+ PhodavVirtualDir *root;<br>
+ g_object_get(s->webdav, "root-file", &root, NULL);<br>
+ phodav_virtual_dir_root_set_real(root, s->shared_dir);<br>
+ g_object_unref(root);<br>
+#endif<br>
}<br>
<br>
G_GNUC_INTERNAL<br>
@@ -2807,21 +2818,39 @@ PhodavServer* spice_session_get_webdav_server(SpiceSession *session)<br>
static GMutex mutex;<br>
<br>
const gchar *shared_dir = spice_session_get_shared_dir(session);<br>
+ /* with HAVE_PHODAV_VIRTUAL, PhodavServer must be created even if shared_dir is NULL */<br>
+#ifndef HAVE_PHODAV_VIRTUAL<br>
if (shared_dir == NULL) {<br>
SPICE_DEBUG("No shared dir set, not creating webdav server");<br>
return NULL;<br>
}<br>
+#endif<br>
<br>
g_mutex_lock(&mutex);<br>
<br>
if (priv->webdav == NULL) {<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+ PhodavVirtualDir *root = phodav_virtual_dir_new_root();<br>
+ priv->webdav = phodav_server_new_for_root_file(G_FILE(root));<br>
+<br>
+ phodav_virtual_dir_root_set_real(root, shared_dir);<br>
+<br>
+ g_object_unref(phodav_virtual_dir_new_dir(root, SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH, NULL));<br>
+ g_object_unref(root);<br>
+#else<br>
priv->webdav = phodav_server_new(shared_dir);<br>
+#endif<br>
+<br>
g_object_bind_property(session, "share-dir-ro",<br>
priv->webdav, "read-only",<br>
G_BINDING_SYNC_CREATE|G_BINDING_BIDIRECTIONAL);<br>
+<br>
+ /* with HAVE_PHODAV_VIRTUAL, the update is done in spice_session_set_shared_dir() */<br>
+#ifndef HAVE_PHODAV_VIRTUAL<br>
g_object_bind_property(session, "shared-dir",<br>
priv->webdav, "root",<br>
G_BINDING_SYNC_CREATE|G_BINDING_BIDIRECTIONAL);<br>
+#endif<br>
}<br>
<br>
g_mutex_unlock(&mutex);<br>
diff --git a/src/spice-session.h b/src/spice-session.h<br>
index 9436be8..665d2f3 100644<br>
--- a/src/spice-session.h<br>
+++ b/src/spice-session.h<br>
@@ -36,6 +36,8 @@ G_BEGIN_DECLS<br>
#define SPICE_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_SESSION))<br>
#define SPICE_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_SESSION, SpiceSessionClass))<br>
<br>
+#define SPICE_WEBDAV_CLIPBOARD_FOLDER_PATH "/.spice-clipboard"<br>
+<br>
typedef struct _PhodavServer PhodavServer;<br>
<br>
/**<br>
commit 996bfb48dc5d1d17bb56b4936f79d8705bcd5c48<br>
Author: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Date: Tue Jun 30 15:15:39 2020 +0200<br>
<br>
spice-gtk-session: cache atoms<br>
<br>
At the moment, spice-gtk only sends a grab message to the vdagent<br>
based on the retrieved atoms.<br>
<br>
With the upcoming changes, spice-gtk will have to know which<br>
targets were advertised outside of clipboard_get_targets() callback.<br>
<br>
We could use gtk_clipboard_wait_for_targets() or<br>
gtk_clipboard_wait_is_*_available(), but the targets are not cached<br>
by GTK+ on wayland for some reason. So let's cache them in spice-gtk<br>
to avoid having to talk to the clipboard owner.<br>
<br>
Signed-off-by: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Acked-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>><br>
<br>
diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c<br>
index 2b86616..5e6be4a 100644<br>
--- a/src/spice-gtk-session.c<br>
+++ b/src/spice-gtk-session.c<br>
@@ -55,6 +55,8 @@ struct _SpiceGtkSessionPrivate {<br>
GtkClipboard *clipboard_primary;<br>
GtkTargetEntry *clip_targets[CLIPBOARD_LAST];<br>
guint nclip_targets[CLIPBOARD_LAST];<br>
+ GdkAtom *atoms[CLIPBOARD_LAST];<br>
+ guint n_atoms[CLIPBOARD_LAST];<br>
gboolean clip_hasdata[CLIPBOARD_LAST];<br>
gboolean clip_grabbed[CLIPBOARD_LAST];<br>
gboolean clipboard_by_guest[CLIPBOARD_LAST];<br>
@@ -284,6 +286,8 @@ static void spice_gtk_session_finalize(GObject *gobject)<br>
for (i = 0; i < CLIPBOARD_LAST; ++i) {<br>
g_clear_pointer(&s->clip_targets[i], g_free);<br>
clipboard_release_delay_remove(self, i, true);<br>
+ g_clear_pointer(&s->atoms[i], g_free);<br>
+ s->n_atoms[i] = 0;<br>
}<br>
<br>
/* Chain up to the parent class */<br>
@@ -589,6 +593,16 @@ static SpiceWebdavChannel *clipboard_get_open_webdav(SpiceSession *session)<br>
g_list_free(list);<br>
return open ? SPICE_WEBDAV_CHANNEL(channel) : NULL;<br>
}<br>
+<br>
+static GdkAtom clipboard_find_atom(SpiceGtkSessionPrivate *s, guint selection, GdkAtom a)<br>
+{<br>
+ for (int i = 0; i < s->n_atoms[selection]; i++) {<br>
+ if (s->atoms[selection][i] == a) {<br>
+ return a;<br>
+ }<br>
+ }<br>
+ return GDK_NONE;<br>
+}<br>
#endif<br>
<br>
static void clipboard_get_targets(GtkClipboard *clipboard,<br>
@@ -622,6 +636,11 @@ static void clipboard_get_targets(GtkClipboard *clipboard,<br>
selection = get_selection_from_clipboard(s, clipboard);<br>
g_return_if_fail(selection != -1);<br>
<br>
+ /* GTK+ does seem to cache atoms, but not for Wayland */<br>
+ g_free(s->atoms[selection]);<br>
+ s->atoms[selection] = g_memdup(atoms, n_atoms * sizeof(GdkAtom));<br>
+ s->n_atoms[selection] = n_atoms;<br>
+<br>
if (s->clip_grabbed[selection]) {<br>
SPICE_DEBUG("Clipboard is already grabbed, re-grab: %d atoms", n_atoms);<br>
}<br>
@@ -705,6 +724,9 @@ static void clipboard_owner_change(GtkClipboard *clipboard,<br>
return;<br>
}<br>
<br>
+ g_clear_pointer(&s->atoms[selection], g_free);<br>
+ s->n_atoms[selection] = 0;<br>
+<br>
if (event->reason != GDK_OWNER_CHANGE_NEW_OWNER) {<br>
if (s->clip_grabbed[selection]) {<br>
/* grab was sent to the agent, so release it */<br>
commit 852b847c868a199b5127644ca689f8a7d70fbda1<br>
Author: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Date: Fri May 29 17:57:51 2020 +0200<br>
<br>
spice-gtk-session: add clipboard_get_open_webdav()<br>
<br>
File copy&paste functionality will only be enabled when there is an open<br>
webdav channel.<br>
<br>
Signed-off-by: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Acked-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>><br>
<br>
diff --git a/src/spice-gtk-session.c b/src/spice-gtk-session.c<br>
index 48058c7..2b86616 100644<br>
--- a/src/spice-gtk-session.c<br>
+++ b/src/spice-gtk-session.c<br>
@@ -565,6 +565,32 @@ static gpointer free_weak_ref(gpointer data)<br>
return object;<br>
}<br>
<br>
+#ifdef HAVE_PHODAV_VIRTUAL<br>
+static SpiceWebdavChannel *clipboard_get_open_webdav(SpiceSession *session)<br>
+{<br>
+ GList *list, *l;<br>
+ SpiceChannel *channel = NULL;<br>
+ gboolean open = FALSE;<br>
+<br>
+ g_return_val_if_fail(session != NULL, NULL);<br>
+<br>
+ list = spice_session_get_channels(session);<br>
+ for (l = g_list_first(list); l != NULL; l = g_list_next(l)) {<br>
+ channel = l->data;<br>
+<br>
+ if (!SPICE_IS_WEBDAV_CHANNEL(channel)) {<br>
+ continue;<br>
+ }<br>
+<br>
+ g_object_get(channel, "port-opened", &open, NULL);<br>
+ break;<br>
+ }<br>
+<br>
+ g_list_free(list);<br>
+ return open ? SPICE_WEBDAV_CHANNEL(channel) : NULL;<br>
+}<br>
+#endif<br>
+<br>
static void clipboard_get_targets(GtkClipboard *clipboard,<br>
GdkAtom *atoms,<br>
gint n_atoms,<br>
commit c1b5433815e5cd7683671d33a0d579b7b185efe8<br>
Author: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Date: Mon Jun 29 19:40:25 2020 +0200<br>
<br>
build: require GLib 2.52+<br>
<br>
This adds g_uuid_string_random()<br>
which is necessary for the following file copy&paste<br>
functionality.<br>
<br>
Signed-off-by: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Acked-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>><br>
<br>
diff --git a/meson.build b/meson.build<br>
index 1c4e9d9..7ade460 100644<br>
--- a/meson.build<br>
+++ b/meson.build<br>
@@ -89,7 +89,7 @@ endforeach<br>
#<br>
# check for mandatory dependencies<br>
#<br>
-glib_version = '2.46'<br>
+glib_version = '2.52'<br>
glib_version_info = '>= @0@'.format(glib_version)<br>
pixman_version = '>= 0.17.7'<br>
<br>
commit 979b752b24d6f8d7089a23760fd5adda18f0e7ed<br>
Author: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Date: Sat May 23 13:40:39 2020 +0200<br>
<br>
build: define HAVE_PHODAV_VIRTUAL if phodav >= 2.5<br>
<br>
Phodav 2.5 brings PhodavVirtualDir API needed for the<br>
file copy and paste functionality.<br>
<br>
If the library version is not sufficient, this new feature<br>
will be disabled, but the standard shared folders can still<br>
be used.<br>
<br>
Signed-off-by: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Acked-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>><br>
<br>
diff --git a/meson.build b/meson.build<br>
index 6bbb4a8..1c4e9d9 100644<br>
--- a/meson.build<br>
+++ b/meson.build<br>
@@ -177,14 +177,17 @@ endif<br>
<br>
# webdav<br>
spice_gtk_has_phodav = false<br>
-d = dependency('libphodav-2.0', required: get_option('webdav'))<br>
-if d.found()<br>
- spice_glib_deps += d<br>
+phodav_dep = dependency('libphodav-2.0', required: get_option('webdav'))<br>
+if phodav_dep.found()<br>
+ spice_glib_deps += phodav_dep<br>
d = dependency('libsoup-2.4', version : '>= 2.49.91', required: get_option('webdav'))<br>
if d.found()<br>
spice_glib_deps += d<br>
spice_gtk_config_data.set('USE_PHODAV', '1')<br>
spice_gtk_has_phodav = true<br>
+ if phodav_dep.version().version_compare('>= 2.5')<br>
+ spice_gtk_config_data.set('HAVE_PHODAV_VIRTUAL', '1')<br>
+ endif<br>
endif<br>
endif<br>
<br>
commit 4b9092b96b8da946ff3d17922b0fcf225c5dc81f<br>
Author: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Date: Sat May 23 16:28:52 2020 +0200<br>
<br>
session: make spice_session_get_webdav_server() public<br>
<br>
It will be necessary to access the webdav server from spice-gtk-session.c<br>
which isn't compiled with spice-session-priv.h, so make<br>
spice_session_get_webdav_server() public.<br></blockquote><div><br></div><div>I haven't looked at the whole series. Wouldn't it make sense to make it a read-only property instead?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Signed-off-by: Jakub Janků <<a href="mailto:jjanku@redhat.com" target="_blank">jjanku@redhat.com</a>><br>
Acked-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>><br>
<br>
diff --git a/src/map-file b/src/map-file<br>
index acdd38f..86f371d 100644<br>
--- a/src/map-file<br>
+++ b/src/map-file<br>
@@ -144,6 +144,7 @@ spice_session_new;<br>
spice_session_open_fd;<br>
spice_session_verify_get_type;<br>
spice_set_session_option;<br>
+spice_session_get_webdav_server;<br>
spice_smartcard_channel_get_type;<br>
spice_smartcard_manager_get;<br>
spice_smartcard_manager_get_readers;<br>
diff --git a/src/spice-glib-sym-file b/src/spice-glib-sym-file<br>
index 72e6ef0..effcd09 100644<br>
--- a/src/spice-glib-sym-file<br>
+++ b/src/spice-glib-sym-file<br>
@@ -123,6 +123,7 @@ spice_session_new<br>
spice_session_open_fd<br>
spice_session_verify_get_type<br>
spice_set_session_option<br>
+spice_session_get_webdav_server<br>
spice_smartcard_channel_get_type<br>
spice_smartcard_manager_get<br>
spice_smartcard_manager_get_readers<br>
diff --git a/src/spice-session-priv.h b/src/spice-session-priv.h<br>
index b4919a4..5b52f8d 100644<br>
--- a/src/spice-session-priv.h<br>
+++ b/src/spice-session-priv.h<br>
@@ -87,7 +87,6 @@ gboolean spice_session_get_smartcard_enabled(SpiceSession *session);<br>
gboolean spice_session_get_usbredir_enabled(SpiceSession *session);<br>
gboolean spice_session_get_gl_scanout_enabled(SpiceSession *session);<br>
<br>
-PhodavServer *spice_session_get_webdav_server(SpiceSession *session);<br>
guint spice_session_get_n_display_channels(SpiceSession *session);<br>
gboolean spice_session_set_migration_session(SpiceSession *session, SpiceSession *mig_session);<br>
SpiceAudio *spice_audio_get(SpiceSession *session, GMainContext *context);<br>
diff --git a/src/spice-session.c b/src/spice-session.c<br>
index 6915736..f0ac891 100644<br>
--- a/src/spice-session.c<br>
+++ b/src/spice-session.c<br>
@@ -2796,7 +2796,6 @@ gboolean spice_session_get_smartcard_enabled(SpiceSession *session)<br>
return session->priv->smartcard;<br>
}<br>
<br>
-G_GNUC_INTERNAL<br>
PhodavServer* spice_session_get_webdav_server(SpiceSession *session)<br>
{<br>
SpiceSessionPrivate *priv;<br>
diff --git a/src/spice-session.h b/src/spice-session.h<br>
index ed01c01..9436be8 100644<br>
--- a/src/spice-session.h<br>
+++ b/src/spice-session.h<br>
@@ -36,6 +36,8 @@ G_BEGIN_DECLS<br>
#define SPICE_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_SESSION))<br>
#define SPICE_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_SESSION, SpiceSessionClass))<br>
<br>
+typedef struct _PhodavServer PhodavServer;<br>
+<br>
/**<br>
* SpiceSessionVerify:<br>
* @SPICE_SESSION_VERIFY_PUBKEY: verify certificate public key matching<br>
@@ -113,6 +115,7 @@ gboolean spice_session_has_channel_type(SpiceSession *session, gint type);<br>
gboolean spice_session_get_read_only(SpiceSession *session);<br>
SpiceURI *spice_session_get_proxy_uri(SpiceSession *session);<br>
gboolean spice_session_is_for_migration(SpiceSession *session);<br>
+PhodavServer *spice_session_get_webdav_server(SpiceSession *session);<br>
<br>
G_END_DECLS<br>
<br>
_______________________________________________<br>
Spice-commits mailing list<br>
<a href="mailto:Spice-commits@lists.freedesktop.org" target="_blank">Spice-commits@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/spice-commits" rel="noreferrer" target="_blank">https://lists.freedesktop.org/mailman/listinfo/spice-commits</a><br>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature">Marc-André Lureau<br></div></div>