[Patch] Improve "Quit App" and system shutdown/logout
Original Reporter info from Mantis: zpeterson @boramis
-
Reporter name: Zoë Peterson
Original Reporter info from Mantis: zpeterson @boramis
- Reporter name: Zoë Peterson
Description:
LCL/Cocoa's "Quit Application" menu item currently blindly calls "Application.Mainform.Close", which crashes if the mainform isn't assigned. The widgetset also doesn't listen for "Quit" events coming from the system, so if the app is running when the user tries to shutdown or log out, it's killed without running any of the usual OnCloseQuery/OnClose/finalization blocks.
This patch cleans everything up by doing the following:
-
The "Quit Application" menu now calls a new TCocoaWidgetSet.QuitApp method. QuitApp calls a new TCocoaWidgetSet.OnQuitApp event and then calls Application.MainForm.Close or Application.Terminate. This allows third-party apps to trap it and do any app-wide confirmations.
-
Adds an event handler for the "kAEQuitApplication" Apple Event, which the system sends when the user has requested a shutdown or logout, or if the user or another app uses Apple Script to tell the app to close. The event handler likewise goes through TCocoaWidgetSet.QuitApp, so everything is centralized.
-
The kAEQuitApplication event handler also detects if the quit request was system-wide and calls TApplication's OnQueryEndSession/OnEndSession events if it is. Neither Carbon nor Cocoa did this previously, but the Win32 and Qt widgetset do.
Steps to reproduce:
*quitapp-Shutdown.zip* is a sample project that overrides TForm OnCloseQuery and OnClose, TApplication OnQueryEndSession and OnEndSession, and finalization. With the patch, the event handlers are all called in the same order as Win32 for both closing the application and shutting down, and you can cancel in either OnCloseQuery or OnQueryEndSession.
*quitapp-NoMainForm.zip* is a sample project that just creates a global menu without a corresponding form and waits until the user selects "Quit". I didn't include event handlers since it isn't necessary to show the desired behavior.
Additional information:
-
The most "Cocoa" way to handle shutdown is by overriding applicationShouldTerminate: in the app delegate. That doesn't work because after it returns the system calls applicationWillTerminate: and then immediately kills the application. We don't get a chance to run any code after Application.Run in the lpr, and none of the finalization sections run. Calling Application.Terminate and returning terminateLater also doesn't work, because it starts a modal message loop, so we never get back to the LCL RunLoop. LCLCOCOA already avoids using [NSApp terminate] for that exact reason.
-
The event handler we are adding needs to be assigned in applicationWillFinishLaunching: to take effect. Calling setEventHandler_... immediately after creating the app delegate doesn't work.
-
OnQuitApp could be exposed at a higher level to avoid needing to interact with TCocoaWidgetSet directly, but since it's Cocoa-specific behavior, it seemed appropriate to put it there. TWin32WidgetSet has an OnAsyncSocketMsg event that's similarly exposed.
-
OnQuitApp wasn't necessary on LCLCarbon because the system-provided "Quit Application" menu item also sent the kAEQuitApplication event, so we could just trap that. To get the same behavior you could move the MainForm.Close call into the event handler and have TCocoaMenuItem_Quit.lclItemSelected create and send a kAEQuitApplication event to the current process, but that seems messier and less pleasant to use.
Mantis conversion info:
- Mantis ID: 34798
- Fixed in revision: 61230 (#09ee3345)