[Spice-devel] [PATCH vdagent v3 2/2] vdagent: add GTK+ clipboard handling

Jakub Janků janku.jakub.jj at gmail.com
Thu Mar 1 10:14:00 UTC 2018


From: Jakub Janků <jjanku at redhat.com>

---
 src/vdagent/clipboard.c | 416 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/vdagent/clipboard.h |   6 +-
 src/vdagent/vdagent.c   |  23 ++-
 src/vdagent/x11-priv.h  |  44 ++---
 src/vdagent/x11.c       |  25 ++-
 src/vdagent/x11.h       |   6 +
 6 files changed, 489 insertions(+), 31 deletions(-)

diff --git a/src/vdagent/clipboard.c b/src/vdagent/clipboard.c
index ab1e875..667c168 100644
--- a/src/vdagent/clipboard.c
+++ b/src/vdagent/clipboard.c
@@ -20,49 +20,461 @@
 # include <config.h>
 #endif
 
+#ifdef WITH_GTK
+# include <gtk/gtk.h>
+# include <syslog.h>
+
+# include "vdagentd-proto.h"
+# include "spice/vd_agent.h"
+#endif
+
 #include "clipboard.h"
 
+#ifdef WITH_GTK
+/* 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;
+#endif
+
 struct VDAgentClipboards {
+#ifdef WITH_GTK
+    struct udscs_connection *conn;
+    Selection                selections[SELECTION_COUNT];
+#else
     struct vdagent_x11 *x11;
+#endif
 };
 
+#ifdef WITH_GTK
+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);
+    int i;
+    for (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 = request_ref_new() is passed to the callback.
+   Callback can check using request_ref_is_cancelled(ref)
+   whether request_ref_cancel(ref) was called.
+   This mechanism enables cancellation of the request
+   as well as passing VDAgentClipboards reference to the desired callback.
+ */
+static gpointer *request_ref_new(gpointer data)
+{
+    gpointer *ref = g_new(gpointer, 1);
+    *ref = data;
+    return ref;
+}
+
+static gpointer request_ref_free(gpointer *ref)
+{
+    gpointer data = *ref;
+    g_free(ref);
+    return data;
+}
+
+static void request_ref_cancel(gpointer *ref)
+{
+    g_return_if_fail(ref != NULL);
+    *ref = NULL;
+}
+
+static gboolean request_ref_is_cancelled(gpointer *ref)
+{
+    g_return_val_if_fail(ref != NULL, TRUE);
+    return *ref == NULL;
+}
+
+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_ref_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)
+{
+    if (request_ref_is_cancelled(user_data))
+        return;
+
+    VDAgentClipboards *c = request_ref_free(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_ref_cancel(sel->last_targets_req);
+
+    sel->last_targets_req = request_ref_new(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)
+{
+    if (request_ref_is_cancelled(user_data))
+        return;
+
+    VDAgentClipboards *c = request_ref_free(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);
+}
+#endif
+
 void vdagent_clipboard_grab(VDAgentClipboards *c, guint sel_id,
                             guint32 *types, guint n_types)
 {
+#ifndef WITH_GTK
     vdagent_x11_clipboard_grab(c->x11, sel_id, types, n_types);
+#else
+    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);
+    }
+#endif
 }
 
 void vdagent_clipboard_data(VDAgentClipboards *c, guint sel_id,
                             guint type, guchar *data, guint size)
 {
+#ifndef WITH_GTK
     vdagent_x11_clipboard_data(c->x11, sel_id, type, data, size);
+#else
+    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);
+#endif
 }
 
 void vdagent_clipboard_release(VDAgentClipboards *c, guint sel_id)
 {
+#ifndef WITH_GTK
     vdagent_x11_clipboard_release(c->x11, sel_id);
+#else
+    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);
+#endif
 }
 
 void vdagent_clipboards_release_all(VDAgentClipboards *c)
 {
+#ifndef WITH_GTK
     vdagent_x11_client_disconnected(c->x11);
+#else
+    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);
+    }
+#endif
 }
 
 void vdagent_clipboard_request(VDAgentClipboards *c, guint sel_id, guint type)
 {
+#ifndef WITH_GTK
     vdagent_x11_clipboard_request(c->x11, sel_id, type);
+#else
+    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 = request_ref_new(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);
+#endif
 }
 
