From 0434e943d1eb302919d0864b735017919471d92b Mon Sep 17 00:00:00 2001 From: Atterpac Date: Thu, 29 May 2025 19:28:05 -0400 Subject: [PATCH 1/5] fix incorrect window destroy --- v3/pkg/events/defaults.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/v3/pkg/events/defaults.go b/v3/pkg/events/defaults.go index 926d86a60f3..35c6309fbaf 100644 --- a/v3/pkg/events/defaults.go +++ b/v3/pkg/events/defaults.go @@ -40,10 +40,8 @@ var defaultWindowEventMapping = map[string]map[WindowEventType]WindowEventType{ Mac.WindowZoomIn: Common.WindowZoomIn, Mac.WindowZoomOut: Common.WindowZoomOut, Mac.WindowZoomReset: Common.WindowZoomReset, - Mac.WindowShouldClose: Common.WindowClosing, - Mac.WindowDidResignKey: Common.WindowLostFocus, + Mac.WindowWillClose: Common.WindowClosing, Mac.WindowDidResignMain: Common.WindowLostFocus, - Mac.WindowDidResize: Common.WindowDidResize, }, "linux": { Linux.WindowDeleteEvent: Common.WindowClosing, From d9bd2724122e753d972f2e894b50eed1867c5848 Mon Sep 17 00:00:00 2001 From: Atterpac Date: Thu, 29 May 2025 19:57:44 -0400 Subject: [PATCH 2/5] remove duplicate common signals --- v3/pkg/events/defaults.go | 1 - 1 file changed, 1 deletion(-) diff --git a/v3/pkg/events/defaults.go b/v3/pkg/events/defaults.go index 35c6309fbaf..058498b1b13 100644 --- a/v3/pkg/events/defaults.go +++ b/v3/pkg/events/defaults.go @@ -41,7 +41,6 @@ var defaultWindowEventMapping = map[string]map[WindowEventType]WindowEventType{ Mac.WindowZoomOut: Common.WindowZoomOut, Mac.WindowZoomReset: Common.WindowZoomReset, Mac.WindowWillClose: Common.WindowClosing, - Mac.WindowDidResignMain: Common.WindowLostFocus, }, "linux": { Linux.WindowDeleteEvent: Common.WindowClosing, From e64acd7d39211c7abcea715e3f4de3ca3374ea6c Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 10 Jun 2025 22:42:15 +1000 Subject: [PATCH 3/5] Better handling of "shouldclose" --- v3/pkg/application/webview_window_darwin.go | 10 ++++++++++ v3/pkg/application/webview_window_darwin.h | 1 + v3/pkg/application/webview_window_darwin.m | 5 +++++ v3/pkg/events/defaults.go | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 28e693a2372..1e09d86d320 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -43,6 +43,7 @@ void* windowNew(unsigned int id, int width, int height, bool fraudulentWebsiteWa // Set delegate [window setDelegate:delegate]; delegate.windowId = id; + delegate.shouldClose = false; // Add NSView to window NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; @@ -600,8 +601,17 @@ void windowSetShadow(void* nsWindow, bool hasShadow) { } +// windowSetShouldClose sets the shouldClose flag on the window delegate +static void windowSetShouldClose(void *window, bool shouldClose) { + WebviewWindow* nsWindow = (WebviewWindow*)window; + WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; + delegate.shouldClose = shouldClose; +} + // windowClose closes the current window static void windowClose(void *window) { + // Set the shouldClose flag to allow the window to close + windowSetShouldClose(window, true); [(WebviewWindow*)window close]; } diff --git a/v3/pkg/application/webview_window_darwin.h b/v3/pkg/application/webview_window_darwin.h index f0f58d89635..cf109edbdf1 100644 --- a/v3/pkg/application/webview_window_darwin.h +++ b/v3/pkg/application/webview_window_darwin.h @@ -25,6 +25,7 @@ @property unsigned int invisibleTitleBarHeight; @property BOOL showToolbarWhenFullscreen; @property NSWindowStyleMask previousStyleMask; // Used to restore the window style mask when using frameless +@property BOOL shouldClose; // Flag to indicate if the window should close unconditionally - (void)handleLeftMouseUp:(NSWindow *)window; - (void)handleLeftMouseDown:(NSEvent*)event; diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index 616d8f4a50e..3654c891e9e 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -231,6 +231,11 @@ - (void)performZoomReset:(id)sender { @implementation WebviewWindowDelegate - (BOOL)windowShouldClose:(NSWindow *)sender { WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[sender delegate]; + // Check if this window should close unconditionally (called from Close() method) + if (delegate.shouldClose) { + return true; + } + // Otherwise, emit the WindowClosing event and let the application decide processWindowEvent(delegate.windowId, EventWindowShouldClose); return false; } diff --git a/v3/pkg/events/defaults.go b/v3/pkg/events/defaults.go index 058498b1b13..376bef36f9f 100644 --- a/v3/pkg/events/defaults.go +++ b/v3/pkg/events/defaults.go @@ -40,7 +40,7 @@ var defaultWindowEventMapping = map[string]map[WindowEventType]WindowEventType{ Mac.WindowZoomIn: Common.WindowZoomIn, Mac.WindowZoomOut: Common.WindowZoomOut, Mac.WindowZoomReset: Common.WindowZoomReset, - Mac.WindowWillClose: Common.WindowClosing, + Mac.WindowShouldClose: Common.WindowClosing, }, "linux": { Linux.WindowDeleteEvent: Common.WindowClosing, From 963024ec5bcb19f56c51d0e83de55e04e40cc431 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 11 Jun 2025 22:53:02 +1000 Subject: [PATCH 4/5] Better handling of "shouldclose" --- .../webview_window_close_darwin.go | 24 +++++++++++++++++++ v3/pkg/application/webview_window_darwin.go | 15 +++++------- v3/pkg/application/webview_window_darwin.h | 1 - v3/pkg/application/webview_window_darwin.m | 13 ++++++++-- 4 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 v3/pkg/application/webview_window_close_darwin.go diff --git a/v3/pkg/application/webview_window_close_darwin.go b/v3/pkg/application/webview_window_close_darwin.go new file mode 100644 index 00000000000..b3649f60c97 --- /dev/null +++ b/v3/pkg/application/webview_window_close_darwin.go @@ -0,0 +1,24 @@ +//go:build darwin + +package application + +/* +#include +*/ +import "C" + +//export windowShouldUnconditionallyClose +func windowShouldUnconditionallyClose(windowId uint) bool { + window := globalApplication.getWindowForID(windowId) + if window == nil { + globalApplication.debug("windowShouldUnconditionallyClose: window not found", "windowId", windowId) + return false + } + webviewWindow, ok := window.(*WebviewWindow) + if !ok { + globalApplication.debug("windowShouldUnconditionallyClose: window is not WebviewWindow", "windowId", windowId) + return false + } + globalApplication.debug("windowShouldUnconditionallyClose check", "windowId", windowId, "unconditionallyClose", webviewWindow.unconditionallyClose) + return webviewWindow.unconditionallyClose +} diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index 1e09d86d320..c436ab8bb80 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -43,7 +43,6 @@ void* windowNew(unsigned int id, int width, int height, bool fraudulentWebsiteWa // Set delegate [window setDelegate:delegate]; delegate.windowId = id; - delegate.shouldClose = false; // Add NSView to window NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width-1, height-1)]; @@ -601,17 +600,9 @@ void windowSetShadow(void* nsWindow, bool hasShadow) { } -// windowSetShouldClose sets the shouldClose flag on the window delegate -static void windowSetShouldClose(void *window, bool shouldClose) { - WebviewWindow* nsWindow = (WebviewWindow*)window; - WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[nsWindow delegate]; - delegate.shouldClose = shouldClose; -} // windowClose closes the current window static void windowClose(void *window) { - // Set the shouldClose flag to allow the window to close - windowSetShouldClose(window, true); [(WebviewWindow*)window close]; } @@ -913,10 +904,12 @@ func (w *macosWebviewWindow) getScreen() (*Screen, error) { } func (w *macosWebviewWindow) show() { + globalApplication.debug("Window showing", "windowId", w.parent.id, "title", w.parent.options.Title) C.windowShow(w.nsWindow) } func (w *macosWebviewWindow) hide() { + globalApplication.debug("Window hiding", "windowId", w.parent.id, "title", w.parent.options.Title) C.windowHide(w.nsWindow) } @@ -965,7 +958,11 @@ func (w *macosWebviewWindow) windowZoom() { } func (w *macosWebviewWindow) close() { + globalApplication.debug("Window close() called - setting unconditionallyClose flag", "windowId", w.parent.id, "title", w.parent.options.Title) + // Set the unconditionallyClose flag to allow the window to close + w.parent.unconditionallyClose = true C.windowClose(w.nsWindow) + globalApplication.debug("Window close() completed", "windowId", w.parent.id, "title", w.parent.options.Title) // TODO: Check if we need to unregister the window here or not } diff --git a/v3/pkg/application/webview_window_darwin.h b/v3/pkg/application/webview_window_darwin.h index cf109edbdf1..f0f58d89635 100644 --- a/v3/pkg/application/webview_window_darwin.h +++ b/v3/pkg/application/webview_window_darwin.h @@ -25,7 +25,6 @@ @property unsigned int invisibleTitleBarHeight; @property BOOL showToolbarWhenFullscreen; @property NSWindowStyleMask previousStyleMask; // Used to restore the window style mask when using frameless -@property BOOL shouldClose; // Flag to indicate if the window should close unconditionally - (void)handleLeftMouseUp:(NSWindow *)window; - (void)handleLeftMouseDown:(NSEvent*)event; diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index 3654c891e9e..bdd5d881077 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -7,6 +7,7 @@ extern void processURLRequest(unsigned int, void *); extern void processWindowKeyDownEvent(unsigned int, const char*); extern bool hasListeners(unsigned int); +extern bool windowShouldUnconditionallyClose(unsigned int); @implementation WebviewWindow - (WebviewWindow*) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; { @@ -231,11 +232,14 @@ - (void)performZoomReset:(id)sender { @implementation WebviewWindowDelegate - (BOOL)windowShouldClose:(NSWindow *)sender { WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[sender delegate]; + NSLog(@"[DEBUG] windowShouldClose called for window %d", delegate.windowId); // Check if this window should close unconditionally (called from Close() method) - if (delegate.shouldClose) { + if (windowShouldUnconditionallyClose(delegate.windowId)) { + NSLog(@"[DEBUG] Window %d closing unconditionally (Close() method called)", delegate.windowId); return true; } - // Otherwise, emit the WindowClosing event and let the application decide + // For user-initiated closes, emit WindowClosing event and let the application decide + NSLog(@"[DEBUG] Window %d close requested by user - emitting WindowClosing event", delegate.windowId); processWindowEvent(delegate.windowId, EventWindowShouldClose); return false; } @@ -451,11 +455,13 @@ - (void)windowDidMove:(NSNotification *)notification { } } - (void)windowDidOrderOffScreen:(NSNotification *)notification { + NSLog(@"[DEBUG] Window %d ordered OFF screen (hidden)", self.windowId); if( hasListeners(EventWindowDidOrderOffScreen) ) { processWindowEvent(self.windowId, EventWindowDidOrderOffScreen); } } - (void)windowDidOrderOnScreen:(NSNotification *)notification { + NSLog(@"[DEBUG] Window %d ordered ON screen (shown)", self.windowId); if( hasListeners(EventWindowDidOrderOnScreen) ) { processWindowEvent(self.windowId, EventWindowDidOrderOnScreen); } @@ -531,6 +537,7 @@ - (void)windowWillChangeOrderingMode:(NSNotification *)notification { } } - (void)windowWillClose:(NSNotification *)notification { + NSLog(@"[DEBUG] Window %d WILL close (window is actually closing)", self.windowId); if( hasListeners(EventWindowWillClose) ) { processWindowEvent(self.windowId, EventWindowWillClose); } @@ -576,11 +583,13 @@ - (void)windowWillMove:(NSNotification *)notification { } } - (void)windowWillOrderOffScreen:(NSNotification *)notification { + NSLog(@"[DEBUG] Window %d WILL order off screen (about to hide)", self.windowId); if( hasListeners(EventWindowWillOrderOffScreen) ) { processWindowEvent(self.windowId, EventWindowWillOrderOffScreen); } } - (void)windowWillOrderOnScreen:(NSNotification *)notification { + NSLog(@"[DEBUG] Window %d WILL order on screen (about to show)", self.windowId); if( hasListeners(EventWindowWillOrderOnScreen) ) { processWindowEvent(self.windowId, EventWindowWillOrderOnScreen); } From f1a3dc6c0de63539368909ed871ca98387cca370 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 11 Jun 2025 23:28:16 +1000 Subject: [PATCH 5/5] Fix ABI safety issues in exported function for macOS window closing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit feedback on ABI safety and data races: - Change exported function signature to use C types (C.uint, C.bool) for ABI safety - Convert unconditionallyClose field from bool to atomic uint32 for thread safety - Update all read/write operations to use atomic operations across platforms 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- v3/pkg/application/webview_window.go | 7 ++++--- v3/pkg/application/webview_window_close_darwin.go | 14 ++++++++------ v3/pkg/application/webview_window_darwin.go | 3 ++- v3/pkg/application/webview_window_windows.go | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index 374c423b7ad..a47b440b4c3 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -7,6 +7,7 @@ import ( "slices" "strings" "sync" + "sync/atomic" "text/template" "github.com/leaanthony/u" @@ -165,8 +166,8 @@ type WebviewWindow struct { // pendingJS holds JS that was sent to the window before the runtime was loaded pendingJS []string - // unconditionallyClose marks the window to be unconditionally closed - unconditionallyClose bool + // unconditionallyClose marks the window to be unconditionally closed (atomic) + unconditionallyClose uint32 } func (w *WebviewWindow) SetMenu(menu *Menu) { @@ -273,7 +274,7 @@ func NewWindow(options WebviewWindowOptions) *WebviewWindow { // Listen for window closing events and de result.OnWindowEvent(events.Common.WindowClosing, func(event *WindowEvent) { - result.unconditionallyClose = true + atomic.StoreUint32(&result.unconditionallyClose, 1) InvokeSync(result.markAsDestroyed) InvokeSync(result.impl.close) globalApplication.deleteWindowByID(result.id) diff --git a/v3/pkg/application/webview_window_close_darwin.go b/v3/pkg/application/webview_window_close_darwin.go index b3649f60c97..6723d6f4a89 100644 --- a/v3/pkg/application/webview_window_close_darwin.go +++ b/v3/pkg/application/webview_window_close_darwin.go @@ -6,19 +6,21 @@ package application #include */ import "C" +import "sync/atomic" //export windowShouldUnconditionallyClose -func windowShouldUnconditionallyClose(windowId uint) bool { - window := globalApplication.getWindowForID(windowId) +func windowShouldUnconditionallyClose(windowId C.uint) C.bool { + window := globalApplication.getWindowForID(uint(windowId)) if window == nil { globalApplication.debug("windowShouldUnconditionallyClose: window not found", "windowId", windowId) - return false + return C.bool(false) } webviewWindow, ok := window.(*WebviewWindow) if !ok { globalApplication.debug("windowShouldUnconditionallyClose: window is not WebviewWindow", "windowId", windowId) - return false + return C.bool(false) } - globalApplication.debug("windowShouldUnconditionallyClose check", "windowId", windowId, "unconditionallyClose", webviewWindow.unconditionallyClose) - return webviewWindow.unconditionallyClose + unconditionallyClose := atomic.LoadUint32(&webviewWindow.unconditionallyClose) != 0 + globalApplication.debug("windowShouldUnconditionallyClose check", "windowId", windowId, "unconditionallyClose", unconditionallyClose) + return C.bool(unconditionallyClose) } diff --git a/v3/pkg/application/webview_window_darwin.go b/v3/pkg/application/webview_window_darwin.go index c436ab8bb80..a620964f713 100644 --- a/v3/pkg/application/webview_window_darwin.go +++ b/v3/pkg/application/webview_window_darwin.go @@ -816,6 +816,7 @@ static void setIgnoreMouseEvents(void *nsWindow, bool ignore) { import "C" import ( "sync" + "sync/atomic" "unsafe" "github.com/wailsapp/wails/v3/internal/assetserver" @@ -960,7 +961,7 @@ func (w *macosWebviewWindow) windowZoom() { func (w *macosWebviewWindow) close() { globalApplication.debug("Window close() called - setting unconditionallyClose flag", "windowId", w.parent.id, "title", w.parent.options.Title) // Set the unconditionallyClose flag to allow the window to close - w.parent.unconditionallyClose = true + atomic.StoreUint32(&w.parent.unconditionallyClose, 1) C.windowClose(w.nsWindow) globalApplication.debug("Window close() completed", "windowId", w.parent.id, "title", w.parent.options.Title) // TODO: Check if we need to unregister the window here or not diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index a49255dd5c1..4811e2ee3eb 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -1129,7 +1129,7 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp } case w32.WM_CLOSE: - if w.parent.unconditionallyClose == false { + if atomic.LoadUint32(&w.parent.unconditionallyClose) == 0 { // We were called by `Close()` or pressing the close button on the window w.parent.emit(events.Windows.WindowClosing) return 0