diff options
Diffstat (limited to 'widget/uikit/nsAppShell.mm')
-rw-r--r-- | widget/uikit/nsAppShell.mm | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/widget/uikit/nsAppShell.mm b/widget/uikit/nsAppShell.mm new file mode 100644 index 000000000..ac007132f --- /dev/null +++ b/widget/uikit/nsAppShell.mm @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#import <UIKit/UIApplication.h> +#import <UIKit/UIScreen.h> +#import <UIKit/UIWindow.h> +#import <UIKit/UIViewController.h> + +#include "nsAppShell.h" +#include "nsCOMPtr.h" +#include "nsIFile.h" +#include "nsDirectoryServiceDefs.h" +#include "nsString.h" +#include "nsIRollupListener.h" +#include "nsIWidget.h" +#include "nsThreadUtils.h" +#include "nsIWindowMediator.h" +#include "nsMemoryPressure.h" +#include "nsServiceManagerUtils.h" +#include "nsIInterfaceRequestor.h" +#include "nsIWebBrowserChrome.h" + +nsAppShell *nsAppShell::gAppShell = NULL; +UIWindow *nsAppShell::gWindow = nil; +NSMutableArray *nsAppShell::gTopLevelViews = [[NSMutableArray alloc] init]; + +#define ALOG(args...) fprintf(stderr, args); fprintf(stderr, "\n") + +// ViewController +@interface ViewController : UIViewController +@end + + +@implementation ViewController + +- (void)loadView { + ALOG("[ViewController loadView]"); + CGRect r = {{0, 0}, {100, 100}}; + self.view = [[UIView alloc] initWithFrame:r]; + [self.view setBackgroundColor:[UIColor lightGrayColor]]; + // add all of the top level views as children + for (UIView* v in nsAppShell::gTopLevelViews) { + ALOG("[ViewController.view addSubView:%p]", v); + [self.view addSubview:v]; + } + [nsAppShell::gTopLevelViews release]; + nsAppShell::gTopLevelViews = nil; +} +@end + +// AppShellDelegate +// +// Acts as a delegate for the UIApplication + +@interface AppShellDelegate : NSObject <UIApplicationDelegate> { +} +@property (strong, nonatomic) UIWindow *window; +@end + +@implementation AppShellDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]"); + // We only create one window, since we can only display one window at + // a time anyway. Also, iOS 4 fails to display UIWindows if you + // create them before calling UIApplicationMain, so this makes more sense. + nsAppShell::gWindow = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain]; + self.window = nsAppShell::gWindow; + + self.window.rootViewController = [[ViewController alloc] init]; + + // just to make things more visible for now + nsAppShell::gWindow.backgroundColor = [UIColor blueColor]; + [nsAppShell::gWindow makeKeyAndVisible]; + + return YES; +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationWillTerminate:]"); + nsAppShell::gAppShell->WillTerminate(); +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationDidBecomeActive:]"); +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationWillResignActive:]"); +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]"); + NS_DispatchMemoryPressure(MemPressure_New); +} +@end + +// nsAppShell implementation + +NS_IMETHODIMP +nsAppShell::ResumeNative(void) +{ + return nsBaseAppShell::ResumeNative(); +} + +nsAppShell::nsAppShell() + : mAutoreleasePool(NULL), + mDelegate(NULL), + mCFRunLoop(NULL), + mCFRunLoopSource(NULL), + mTerminated(false), + mNotifiedWillTerminate(false) +{ + gAppShell = this; +} + +nsAppShell::~nsAppShell() +{ + if (mAutoreleasePool) { + [mAutoreleasePool release]; + mAutoreleasePool = NULL; + } + + if (mCFRunLoop) { + if (mCFRunLoopSource) { + ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource, + kCFRunLoopCommonModes); + ::CFRelease(mCFRunLoopSource); + } + ::CFRelease(mCFRunLoop); + } + + gAppShell = NULL; +} + +// Init +// +// public +nsresult +nsAppShell::Init() +{ + mAutoreleasePool = [[NSAutoreleasePool alloc] init]; + + // Add a CFRunLoopSource to the main native run loop. The source is + // responsible for interrupting the run loop when Gecko events are ready. + + mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; + NS_ENSURE_STATE(mCFRunLoop); + ::CFRetain(mCFRunLoop); + + CFRunLoopSourceContext context; + bzero(&context, sizeof(context)); + // context.version = 0; + context.info = this; + context.perform = ProcessGeckoEvents; + + mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + NS_ENSURE_STATE(mCFRunLoopSource); + + ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes); + + return nsBaseAppShell::Init(); +} + +// ProcessGeckoEvents +// +// The "perform" target of mCFRunLoop, called when mCFRunLoopSource is +// signalled from ScheduleNativeEventCallback. +// +// protected static +void +nsAppShell::ProcessGeckoEvents(void* aInfo) +{ + nsAppShell* self = static_cast<nsAppShell*> (aInfo); + self->NativeEventCallback(); + self->Release(); +} + +// WillTerminate +// +// public +void +nsAppShell::WillTerminate() +{ + mNotifiedWillTerminate = true; + if (mTerminated) + return; + mTerminated = true; + // We won't get another chance to process events + NS_ProcessPendingEvents(NS_GetCurrentThread()); + + // Unless we call nsBaseAppShell::Exit() here, it might not get called + // at all. + nsBaseAppShell::Exit(); +} + +// ScheduleNativeEventCallback +// +// protected virtual +void +nsAppShell::ScheduleNativeEventCallback() +{ + if (mTerminated) + return; + + NS_ADDREF_THIS(); + + // This will invoke ProcessGeckoEvents on the main thread. + ::CFRunLoopSourceSignal(mCFRunLoopSource); + ::CFRunLoopWakeUp(mCFRunLoop); +} + +// ProcessNextNativeEvent +// +// protected virtual +bool +nsAppShell::ProcessNextNativeEvent(bool aMayWait) +{ + if (mTerminated) + return false; + + NSString* currentMode = nil; + NSDate* waitUntil = nil; + if (aMayWait) + waitUntil = [NSDate distantFuture]; + NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; + + BOOL eventProcessed = NO; + do { + currentMode = [currentRunLoop currentMode]; + if (!currentMode) + currentMode = NSDefaultRunLoopMode; + + if (aMayWait) + eventProcessed = [currentRunLoop runMode:currentMode beforeDate:waitUntil]; + else + [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil]; + } while(eventProcessed && aMayWait); + + return false; +} + +// Run +// +// public +NS_IMETHODIMP +nsAppShell::Run(void) +{ + ALOG("nsAppShell::Run"); + char argv[1][4] = {"app"}; + UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate"); + // UIApplicationMain doesn't exit. :-( + return NS_OK; +} + +NS_IMETHODIMP +nsAppShell::Exit(void) +{ + if (mTerminated) + return NS_OK; + + mTerminated = true; + return nsBaseAppShell::Exit(); +} |