#include #include #include #include #include "MacUtility.hpp" #include "GosuView.hpp" #import #import #import #import #include #include using namespace std::tr1::placeholders; namespace Gosu { static CGRect &screenRect() { static CGRect screenRect = [[UIScreen mainScreen] nativeBounds]; return screenRect; } unsigned screenWidth() { return screenRect().size.width; } unsigned screenHeight() { return screenRect().size.height; } ALCcontext *sharedContext(); } static void handleAudioInterruption(void *unused, UInt32 inInterruptionState) { if (inInterruptionState == kAudioSessionBeginInterruption) { alcMakeContextCurrent(NULL); } else if (inInterruptionState == kAudioSessionEndInterruption) { alcMakeContextCurrent(Gosu::sharedContext()); } } int main(int argc, char *argv[]) { try { [[NSAutoreleasePool alloc] init]; return UIApplicationMain(argc, argv, nil, @"GosuAppDelegate"); } catch (const std::exception& e) { NSLog(@"Terminating due to C++ exception: %s", e.what()); throw; } } class Gosu::Audio {}; struct Gosu::Window::Impl { ObjRef window; ObjRef controller; std::auto_ptr graphics; std::auto_ptr input; double interval; }; Gosu::Window& windowInstance(); @interface GosuAppDelegate : NSObject @end namespace { // Ugly patching to bridge the C++ and ObjC sides. GosuView* gosuView = nil; bool pausedSong = false; bool paused = false; id timerOrDisplayLink = nil; } @implementation GosuAppDelegate - (void)setupTimerOrDisplayLink { if (timerOrDisplayLink) return; NSInteger targetFPS = round(1000.0 / windowInstance().updateInterval()); if (60 % targetFPS != 0) { NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:windowInstance().updateInterval() / 1000.0 target:self selector:@selector(doTick:) userInfo:nil repeats:YES]; timerOrDisplayLink = [timer retain]; } else { CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doTick:)]; displayLink.frameInterval = 60 / targetFPS; [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; timerOrDisplayLink = [displayLink retain]; } } - (void)applicationDidFinishLaunching:(UIApplication *)application { [UIApplication sharedApplication].statusBarHidden = YES; [self setupTimerOrDisplayLink]; AudioSessionInitialize(NULL, NULL, handleAudioInterruption, NULL); } - (void)applicationWillResignActive:(UIApplication *)application { if (Gosu::Song::currentSong()) { Gosu::Song::currentSong()->pause(); pausedSong = true; } paused = true; windowInstance().loseFocus(); } - (void)applicationDidBecomeActive:(UIApplication *)application { if (pausedSong) { if (Gosu::Song::currentSong()) Gosu::Song::currentSong()->play(); pausedSong = false; } paused = false; } - (void)applicationDidEnterBackground:(UIApplication *)application { [timerOrDisplayLink invalidate]; [timerOrDisplayLink release]; timerOrDisplayLink = nil; } - (void)applicationWillEnterForeground:(UIApplication *)application { [self setupTimerOrDisplayLink]; } - (void)doTick:(id)sender { if (!paused) windowInstance().update(); [gosuView drawView]; Gosu::Song::update(); windowInstance().input().update(); } @end Gosu::Window::Window(unsigned width, unsigned height, bool fullscreen, double updateInterval) : pimpl(new Impl) { pimpl->window.reset([[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]); pimpl->controller.reset([[GosuViewController alloc] init]); gosuView = (GosuView*)pimpl->controller.obj().view; pimpl->window.obj().rootViewController = pimpl->controller.obj(); pimpl->graphics.reset(new Graphics(screenHeight(), screenWidth(), false)); pimpl->graphics->setResolution(width, height); pimpl->input.reset(new Input(gosuView, updateInterval)); pimpl->input->onTouchBegan = std::tr1::bind(&Window::touchBegan, this, _1); pimpl->input->onTouchMoved = std::tr1::bind(&Window::touchMoved, this, _1); pimpl->input->onTouchEnded = std::tr1::bind(&Window::touchEnded, this, _1); pimpl->interval = updateInterval; // TODO: Get rid of performSelector:withObject:afterDelay:, without causing a C++ static initialization error [pimpl->window.obj() performSelector:@selector(makeKeyAndVisible) withObject:nil afterDelay:0]; } Gosu::Window::~Window() { } std::wstring Gosu::Window::caption() const { return L""; } void Gosu::Window::setCaption(const std::wstring& caption) { } double Gosu::Window::updateInterval() const { return pimpl->interval; } const Gosu::Graphics& Gosu::Window::graphics() const { return *pimpl->graphics; } Gosu::Graphics& Gosu::Window::graphics() { return *pimpl->graphics; } const Gosu::Audio& Gosu::Window::audio() const { static Gosu::Audio audio; return audio; } Gosu::Audio& Gosu::Window::audio() { static Gosu::Audio audio; return audio; } const Gosu::Input& Gosu::Window::input() const { return *pimpl->input; } Gosu::Input& Gosu::Window::input() { return *pimpl->input; } void Gosu::Window::show() { } void Gosu::Window::close() { throw std::logic_error("Cannot close windows manually on iOS"); } void* Gosu::Window::rootViewController() const { return pimpl->controller.get(); }