Fix macOS quit: intercept applicationShouldTerminate: to allow real exit

GLFW's applicationShouldTerminate: converts all quit paths (Cmd+Q, dock,
menu bar) into window close requests and returns NSTerminateCancel, so our
close-to-hide intercept was hiding the window instead of letting the app exit.

Fix by implementing applicationShouldTerminate: in ReopenDelegate, setting a
darwinQuitting flag before forwarding to GLFW's delegate. The close intercept
checks this flag and calls Window.Close() instead of Hide() when quitting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Drew Weymouth
2026-03-30 17:57:20 -07:00
co-authored by Claude Sonnet 4.6
parent 9a30559343
commit d43e0c126b
4 changed files with 29 additions and 1 deletions
+13
View File
@@ -11,6 +11,14 @@ import "fyne.io/fyne/v2"
var darwinAppDelegateReopenWindow fyne.Window var darwinAppDelegateReopenWindow fyne.Window
// darwinQuitting is set to true when applicationShouldTerminate: fires so
// the close intercept knows to actually close instead of hide.
var darwinQuitting bool
func isRealQuit() bool {
return darwinQuitting
}
func installReopenHandler(w fyne.Window) { func installReopenHandler(w fyne.Window) {
darwinAppDelegateReopenWindow = w darwinAppDelegateReopenWindow = w
C.installReopenDelegate() C.installReopenDelegate()
@@ -25,3 +33,8 @@ func appReopened() {
fyne.Do(darwinAppDelegateReopenWindow.Show) fyne.Do(darwinAppDelegateReopenWindow.Show)
}() }()
} }
//export appShouldTerminate
func appShouldTerminate() {
darwinQuitting = true
}
+11
View File
@@ -3,6 +3,7 @@
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
extern void appReopened(void); extern void appReopened(void);
extern void appShouldTerminate(void);
@interface ReopenDelegate : NSObject<NSApplicationDelegate> @interface ReopenDelegate : NSObject<NSApplicationDelegate>
@property (nonatomic, strong) id<NSApplicationDelegate> wrapped; @property (nonatomic, strong) id<NSApplicationDelegate> wrapped;
@@ -11,6 +12,16 @@ extern void appReopened(void);
@implementation ReopenDelegate @implementation ReopenDelegate
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
// Set the quitting flag before GLFW's delegate fires close requests for
// each window, so our close intercept knows to close rather than hide.
appShouldTerminate();
if ([self.wrapped respondsToSelector:_cmd]) {
return [self.wrapped applicationShouldTerminate:sender];
}
return NSTerminateNow;
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)app - (BOOL)applicationShouldHandleReopen:(NSApplication *)app
hasVisibleWindows:(BOOL)hasVisible { hasVisibleWindows:(BOOL)hasVisible {
if (!hasVisible) { if (!hasVisible) {
+4
View File
@@ -6,3 +6,7 @@ import "fyne.io/fyne/v2"
func installReopenHandler(w fyne.Window) { func installReopenHandler(w fyne.Window) {
} }
func isRealQuit() bool {
return false
}
+1 -1
View File
@@ -185,7 +185,7 @@ func NewMainWindow(fyneApp fyne.App, appName, displayAppName, appVersion string,
m.Window.SetCloseIntercept(func() { m.Window.SetCloseIntercept(func() {
m.SaveWindowSettings() m.SaveWindowSettings()
if runtime.GOOS == "darwin" || (app.Config.Application.CloseToSystemTray && m.HaveSystemTray()) { if (runtime.GOOS == "darwin" && !isRealQuit()) || (app.Config.Application.CloseToSystemTray && m.HaveSystemTray()) {
m.Window.Hide() m.Window.Hide()
} else { } else {
m.Window.Close() m.Window.Close()