From 0ff960c45b6a1b2c4ce330d88eb10b137d98906e Mon Sep 17 00:00:00 2001 From: Jehan Date: Thu, 23 Oct 2025 18:59:04 +0200 Subject: [PATCH] Issue #11613: crashing when freeing window handle. It is a tentative fix as I had this crash once but could not reproduce it, even redoing dozens of times the same thing I had done. It is mostly based on something which GDK docs of gdk_wayland_window_export_handle() says: > To unexport the window, gdk_wayland_window_unexport_handle() must be > called the same number of times as gdk_wayland_window_export_handle() > was called. Any 'exported' callback may still be invoked until the > window is unexported or destroyed. So when the GdkWindow of the widget still exists, let's make sure we call gdk_wayland_window_unexport_handle() as many times as necessary (no less, but also no more!), and also that we disconnect all handlers which could possibly call more export_handle(). --- libgimpwidgets/gimpwidgetsutils.c | 46 +++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/libgimpwidgets/gimpwidgetsutils.c b/libgimpwidgets/gimpwidgetsutils.c index b0650f4a11..65140c5da8 100644 --- a/libgimpwidgets/gimpwidgetsutils.c +++ b/libgimpwidgets/gimpwidgetsutils.c @@ -1194,15 +1194,40 @@ void gimp_widget_free_native_handle (GtkWidget *widget, GBytes **window_handle) { + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); +#ifdef GDK_WINDOWING_WAYLAND + GdkWindow *surface; +#endif + g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (window_handle != NULL); - #ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()) && - /* The GdkWindow is likely already destroyed. */ - gtk_widget_get_window (widget) != NULL) - gdk_wayland_window_unexport_handle (gtk_widget_get_window (widget)); - #endif + g_signal_handlers_disconnect_by_func (toplevel, + G_CALLBACK (gimp_widget_set_handle_on_mapped), + window_handle); + g_signal_handlers_disconnect_by_func (widget, + G_CALLBACK (gimp_widget_set_handle_on_mapped), + window_handle); + g_signal_handlers_disconnect_by_func (widget, + G_CALLBACK (gimp_widget_set_handle_on_realize), + window_handle); + +#ifdef GDK_WINDOWING_WAYLAND + surface = gtk_widget_get_window (widget); + + if (surface != NULL && GDK_IS_WAYLAND_WINDOW (surface)) + { + gint count; + + count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (surface), + "gimp_widget_set_handle_on_mapped-call-count")); + g_object_set_data (G_OBJECT (surface), + "gimp_widget_set_handle_on_mapped-call-count", + NULL); + while (count--) + gdk_wayland_window_unexport_handle (surface); + } +#endif g_clear_pointer (window_handle, g_bytes_unref); } @@ -1356,6 +1381,15 @@ gimp_widget_set_handle_on_mapped (GtkWidget *widget, #ifdef GDK_WINDOWING_WAYLAND if (GDK_IS_WAYLAND_WINDOW (surface)) { + gint count; + + count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (surface), + "gimp_widget_set_handle_on_mapped-call-count")); + count += 1; + g_object_set_data (G_OBJECT (surface), + "gimp_widget_set_handle_on_mapped-call-count", + GINT_TO_POINTER (count)); + /* I don't run this on "realize" event because somehow it locks * the whole processus in Wayland. The "map-event" happens * slightly after the window became visible and I didn't