-VDAgentClipboards *vdagent_clipboards_init(struct vdagent_x11 *x11)
+VDAgentClipboards *vdagent_clipboards_init(struct vdagent_x11      *x11,
+                                           struct udscs_connection *conn)
 {
+#ifdef WITH_GTK
+    guint sel_id;
+    const GdkAtom sel_atom[SELECTION_COUNT] = {
+        GDK_SELECTION_CLIPBOARD, /* VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD */
+        GDK_SELECTION_PRIMARY,   /* VD_AGENT_CLIPBOARD_SELECTION_PRIMARY */
+    };
+#endif
+
     VDAgentClipboards *c;
     c = g_new0(VDAgentClipboards, 1);
+#ifndef WITH_GTK
     c->x11 = x11;
+#else
+    c->conn = conn;
+
+    for (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);
+    }
+#endif
 
     return c;
 }
 
-void vdagent_clipboards_finalize(VDAgentClipboards *c)
+void vdagent_clipboards_finalize(VDAgentClipboards *c, gboolean conn_alive)
 {
+#ifdef WITH_GTK
+    guint sel_id;
+    for (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);
+
+    if (conn_alive == FALSE)
+        c->conn = NULL;
+    vdagent_clipboards_release_all(c);
+#endif
+
     g_free(c);
 }
diff --git a/src/vdagent/clipboard.h b/src/vdagent/clipboard.h
index aac3143..f819b49 100644
--- a/src/vdagent/clipboard.h
+++ b/src/vdagent/clipboard.h
@@ -22,11 +22,13 @@
 #include <glib.h>
 
 #include "x11.h"
+#include "udscs.h"
 
 typedef struct VDAgentClipboards VDAgentClipboards;
 
-VDAgentClipboards *vdagent_clipboards_init(struct vdagent_x11 *x11);
-void vdagent_clipboards_finalize(VDAgentClipboards *c);
+VDAgentClipboards *vdagent_clipboards_init(struct vdagent_x11      *x11,
+                                           struct udscs_connection *conn);
+void vdagent_clipboards_finalize(VDAgentClipboards *c, gboolean conn_alive);
 
 void vdagent_clipboard_request(VDAgentClipboards *c, guint sel_id, guint type);
 
diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c
index 83180ac..3f8ef31 100644
--- a/src/vdagent/vdagent.c
+++ b/src/vdagent/vdagent.c
@@ -160,6 +160,17 @@ static gboolean vdagent_finalize_file_xfer(VDAgent *agent)
     return TRUE;
 }
 
