From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.5 required=3.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,HTML_MESSAGE,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 883B8C433E0 for ; Thu, 18 Mar 2021 10:57:30 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id BB3CC64F24 for ; Thu, 18 Mar 2021 10:57:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org BB3CC64F24 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:60602 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lMqLI-0008Us-If for qemu-devel@archiver.kernel.org; Thu, 18 Mar 2021 06:57:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35386) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lMqJQ-0007uW-2Q for qemu-devel@nongnu.org; Thu, 18 Mar 2021 06:55:32 -0400 Received: from mail-ej1-x632.google.com ([2a00:1450:4864:20::632]:37502) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1lMqJH-00031Y-Iy for qemu-devel@nongnu.org; Thu, 18 Mar 2021 06:55:31 -0400 Received: by mail-ej1-x632.google.com with SMTP id w3so3191381ejc.4 for ; Thu, 18 Mar 2021 03:55:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=XoPj6iQO84uO2oKB1YKcgIKLV7SSmutwWWCUS2X5HEM=; b=PeoftYYy+Rhn3aq79IS1FXAdBO17iPQd5CJV6NaoF+zDKLSb4+QPSfa5Lfx7E5RLCV 8I6RwoMZzAjOFgC8JOJoHmrmebhEnMVuzOf1FGvu3XVGXa2Y72ftlUdiUCNYXjvTpBS7 M4SsSNsKGV6q/LcUVfjTHbS0m+2PiICpo0ld8ZXKY3VBCDBphLxzSBUULE1z9oHaOIme jqNiD6qgaO1heA0WKtFsuikRUn4jT1gA/zRZekCgnrgVZfg+kcQ2+eHTlxR/faQiO2ML dhcOB7n5cS3vNiH7wE/jrtst2l/zMZYlt5B3gSwoBHLWHJUlhNcxsHfbypH6xdy/cycd jjyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=XoPj6iQO84uO2oKB1YKcgIKLV7SSmutwWWCUS2X5HEM=; b=M6oV8vk2/X3tcj2j+a1+WSDyiPkj3bhVCag7XUJkdbaYP48nd7CvXzeUcf1Dye6YFg ft4Dz34uc7u7c4W45DdQku8rmTpOHfC8oof7eA2LO7trA6APEyo3n1D/K687Tijufge+ Fk8RtkDwNB4/x/v6N1s1Gpj95dOP2nkWj3ePs8tRrBV2osREZOruv8dGojJyzk6/XIsX 7UCH331pgNkCh67U5Jcz1taAMEFdE0iWORg7mwtRa8vtxneMSmsVbavmqAb6xuOkhf+K S/pGKFWQgzR4qFF0cEeykMgj0S9gnHuV3Es7RXx78AlRAknCEEcrvadHQeOwZeUkzhVX SngQ== X-Gm-Message-State: AOAM532LRQFMNjFzSBFDgC0k2Xa8pCcjSNGxV04a+Y044zF8bxFWf8Lt GOEuT9lUMqpgWE74iaBKTOTN9m+43OhyWB9k9UA= X-Google-Smtp-Source: ABdhPJyXAKGOcL7CpayihvxhSIc4kEqlIuOGzbiBi4iZrsQyxzytHTA0Ied+Did3TSLM3TrDf1LHCtUBas6g9yDSzig= X-Received: by 2002:a17:906:a1c8:: with SMTP id bx8mr39316731ejb.381.1616064920983; Thu, 18 Mar 2021 03:55:20 -0700 (PDT) MIME-Version: 1.0 References: <20210318091647.3233178-1-kraxel@redhat.com> <20210318091647.3233178-8-kraxel@redhat.com> In-Reply-To: <20210318091647.3233178-8-kraxel@redhat.com> From: =?UTF-8?B?TWFyYy1BbmRyw6kgTHVyZWF1?= Date: Thu, 18 Mar 2021 14:55:09 +0400 Message-ID: Subject: Re: [PATCH v2 7/7] ui/gtk: add clipboard support To: Gerd Hoffmann Content-Type: multipart/alternative; boundary="0000000000008c9d4905bdcd6f9e" Received-SPF: pass client-ip=2a00:1450:4864:20::632; envelope-from=marcandre.lureau@gmail.com; helo=mail-ej1-x632.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , QEMU , Markus Armbruster Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" --0000000000008c9d4905bdcd6f9e Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi On Thu, Mar 18, 2021 at 1:25 PM Gerd Hoffmann wrote: > This patch adds clipboard support to the qemu gtk ui. > > Signed-off-by: Gerd Hoffmann > --- > include/ui/gtk.h | 10 +++ > ui/gtk-clipboard.c | 189 +++++++++++++++++++++++++++++++++++++++++++++ > ui/gtk.c | 1 + > ui/meson.build | 2 +- > 4 files changed, 201 insertions(+), 1 deletion(-) > create mode 100644 ui/gtk-clipboard.c > > diff --git a/include/ui/gtk.h b/include/ui/gtk.h > index 6e751794043f..9516670ebc87 100644 > --- a/include/ui/gtk.h > +++ b/include/ui/gtk.h > @@ -18,6 +18,7 @@ > #include > #endif > > +#include "ui/clipboard.h" > #include "ui/console.h" > #include "ui/kbd-state.h" > #if defined(CONFIG_OPENGL) > @@ -137,6 +138,12 @@ struct GtkDisplayState { > > bool external_pause_update; > > + QemuClipboardPeer cbpeer; > + QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; > + uint32_t cbpending[QEMU_CLIPBOARD_SELECTION__COUNT]; > + GtkClipboard *gtkcb[QEMU_CLIPBOARD_SELECTION__COUNT]; > + bool cbowner[QEMU_CLIPBOARD_SELECTION__COUNT]; > + > DisplayOptions *opts; > }; > > @@ -207,4 +214,7 @@ void gtk_gl_area_init(void); > int gd_gl_area_make_current(DisplayChangeListener *dcl, > QEMUGLContext ctx); > > +/* gtk-clipboard.c */ > +void gd_clipboard_init(GtkDisplayState *gd); > + > #endif /* UI_GTK_H */ > diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c > new file mode 100644 > index 000000000000..3585e27cab78 > --- /dev/null > +++ b/ui/gtk-clipboard.c > @@ -0,0 +1,189 @@ > +/* > + * GTK UI -- clipboard support > + * > + * Copyright (C) 2021 Gerd Hoffmann > + * > + * 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 2 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 . > + * > + */ > + > +#include "qemu/osdep.h" > +#include "qemu-common.h" > +#include "qemu/main-loop.h" > + > +#include "ui/gtk.h" > + > +static QemuClipboardSelection gd_find_selection(GtkDisplayState *gd, > + GtkClipboard *clipboard) > +{ > + QemuClipboardSelection s; > + > + for (s =3D 0; s < QEMU_CLIPBOARD_SELECTION__COUNT; s++) { > + if (gd->gtkcb[s] =3D=3D clipboard) { > + return s; > + } > + } > + return QEMU_CLIPBOARD_SELECTION_CLIPBOARD; > +} > + > +static void gd_clipboard_get_data(GtkClipboard *clipboard, > + GtkSelectionData *selection_data, > + guint selection_info, > + gpointer data) > +{ > + GtkDisplayState *gd =3D data; > + QemuClipboardSelection s =3D gd_find_selection(gd, clipboard); > + QemuClipboardType type =3D QEMU_CLIPBOARD_TYPE_TEXT; > + QemuClipboardInfo *info =3D qemu_clipboard_info_get(gd->cbinfo[s]); > + > + qemu_clipboard_request(info, type); > + while (info =3D=3D gd->cbinfo[s] && > + info->types[type].available && > + info->types[type].data =3D=3D NULL) { > + main_loop_wait(false); > Added to the list of reasons not to want running the clipboard (or shall I say any UI code) inside qemu. (gtk4 is supposed to have improved that, fwiw) + } > + > + if (info =3D=3D gd->cbinfo[s] && gd->cbowner[s]) { > + gtk_selection_data_set_text(selection_data, > + info->types[type].data, > + info->types[type].size); > + } else { > + /* clipboard owner changed while waiting for the data */ > + } > + > + qemu_clipboard_info_put(info); > +} > + > +static void gd_clipboard_clear(GtkClipboard *clipboard, > + gpointer data) > +{ > + GtkDisplayState *gd =3D data; > + QemuClipboardSelection s =3D gd_find_selection(gd, clipboard); > + > + gd->cbowner[s] =3D false; > +} > + > +static void gd_clipboard_notify(Notifier *notifier, void *data) > +{ > + GtkDisplayState *gd =3D container_of(notifier, GtkDisplayState, > cbpeer.update); > + QemuClipboardInfo *info =3D data; > + QemuClipboardSelection s =3D info->selection; > + bool self_update =3D info->owner =3D=3D &gd->cbpeer; > + > + if (info !=3D gd->cbinfo[s]) { > + qemu_clipboard_info_put(gd->cbinfo[s]); > + gd->cbinfo[s] =3D qemu_clipboard_info_get(info); > + gd->cbpending[s] =3D 0; > + if (!self_update) { > + GtkTargetList *list; > + GtkTargetEntry *targets; > + gint n_targets; > + > + list =3D gtk_target_list_new(NULL, 0); > + if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) { > + gtk_target_list_add_text_targets(list, 0); > + } > + targets =3D gtk_target_table_new_from_list(list, &n_targets)= ; > + > + gtk_clipboard_clear(gd->gtkcb[s]); > + gd->cbowner[s] =3D true; > + gtk_clipboard_set_with_data(gd->gtkcb[s], > + targets, n_targets, > + gd_clipboard_get_data, > + gd_clipboard_clear, > + gd); > + > + gtk_target_table_free(targets, n_targets); > + gtk_target_list_unref(list); > + } > + return; > + } > + > + if (self_update) { > + return; > + } > + > + /* > + * Clipboard got updated, with data probably. No action here, we > + * are waiting for updates in gd_clipboard_get_data(). > + */ > +} > + > +static void gd_clipboard_request(QemuClipboardInfo *info, > + QemuClipboardType type) > +{ > + GtkDisplayState *gd =3D container_of(info->owner, GtkDisplayState, > cbpeer); > + char *text; > + > + switch (type) { > + case QEMU_CLIPBOARD_TYPE_TEXT: > + text =3D gtk_clipboard_wait_for_text(gd->gtkcb[info->selection])= ; > + qemu_clipboard_set_data(&gd->cbpeer, info, type, > + strlen(text), text, true); > >From v1: text might be NULL if it failed. And you must free it. + break; > + default: > + break; > + } > +} > + > +static void gd_owner_change(GtkClipboard *clipboard, > + GdkEvent *event, > + gpointer data) > +{ > + GtkDisplayState *gd =3D data; > + QemuClipboardSelection s =3D gd_find_selection(gd, clipboard); > + QemuClipboardInfo *info; > + > + if (gd->cbowner[s]) { > + /* ignore notifications about our own grabs */ > + return; > + } > + > + > + switch (event->owner_change.reason) { > + case GDK_SETTING_ACTION_NEW: > + info =3D qemu_clipboard_info_new(&gd->cbpeer, s); > + if (gtk_clipboard_wait_is_text_available(clipboard)) { > + info->types[QEMU_CLIPBOARD_TYPE_TEXT].available =3D true; > + } > Same comment as v1: So after gtk_clipboard_set_text() the client side is actually taking the ownership away from the guest clipboard I presume. That might have some weird interaction issues. Hopefully the other side isn't playing the same game... If we don't address it now, I think it deserves a warning comment, possibly a FIXME. + > + qemu_clipboard_update(info); > + qemu_clipboard_info_put(info); > + break; > + default: > + break; > + } > +} > + > +void gd_clipboard_init(GtkDisplayState *gd) > +{ > + gd->cbpeer.name =3D "gtk"; > + gd->cbpeer.update.notify =3D gd_clipboard_notify; > + gd->cbpeer.request =3D gd_clipboard_request; > + qemu_clipboard_peer_register(&gd->cbpeer); > + > + gd->gtkcb[QEMU_CLIPBOARD_SELECTION_CLIPBOARD] =3D > + gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)); > Why not use GDK_SELECTION_* ? + gd->gtkcb[QEMU_CLIPBOARD_SELECTION_PRIMARY] =3D > + gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE)); > + gd->gtkcb[QEMU_CLIPBOARD_SELECTION_SECONDARY] =3D > + gtk_clipboard_get(gdk_atom_intern("SECONDARY", FALSE)); > + > + g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_CLIPBOARD], > + "owner-change", G_CALLBACK(gd_owner_change), gd); > + g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_PRIMARY], > + "owner-change", G_CALLBACK(gd_owner_change), gd); > + g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_SECONDARY], > + "owner-change", G_CALLBACK(gd_owner_change), gd); > +} > diff --git a/ui/gtk.c b/ui/gtk.c > index 7da288a25156..98046f577b9d 100644 > --- a/ui/gtk.c > +++ b/ui/gtk.c > @@ -2267,6 +2267,7 @@ static void gtk_display_init(DisplayState *ds, > DisplayOptions *opts) > opts->u.gtk.grab_on_hover) { > gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item)); > } > + gd_clipboard_init(s); > } > > static void early_gtk_display_init(DisplayOptions *opts) > diff --git a/ui/meson.build b/ui/meson.build > index 6c0c6ddd74eb..9ec0c03b5ad1 100644 > --- a/ui/meson.build > +++ b/ui/meson.build > @@ -65,7 +65,7 @@ if gtk.found() > softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('win32-kbd-hook.c'= )) > > gtk_ss =3D ss.source_set() > - gtk_ss.add(gtk, vte, pixman, files('gtk.c')) > + gtk_ss.add(gtk, vte, pixman, files('gtk.c', 'gtk-clipboard.c')) > gtk_ss.add(when: x11, if_true: files('x_keymap.c')) > gtk_ss.add(when: [opengl, 'CONFIG_OPENGL'], if_true: > files('gtk-gl-area.c')) > gtk_ss.add(when: [x11, opengl, 'CONFIG_OPENGL'], if_true: > files('gtk-egl.c')) > -- > 2.30.2 > > > --=20 Marc-Andr=C3=A9 Lureau --0000000000008c9d4905bdcd6f9e Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi

