:p
atchew
Login
The following patches change blocking clipboard retrieval function calls in gtk-clipboard.c to non-blocking variants using callbacks to avoid UI lockup. It is a follow-up to the patch proposed in the GitLab issue https://gitlab.com/qemu-project/qemu/-/work_items/1150 This version adds a similar change to gd_clipboard_request that also uses the blocking clipboard API. The second patch removes the gtk-clipboard flag, keeping the GTK clipboard always enabled. Signed-off-by: Jindrich Makovicka <makovick@gmail.com> --- Changes in v3: - Use gtk_clipboard_request_contents to avoid text conversion when we only need to check if the text is available. - Link to v2: https://lore.kernel.org/qemu-devel/20260425-gtk-clipboard-v2-0-b098f3cf69cc@gmail.com Changes in v2: - Removed forgotten gtk-clipboard comment in meson_options.txt - Link to v1: https://lore.kernel.org/qemu-devel/20260425-gtk-clipboard-v1-0-6bd99e708e14@gmail.com --- Jindřich Makovička (2): ui/gtk: Use non-blocking clipboard retrieval ui/gtk: Keep GTK clipboard enabled meson.build | 4 ---- meson_options.txt | 7 ------- ui/gtk-clipboard.c | 52 +++++++++++++++++++++++++++++++++++++++------------- ui/gtk.c | 2 -- ui/meson.build | 4 +--- 5 files changed, 40 insertions(+), 29 deletions(-) --- base-commit: aa15257174da180c6a8a9d58f87319cfe61c5520 change-id: 20260425-gtk-clipboard-fdf99db13ac0 Best regards, -- Jindrich Makovicka
Signed-off-by: Jindrich Makovicka <makovick@gmail.com> --- ui/gtk-clipboard.c | 52 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk-clipboard.c +++ b/ui/gtk-clipboard.c @@ -XXX,XX +XXX,XX @@ static void gd_clipboard_notify(Notifier *notifier, void *data) } } +static void +gd_clipboard_request_text_received_callback(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + QemuClipboardInfo *info = (QemuClipboardInfo *)data; + + if (text) { + qemu_clipboard_set_data(info->owner, info, QEMU_CLIPBOARD_TYPE_TEXT, + strlen(text), text, true); + } + qemu_clipboard_info_unref(info); +} + static void gd_clipboard_request(QemuClipboardInfo *info, QemuClipboardType type) { GtkDisplayState *gd = container_of(info->owner, GtkDisplayState, cbpeer); - char *text; switch (type) { case QEMU_CLIPBOARD_TYPE_TEXT: - text = gtk_clipboard_wait_for_text(gd->gtkcb[info->selection]); - if (text) { - qemu_clipboard_set_data(&gd->cbpeer, info, type, - strlen(text), text, true); - g_free(text); - } + qemu_clipboard_info_ref(info); + gtk_clipboard_request_text(gd->gtkcb[info->selection], + gd_clipboard_request_text_received_callback, + info); break; default: break; } } +static void gd_clipboard_owner_change_contents_received_callback( + GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + QemuClipboardInfo *info = (QemuClipboardInfo *)data; + + if (selection_data) { + if (gtk_selection_data_targets_include_text(selection_data)) { + info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; + } + } + + qemu_clipboard_update(info); + qemu_clipboard_info_unref(info); +} + static void gd_owner_change(GtkClipboard *clipboard, GdkEvent *event, gpointer data) @@ -XXX,XX +XXX,XX @@ static void gd_owner_change(GtkClipboard *clipboard, switch (event->owner_change.reason) { case GDK_OWNER_CHANGE_NEW_OWNER: info = qemu_clipboard_info_new(&gd->cbpeer, s); - if (gtk_clipboard_wait_is_text_available(clipboard)) { - info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; - } - - qemu_clipboard_update(info); - qemu_clipboard_info_unref(info); + gtk_clipboard_request_contents( + clipboard, gdk_atom_intern_static_string("TARGETS"), + gd_clipboard_owner_change_contents_received_callback, + info); break; default: qemu_clipboard_peer_release(&gd->cbpeer, s); -- 2.53.0
Signed-off-by: Jindrich Makovicka <makovick@gmail.com> --- meson.build | 4 ---- meson_options.txt | 7 ------- ui/gtk.c | 2 -- ui/meson.build | 4 +--- 4 files changed, 1 insertion(+), 16 deletions(-) diff --git a/meson.build b/meson.build index XXXXXXX..XXXXXXX 100644 --- a/meson.build +++ b/meson.build @@ -XXX,XX +XXX,XX @@ endif gtk = not_found gtkx11 = not_found vte = not_found -have_gtk_clipboard = get_option('gtk_clipboard').enabled() if get_option('gtk') \ .disable_auto_if(not have_system) \ @@ -XXX,XX +XXX,XX @@ if get_option('gtk') \ method: 'pkg-config', required: get_option('vte')) endif - elif have_gtk_clipboard - error('GTK clipboard requested, but GTK not found') endif endif @@ -XXX,XX +XXX,XX @@ if glusterfs.found() endif config_host_data.set('CONFIG_GTK', gtk.found()) config_host_data.set('CONFIG_VTE', vte.found()) -config_host_data.set('CONFIG_GTK_CLIPBOARD', have_gtk_clipboard) config_host_data.set('CONFIG_HEXAGON_IDEF_PARSER', get_option('hexagon_idef_parser')) config_host_data.set('CONFIG_LIBATTR', have_old_libattr) config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found()) diff --git a/meson_options.txt b/meson_options.txt index XXXXXXX..XXXXXXX 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -XXX,XX +XXX,XX @@ option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('vte', type : 'feature', value : 'auto', description: 'vte support for the gtk UI') - -# GTK Clipboard implementation is disabled by default, since it may cause hangs -# of the guest VCPUs. See gitlab issue 1150: -# https://gitlab.com/qemu-project/qemu/-/issues/1150 - -option('gtk_clipboard', type: 'feature', value : 'disabled', - description: 'clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)') option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') option('zstd', type : 'feature', value : 'auto', diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) opts->u.gtk.show_tabs) { gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item)); } -#ifdef CONFIG_GTK_CLIPBOARD gd_clipboard_init(s); -#endif /* CONFIG_GTK_CLIPBOARD */ /* GTK's event polling must happen on the main thread. */ qemu_main = NULL; diff --git a/ui/meson.build b/ui/meson.build index XXXXXXX..XXXXXXX 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -XXX,XX +XXX,XX @@ if gtk.found() gtk_ss = ss.source_set() gtk_ss.add(gtk, vte, pixman, files('gtk.c')) - if have_gtk_clipboard - gtk_ss.add(files('gtk-clipboard.c')) - endif + gtk_ss.add(files('gtk-clipboard.c')) gtk_ss.add(when: x11, if_true: files('x_keymap.c')) gtk_ss.add(when: opengl, if_true: files('gtk-gl-area.c')) gtk_ss.add(when: [x11, opengl], if_true: files('gtk-egl.c')) -- 2.53.0
The following patches change blocking clipboard retrieval function calls in gtk-clipboard.c to non-blocking variants using callbacks to avoid UI lockup. It is a follow-up to the patch proposed in the GitLab issue https://gitlab.com/qemu-project/qemu/-/work_items/1150 This version adds a similar change to gd_clipboard_request that also uses the blocking clipboard API. The second patch removes the gtk-clipboard compile flag, keeping the GTK clipboard always compiled in. By default, clipboard is off, can be enabled by -display gtk,clipboard=on . Signed-off-by: Jindrich Makovicka <makovick@gmail.com> --- Changes in v5: - Runtime option added, clipboard still off by default - Link to v4: https://lore.kernel.org/qemu-devel/20260427-gtk-clipboard-v4-0-176ad9919931@gmail.com Changes in v4: - Replace gtk_clipboard_request_contents with gtk_clipboard_request_targets which can utilize GTK's target cache - Link to v3: https://lore.kernel.org/qemu-devel/20260426-gtk-clipboard-v3-0-09555aaeda34@gmail.com Changes in v3: - Use gtk_clipboard_request_contents to avoid text conversion when we only need to check if the text is available. - Link to v2: https://lore.kernel.org/qemu-devel/20260425-gtk-clipboard-v2-0-b098f3cf69cc@gmail.com Changes in v2: - Removed forgotten gtk-clipboard comment in meson_options.txt - Link to v1: https://lore.kernel.org/qemu-devel/20260425-gtk-clipboard-v1-0-6bd99e708e14@gmail.com --- Jindřich Makovička (2): ui/gtk: Use non-blocking clipboard retrieval ui/gtk: Turn clipboard flag into runtime option meson.build | 4 ---- meson_options.txt | 7 ------- qapi/ui.json | 6 +++++- qemu-options.hx | 9 ++++++--- ui/gtk-clipboard.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- ui/gtk.c | 8 +++++--- ui/meson.build | 4 +--- 7 files changed, 57 insertions(+), 34 deletions(-) --- base-commit: aa15257174da180c6a8a9d58f87319cfe61c5520 change-id: 20260425-gtk-clipboard-fdf99db13ac0 Best regards, -- Jindrich Makovicka
Signed-off-by: Jindrich Makovicka <makovick@gmail.com> --- ui/gtk-clipboard.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk-clipboard.c +++ b/ui/gtk-clipboard.c @@ -XXX,XX +XXX,XX @@ static void gd_clipboard_notify(Notifier *notifier, void *data) } } +static void +gd_clipboard_request_text_received_callback(GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + QemuClipboardInfo *info = (QemuClipboardInfo *)data; + + if (text) { + qemu_clipboard_set_data(info->owner, info, QEMU_CLIPBOARD_TYPE_TEXT, + strlen(text), text, true); + } + qemu_clipboard_info_unref(info); +} + static void gd_clipboard_request(QemuClipboardInfo *info, QemuClipboardType type) { GtkDisplayState *gd = container_of(info->owner, GtkDisplayState, cbpeer); - char *text; switch (type) { case QEMU_CLIPBOARD_TYPE_TEXT: - text = gtk_clipboard_wait_for_text(gd->gtkcb[info->selection]); - if (text) { - qemu_clipboard_set_data(&gd->cbpeer, info, type, - strlen(text), text, true); - g_free(text); - } + qemu_clipboard_info_ref(info); + gtk_clipboard_request_text(gd->gtkcb[info->selection], + gd_clipboard_request_text_received_callback, + info); break; default: break; } } +static void gd_clipboard_owner_change_targets_received_callback( + GtkClipboard *clipboard, + GdkAtom *targets, + gint n_targets, + gpointer data) +{ + QemuClipboardInfo *info = (QemuClipboardInfo *)data; + + if (n_targets) { + if (gtk_targets_include_text(targets, n_targets)) { + info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; + } + } + + qemu_clipboard_update(info); + qemu_clipboard_info_unref(info); +} + static void gd_owner_change(GtkClipboard *clipboard, GdkEvent *event, gpointer data) @@ -XXX,XX +XXX,XX @@ static void gd_owner_change(GtkClipboard *clipboard, switch (event->owner_change.reason) { case GDK_OWNER_CHANGE_NEW_OWNER: info = qemu_clipboard_info_new(&gd->cbpeer, s); - if (gtk_clipboard_wait_is_text_available(clipboard)) { - info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; - } - - qemu_clipboard_update(info); - qemu_clipboard_info_unref(info); + gtk_clipboard_request_targets( + clipboard, + gd_clipboard_owner_change_targets_received_callback, + info); break; default: qemu_clipboard_peer_release(&gd->cbpeer, s); -- 2.53.0
- Compile the GTK clipboard support unconditionally - Introduce GTK clipboard option, defaulting to off Signed-off-by: Jindrich Makovicka <makovick@gmail.com> --- meson.build | 4 ---- meson_options.txt | 7 ------- qapi/ui.json | 6 +++++- qemu-options.hx | 9 ++++++--- ui/gtk.c | 8 +++++--- ui/meson.build | 4 +--- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/meson.build b/meson.build index XXXXXXX..XXXXXXX 100644 --- a/meson.build +++ b/meson.build @@ -XXX,XX +XXX,XX @@ endif gtk = not_found gtkx11 = not_found vte = not_found -have_gtk_clipboard = get_option('gtk_clipboard').enabled() if get_option('gtk') \ .disable_auto_if(not have_system) \ @@ -XXX,XX +XXX,XX @@ if get_option('gtk') \ method: 'pkg-config', required: get_option('vte')) endif - elif have_gtk_clipboard - error('GTK clipboard requested, but GTK not found') endif endif @@ -XXX,XX +XXX,XX @@ if glusterfs.found() endif config_host_data.set('CONFIG_GTK', gtk.found()) config_host_data.set('CONFIG_VTE', vte.found()) -config_host_data.set('CONFIG_GTK_CLIPBOARD', have_gtk_clipboard) config_host_data.set('CONFIG_HEXAGON_IDEF_PARSER', get_option('hexagon_idef_parser')) config_host_data.set('CONFIG_LIBATTR', have_old_libattr) config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found()) diff --git a/meson_options.txt b/meson_options.txt index XXXXXXX..XXXXXXX 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -XXX,XX +XXX,XX @@ option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('vte', type : 'feature', value : 'auto', description: 'vte support for the gtk UI') - -# GTK Clipboard implementation is disabled by default, since it may cause hangs -# of the guest VCPUs. See gitlab issue 1150: -# https://gitlab.com/qemu-project/qemu/-/issues/1150 - -option('gtk_clipboard', type: 'feature', value : 'disabled', - description: 'clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)') option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') option('zstd', type : 'feature', value : 'auto', diff --git a/qapi/ui.json b/qapi/ui.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -XXX,XX +XXX,XX @@ # # GTK display options. # +# @clipboard: Enable host-guest clipboard sharing. Defaults to "off". +# (Since 11.1) +# # @grab-on-hover: Grab keyboard input on mouse hover. # # @zoom-to-fit: Zoom guest display to fit into the host window. When @@ -XXX,XX +XXX,XX @@ # Since: 2.12 ## { 'struct' : 'DisplayGTK', - 'data' : { '*grab-on-hover' : 'bool', + 'data' : { '*clipboard' : 'bool', + '*grab-on-hover' : 'bool', '*zoom-to-fit' : 'bool', '*show-tabs' : 'bool', '*show-menubar' : 'bool', diff --git a/qemu-options.hx b/qemu-options.hx index XXXXXXX..XXXXXXX 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -XXX,XX +XXX,XX @@ DEF("display", HAS_ARG, QEMU_OPTION_display, " [,window-close=on|off]\n" #endif #if defined(CONFIG_GTK) - "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n" - " [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n" - " [,show-menubar=on|off][,zoom-to-fit=on|off]\n" + "-display gtk[,clipboard=on|off][,full-screen=on|off][,gl=on|off]\n" + " [,grab-on-hover=on|off][,show-tabs=on|off][,show-cursor=on|off]\n" + " [,window-close=on|off][,show-menubar=on|off][,zoom-to-fit=on|off]\n" #endif #if defined(CONFIG_VNC) "-display vnc=<display>[,<optargs>]\n" @@ -XXX,XX +XXX,XX @@ SRST drop-down menus and other UI elements to configure and control the VM during runtime. Valid parameters are: + ``clipboard=on|off`` : Enable host-guest clipboard sharing, + defaults to "off" + ``full-screen=on|off`` : Start in fullscreen mode ``gl=on|off`` : Use OpenGL for displaying diff --git a/ui/gtk.c b/ui/gtk.c index XXXXXXX..XXXXXXX 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -XXX,XX +XXX,XX @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) opts->u.gtk.show_tabs) { gtk_menu_item_activate(GTK_MENU_ITEM(s->show_tabs_item)); } -#ifdef CONFIG_GTK_CLIPBOARD - gd_clipboard_init(s); -#endif /* CONFIG_GTK_CLIPBOARD */ + + if (opts->u.gtk.has_clipboard && + opts->u.gtk.clipboard) { + gd_clipboard_init(s); + } /* GTK's event polling must happen on the main thread. */ qemu_main = NULL; diff --git a/ui/meson.build b/ui/meson.build index XXXXXXX..XXXXXXX 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -XXX,XX +XXX,XX @@ if gtk.found() gtk_ss = ss.source_set() gtk_ss.add(gtk, vte, pixman, files('gtk.c')) - if have_gtk_clipboard - gtk_ss.add(files('gtk-clipboard.c')) - endif + gtk_ss.add(files('gtk-clipboard.c')) gtk_ss.add(when: x11, if_true: files('x_keymap.c')) gtk_ss.add(when: opengl, if_true: files('gtk-gl-area.c')) gtk_ss.add(when: [x11, opengl], if_true: files('gtk-egl.c')) -- 2.53.0