[Spice-devel] [PATCH vdagent v2 1/1] vdagent: add GTK+ clipboard handling
Jakub Janků
janku.jakub.jj at gmail.com
Mon Feb 26 19:01:28 UTC 2018
From: Jakub Janků <jjanku at redhat.com>
Add clipboard handling that uses GTK+ instead of Xlib.
Place the code into new files: clipboard.c, clipboard.h
Use the old Xlib implementation by default.
Add configure option --with-gtk-clipboard.
Guard the code by WITH_GTK_CLIPBOARD variable -
compile vdagent either with GTK+ impl or with the Xlib one.
---
Makefile.am | 6 +
configure.ac | 11 ++
src/vdagent/clipboard.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++++
src/vdagent/clipboard.h | 42 +++++
src/vdagent/vdagent.c | 53 +++++-
src/vdagent/x11-priv.h | 20 +++
src/vdagent/x11.c | 30 +++-
src/vdagent/x11.h | 6 +
8 files changed, 590 insertions(+), 8 deletions(-)
create mode 100644 src/vdagent/clipboard.c
create mode 100644 src/vdagent/clipboard.h
diff --git a/Makefile.am b/Makefile.am
index c4bd3dd..1d06928 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -85,6 +85,12 @@ src_spice_vdagentd_SOURCES += src/vdagentd/dummy-session-info.c
endif
endif
+if WITH_GTK_CLIPBOARD
+src_spice_vdagent_SOURCES += \
+ src/vdagent/clipboard.c \
+ src/vdagent/clipboard.h
+endif
+
xdgautostartdir = $(sysconfdir)/xdg/autostart
xdgautostart_DATA = $(top_srcdir)/data/spice-vdagent.desktop
diff --git a/configure.ac b/configure.ac
index 1eb17a9..9a85870 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,6 +75,15 @@ if test "x$init_systemd" = "xyes"; then
fi
fi
+AC_ARG_WITH([gtk-clipboard],
+ [AS_HELP_STRING([--with-gtk-clipboard], [Handle clipboard using GTK+ instead of Xlib])],
+ [],
+ [with_gtk_clipboard="no"])
+if test "x$with_gtk_clipboard" = "xyes"; then
+ AC_DEFINE([WITH_GTK_CLIPBOARD], [1], [If defined, vdagent will handle clipboard using GTK+])
+fi
+AM_CONDITIONAL([WITH_GTK_CLIPBOARD], [test "x$with_gtk_clipboard" = "xyes"])
+
AC_ARG_ENABLE([pciaccess],
[AS_HELP_STRING([--enable-pciaccess], [Enable libpciaccess use for auto generation of Xinerama xorg.conf (default: yes)])],
[enable_pciaccess="$enableval"],
@@ -210,6 +219,8 @@ AC_MSG_NOTICE([
install systemd service: ${init_systemd}
udevdir: ${udevdir}
+ GTK+ clipboard: ${with_gtk_clipboard}
+
Now type 'make' to build $PACKAGE
])
diff --git a/src/vdagent/clipboard.c b/src/vdagent/clipboard.c
new file mode 100644
index 0000000..5ca1d57
--- /dev/null
+++ b/src/vdagent/clipboard.c
@@ -0,0 +1,430 @@
+/* clipboard.c - vdagent clipboard handling code
+
+ Copyright 2017 Red Hat, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <syslog.h>
+
+#include "vdagentd-proto.h"
+#include "spice/vd_agent.h"
+#include "udscs.h"
+#include "clipboard.h"
+
+/* 2 selections supported - _SELECTION_CLIPBOARD = 0, _SELECTION_PRIMARY = 1 */
+#define SELECTION_COUNT (VD_AGENT_CLIPBOARD_SELECTION_PRIMARY + 1)
+#define TYPE_COUNT (VD_AGENT_CLIPBOARD_IMAGE_JPG + 1)
+
+#define sel_id_from_clip(clipboard) \
+ GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(clipboard), "vdagent-selection-id"))
+
+enum {
+ OWNER_NONE,
+ OWNER_GUEST,
+ OWNER_CLIENT
+};
+
+typedef struct {
+ GMainLoop *loop;
+ GtkSelectionData *sel_data;
+} AppRequest;
+
+typedef struct {
+ GtkClipboard *clipboard;
+ guint owner;
+
+ GList *requests_from_apps; /* VDAgent --> Client */
+ GList *requests_from_client; /* Client --> VDAgent */
+ gpointer *last_targets_req;
+
+ GdkAtom targets[TYPE_COUNT];
+} Selection;
+
+struct VDAgentClipboards {
+ struct udscs_connection *conn;
+ Selection selections[SELECTION_COUNT];
+};
+
+static const struct {
+ guint type;
+ const gchar *atom_name;
+} atom2agent[] = {
+ {VD_AGENT_CLIPBOARD_UTF8_TEXT, "UTF8_STRING"},
+ {VD_AGENT_CLIPBOARD_UTF8_TEXT, "text/plain;charset=utf-8"},
+ {VD_AGENT_CLIPBOARD_UTF8_TEXT, "STRING"},
+ {VD_AGENT_CLIPBOARD_UTF8_TEXT, "TEXT"},
+ {VD_AGENT_CLIPBOARD_UTF8_TEXT, "text/plain"},
+ {VD_AGENT_CLIPBOARD_IMAGE_PNG, "image/png"},
+ {VD_AGENT_CLIPBOARD_IMAGE_BMP, "image/bmp"},
+ {VD_AGENT_CLIPBOARD_IMAGE_BMP, "image/x-bmp"},
+ {VD_AGENT_CLIPBOARD_IMAGE_BMP, "image/x-MS-bmp"},
+ {VD_AGENT_CLIPBOARD_IMAGE_BMP, "image/x-win-bitmap"},
+ {VD_AGENT_CLIPBOARD_IMAGE_TIFF,"image/tiff"},
+ {VD_AGENT_CLIPBOARD_IMAGE_JPG, "image/jpeg"},
+};
+
+static guint get_type_from_atom(GdkAtom atom)
+{
+ gchar *name = gdk_atom_name(atom);
+ for (int i = 0; i < G_N_ELEMENTS(atom2agent); i++) {
+ if (!g_ascii_strcasecmp(name, atom2agent[i].atom_name)) {
+ g_free(name);
+ return atom2agent[i].type;
+ }
+ }
+ g_free(name);
+ return VD_AGENT_CLIPBOARD_NONE;
+}
+
+/* gtk_clipboard_request_(, callback, user_data) cannot be cancelled.
+ Instead, gpointer *ref = new_request_ref() is passed to the callback.
+ If request_cancel(ref) is called,
+ callback using return_if_request_cancelled() returns.
+ This mechanism enables cancellation of the request
+ as well as passing VDAgentClipboards reference to the desired callback.
+ */
+static gpointer *new_request_ref(gpointer data)
+{
+ gpointer *ref = g_new(gpointer, 1);
+ *ref = data;
+ return ref;
+}
+
+static gpointer free_request_ref(gpointer *ref)
+{
+ gpointer data = *ref;
+ g_free(ref);
+ return data;
+}
+
+#define request_cancel(ref) *((gpointer *)ref) = NULL
+
+#define return_if_request_cancelled(ref) G_STMT_START { \
+ g_return_if_fail(ref != NULL); \
+ if (*((gpointer *)ref) == NULL) \
+ return; \
+} G_STMT_END
+
+static void clipboard_new_owner(VDAgentClipboards *c, guint sel_id, guint new_owner)
+{
+ Selection *sel = &c->selections[sel_id];
+ GList *l;
+ /* let the other apps know no data is coming */
+ for (l = sel->requests_from_apps; l != NULL; l= l->next) {
+ AppRequest *req = l->data;
+ g_main_loop_quit(req->loop);
+ }
+ g_clear_pointer(&sel->requests_from_apps, g_list_free);
+
+ /* respond to pending client's data requests */
+ for (l = sel->requests_from_client; l != NULL; l = l->next) {
+ request_cancel(l->data);
+ if (c->conn)
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA,
+ sel_id, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
+ }
+ g_clear_pointer(&sel->requests_from_client, g_list_free);
+
+ sel->owner = new_owner;
+}
+
+static void clipboard_targets_received_cb(GtkClipboard *clipboard,
+ GdkAtom *atoms,
+ gint n_atoms,
+ gpointer user_data)
+{
+ return_if_request_cancelled(user_data);
+ VDAgentClipboards *c = free_request_ref(user_data);
+ Selection *sel;
+ guint32 types[G_N_ELEMENTS(atom2agent)];
+ guint sel_id, type, n_types, a;
+
+ sel_id = sel_id_from_clip(clipboard);
+ sel = &c->selections[sel_id];
+ sel->last_targets_req = NULL;
+
+ if (atoms == NULL)
+ return;
+
+ for (type = 0; type < TYPE_COUNT; type++)
+ sel->targets[type] = GDK_NONE;
+
+ n_types = 0;
+ for (a = 0; a < n_atoms; a++) {
+ type = get_type_from_atom(atoms[a]);
+ if (type == VD_AGENT_CLIPBOARD_NONE || sel->targets[type] != GDK_NONE)
+ continue;
+
+ sel->targets[type] = atoms[a];
+ types[n_types] = type;
+ n_types++;
+ }
+
+ if (n_types == 0) {
+ syslog(LOG_WARNING, "%s: sel_id=%u: no target supported", __func__, sel_id);
+ return;
+ }
+
+ clipboard_new_owner(c, sel_id, OWNER_GUEST);
+
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_GRAB, sel_id, 0,
+ (guint8 *)types, n_types * sizeof(guint32));
+}
+
+static void clipboard_owner_change_cb(GtkClipboard *clipboard,
+ GdkEventOwnerChange *event,
+ gpointer user_data)
+{
+ VDAgentClipboards *c = user_data;
+ guint sel_id = sel_id_from_clip(clipboard);
+ Selection *sel = &c->selections[sel_id];
+
+ /* if the event was caused by gtk_clipboard_set_with_data(), ignore it */
+ if (sel->owner == OWNER_CLIENT)
+ return;
+
+ if (sel->owner == OWNER_GUEST) {
+ clipboard_new_owner(c, sel_id, OWNER_NONE);
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_RELEASE, sel_id, 0, NULL, 0);
+ }
+
+ if (event->reason != GDK_OWNER_CHANGE_NEW_OWNER)
+ return;
+
+ /* if there's a pending request for clipboard targets, cancel it */
+ if (sel->last_targets_req)
+ request_cancel(sel->last_targets_req);
+
+ sel->last_targets_req = new_request_ref(c);
+ gtk_clipboard_request_targets(clipboard, clipboard_targets_received_cb,
+ sel->last_targets_req);
+}
+
+static void clipboard_contents_received_cb(GtkClipboard *clipboard,
+ GtkSelectionData *sel_data,
+ gpointer user_data)
+{
+ return_if_request_cancelled(user_data);
+ VDAgentClipboards *c = free_request_ref(user_data);
+ guint sel_id, type, target;
+
+ sel_id = sel_id_from_clip(clipboard);
+ c->selections[sel_id].requests_from_client =
+ g_list_remove(c->selections[sel_id].requests_from_client, user_data);
+
+ type = get_type_from_atom(gtk_selection_data_get_data_type(sel_data));
+ target = get_type_from_atom(gtk_selection_data_get_target(sel_data));
+
+ if (type == target) {
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA, sel_id, type,
+ gtk_selection_data_get_data(sel_data),
+ gtk_selection_data_get_length(sel_data));
+ } else {
+ syslog(LOG_WARNING, "%s: sel_id=%u: expected type %u, recieved %u, "
+ "skipping", __func__, sel_id, target, type);
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA, sel_id,
+ VD_AGENT_CLIPBOARD_NONE, NULL, 0);
+ }
+}
+
+static void clipboard_get_cb(GtkClipboard *clipboard,
+ GtkSelectionData *sel_data,
+ guint info,
+ gpointer user_data)
+{
+ AppRequest req;
+ VDAgentClipboards *c = user_data;
+ guint sel_id, type;
+
+ sel_id = sel_id_from_clip(clipboard);
+ g_return_if_fail(c->selections[sel_id].owner == OWNER_CLIENT);
+
+ type = get_type_from_atom(gtk_selection_data_get_target(sel_data));
+ g_return_if_fail(type != VD_AGENT_CLIPBOARD_NONE);
+
+ req.sel_data = sel_data;
+ req.loop = g_main_loop_new(NULL, FALSE);
+ c->selections[sel_id].requests_from_apps =
+ g_list_prepend(c->selections[sel_id].requests_from_apps, &req);
+
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_REQUEST, sel_id, type, NULL, 0);
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gdk_threads_leave();
+ g_main_loop_run(req.loop);
+ gdk_threads_enter();
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+ g_main_loop_unref(req.loop);
+}
+
+static void clipboard_clear_cb(GtkClipboard *clipboard, gpointer user_data)
+{
+ VDAgentClipboards *c = user_data;
+ clipboard_new_owner(c, sel_id_from_clip(clipboard), OWNER_NONE);
+}
+
+void vdagent_clipboard_grab(VDAgentClipboards *c, guint sel_id,
+ guint32 *types, guint n_types)
+{
+ GtkTargetEntry targets[G_N_ELEMENTS(atom2agent)];
+ guint n_targets, i, t;
+
+ g_return_if_fail(sel_id < SELECTION_COUNT);
+
+ n_targets = 0;
+ for (i = 0; i < G_N_ELEMENTS(atom2agent); i++)
+ for (t = 0; t < n_types; t++)
+ if (atom2agent[i].type == types[t]) {
+ targets[n_targets].target = (gchar *)atom2agent[i].atom_name;
+ n_targets++;
+ break;
+ }
+
+ if (n_targets == 0) {
+ syslog(LOG_WARNING, "%s: sel_id=%u: no type supported", __func__, sel_id);
+ return;
+ }
+
+ if (gtk_clipboard_set_with_data(c->selections[sel_id].clipboard,
+ targets, n_targets,
+ clipboard_get_cb, clipboard_clear_cb, c))
+ clipboard_new_owner(c, sel_id, OWNER_CLIENT);
+ else {
+ syslog(LOG_ERR, "%s: sel_id=%u: clipboard grab failed", __func__, sel_id);
+ clipboard_new_owner(c, sel_id, OWNER_NONE);
+ }
+}
+
+void vdagent_clipboard_data(VDAgentClipboards *c, guint sel_id,
+ guint type, const guchar *data, guint size)
+{
+ g_return_if_fail(sel_id < SELECTION_COUNT);
+ Selection *sel = &c->selections[sel_id];
+ AppRequest *req;
+ GList *l;
+
+ for (l = sel->requests_from_apps; l != NULL; l = l->next) {
+ req = l->data;
+ if (get_type_from_atom(gtk_selection_data_get_target(req->sel_data)) == type)
+ break;
+ }
+ if (l == NULL) {
+ syslog(LOG_WARNING, "%s: sel_id=%u: no corresponding request found for "
+ "type=%u, skipping", __func__, sel_id, type);
+ return;
+ }
+ sel->requests_from_apps = g_list_delete_link(sel->requests_from_apps, l);
+
+ gtk_selection_data_set(req->sel_data,
+ gtk_selection_data_get_target(req->sel_data),
+ 8, data, size);
+
+ g_main_loop_quit(req->loop);
+}
+
+void vdagent_clipboard_release(VDAgentClipboards *c, guint sel_id)
+{
+ g_return_if_fail(sel_id < SELECTION_COUNT);
+ if (c->selections[sel_id].owner != OWNER_CLIENT)
+ return;
+
+ clipboard_new_owner(c, sel_id, OWNER_NONE);
+ gtk_clipboard_clear(c->selections[sel_id].clipboard);
+}
+
+void vdagent_clipboards_release_all(VDAgentClipboards *c)
+{
+ guint sel_id, owner;
+
+ for (sel_id = 0; sel_id < SELECTION_COUNT; sel_id++) {
+ owner = c->selections[sel_id].owner;
+ clipboard_new_owner(c, sel_id, OWNER_NONE);
+ if (owner == OWNER_CLIENT)
+ gtk_clipboard_clear(c->selections[sel_id].clipboard);
+ else if (owner == OWNER_GUEST && c->conn)
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_RELEASE, sel_id, 0, NULL, 0);
+ }
+}
+
+void vdagent_clipboard_request(VDAgentClipboards *c, guint sel_id, guint type)
+{
+ Selection *sel;
+
+ if (sel_id >= SELECTION_COUNT)
+ goto err;
+ sel = &c->selections[sel_id];
+ if (sel->owner != OWNER_GUEST) {
+ syslog(LOG_WARNING, "%s: sel_id=%d: received request "
+ "while not owning clipboard", __func__, sel_id);
+ goto err;
+ }
+ if (type >= TYPE_COUNT || sel->targets[type] == GDK_NONE) {
+ syslog(LOG_WARNING, "%s: sel_id=%d: unadvertised data type requested",
+ __func__, sel_id);
+ goto err;
+ }
+
+ gpointer *ref = new_request_ref(c);
+ sel->requests_from_client = g_list_prepend(sel->requests_from_client, ref);
+ gtk_clipboard_request_contents(sel->clipboard, sel->targets[type],
+ clipboard_contents_received_cb, ref);
+ return;
+err:
+ udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA, sel_id,
+ VD_AGENT_CLIPBOARD_NONE, NULL, 0);
+}
+
+VDAgentClipboards *vdagent_clipboards_init(struct udscs_connection *conn)
+{
+ const GdkAtom sel_atom[SELECTION_COUNT] = {
+ GDK_SELECTION_CLIPBOARD, /* VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD */
+ GDK_SELECTION_PRIMARY, /* VD_AGENT_CLIPBOARD_SELECTION_PRIMARY */
+ };
+
+ VDAgentClipboards *c;
+ c = g_new0(VDAgentClipboards, 1);
+ c->conn = conn;
+
+ for (guint sel_id = 0; sel_id < SELECTION_COUNT; sel_id++) {
+ GtkClipboard *clipboard = gtk_clipboard_get(sel_atom[sel_id]);
+ c->selections[sel_id].clipboard = clipboard;
+ /* enables the use of sel_id_from_clipboard(clipboard) macro */
+ g_object_set_data(G_OBJECT(clipboard), "vdagent-selection-id",
+ GUINT_TO_POINTER(sel_id));
+ g_signal_connect(G_OBJECT(clipboard), "owner-change",
+ G_CALLBACK(clipboard_owner_change_cb), c);
+ }
+
+ return c;
+}
+
+void vdagent_clipboards_finalize(VDAgentClipboards *c,
+ struct udscs_connection *conn)
+{
+ for (guint sel_id = 0; sel_id < SELECTION_COUNT; sel_id++)
+ g_signal_handlers_disconnect_by_func(c->selections[sel_id].clipboard,
+ G_CALLBACK(clipboard_owner_change_cb), c);
+
+ c->conn = conn;
+ vdagent_clipboards_release_all(c);
+
+ g_free(c);
+}
diff --git a/src/vdagent/clipboard.h b/src/vdagent/clipboard.h
new file mode 100644
index 0000000..54b8aef
--- /dev/null
+++ b/src/vdagent/clipboard.h
@@ -0,0 +1,42 @@
+/* clipboard.h - vdagent clipboard handling header
+
+ Copyright 2017 Red Hat, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VDAGENT_CLIPBOARD_H
+#define __VDAGENT_CLIPBOARD_H
+
+#include "udscs.h"
+
+typedef struct VDAgentClipboards VDAgentClipboards;
+
+VDAgentClipboards *vdagent_clipboards_init(struct udscs_connection *conn);
+void vdagent_clipboards_finalize(VDAgentClipboards *c,
+ struct udscs_connection *conn);
+
+void vdagent_clipboard_request(VDAgentClipboards *c, guint sel_id, guint type);
+
+void vdagent_clipboard_release(VDAgentClipboards *c, guint sel_id);
+
+void vdagent_clipboards_release_all(VDAgentClipboards *c);
+
+void vdagent_clipboard_data(VDAgentClipboards *c, guint sel_id,
+ guint type, const guchar *data, guint size);
+
+void vdagent_clipboard_grab(VDAgentClipboards *c, guint sel_id,
+ guint32 *types, guint n_types);
+
+#endif
diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c
index d86ee25..7dd7da6 100644
--- a/src/vdagent/vdagent.c
+++ b/src/vdagent/vdagent.c
@@ -44,8 +44,14 @@
#include "audio.h"
#include "x11.h"
#include "file-xfers.h"
+#ifdef WITH_GTK_CLIPBOARD
+# include "clipboard.h"
+#endif
typedef struct VDAgent {
+#ifdef WITH_GTK_CLIPBOARD
+ VDAgentClipboards *clipboards;
+#endif
struct vdagent_x11 *x11;
struct vdagent_file_xfers *xfers;
struct udscs_connection *conn;
@@ -154,6 +160,19 @@ static gboolean vdagent_finalize_file_xfer(VDAgent *agent)
return TRUE;
}
+static void vdagent_quit_loop(VDAgent *agent)
+{
+#ifdef WITH_GTK_CLIPBOARD
+ /* other GMainLoop(s) might be running, quit them before agent->loop */
+ if (agent->clipboards) {
+ vdagent_clipboards_finalize(agent->clipboards, agent->conn);
+ agent->clipboards = NULL;
+ }
+#endif
+ if (agent->loop)
+ g_main_loop_quit(agent->loop);
+}
+
static void daemon_read_complete(struct udscs_connection **connp,
struct udscs_message_header *header, uint8_t *data)
{
@@ -163,6 +182,22 @@ static void daemon_read_complete(struct udscs_connection **connp,
case VDAGENTD_MONITORS_CONFIG:
vdagent_x11_set_monitor_config(agent->x11, (VDAgentMonitorsConfig *)data, 0);
break;
+#ifdef WITH_GTK_CLIPBOARD
+ case VDAGENTD_CLIPBOARD_REQUEST:
+ vdagent_clipboard_request(agent->clipboards, header->arg1, header->arg2);
+ break;
+ case VDAGENTD_CLIPBOARD_GRAB:
+ vdagent_clipboard_grab(agent->clipboards, header->arg1,
+ (guint32 *)data, header->size / sizeof(guint32));
+ break;
+ case VDAGENTD_CLIPBOARD_DATA:
+ vdagent_clipboard_data(agent->clipboards, header->arg1, header->arg2,
+ data, header->size);
+ break;
+ case VDAGENTD_CLIPBOARD_RELEASE:
+ vdagent_clipboard_release(agent->clipboards, header->arg1);
+ break;
+#else
case VDAGENTD_CLIPBOARD_REQUEST:
vdagent_x11_clipboard_request(agent->x11, header->arg1, header->arg2);
break;
@@ -177,11 +212,12 @@ static void daemon_read_complete(struct udscs_connection **connp,
case VDAGENTD_CLIPBOARD_RELEASE:
vdagent_x11_clipboard_release(agent->x11, header->arg1);
break;
+#endif
case VDAGENTD_VERSION:
if (strcmp((char *)data, VERSION) != 0) {
syslog(LOG_INFO, "vdagentd version mismatch: got %s expected %s",
data, VERSION);
- g_main_loop_quit(agent->loop);
+ vdagent_quit_loop(agent);
version_mismatch = 1;
}
break;
@@ -228,7 +264,11 @@ static void daemon_read_complete(struct udscs_connection **connp,
}
break;
case VDAGENTD_CLIENT_DISCONNECTED:
+#ifdef WITH_GTK_CLIPBOARD
+ vdagent_clipboards_release_all(agent->clipboards);
+#else
vdagent_x11_client_disconnected(agent->x11);
+#endif
if (vdagent_finalize_file_xfer(agent)) {
vdagent_init_file_xfer(agent);
}
@@ -243,8 +283,7 @@ static void daemon_disconnect_cb(struct udscs_connection *conn)
{
VDAgent *agent = udscs_get_user_data(conn);
agent->conn = NULL;
- if (agent->loop)
- g_main_loop_quit(agent->loop);
+ vdagent_quit_loop(agent);
}
/* When we daemonize, it is useful to have the main process
@@ -316,7 +355,7 @@ gboolean vdagent_signal_handler(gpointer user_data)
{
VDAgent *agent = user_data;
quit = TRUE;
- g_main_loop_quit(agent->loop);
+ vdagent_quit_loop(agent);
return G_SOURCE_REMOVE;
}
@@ -375,6 +414,10 @@ static gboolean vdagent_init_async_cb(gpointer user_data)
if (!vdagent_init_file_xfer(agent))
syslog(LOG_WARNING, "File transfer is disabled");
+#ifdef WITH_GTK_CLIPBOARD
+ agent->clipboards = vdagent_clipboards_init(agent->conn);
+#endif
+
if (parent_socket != -1) {
if (write(parent_socket, "OK", 2) != 2)
syslog(LOG_WARNING, "Parent already gone.");
@@ -385,7 +428,7 @@ static gboolean vdagent_init_async_cb(gpointer user_data)
return G_SOURCE_REMOVE;
err_init:
- g_main_loop_quit(agent->loop);
+ vdagent_quit_loop(agent);
quit = TRUE;
return G_SOURCE_REMOVE;
}
diff --git a/src/vdagent/x11-priv.h b/src/vdagent/x11-priv.h
index 3776098..48463ec 100644
--- a/src/vdagent/x11-priv.h
+++ b/src/vdagent/x11-priv.h
@@ -1,6 +1,10 @@
#ifndef VDAGENT_X11_PRIV
#define VDAGENT_X11_PRIV
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
#include <stdint.h>
#include <stdio.h>
@@ -8,6 +12,7 @@
#include <X11/extensions/Xrandr.h>
+#ifndef WITH_GTK_CLIPBOARD
/* Macros to print a message to the logfile prefixed by the selection */
#define SELPRINTF(format, ...) \
syslog(LOG_ERR, "%s: " format, \
@@ -20,11 +25,13 @@
vdagent_x11_sel_to_str(selection), ##__VA_ARGS__); \
} \
} while (0)
+#endif
#define MAX_SCREENS 16
/* Same as qxl_dev.h client_monitors_config.heads count */
#define MONITOR_SIZE_COUNT 64
+#ifndef WITH_GTK_CLIPBOARD
enum { owner_none, owner_guest, owner_client };
/* X11 terminology is confusing a selection request is a request from an
@@ -57,12 +64,14 @@ struct clipboard_format_info {
Atom atoms[16];
int atom_count;
};
+#endif
struct monitor_size {
int width;
int height;
};
+#ifndef WITH_GTK_CLIPBOARD
static const struct clipboard_format_tmpl clipboard_format_templates[] = {
{ VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING", "text/plain;charset=UTF-8",
"text/plain;charset=utf-8", "STRING", NULL }, },
@@ -74,27 +83,37 @@ static const struct clipboard_format_tmpl clipboard_format_templates[] = {
};
#define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
+#endif
struct vdagent_x11 {
+#ifndef WITH_GTK_CLIPBOARD
struct clipboard_format_info clipboard_formats[clipboard_format_count];
+#endif
Display *display;
+#ifndef WITH_GTK_CLIPBOARD
Atom clipboard_atom;
Atom clipboard_primary_atom;
Atom targets_atom;
Atom incr_atom;
Atom multiple_atom;
Atom timestamp_atom;
+#endif
Window root_window[MAX_SCREENS];
+#ifndef WITH_GTK_CLIPBOARD
Window selection_window;
+#endif
struct udscs_connection *vdagentd;
int debug;
int fd;
int screen_count;
int width[MAX_SCREENS];
int height[MAX_SCREENS];
+#ifndef WITH_GTK_CLIPBOARD
int has_xfixes;
int xfixes_event_base;
+#endif
int xrandr_event_base;
+#ifndef WITH_GTK_CLIPBOARD
int max_prop_size;
int expected_targets_notifies[256];
int clipboard_owner[256];
@@ -113,6 +132,7 @@ struct vdagent_x11 {
uint32_t selection_req_data_pos;
uint32_t selection_req_data_size;
Atom selection_req_atom;
+#endif
/* resolution change state */
struct {
XRRScreenResources *res;
diff --git a/src/vdagent/x11.c b/src/vdagent/x11.c
index 9700847..f1a30e2 100644
--- a/src/vdagent/x11.c
+++ b/src/vdagent/x11.c
@@ -31,6 +31,10 @@
Calling XPending when-ever we return to the mainloop also ensures any
pending writes are flushed. */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
#include <glib.h>
#include <gdk/gdk.h>
#ifdef GDK_WINDOWING_X11
@@ -53,6 +57,7 @@
int (*vdagent_x11_prev_error_handler)(Display *, XErrorEvent *);
int vdagent_x11_caught_error;
+#ifndef WITH_GTK_CLIPBOARD
static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
XEvent *event, int incr);
static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11);
@@ -77,6 +82,7 @@ static const char *vdagent_x11_sel_to_str(uint8_t selection) {
return "unknown";
}
}
+#endif
static int vdagent_x11_debug_error_handler(
Display *display, XErrorEvent *error)
@@ -84,6 +90,7 @@ static int vdagent_x11_debug_error_handler(
abort();
}
+#ifndef WITH_GTK_CLIPBOARD
/* With the clipboard we're sometimes dealing with Properties on another apps
Window. which can go away at any time. */
static int vdagent_x11_ignore_bad_window_handler(
@@ -94,6 +101,7 @@ static int vdagent_x11_ignore_bad_window_handler(
return vdagent_x11_prev_error_handler(display, error);
}
+#endif
void vdagent_x11_set_error_handler(struct vdagent_x11 *x11,
int (*handler)(Display *, XErrorEvent *))
@@ -131,7 +139,11 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
{
struct vdagent_x11 *x11;
XWindowAttributes attrib;
+#ifdef WITH_GTK_CLIPBOARD
+ int i;
+#else
int i, j, major, minor;
+#endif
const gchar *net_wm_name;
x11 = calloc(1, sizeof(*x11));
@@ -167,6 +179,7 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
for (i = 0; i < x11->screen_count; i++)
x11->root_window[i] = RootWindow(x11->display, i);
x11->fd = ConnectionNumber(x11->display);
+#ifndef WITH_GTK_CLIPBOARD
x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False);
x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False);
x11->targets_atom = XInternAtom(x11->display, "TARGETS", False);
@@ -189,9 +202,11 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
0, 0, 1, 1, 0, 0, 0);
if (x11->debug)
syslog(LOG_DEBUG, "Selection window: %u", (int)x11->selection_window);
+#endif
vdagent_x11_randr_init(x11);
+#ifndef WITH_GTK_CLIPBOARD
if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
x11->has_xfixes = 1;
@@ -217,6 +232,7 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
/* Be a good X11 citizen and maximize the amount of data we send at once */
if (x11->max_prop_size > 262144)
x11->max_prop_size = 262144;
+#endif
for (i = 0; i < x11->screen_count; i++) {
/* Catch resolution changes */
@@ -249,17 +265,17 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
void vdagent_x11_destroy(struct vdagent_x11 *x11, int vdagentd_disconnected)
{
- uint8_t sel;
-
if (!x11)
return;
if (vdagentd_disconnected)
x11->vdagentd = NULL;
- for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
+#ifndef WITH_GTK_CLIPBOARD
+ for (uint8_t sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
vdagent_x11_set_clipboard_owner(x11, sel, owner_none);
}
+#endif
XCloseDisplay(x11->display);
free(x11->randr.failed_conf);
@@ -271,6 +287,7 @@ int vdagent_x11_get_fd(struct vdagent_x11 *x11)
return x11->fd;
}
+#ifndef WITH_GTK_CLIPBOARD
static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
{
struct vdagent_x11_selection_request *selection_request;
@@ -406,10 +423,12 @@ static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
return 0;
}
+#endif
static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
{
int i, handled = 0;
+#ifndef WITH_GTK_CLIPBOARD
uint8_t selection;
if (event.type == x11->xfixes_event_base) {
@@ -455,6 +474,7 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
x11->expected_targets_notifies[selection]++;
return;
}
+#endif
if (vdagent_x11_randr_handle_event(x11, event))
return;
@@ -475,6 +495,7 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
/* These are uninteresting */
handled = 1;
break;
+#ifndef WITH_GTK_CLIPBOARD
case SelectionNotify:
if (event.xselection.target == x11->targets_atom)
vdagent_x11_handle_targets_notify(x11, &event);
@@ -534,6 +555,7 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
req->next = new_req;
break;
}
+#endif
}
if (!handled && x11->debug)
syslog(LOG_DEBUG, "unhandled x11 event, type %d, window %d",
@@ -550,6 +572,7 @@ void vdagent_x11_do_read(struct vdagent_x11 *x11)
}
}
+#ifndef WITH_GTK_CLIPBOARD
static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
{
if (a == None)
@@ -1284,6 +1307,7 @@ void vdagent_x11_client_disconnected(struct vdagent_x11 *x11)
vdagent_x11_clipboard_release(x11, sel);
}
}
+#endif
/* Function used to determine the default location to save file-xfers,
xdg desktop dir or xdg download dir. We err on the safe side and use a
diff --git a/src/vdagent/x11.h b/src/vdagent/x11.h
index a8ceb08..c001fac 100644
--- a/src/vdagent/x11.h
+++ b/src/vdagent/x11.h
@@ -22,6 +22,10 @@
#ifndef __VDAGENT_X11_H
#define __VDAGENT_X11_H
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
#include <stdio.h>
#include <spice/vd_agent.h>
#include "udscs.h"
@@ -38,6 +42,7 @@ void vdagent_x11_do_read(struct vdagent_x11 *x11);
void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
VDAgentMonitorsConfig *mon_config, int fallback);
+#ifndef WITH_GTK_CLIPBOARD
void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection,
uint32_t *types, uint32_t type_count);
void vdagent_x11_clipboard_request(struct vdagent_x11 *x11,
@@ -47,6 +52,7 @@ void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection,
void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection);
void vdagent_x11_client_disconnected(struct vdagent_x11 *x11);
+#endif
int vdagent_x11_has_icons_on_desktop();
--
2.14.3
More information about the Spice-devel
mailing list