On Thu, Mar 18, 2021 at 1:25 PM Ger= d Hoffmann <kraxel@redhat.com&g= t; wrote:
This p= atch adds clipboard support to the qemu gtk ui.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
=C2=A0include/ui/gtk.h=C2=A0 =C2=A0|=C2=A0 10 +++
=C2=A0ui/gtk-clipboard.c | 189 ++++++++++++++++++++++++++++++++++++++++++++= +
=C2=A0ui/gtk.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A01 + =C2=A0ui/meson.build=C2=A0 =C2=A0 =C2=A0|=C2=A0 =C2=A02 +-
=C2=A04 files changed, 201 insertions(+), 1 deletion(-)
=C2=A0create mode 100644 ui/gtk-clipboard.c

diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 6e751794043f..9516670ebc87 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -18,6 +18,7 @@
=C2=A0#include <gdk/gdkwayland.h>
=C2=A0#endif

+#include "ui/clipboard.h"
=C2=A0#include "ui/console.h"
=C2=A0#include "ui/kbd-state.h"
=C2=A0#if defined(CONFIG_OPENGL)
@@ -137,6 +138,12 @@ struct GtkDisplayState {

=C2=A0 =C2=A0 =C2=A0bool external_pause_update;

+=C2=A0 =C2=A0 QemuClipboardPeer cbpeer;
+=C2=A0 =C2=A0 QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];<= br> +=C2=A0 =C2=A0 uint32_t cbpending[QEMU_CLIPBOARD_SELECTION__COUNT];
+=C2=A0 =C2=A0 GtkClipboard *gtkcb[QEMU_CLIPBOARD_SELECTION__COUNT];
+=C2=A0 =C2=A0 bool cbowner[QEMU_CLIPBOARD_SELECTION__COUNT];
+
=C2=A0 =C2=A0 =C2=A0DisplayOptions *opts;
=C2=A0};

