From 91823b976ef079725928618056bb0035efdf06c6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 14:32:12 +0000 Subject: [PATCH 1/3] Initial plan From 65586c356d4bf659a7f1cbdec20d17c727b339aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 14:40:33 +0000 Subject: [PATCH 2/3] Fix GTK4 crash when creating and closing many shells Add signal handler tracking for GdkSurface signals in Shell.java. Store handler IDs when connecting signals and disconnect them in releaseWidget(). Add test for creating and disposing multiple shells. Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com> --- .../gtk/org/eclipse/swt/widgets/Shell.java | 19 +++++++++++++++++-- .../Test_org_eclipse_swt_widgets_Shell.java | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java index e6efbc73db..11f8ef0f04 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java @@ -133,6 +133,7 @@ public class Shell extends Decorations { boolean ignoreFocusOut, ignoreFocusIn; boolean ignoreFocusOutAfterGrab, grabbedFocus; Region originalRegion; + long gdkSurfaceNotifyStateHandler, gdkSurfaceComputeSizeHandler; static final int MAXIMUM_TRIM = 128; static final int BORDER = 3; @@ -965,8 +966,8 @@ void hookEvents () { // Replaced "window-state-event" with GdkSurface "notify::state", pass shellHandle as user_data GTK.gtk_widget_realize(shellHandle); long gdkSurface = gtk_widget_get_surface (shellHandle); - OS.g_signal_connect (gdkSurface, OS.notify_state, display.notifyProc, shellHandle); - OS.g_signal_connect (gdkSurface, OS.compute_size, display.computeSizeProc, shellHandle); + gdkSurfaceNotifyStateHandler = OS.g_signal_connect (gdkSurface, OS.notify_state, display.notifyProc, shellHandle); + gdkSurfaceComputeSizeHandler = OS.g_signal_connect (gdkSurface, OS.compute_size, display.computeSizeProc, shellHandle); OS.g_signal_connect(shellHandle, OS.notify_default_height, display.notifyProc, Widget.NOTIFY_DEFAULT_HEIGHT); OS.g_signal_connect(shellHandle, OS.notify_default_width, display.notifyProc, Widget.NOTIFY_DEFAULT_WIDTH); OS.g_signal_connect(shellHandle, OS.notify_maximized, display.notifyProc, Widget.NOTIFY_MAXIMIZED); @@ -3431,6 +3432,20 @@ void releaseWidget () { if (group != 0) OS.g_object_unref (group); group = modalGroup = 0; lastActive = null; + // Disconnect GdkSurface signal handlers for GTK4 + if (GTK.GTK4 && shellHandle != 0) { + long gdkSurface = gtk_widget_get_surface(shellHandle); + if (gdkSurface != 0) { + if (gdkSurfaceNotifyStateHandler != 0) { + OS.g_signal_handler_disconnect(gdkSurface, gdkSurfaceNotifyStateHandler); + gdkSurfaceNotifyStateHandler = 0; + } + if (gdkSurfaceComputeSizeHandler != 0) { + OS.g_signal_handler_disconnect(gdkSurface, gdkSurfaceComputeSizeHandler); + gdkSurfaceComputeSizeHandler = 0; + } + } + } if (regionToDispose != null) { regionToDispose.dispose(); } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Shell.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Shell.java index dccc258988..86eba82771 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Shell.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Shell.java @@ -1004,6 +1004,22 @@ public void test_Issue450_NoShellActivateOnSetFocus() { } } +@Test +public void test_createAndDisposeMultipleShells() { + // Test for GTK4 crash when creating and disposing many shells + // See https://github.com/eclipse-platform/eclipse.platform.swt/issues/XXXX + Display display = shell.getDisplay(); + for (int i = 0; i < 100; i++) { + Shell testShell = new Shell(display); + testShell.open(); + while (display.readAndDispatch()) { + // Process events + } + testShell.close(); + testShell.dispose(); + } +} + @Override public void test_setLocationLorg_eclipse_swt_graphics_Point() { //Setting location for Windows is not supported in GTK4 From 9ea3e4ceec6f792932bf2250973bf5f9fb0f519f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:23:30 +0000 Subject: [PATCH 3/3] Improve GTK4 signal disconnection to be more robust Check if widget is realized before accessing surface. Use gtk_native_get_surface directly to avoid calling realize during cleanup. Add error handling to prevent crashes during widget destruction. Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com> --- .../gtk/org/eclipse/swt/widgets/Shell.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java index 11f8ef0f04..74d490c4c3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java @@ -3433,17 +3433,21 @@ void releaseWidget () { group = modalGroup = 0; lastActive = null; // Disconnect GdkSurface signal handlers for GTK4 - if (GTK.GTK4 && shellHandle != 0) { - long gdkSurface = gtk_widget_get_surface(shellHandle); - if (gdkSurface != 0) { - if (gdkSurfaceNotifyStateHandler != 0) { - OS.g_signal_handler_disconnect(gdkSurface, gdkSurfaceNotifyStateHandler); - gdkSurfaceNotifyStateHandler = 0; - } - if (gdkSurfaceComputeSizeHandler != 0) { - OS.g_signal_handler_disconnect(gdkSurface, gdkSurfaceComputeSizeHandler); - gdkSurfaceComputeSizeHandler = 0; + if (GTK.GTK4 && shellHandle != 0 && GTK.gtk_widget_get_realized(shellHandle)) { + try { + long gdkSurface = GTK4.gtk_native_get_surface(GTK4.gtk_widget_get_native(shellHandle)); + if (gdkSurface != 0) { + if (gdkSurfaceNotifyStateHandler != 0) { + OS.g_signal_handler_disconnect(gdkSurface, gdkSurfaceNotifyStateHandler); + gdkSurfaceNotifyStateHandler = 0; + } + if (gdkSurfaceComputeSizeHandler != 0) { + OS.g_signal_handler_disconnect(gdkSurface, gdkSurfaceComputeSizeHandler); + gdkSurfaceComputeSizeHandler = 0; + } } + } catch (Throwable t) { + // Ignore errors during cleanup - widget may already be partially destroyed } } if (regionToDispose != null) {