[Spice-devel] [PATCH vdagent 2/2] vdagent: handle clipboard using GTK+
Christophe de Dinechin
christophe.de.dinechin at gmail.com
Wed Feb 7 19:16:50 UTC 2018
> On 7 Feb 2018, at 19:39, Jakub Janků <janku.jakub.jj at gmail.com> wrote:
>
> Hi Christophe,
>
> On Wed, Feb 7, 2018 at 5:12 PM, Christophe de Dinechin
> <christophe.de.dinechin at gmail.com> 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 at gmail.com> wrote:
>>>
>>> From: Jakub Janků <jjanku at redhat.com>
>>>
>>> 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 at lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/spice-devel
>>
>
> Thanks for the review,
> Jakub
More information about the Spice-devel
mailing list