|
| 1 | +// Copyright 2013 The Flutter Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterApplication.h" |
| 6 | +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterApplication_Internal.h" |
| 7 | + |
| 8 | +#include "flutter/shell/platform/embedder/embedder.h" |
| 9 | +#import "shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" |
| 10 | +#import "shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h" |
| 11 | +#import "shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" |
| 12 | + |
| 13 | +// An NSApplication subclass that implements overrides necessary for some |
| 14 | +// Flutter features, like application lifecycle handling. |
| 15 | +@implementation FlutterApplication |
| 16 | + |
| 17 | +// Initialize NSApplication using the custom subclass. Check whether NSApp was |
| 18 | +// already initialized using another class, because that would break some |
| 19 | +// things. Warn about the mismatch only once, and only in debug builds. |
| 20 | ++ (NSApplication*)sharedApplication { |
| 21 | + NSApplication* app = [super sharedApplication]; |
| 22 | + |
| 23 | + // +sharedApplication initializes the global NSApp, so if we're delivering |
| 24 | + // something other than a FlutterApplication, warn the developer once. |
| 25 | +#ifndef FLUTTER_RELEASE |
| 26 | + static dispatch_once_t onceToken = 0; |
| 27 | + dispatch_once(&onceToken, ^{ |
| 28 | + if (![app respondsToSelector:@selector(terminateApplication:)]) { |
| 29 | + NSLog(@"NSApp should be of type %s, not %s.\n" |
| 30 | + "System requests for the application to terminate will not be sent to " |
| 31 | + "the Flutter framework, so the framework will be unable to cancel " |
| 32 | + "those requests.\n" |
| 33 | + "Modify the application's NSPrincipleClass to be %s in the " |
| 34 | + "Info.plist to fix this.", |
| 35 | + [[self className] UTF8String], [[NSApp className] UTF8String], |
| 36 | + [[self className] UTF8String]); |
| 37 | + } |
| 38 | + }); |
| 39 | +#endif // !FLUTTER_RELEASE |
| 40 | + return app; |
| 41 | +} |
| 42 | + |
| 43 | +// |terminate| is the entry point for orderly "quit" operations in Cocoa. This |
| 44 | +// includes the application menu's Quit menu item and keyboard equivalent, the |
| 45 | +// application's dock icon menu's Quit menu item, "quit" (not "force quit") in |
| 46 | +// the Activity Monitor, and quits triggered by user logout and system restart |
| 47 | +// and shutdown. |
| 48 | +// |
| 49 | +// We override the normal |terminate| implementation. Our implementation, which |
| 50 | +// is specific to the asynchronous nature of Flutter, works by asking the |
| 51 | +// application delegate to terminate using its |requestApplicationTermination| |
| 52 | +// method instead of going through |applicationShouldTerminate|. |
| 53 | +// |
| 54 | +// The standard |applicationShouldTerminate| is not used because returning |
| 55 | +// NSTerminateLater from that function moves the run loop into a modal dialog |
| 56 | +// mode (NSModalPanelRunLoopMode), which stops the main run loop from processing |
| 57 | +// messages like, for instance, the response to the method channel call, and |
| 58 | +// code paths leading to it must be redirected to |requestApplicationTermination|. |
| 59 | +// |
| 60 | +// |requestApplicationTermination| differs from the standard |
| 61 | +// |applicationShouldTerminate| in that no special event loop is run in the case |
| 62 | +// that immediate termination is not possible (e.g., if dialog boxes allowing |
| 63 | +// the user to cancel have to be shown, or data needs to be saved). Instead, |
| 64 | +// requestApplicationTermination sends a method channel call to the framework asking |
| 65 | +// it if it is OK to terminate. When that method channel call returns with a |
| 66 | +// result, the application either terminates or continues running. |
| 67 | +- (void)terminate:(id)sender { |
| 68 | + FlutterAppDelegate* delegate = [self delegate]; |
| 69 | + if (!delegate || ![delegate respondsToSelector:@selector(terminationHandler)] || |
| 70 | + [delegate terminationHandler] == nil) { |
| 71 | + // If there's no termination handler, then just terminate. |
| 72 | + [super terminate:sender]; |
| 73 | + } |
| 74 | + FlutterEngineTerminationHandler* terminationHandler = |
| 75 | + [static_cast<FlutterAppDelegate*>([self delegate]) terminationHandler]; |
| 76 | + [terminationHandler requestApplicationTermination:sender |
| 77 | + exitType:kFlutterAppExitTypeCancelable |
| 78 | + result:nil]; |
| 79 | + // Return, don't exit. The application delegate is responsible for exiting on |
| 80 | + // its own by calling |terminateApplication|. |
| 81 | +} |
| 82 | + |
| 83 | +// Starts the regular Cocoa application termination flow, so that plugins will |
| 84 | +// get the appropriate notifications after the application has already decided |
| 85 | +// to quit. This is called after the application has decided that |
| 86 | +// it's OK to terminate. |
| 87 | +- (void)terminateApplication:(id)sender { |
| 88 | + [super terminate:sender]; |
| 89 | +} |
| 90 | +@end |
0 commit comments