fix(v3): App Exposé shows “ghost” window after calling App.Window.Current() (#4947)

* fix(macOS): run getCurrentWindowID on main thread and add nil checks

AppKit must be used on the main thread. getCurrentWindowID could be called from arbitrary Go goroutines, so dispatch to the main queue when not already on the main thread. Also guard NSApp, window, and delegate with nil checks and fall back to mainWindow when keyWindow is nil to avoid wrong or missing window ID.

* Update UNRELEASED_CHANGELOG.md

* Update UNRELEASED_CHANGELOG.md

* Update UNRELEASED_CHANGELOG.md

* fix: consistent indentation and changelog formatting

- Convert spaces to tabs in getCurrentWindowID() for codebase consistency
- Move changelog entry below the <!-- Bug fixes --> comment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Wilko 2026-02-03 21:41:14 +01:00 committed by GitHub
commit 247ce87fe7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 4 deletions

View file

@ -28,6 +28,7 @@ After processing, the content will be moved to the main changelog and this file
## Fixed
<!-- Bug fixes -->
- Fix "ghost windows" issue on macOS caused by not accessing AppKit APIs from the Main Thread in App.Window.Current() (#4947) by @wimaha
- Fix HTML `<input type="file">` not working on macOS by implementing WKUIDelegate runOpenPanelWithParameters (#4862)
- Fix native file drag-and-drop not working when using `@wailsio/runtime` npm module on macOS/Linux (#4953) by @leaanthony
- Fix binding generation for cross-package type aliases (#4578) by @fbbdev

View file

@ -146,10 +146,31 @@ static char* getAppName(void) {
// get the current window ID
static unsigned int getCurrentWindowID(void) {
NSWindow *window = [NSApp keyWindow];
// Get the window delegate
WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)[window delegate];
return delegate.windowId;
// AppKit must be accessed on the main thread. This function may be called
// from arbitrary Go goroutines, so we hop to the main queue when needed.
__block unsigned int result = 0;
if (NSApp == nil) {
return result;
}
void (^resolve)(void) = ^{
NSWindow *window = [NSApp keyWindow];
if (window == nil) {
window = [NSApp mainWindow];
}
if (window == nil) {
return;
}
WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)[window delegate];
if (delegate != nil) {
result = delegate.windowId;
}
};
if ([NSThread isMainThread]) {
resolve();
} else {
dispatch_sync(dispatch_get_main_queue(), resolve);
}
return result;
}
// Set the application icon