@@ -207,4 +214,7 @@ void gtk_gl_area_init(void);
=C2=A0int gd_gl_area_make_current(DisplayChangeListener *dcl,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0QEMUGLContext ctx);

+/* gtk-clipboard.c */
+void gd_clipboard_init(GtkDisplayState *gd);
+
=C2=A0#endif /* UI_GTK_H */
diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c
new file mode 100644
index 000000000000..3585e27cab78
--- /dev/null
+++ b/ui/gtk-clipboard.c
@@ -0,0 +1,189 @@
+/*
+ * GTK UI -- clipboard support
+ *
+ * Copyright (C) 2021 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * 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 2 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.=C2=A0 See the GNU<= br> + * 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= />.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/main-loop.h"
+
+#include "ui/gtk.h"
+
+static QemuClipboardSelection gd_find_selection(GtkDisplayState *gd,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 GtkClipboard *clipboard)
+{
+=C2=A0 =C2=A0 QemuClipboardSelection s;
+
+=C2=A0 =C2=A0 for (s =3D 0; s < QEMU_CLIPBOARD_SELECTION__COUNT; s++) {=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (gd->gtkcb[s] =3D=3D clipboard) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return s;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 return QEMU_CLIPBOARD_SELECTION_CLIPBOARD;
+}
+
+static void gd_clipboard_get_data(GtkClipboard=C2=A0 =C2=A0 =C2=A0*clipboa= rd,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 GtkSelectionData *selection_d= ata,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 guint=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0selection_info,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gpointer=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 data)
+{
+=C2=A0 =C2=A0 GtkDisplayState *gd =3D data;
+=C2=A0 =C2=A0 QemuClipboardSelection s =3D gd_find_selection(gd, clipboard= );
+=C2=A0 =C2=A0 QemuClipboardType type =3D QEMU_CLIPBOARD_TYPE_TEXT;
+=C2=A0 =C2=A0 QemuClipboardInfo *info =3D qemu_clipboard_info_get(gd->c= binfo[s]);
+
+=C2=A0 =C2=A0 qemu_clipboard_request(info, type);
+=C2=A0 =C2=A0 while (info =3D=3D gd->cbinfo[s] &&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0info->types[type].available &a= mp;&
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0info->types[type].data =3D=3D = NULL) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 main_loop_wait(false);
Added to the list of reasons not to want running the clipboard= =C2=A0 (or shall I say any UI code) inside qemu.

(= gtk4 is supposed to have improved that, fwiw)

+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (info =3D=3D gd->cbinfo[s] && gd->cbowner[s= ]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_selection_data_set_text(selection_data, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 info->types[type].d= ata,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 info->types[type].s= ize);
+=C2=A0 =C2=A0 } else {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* clipboard owner changed while waiting for t= he data */
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 qemu_clipboard_info_put(info);
+}
+
+static void gd_clipboard_clear(GtkClipboard *clipboard,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0gpointer data)
+{
+=C2=A0 =C2=A0 GtkDisplayState *gd =3D data;
+=C2=A0 =C2=A0 QemuClipboardSelection s =3D gd_find_selection(gd, clipboard= );
+
+=C2=A0 =C2=A0 gd->cbowner[s] =3D false;
+}
+
+static void gd_clipboard_notify(Notifier *notifier, void *data)
+{
+=C2=A0 =C2=A0 GtkDisplayState *gd =3D container_of(notifier, GtkDisplaySta= te, cbpeer.update);
+=C2=A0 =C2=A0 QemuClipboardInfo *info =3D data;
+=C2=A0 =C2=A0 QemuClipboardSelection s =3D info->selection;
+=C2=A0 =C2=A0 bool self_update =3D info->owner =3D=3D &gd->cbpee= r;
+
+=C2=A0 =C2=A0 if (info !=3D gd->cbinfo[s]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_clipboard_info_put(gd->cbinfo[s]);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 gd->cbinfo[s] =3D qemu_clipboard_info_get(i= nfo);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 gd->cbpending[s] =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (!self_update) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 GtkTargetList *list;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 GtkTargetEntry *targets;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gint n_targets;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 list =3D gtk_target_list_new(NUL= L, 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (info->types[QEMU_CLIPBOAR= D_TYPE_TEXT].available) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_target_list_ad= d_text_targets(list, 0);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 targets =3D gtk_target_table_new= _from_list(list, &n_targets);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_clipboard_clear(gd->gtkcb= [s]);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gd->cbowner[s] =3D true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_clipboard_set_with_data(gd-&= gt;gtkcb[s],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 targets,= n_targets,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gd_clipb= oard_get_data,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gd_clipb= oard_clear,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gd);
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_target_table_free(targets, n= _targets);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_target_list_unref(list);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (self_update) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* Clipboard got updated, with data probably.=C2=A0 No = action here, we
+=C2=A0 =C2=A0 =C2=A0* are waiting for updates in gd_clipboard_get_data().<= br> +=C2=A0 =C2=A0 =C2=A0*/
+}
+
+static void gd_clipboard_request(QemuClipboardInfo *info,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0QemuClipboardType type)
+{
+=C2=A0 =C2=A0 GtkDisplayState *gd =3D container_of(info->owner, GtkDisp= layState, cbpeer);
+=C2=A0 =C2=A0 char *text;
+
+=C2=A0 =C2=A0 switch (type) {
+=C2=A0 =C2=A0 case QEMU_CLIPBOARD_TYPE_TEXT:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 text =3D gtk_clipboard_wait_for_text(gd->gt= kcb[info->selection]);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_clipboard_set_data(&gd->cbpeer, in= fo, type,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 strlen(text), text, true);

From v1:
text might be NULL if it fail= ed.

And you must free it.

+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+static void gd_owner_change(GtkClipboard *clipboard,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 GdkEvent *event,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 gpointer data)
+{
+=C2=A0 =C2=A0 GtkDisplayState *gd =3D data;
+=C2=A0 =C2=A0 QemuClipboardSelection s =3D gd_find_selection(gd, clipboard= );
+=C2=A0 =C2=A0 QemuClipboardInfo *info;
+
+=C2=A0 =C2=A0 if (gd->cbowner[s]) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* ignore notifications about our own grabs */=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+
+=C2=A0 =C2=A0 switch (event->owner_change.reason) {
+=C2=A0 =C2=A0 case GDK_SETTING_ACTION_NEW:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 info =3D qemu_clipboard_info_new(&gd->c= bpeer, s);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (gtk_clipboard_wait_is_text_available(clipb= oard)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 info->types[QEMU_CLIPBOARD_TY= PE_TEXT].available =3D true;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }

Same comment as v1:
So after g= tk_clipboard_set_text() the client side is actually taking
the ownership away from the guest clipboard I pres= ume. That might have some
weird interaction issues. Hopefully the other = side isn't playing the same
game...
=C2=A0

If we don't address it now, I think it deserves a warning comment, p= ossibly a FIXME.

+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_clipboard_update(info);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_clipboard_info_put(info);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+void gd_clipboard_init(GtkDisplayState *gd)
+{
+=C2=A0 =C2=A0 gd->cbpeer.name =3D "gtk";
+=C2=A0 =C2=A0 gd->cbpeer.update.notify =3D gd_clipboard_notify;
+=C2=A0 =C2=A0 gd->cbpeer.request =3D gd_clipboard_request;
+=C2=A0 =C2=A0 qemu_clipboard_peer_register(&gd->cbpeer);
+
+=C2=A0 =C2=A0 gd->gtkcb[QEMU_CLIPBOARD_SELECTION_CLIPBOARD] =3D
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_clipboard_get(gdk_atom_intern("CLIPBO= ARD", FALSE));

Why not use GDK_SEL= ECTION_* ?

+=C2=A0 =C2=A0 gd->gtkcb[QEMU_CLIPBOARD_SELECTION_PRIMARY] =3D
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_clipboard_get(gdk_atom_intern("PRIMAR= Y", FALSE));
+=C2=A0 =C2=A0 gd->gtkcb[QEMU_CLIPBOARD_SELECTION_SECONDARY] =3D
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 gtk_clipboard_get(gdk_atom_intern("SECOND= ARY", FALSE));
+
+=C2=A0 =C2=A0 g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_CLIPB= OARD],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0"owner-change", G_CALLBACK(gd_owner_change), gd);
+=C2=A0 =C2=A0 g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_PRIMA= RY],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0"owner-change", G_CALLBACK(gd_owner_change), gd);
+=C2=A0 =C2=A0 g_signal_connect(gd->gtkcb[QEMU_CLIPBOARD_SELECTION_SECON= DARY],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0"owner-change", G_CALLBACK(gd_owner_change), gd);
+}
diff --git a/ui/gtk.c b/ui/gtk.c
index 7da288a25156..98046f577b9d 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -2267,6 +2267,7 @@ static void gtk_display_init(DisplayState *ds, Displa= yOptions *opts)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0opts->u.gtk.grab_on_hover) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0gtk_menu_item_activate(GTK_MENU_ITEM(s-&g= t;grab_on_hover_item));
=C2=A0 =C2=A0 =C2=A0}
+=C2=A0 =C2=A0 gd_clipboard_init(s);
=C2=A0}

=C2=A0static void early_gtk_display_init(DisplayOptions *opts)
diff --git a/ui/meson.build b/ui/meson.build
index 6c0c6ddd74eb..9ec0c03b5ad1 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -65,7 +65,7 @@ if gtk.found()
=C2=A0 =C2=A0softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files(&#= 39;win32-kbd-hook.c'))

=C2=A0 =C2=A0gtk_ss =3D ss.source_set()
-=C2=A0 gtk_ss.add(gtk, vte, pixman, files('gtk.c'))
+=C2=A0 gtk_ss.add(gtk, vte, pixman, files('gtk.c', 'gtk-clipbo= ard.c'))
=C2=A0 =C2=A0gtk_ss.add(when: x11, if_true: files('x_keymap.c')) =C2=A0 =C2=A0gtk_ss.add(when: [opengl, 'CONFIG_OPENGL'], if_true: f= iles('gtk-gl-area.c'))
=C2=A0 =C2=A0gtk_ss.add(when: [x11, opengl, 'CONFIG_OPENGL'], if_tr= ue: files('gtk-egl.c'))
--
2.30.2




--
Marc-Andr=C3=A9 Lureau
--0000000000008c9d4905bdcd6f9e--