+static void vdagent_quit_loop(VDAgent *agent)
+{
+    /* other GMainLoop(s) might be running, quit them before agent->loop */
+    if (agent->clipboards) {
+        vdagent_clipboards_finalize(agent->clipboards, agent->conn != NULL);
+        agent->clipboards = NULL;
+    }
+    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)
 {
@@ -187,7 +198,7 @@ static void daemon_read_complete(struct udscs_connection **connp,
         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;
@@ -249,8 +260,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
@@ -322,7 +332,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;
 }
 
@@ -342,7 +352,6 @@ static VDAgent *vdagent_new(void)
 static void vdagent_destroy(VDAgent *agent)
 {
     vdagent_finalize_file_xfer(agent);
-    vdagent_clipboards_finalize(agent->clipboards);
     vdagent_x11_destroy(agent->x11, agent->conn == NULL);
     udscs_destroy_connection(&agent->conn);
 
@@ -382,7 +391,7 @@ static gboolean vdagent_init_async_cb(gpointer user_data)
     if (!vdagent_init_file_xfer(agent))
         syslog(LOG_WARNING, "File transfer is disabled");
 
-    agent->clipboards = vdagent_clipboards_init(agent->x11);
+    agent->clipboards = vdagent_clipboards_init(agent->x11, agent->conn);
 
     if (parent_socket != -1) {
         if (write(parent_socket, "OK", 2) != 2)
@@ -394,7 +403,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..e7c64bd 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
 /* Macros to print a message to the logfile prefixed by the selection */
 #define SELPRINTF(format, ...) \
     syslog(LOG_ERR, "%s: " format, \
@@ -21,10 +26,6 @@
         } \
     } while (0)
 
-#define MAX_SCREENS 16
-/* Same as qxl_dev.h client_monitors_config.heads count */
-#define MONITOR_SIZE_COUNT 64
-
 enum { owner_none, owner_guest, owner_client };
 
 /* X11 terminology is confusing a selection request is a request from an
@@ -58,11 +59,6 @@ struct clipboard_format_info {
     int atom_count;
 };
 
-struct monitor_size {
-    int width;
-    int height;
-};
-
 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 +70,30 @@ static const struct clipboard_format_tmpl clipboard_format_templates[] = {
 };
 
 #define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0]))
+#endif
+
+#define MAX_SCREENS 16
+/* Same as qxl_dev.h client_monitors_config.heads count */
+#define MONITOR_SIZE_COUNT 64
+
+struct monitor_size {
+    int width;
+    int height;
+};
 
 struct vdagent_x11 {
-    struct clipboard_format_info clipboard_formats[clipboard_format_count];
     Display *display;
+#ifndef WITH_GTK
+    struct clipboard_format_info clipboard_formats[clipboard_format_count];
     Atom clipboard_atom;
     Atom clipboard_primary_atom;
     Atom targets_atom;
     Atom incr_atom;
     Atom multiple_atom;
     Atom timestamp_atom;
-    Window root_window[MAX_SCREENS];
     Window selection_window;
-    struct udscs_connection *vdagentd;
-    int debug;
-    int fd;
-    int screen_count;
-    int width[MAX_SCREENS];
-    int height[MAX_SCREENS];
     int has_xfixes;
     int xfixes_event_base;
-    int xrandr_event_base;
     int max_prop_size;
     int expected_targets_notifies[256];
     int clipboard_owner[256];
@@ -113,6 +112,15 @@ struct vdagent_x11 {
     uint32_t selection_req_data_pos;
     uint32_t selection_req_data_size;
     Atom selection_req_atom;
+#endif
+    Window root_window[MAX_SCREENS];
+    struct udscs_connection *vdagentd;
+    int debug;
+    int fd;
+    int screen_count;
+    int width[MAX_SCREENS];
+    int height[MAX_SCREENS];
+    int xrandr_event_base;
     /* resolution change state */
     struct {
         XRRScreenResources *res;
diff --git a/src/vdagent/x11.c b/src/vdagent/x11.c
index ee148ae..0ea5668 100644
--- a/src/vdagent/x11.c
+++ b/src/vdagent/x11.c
@@ -59,6 +59,7 @@
 int (*vdagent_x11_prev_error_handler)(Display *, XErrorEvent *);
 int vdagent_x11_caught_error;
 
+#ifndef WITH_GTK
 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);
@@ -83,6 +84,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)
@@ -90,6 +92,7 @@ static int vdagent_x11_debug_error_handler(
     abort();
 }
 
+#ifndef WITH_GTK
 /* 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(
@@ -100,6 +103,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 *))
@@ -199,7 +203,11 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
 {
     struct vdagent_x11 *x11;
     XWindowAttributes attrib;
+#ifdef WITH_GTK
+    int i;
+#else
     int i, j, major, minor;
+#endif
     gchar *net_wm_name = NULL;
 
     x11 = calloc(1, sizeof(*x11));
@@ -235,6 +243,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
     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);
@@ -257,9 +266,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
     if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) &&
         XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) {
         x11->has_xfixes = 1;
@@ -285,6 +296,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 */
@@ -319,17 +331,18 @@ 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;
 
+#ifndef WITH_GTK
     if (vdagentd_disconnected)
         x11->vdagentd = NULL;
 
+    uint8_t sel;
     for (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);
@@ -341,6 +354,7 @@ int vdagent_x11_get_fd(struct vdagent_x11 *x11)
     return x11->fd;
 }
 
+#ifndef WITH_GTK
 static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11)
 {
     struct vdagent_x11_selection_request *selection_request;
@@ -476,10 +490,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
     uint8_t selection;
 
     if (event.type == x11->xfixes_event_base) {
@@ -525,6 +541,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;
@@ -545,6 +562,7 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
         /* These are uninteresting */
         handled = 1;
         break;
+#ifndef WITH_GTK
     case SelectionNotify:
         if (event.xselection.target == x11->targets_atom)
             vdagent_x11_handle_targets_notify(x11, &event);
@@ -604,6 +622,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",
@@ -620,6 +639,7 @@ void vdagent_x11_do_read(struct vdagent_x11 *x11)
     }
 }
 
+#ifndef WITH_GTK
 static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a)
 {
     if (a == None)
@@ -1354,6 +1374,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 4fd0380..1505f58 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
 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(struct vdagent_x11 *x11);
 
-- 
2.14.3



More information about the Spice-devel mailing list