> On 7 Feb 2018, at 19:39, Jakub Janků <janku.jakub.jj@xxxxxxxxx> wrote: > > Hi Christophe, > > On Wed, Feb 7, 2018 at 5:12 PM, Christophe de Dinechin > <christophe.de.dinechin@xxxxxxxxx> wrote: >> Hi Jakub, >> >> >> I have not looked at everything, but here are a few quick notes just from glancing… >> >>> On 21 Jan 2018, at 21:03, Jakub Janků <janku.jakub.jj@xxxxxxxxx> wrote: >>> >>> From: Jakub Janků <jjanku@xxxxxxxxxx> >>> >>> Place the code that handles clipboard >>> into a separate file - clipboard.c >>> --- >>> Makefile.am | 2 + >>> src/vdagent/clipboard.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++++ >>> src/vdagent/clipboard.h | 42 +++++ >>> src/vdagent/vdagent.c | 31 +++- >>> 4 files changed, 471 insertions(+), 5 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..88550c6 100644 >>> --- a/Makefile.am >>> +++ b/Makefile.am >>> @@ -33,6 +33,8 @@ src_spice_vdagent_SOURCES = \ >>> $(common_sources) \ >>> src/vdagent/audio.c \ >>> src/vdagent/audio.h \ >>> + src/vdagent/clipboard.c \ >>> + src/vdagent/clipboard.h \ >>> src/vdagent/file-xfers.c \ >>> src/vdagent/file-xfers.h \ >>> src/vdagent/x11-priv.h \ >>> diff --git a/src/vdagent/clipboard.c b/src/vdagent/clipboard.c >>> new file mode 100644 >>> index 0000000..657a6b4 >>> --- /dev/null >>> +++ b/src/vdagent/clipboard.c >>> @@ -0,0 +1,401 @@ >>> +/* 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_from_clip(clipboard) \ >>> + GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(clipboard), "vdagent-selection")) >>> + >>> +enum { >>> + OWNER_NONE, >>> + OWNER_GUEST, >>> + OWNER_CLIENT >>> +}; >>> + >>> +typedef struct { >>> + GMainLoop *loop; >>> + GtkSelectionData *sel_data; >>> +} DataRetrieval; >>> + >>> +struct VDAgentClipboards { >> >> That looks C++-y. Any reason to not use a typedef struct here? Does this compile in strict C mode? >> > > The typedef is included in clipboard.h since VDAgentClipboards is also > used in vdagent.c > >>> + struct udscs_connection *conn; >>> + >>> + GtkClipboard *clipboard[SELECTION_COUNT]; >>> + guint owner[SELECTION_COUNT]; >>> + >>> + GList *data_retrievals[SELECTION_COUNT]; >>> + GList *data_requests[SELECTION_COUNT]; >>> + gpointer *last_targets_req[SELECTION_COUNT]; >>> + >>> + GdkAtom targets[SELECTION_COUNT][TYPE_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) >>> +{ >>> + int i; >>> + gchar *name = gdk_atom_name(atom); >>> + for (i = 0; i < G_N_ELEMENTS(atom2agent); i++) >>> + if (!g_ascii_strcasecmp(name, atom2agent[i].atom_name)) >>> + break; >>> + g_free(name); >>> + return i < G_N_ELEMENTS(atom2agent) ? atom2agent[i].type >>> + : VD_AGENT_CLIPBOARD_NONE; >>> +} >> >> Maybe a minor style point, but I’d find it more readable with the test not repeated: >> >> static guint get_type_from_atom(GdkAtom atom) >> { >> int i; >> gchar *name = gdk_atom_name(atom); >> 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; >> } >> > OK, I'll change this in the next version. > >>> + >>> +/* gtk_clipboard_request_(, callback, user_data) cannot be cancelled. Instead, >>> + gpointer *ref = get_weak_ref(VDAgentClipboards *) is passed to the callback. >>> + The callback returns if free_weak_ref(ref) == NULL, which can be done >>> + by setting *ref = NULL. This substitutes cancellation of the request. >>> + */ >>> +static gpointer *get_weak_ref(gpointer data) >>> +{ >>> + gpointer *ref = g_new(gpointer, 1); >>> + *ref = data; >>> + return ref; >>> +} >>> + >>> +static gpointer free_weak_ref(gpointer *ref) >>> +{ >>> + gpointer data = *ref; >>> + g_free(ref); >>> + return data; >>> +} >>> + >>> +static void clipboard_new_owner(VDAgentClipboards *c, guint sel, guint new_owner) >>> +{ >>> + GList *l; >>> + /* let the other apps know no data is coming */ >>> + for (l = c->data_retrievals[sel]; l != NULL; l= l->next) { >>> + DataRetrieval *retrv = l->data; >>> + g_main_loop_quit(retrv->loop); >>> + } >>> + g_clear_pointer(&c->data_retrievals[sel], g_list_free); >>> + >>> + /* respond to pending client's data requests */ >>> + for (l = c->data_requests[sel]; l != NULL; l = l->next) { >>> + gpointer *ref = l->data; >>> + *ref = NULL; >>> + if (c->conn) >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA, >>> + sel, VD_AGENT_CLIPBOARD_NONE, NULL, 0); >>> + } >>> + g_clear_pointer(&c->data_requests[sel], g_list_free); >>> + >>> + c->owner[sel] = new_owner; >>> +} >>> + >>> +static void clipboard_targets_received_cb(GtkClipboard *clipboard, >>> + GdkAtom *atoms, >>> + gint n_atoms, >>> + gpointer user_data) >>> +{ >>> + VDAgentClipboards *c = free_weak_ref(user_data); >>> + guint32 types[G_N_ELEMENTS(atom2agent)]; >>> + guint sel, type, n_types = 0, a; >> >> I don’t like the lonely initialization in the middle of the decls. >> > Should I put all the inits on separate lines (including the c = > free_weak_ref...)? Or is it enough to put the "n_types = 0" at the end > of line? I’d personally prefer to have one line per init. That makes the code more consistent e.g. with initialization of pointers. Avoiding: int x = 0, *y = &x; When things are related, I don’t mind them being on the same line, e.g. int x = 0, y = 0, z = 0; Does that make sense? Thanks Christophe > >>> + >>> + if (c == NULL) /* request was cancelled */ >>> + return; >>> + >>> + sel = sel_from_clip(clipboard); >>> + c->last_targets_req[sel] = NULL; >>> + >>> + if (atoms == NULL) >>> + return; >>> + >>> + for (type = 0; type < TYPE_COUNT; type++) >>> + c->targets[sel][type] = GDK_NONE; >>> + >>> + for (a = 0; a < n_atoms; a++) { >>> + type = get_type_from_atom(atoms[a]); >>> + if (type == VD_AGENT_CLIPBOARD_NONE || c->targets[sel][type] != GDK_NONE) >>> + continue; >>> + >>> + c->targets[sel][type] = atoms[a]; >>> + types[n_types] = type; >>> + n_types++; >>> + } >>> + >>> + if (n_types == 0) { >>> + syslog(LOG_WARNING, "%s: sel=%u: no target supported", __func__, sel); >>> + return; >>> + } >>> + >>> + clipboard_new_owner(c, sel, OWNER_GUEST); >>> + >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_GRAB, sel, 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 = sel_from_clip(clipboard); >>> + >>> + /* if the event was caused by gtk_clipboard_set_with_data(), ignore it */ >>> + if (c->owner[sel] == OWNER_CLIENT) >>> + return; >>> + >>> + if (c->owner[sel] == OWNER_GUEST) { >>> + clipboard_new_owner(c, sel, OWNER_NONE); >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_RELEASE, sel, 0, NULL, 0); >>> + } >>> + >>> + if (event->reason != GDK_OWNER_CHANGE_NEW_OWNER) >>> + return; >>> + >>> + /* if there's a pending request for clipboard targets, cancel it */ >>> + if (c->last_targets_req[sel]) >>> + *(c->last_targets_req[sel]) = NULL; >>> + >>> + c->last_targets_req[sel] = get_weak_ref(c); >>> + gtk_clipboard_request_targets(clipboard, clipboard_targets_received_cb, >>> + c->last_targets_req[sel]); >>> +} >>> + >>> +static void clipboard_contents_received_cb(GtkClipboard *clipboard, >>> + GtkSelectionData *sel_data, >>> + gpointer user_data) >>> +{ >>> + guint sel, type, target; >>> + VDAgentClipboards *c = free_weak_ref(user_data); >>> + if (c == NULL) /* request was cancelled */ >>> + return; >>> + >>> + sel = sel_from_clip(clipboard); >>> + c->data_requests[sel] = g_list_remove(c->data_requests[sel], 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, type, >>> + gtk_selection_data_get_data(sel_data), >>> + gtk_selection_data_get_length(sel_data)); >>> + } else { >>> + syslog(LOG_WARNING, "%s: sel=%u: expected type %u, recieved %u, " >>> + "skipping", __func__, sel, target, type); >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA, sel, >>> + VD_AGENT_CLIPBOARD_NONE, NULL, 0); >>> + } >>> +} >>> + >>> +static void clipboard_get_cb(GtkClipboard *clipboard, >>> + GtkSelectionData *sel_data, >>> + guint info, >>> + gpointer user_data) >>> +{ >>> + DataRetrieval retrv; >>> + VDAgentClipboards *c = user_data; >>> + guint sel, type; >>> + >>> + sel = sel_from_clip(clipboard); >>> + g_return_if_fail(c->owner[sel] == OWNER_CLIENT); >>> + >>> + type = get_type_from_atom(gtk_selection_data_get_target(sel_data)); >>> + g_return_if_fail(type != VD_AGENT_CLIPBOARD_NONE); >>> + >>> + retrv.sel_data = sel_data; >>> + retrv.loop = g_main_loop_new(NULL, FALSE); >>> + c->data_retrievals[sel] = g_list_prepend(c->data_retrievals[sel], &retrv); >>> + >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_REQUEST, sel, type, NULL, 0); >>> + >>> +G_GNUC_BEGIN_IGNORE_DEPRECATIONS >>> + gdk_threads_leave(); >>> + g_main_loop_run(retrv.loop); >>> + gdk_threads_enter(); >>> +G_GNUC_END_IGNORE_DEPRECATIONS >>> + >>> + g_main_loop_unref(retrv.loop); >>> +} >>> + >>> +static void clipboard_clear_cb(GtkClipboard *clipboard, gpointer user_data) >>> +{ >>> + VDAgentClipboards *c = user_data; >>> + clipboard_new_owner(c, sel_from_clip(clipboard), OWNER_NONE); >>> +} >>> + >>> +void vdagent_clipboard_grab(VDAgentClipboards *c, guint sel, >>> + guint32 *types, guint n_types) >>> +{ >>> + GtkTargetEntry targets[G_N_ELEMENTS(atom2agent)]; >>> + guint n_targets = 0, i, t; >>> + >>> + g_return_if_fail(sel < SELECTION_COUNT); >>> + >>> + 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=%u: no type supported", __func__, sel); >>> + return; >>> + } >>> + >>> + if (gtk_clipboard_set_with_data(c->clipboard[sel], targets, n_targets, >>> + clipboard_get_cb, clipboard_clear_cb, c)) >>> + clipboard_new_owner(c, sel, OWNER_CLIENT); >>> + else { >>> + syslog(LOG_ERR, "%s: sel=%u: clipboard grab failed", __func__, sel); >>> + clipboard_new_owner(c, sel, OWNER_NONE); >>> + } >>> +} >>> + >>> +void vdagent_clipboard_data(VDAgentClipboards *c, guint sel, >>> + guint type, const guchar *data, guint size) >>> +{ >>> + g_return_if_fail(sel < SELECTION_COUNT); >>> + >>> + DataRetrieval *retrv; >>> + GList *l; >>> + for (l = c->data_retrievals[sel]; l != NULL; l = l->next) { >>> + retrv = l->data; >>> + if (get_type_from_atom(gtk_selection_data_get_target(retrv->sel_data)) == type) >>> + break; >>> + } >>> + if (l == NULL) { >>> + syslog(LOG_WARNING, "%s: sel=%u: no corresponding request found for " >>> + "type=%u, skipping", __func__, sel, type); >>> + return; >>> + } >>> + c->data_retrievals[sel] = g_list_delete_link(c->data_retrievals[sel], l); >>> + >>> + gtk_selection_data_set(retrv->sel_data, >>> + gtk_selection_data_get_target(retrv->sel_data), >>> + 8, data, size); >>> + >>> + g_main_loop_quit(retrv->loop); >>> +} >>> + >>> +void vdagent_clipboard_release(VDAgentClipboards *c, guint sel) >>> +{ >>> + g_return_if_fail(sel < SELECTION_COUNT); >>> + if (c->owner[sel] != OWNER_CLIENT) >>> + return; >>> + >>> + clipboard_new_owner(c, sel, OWNER_NONE); >>> + gtk_clipboard_clear(c->clipboard[sel]); >>> +} >>> + >>> +void vdagent_clipboard_release_all(VDAgentClipboards *c) >>> +{ >>> + guint sel, owner; >>> + >>> + for (sel = 0; sel < SELECTION_COUNT; sel++) { >>> + owner = c->owner[sel]; >>> + clipboard_new_owner(c, sel, OWNER_NONE); >>> + if (owner == OWNER_CLIENT) >>> + gtk_clipboard_clear(c->clipboard[sel]); >>> + else if (owner == OWNER_GUEST && c->conn) >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_RELEASE, sel, 0, NULL, 0); >>> + } >>> +} >>> + >>> +void vdagent_clipboard_request(VDAgentClipboards *c, guint sel, guint type) >>> +{ >>> + if (sel >= SELECTION_COUNT || c->owner[sel] != OWNER_GUEST) >>> + goto err; >>> + if (type >= TYPE_COUNT || c->targets[sel][type] == GDK_NONE) { >>> + syslog(LOG_WARNING, "%s: sel=%d: unadvertised data type requested", >>> + __func__, sel); >>> + goto err; >>> + } >>> + >>> + gpointer *ref = get_weak_ref(c); >>> + c->data_requests[sel] = g_list_prepend(c->data_requests[sel], ref); >>> + gtk_clipboard_request_contents(c->clipboard[sel], c->targets[sel][type], >>> + clipboard_contents_received_cb, ref); >>> + return; >>> +err: >>> + udscs_write(c->conn, VDAGENTD_CLIPBOARD_DATA, sel, >>> + VD_AGENT_CLIPBOARD_NONE, NULL, 0); >>> +} >>> + >>> +VDAgentClipboards *vdagent_clipboard_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 = 0; sel < SELECTION_COUNT; sel++) { >>> + c->clipboard[sel] = gtk_clipboard_get(sel_atom[sel]); >>> + /* enables the use of sel_from_clipboard(clipboard) macro */ >>> + g_object_set_data(G_OBJECT(c->clipboard[sel]), "vdagent-selection", >>> + GUINT_TO_POINTER(sel)); >>> + g_signal_connect(G_OBJECT(c->clipboard[sel]), "owner-change", >>> + G_CALLBACK(clipboard_owner_change_cb), c); >>> + } >>> + >>> + return c; >>> +} >>> + >>> +void vdagent_clipboard_finalize(VDAgentClipboards *c, >>> + struct udscs_connection *conn) >>> +{ >>> + for (guint sel = 0; sel < SELECTION_COUNT; sel++) >>> + g_signal_handlers_disconnect_by_func(c->clipboard[sel], >>> + G_CALLBACK(clipboard_owner_change_cb), c); >>> + >>> + c->conn = conn; >>> + vdagent_clipboard_release_all(c); >>> + >>> + g_free(c); >>> +} >>> diff --git a/src/vdagent/clipboard.h b/src/vdagent/clipboard.h >>> new file mode 100644 >>> index 0000000..f02d8a4 >>> --- /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_clipboard_init(struct udscs_connection *conn); >>> +void vdagent_clipboard_finalize(VDAgentClipboards *c, >>> + struct udscs_connection *conn); >>> + >>> +void vdagent_clipboard_request(VDAgentClipboards *c, guint sel, guint type); >>> + >>> +void vdagent_clipboard_release(VDAgentClipboards *c, guint sel); >>> + >>> +void vdagent_clipboard_release_all(VDAgentClipboards *c); >>> + >>> +void vdagent_clipboard_data(VDAgentClipboards *c, guint sel, >>> + guint type, const guchar *data, guint size); >>> + >>> +void vdagent_clipboard_grab(VDAgentClipboards *c, guint sel, >>> + guint32 *types, guint n_types); >>> + >>> +#endif >>> diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c >>> index 92ffcf3..81b7a5a 100644 >>> --- a/src/vdagent/vdagent.c >>> +++ b/src/vdagent/vdagent.c >>> @@ -44,8 +44,10 @@ >>> #include "audio.h" >>> #include "x11.h" >>> #include "file-xfers.h" >>> +#include "clipboard.h" >>> >>> typedef struct VDAgent { >>> + VDAgentClipboards *clipboards; >>> struct vdagent_x11 *x11; >>> struct vdagent_file_xfers *xfers; >>> struct udscs_connection *conn; >>> @@ -154,6 +156,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_clipboard_finalize(agent->clipboards, agent->conn); >>> + 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) >>> { >>> @@ -164,18 +177,24 @@ static void daemon_read_complete(struct udscs_connection **connp, >>> vdagent_x11_set_monitor_config(agent->x11, (VDAgentMonitorsConfig *)data, 0); >>> break; >>> 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; >>> 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; >>> @@ -222,6 +241,7 @@ static void daemon_read_complete(struct udscs_connection **connp, >>> } >>> break; >>> case VDAGENTD_CLIENT_DISCONNECTED: >>> + vdagent_clipboard_release_all(agent->clipboards); >>> if (vdagent_finalize_file_xfer(agent)) { >>> vdagent_init_file_xfer(agent); >>> } >>> @@ -236,8 +256,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 >>> @@ -309,7 +328,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; >>> } >>> >>> @@ -368,6 +387,8 @@ 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_clipboard_init(agent->conn); >>> + >>> if (parent_socket != -1) { >>> if (write(parent_socket, "OK", 2) != 2) >>> syslog(LOG_WARNING, "Parent already gone."); >>> @@ -378,7 +399,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; >>> } >>> -- >>> 2.14.3 >>> >>> _______________________________________________ >>> Spice-devel mailing list >>> Spice-devel@xxxxxxxxxxxxxxxxxxxxx >>> https://lists.freedesktop.org/mailman/listinfo/spice-devel >> > > Thanks for the review, > Jakub _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel