1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #import <UIKit/UIApplication.h>
7 #import <UIKit/UIScreen.h>
8 #import <UIKit/UIWindow.h>
9 #import <UIKit/UIViewController.h>
11 #include "nsAppShell.h"
13 #include "nsDirectoryServiceDefs.h"
15 #include "nsIRollupListener.h"
16 #include "nsIWidget.h"
17 #include "nsThreadUtils.h"
18 #include "nsMemoryPressure.h"
19 #include "nsServiceManagerUtils.h"
21 nsAppShell* nsAppShell::gAppShell = NULL;
22 UIWindow* nsAppShell::gWindow = nil;
23 NSMutableArray* nsAppShell::gTopLevelViews = [[NSMutableArray alloc] init];
25 #define ALOG(args...) \
26 fprintf(stderr, args); \
30 @interface ViewController : UIViewController
33 @implementation ViewController
36 ALOG("[ViewController loadView]");
37 CGRect r = {{0, 0}, {100, 100}};
38 self.view = [[UIView alloc] initWithFrame:r];
39 [self.view setBackgroundColor:[UIColor lightGrayColor]];
40 // add all of the top level views as children
41 for (UIView* v in nsAppShell::gTopLevelViews) {
42 ALOG("[ViewController.view addSubView:%p]", v);
43 [self.view addSubview:v];
45 [nsAppShell::gTopLevelViews release];
46 nsAppShell::gTopLevelViews = nil;
52 // Acts as a delegate for the UIApplication
54 @interface AppShellDelegate : NSObject <UIApplicationDelegate> {
56 @property(strong, nonatomic) UIWindow* window;
59 @implementation AppShellDelegate
61 - (BOOL)application:(UIApplication*)application
62 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
63 ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]");
64 // We only create one window, since we can only display one window at
65 // a time anyway. Also, iOS 4 fails to display UIWindows if you
66 // create them before calling UIApplicationMain, so this makes more sense.
67 nsAppShell::gWindow = [[[UIWindow alloc]
68 initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain];
69 self.window = nsAppShell::gWindow;
71 self.window.rootViewController = [[ViewController alloc] init];
73 // just to make things more visible for now
74 nsAppShell::gWindow.backgroundColor = [UIColor blueColor];
75 [nsAppShell::gWindow makeKeyAndVisible];
80 - (void)applicationWillTerminate:(UIApplication*)application {
81 ALOG("[AppShellDelegate applicationWillTerminate:]");
82 nsAppShell::gAppShell->WillTerminate();
85 - (void)applicationDidBecomeActive:(UIApplication*)application {
86 ALOG("[AppShellDelegate applicationDidBecomeActive:]");
89 - (void)applicationWillResignActive:(UIApplication*)application {
90 ALOG("[AppShellDelegate applicationWillResignActive:]");
93 - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application {
94 ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]");
95 NS_NotifyOfMemoryPressure(MemoryPressureState::LowMemory);
99 // nsAppShell implementation
102 nsAppShell::ResumeNative(void) { return nsBaseAppShell::ResumeNative(); }
104 nsAppShell::nsAppShell()
105 : mAutoreleasePool(NULL),
108 mCFRunLoopSource(NULL),
110 mNotifiedWillTerminate(false) {
114 nsAppShell::~nsAppShell() {
115 if (mAutoreleasePool) {
116 [mAutoreleasePool release];
117 mAutoreleasePool = NULL;
121 if (mCFRunLoopSource) {
122 ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource,
123 kCFRunLoopCommonModes);
124 ::CFRelease(mCFRunLoopSource);
126 ::CFRelease(mCFRunLoop);
135 nsresult nsAppShell::Init() {
136 mAutoreleasePool = [[NSAutoreleasePool alloc] init];
138 // Add a CFRunLoopSource to the main native run loop. The source is
139 // responsible for interrupting the run loop when Gecko events are ready.
141 mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
142 NS_ENSURE_STATE(mCFRunLoop);
143 ::CFRetain(mCFRunLoop);
145 CFRunLoopSourceContext context;
146 bzero(&context, sizeof(context));
147 // context.version = 0;
149 context.perform = ProcessGeckoEvents;
151 mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
152 NS_ENSURE_STATE(mCFRunLoopSource);
154 ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
156 return nsBaseAppShell::Init();
159 // ProcessGeckoEvents
161 // The "perform" target of mCFRunLoop, called when mCFRunLoopSource is
162 // signalled from ScheduleNativeEventCallback.
165 void nsAppShell::ProcessGeckoEvents(void* aInfo) {
166 nsAppShell* self = static_cast<nsAppShell*>(aInfo);
167 self->NativeEventCallback();
174 void nsAppShell::WillTerminate() {
175 mNotifiedWillTerminate = true;
176 if (mTerminated) return;
178 // We won't get another chance to process events
179 NS_ProcessPendingEvents(NS_GetCurrentThread());
181 // Unless we call nsBaseAppShell::Exit() here, it might not get called
183 nsBaseAppShell::Exit();
186 // ScheduleNativeEventCallback
189 void nsAppShell::ScheduleNativeEventCallback() {
190 if (mTerminated) return;
194 // This will invoke ProcessGeckoEvents on the main thread.
195 ::CFRunLoopSourceSignal(mCFRunLoopSource);
196 ::CFRunLoopWakeUp(mCFRunLoop);
199 // ProcessNextNativeEvent
202 bool nsAppShell::ProcessNextNativeEvent(bool aMayWait) {
203 if (mTerminated) return false;
205 NSString* currentMode = nil;
206 NSDate* waitUntil = nil;
207 if (aMayWait) waitUntil = [NSDate distantFuture];
208 NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
210 BOOL eventProcessed = NO;
212 currentMode = [currentRunLoop currentMode];
213 if (!currentMode) currentMode = NSDefaultRunLoopMode;
216 eventProcessed = [currentRunLoop runMode:currentMode
217 beforeDate:waitUntil];
219 [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil];
220 } while (eventProcessed && aMayWait);
229 nsAppShell::Run(void) {
230 ALOG("nsAppShell::Run");
231 char argv[1][4] = {"app"};
232 UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate");
233 // UIApplicationMain doesn't exit. :-(
238 nsAppShell::Exit(void) {
239 if (mTerminated) return NS_OK;
242 return nsBaseAppShell::Exit();