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 MOZ_RUNINIT NSMutableArray* nsAppShell::gTopLevelViews =
24 [[NSMutableArray alloc] init];
26 #define ALOG(args...) \
27 fprintf(stderr, args); \
31 @interface ViewController : UIViewController
34 @implementation ViewController
37 ALOG("[ViewController loadView]");
38 CGRect r = {{0, 0}, {100, 100}};
39 self.view = [[UIView alloc] initWithFrame:r];
40 [self.view setBackgroundColor:[UIColor lightGrayColor]];
41 // add all of the top level views as children
42 for (UIView* v in nsAppShell::gTopLevelViews) {
43 ALOG("[ViewController.view addSubView:%p]", v);
44 [self.view addSubview:v];
46 [nsAppShell::gTopLevelViews release];
47 nsAppShell::gTopLevelViews = nil;
53 // Acts as a delegate for the UIApplication
55 @interface AppShellDelegate : NSObject <UIApplicationDelegate> {
57 @property(strong, nonatomic) UIWindow* window;
60 @implementation AppShellDelegate
62 - (BOOL)application:(UIApplication*)application
63 didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
64 ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]");
65 // We only create one window, since we can only display one window at
66 // a time anyway. Also, iOS 4 fails to display UIWindows if you
67 // create them before calling UIApplicationMain, so this makes more sense.
68 nsAppShell::gWindow = [[[UIWindow alloc]
69 initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain];
70 self.window = nsAppShell::gWindow;
72 self.window.rootViewController = [[ViewController alloc] init];
74 // just to make things more visible for now
75 nsAppShell::gWindow.backgroundColor = [UIColor blueColor];
76 [nsAppShell::gWindow makeKeyAndVisible];
81 - (void)applicationWillTerminate:(UIApplication*)application {
82 ALOG("[AppShellDelegate applicationWillTerminate:]");
83 nsAppShell::gAppShell->WillTerminate();
86 - (void)applicationDidBecomeActive:(UIApplication*)application {
87 ALOG("[AppShellDelegate applicationDidBecomeActive:]");
90 - (void)applicationWillResignActive:(UIApplication*)application {
91 ALOG("[AppShellDelegate applicationWillResignActive:]");
94 - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application {
95 ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]");
96 NS_NotifyOfMemoryPressure(MemoryPressureState::LowMemory);
100 // nsAppShell implementation
103 nsAppShell::ResumeNative(void) { return nsBaseAppShell::ResumeNative(); }
105 nsAppShell::nsAppShell()
106 : mAutoreleasePool(NULL),
109 mCFRunLoopSource(NULL),
111 mNotifiedWillTerminate(false) {
115 nsAppShell::~nsAppShell() {
116 if (mAutoreleasePool) {
117 [mAutoreleasePool release];
118 mAutoreleasePool = NULL;
122 if (mCFRunLoopSource) {
123 ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource,
124 kCFRunLoopCommonModes);
125 ::CFRelease(mCFRunLoopSource);
127 ::CFRelease(mCFRunLoop);
136 nsresult nsAppShell::Init() {
137 mAutoreleasePool = [[NSAutoreleasePool alloc] init];
139 // Add a CFRunLoopSource to the main native run loop. The source is
140 // responsible for interrupting the run loop when Gecko events are ready.
142 mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
143 NS_ENSURE_STATE(mCFRunLoop);
144 ::CFRetain(mCFRunLoop);
146 CFRunLoopSourceContext context;
147 bzero(&context, sizeof(context));
148 // context.version = 0;
150 context.perform = ProcessGeckoEvents;
152 mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
153 NS_ENSURE_STATE(mCFRunLoopSource);
155 ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
157 return nsBaseAppShell::Init();
160 // ProcessGeckoEvents
162 // The "perform" target of mCFRunLoop, called when mCFRunLoopSource is
163 // signalled from ScheduleNativeEventCallback.
166 void nsAppShell::ProcessGeckoEvents(void* aInfo) {
167 nsAppShell* self = static_cast<nsAppShell*>(aInfo);
168 self->NativeEventCallback();
175 void nsAppShell::WillTerminate() {
176 mNotifiedWillTerminate = true;
177 if (mTerminated) return;
179 // We won't get another chance to process events
180 NS_ProcessPendingEvents(NS_GetCurrentThread());
182 // Unless we call nsBaseAppShell::Exit() here, it might not get called
184 nsBaseAppShell::Exit();
187 // ScheduleNativeEventCallback
190 void nsAppShell::ScheduleNativeEventCallback() {
191 if (mTerminated) return;
195 // This will invoke ProcessGeckoEvents on the main thread.
196 ::CFRunLoopSourceSignal(mCFRunLoopSource);
197 ::CFRunLoopWakeUp(mCFRunLoop);
200 // ProcessNextNativeEvent
203 bool nsAppShell::ProcessNextNativeEvent(bool aMayWait) {
204 if (mTerminated) return false;
206 NSString* currentMode = nil;
207 NSDate* waitUntil = nil;
208 if (aMayWait) waitUntil = [NSDate distantFuture];
209 NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
211 BOOL eventProcessed = NO;
213 currentMode = [currentRunLoop currentMode];
214 if (!currentMode) currentMode = NSDefaultRunLoopMode;
217 eventProcessed = [currentRunLoop runMode:currentMode
218 beforeDate:waitUntil];
220 [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil];
221 } while (eventProcessed && aMayWait);
230 nsAppShell::Run(void) {
231 ALOG("nsAppShell::Run");
232 char argv[1][4] = {"app"};
233 UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate");
234 // UIApplicationMain doesn't exit. :-(
239 nsAppShell::Exit(void) {
240 if (mTerminated) return NS_OK;
243 return nsBaseAppShell::Exit();