From 4f70bdeab5c6805c0334bb124a8c75e7a438f9a0 Mon Sep 17 00:00:00 2001 From: meancoot Date: Wed, 14 Aug 2013 10:07:36 -0400 Subject: [PATCH] (Apple) Split platform specific code out of main.m --- apple/OSX/platform.h | 42 ++ apple/OSX/platform.m | 264 +++++++ apple/RetroArch/RetroArch_Apple.h | 76 +-- apple/RetroArch/main.m | 643 +----------------- apple/RetroArch_OSX.xcodeproj/project.pbxproj | 6 + apple/RetroArch_iOS.xcodeproj/project.pbxproj | 4 + apple/iOS/platform.h | 42 ++ apple/iOS/platform.m | 346 ++++++++++ apple/iOS/settings.m | 2 +- 9 files changed, 767 insertions(+), 658 deletions(-) create mode 100644 apple/OSX/platform.h create mode 100644 apple/OSX/platform.m create mode 100644 apple/iOS/platform.h create mode 100644 apple/iOS/platform.m diff --git a/apple/OSX/platform.h b/apple/OSX/platform.h new file mode 100644 index 0000000000..f858ade962 --- /dev/null +++ b/apple/OSX/platform.h @@ -0,0 +1,42 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __RARCH_OSX_PLATFORM_H +#define __RARCH_OSX_PLATFORM_H + +#import + +@interface RAGameView : NSOpenGLView + ++ (RAGameView*)get; +- (void)display; + +@end + +@interface RetroArch_OSX : NSObject +{ +@public + NSWindow IBOutlet *window; +} + ++ (RetroArch_OSX*)get; + +- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; +- (void)unloadingCore:(RAModuleInfo*)core; + +@end + +#endif diff --git a/apple/OSX/platform.m b/apple/OSX/platform.m new file mode 100644 index 0000000000..b15d053e14 --- /dev/null +++ b/apple/OSX/platform.m @@ -0,0 +1,264 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include + +#import "RetroArch_Apple.h" +#include "rarch_wrapper.h" +#include "../RetroArch/apple_input.h" + +// If USE_XATTR is defined any loaded file will get a com.RetroArch.Core extended attribute +// specifying which core was used to load. +//#define USE_XATTR + +#if defined(USE_XATTR) +#include "sys/xattr.h" +#endif + +#include "file.h" + +@interface RApplication : NSApplication +@end + +@implementation RApplication + +- (void)sendEvent:(NSEvent *)event +{ + [super sendEvent:event]; + + if (event.type == GSEVENT_TYPE_KEYDOWN || event.type == GSEVENT_TYPE_KEYUP) + apple_input_handle_key_event(event.keyCode, event.type == GSEVENT_TYPE_KEYDOWN); +} + +@end + +@implementation RetroArch_OSX +{ + NSWindow IBOutlet* _coreSelectSheet; + + bool _isTerminating; + bool _loaded; + bool _wantReload; + NSString* _file; + RAModuleInfo* _core; +} + ++ (RetroArch_OSX*)get +{ + return (RetroArch_OSX*)[[NSApplication sharedApplication] delegate]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + apple_platform = self; + _loaded = true; + + [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + + RAGameView.get.frame = [window.contentView bounds]; + [window.contentView setAutoresizesSubviews:YES]; + [window.contentView addSubview:RAGameView.get]; + [window makeFirstResponder:RAGameView.get]; + + // Create core select list + NSComboBox* cb = (NSComboBox*)[_coreSelectSheet.contentView viewWithTag:1]; + + for (RAModuleInfo* i in RAModuleInfo.getModules) + [cb addItemWithObjectValue:i]; + + if (cb.numberOfItems) + [cb selectItemAtIndex:0]; + else + apple_display_alert(@"No libretro cores were found.", @"RetroArch"); + + // Run RGUI if needed + if (!_wantReload) + apple_run_core(nil, 0); + else + [self chooseCore]; + + _wantReload = false; + + extern void osx_pad_init(); + osx_pad_init(); +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication +{ + return YES; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + _isTerminating = true; + + if (apple_is_running) + apple_frontend_post_event(apple_event_basic_command, (void*)QUIT); + + return apple_is_running ? NSTerminateCancel : NSTerminateNow; +} + + +- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames +{ + if (filenames.count == 1 && filenames[0]) + { + _file = filenames[0]; + + if (!_loaded) + _wantReload = true; + else + [self chooseCore]; + + [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; + } + else + { + apple_display_alert(@"Cannot open multiple files", @"RetroArch"); + [sender replyToOpenOrPrint:NSApplicationDelegateReplyFailure]; + } +} + +- (void)openDocument:(id)sender +{ + NSOpenPanel* panel = [NSOpenPanel openPanel]; + [panel beginSheetModalForWindow:window completionHandler:^(NSInteger result) + { + [NSApplication.sharedApplication stopModal]; + + if (result == NSOKButton && panel.URL) + { + _file = panel.URL.path; + [self performSelector:@selector(chooseCore) withObject:nil afterDelay:.5f]; + } + }]; + [NSApplication.sharedApplication runModalForWindow:panel]; +} + +// This utility function will queue the _core and _file instance values for running. +// If the emulator thread is already running it will tell it to quit. +- (void)runCore +{ + _wantReload = apple_is_running; + + if (!apple_is_running) + apple_run_core(_core, _file.UTF8String); + else + apple_frontend_post_event(apple_event_basic_command, (void*)QUIT); +} + +- (void)chooseCore +{ +#ifdef USE_XATTR + char stored_name[PATH_MAX]; + if (getxattr(_file.UTF8String, "com.RetroArch.Core", stored_name, PATH_MAX, 0, 0) > 0) + { + for (RAModuleInfo* i in RAModuleInfo.getModules) + { + const char* core_name = i.path.lastPathComponent.UTF8String; + if (strcmp(core_name, stored_name) == 0) + { + _core = i; + [self runCore]; + return; + } + } + } +#endif + + [NSApplication.sharedApplication beginSheet:_coreSelectSheet modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil]; + [NSApplication.sharedApplication runModalForWindow:_coreSelectSheet]; +} + +- (IBAction)coreWasChosen:(id)sender +{ + [NSApplication.sharedApplication stopModal]; + [NSApplication.sharedApplication endSheet:_coreSelectSheet returnCode:0]; + [_coreSelectSheet orderOut:self]; + + if (_isTerminating) + return; + + NSComboBox* cb = (NSComboBox*)[_coreSelectSheet.contentView viewWithTag:1]; + _core = (RAModuleInfo*)cb.objectValueOfSelectedItem; + + [self runCore]; +} + +#pragma mark RetroArch_Platform +- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file +{ + if (file) + { + [NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:file]]]; + +#ifdef USE_XATTR + const char* core_name = core.path.lastPathComponent.UTF8String; + setxattr(file, "com.RetroArch.Core", core_name, strlen(core_name) + 1, 0, 0); +#endif + } +} + +- (void)unloadingCore:(RAModuleInfo*)core +{ + if (_isTerminating) + [NSApplication.sharedApplication terminate:nil]; + + if (_wantReload) + apple_run_core(_core, _file.UTF8String); + else if(apple_use_tv_mode) + apple_run_core(nil, 0); + else + [NSApplication.sharedApplication terminate:nil]; + + _wantReload = false; +} + +- (NSString*)retroarchConfigPath +{ + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + return [paths[0] stringByAppendingPathComponent:@"RetroArch/retroarch.cfg"]; +} + +- (NSString*)corePath +{ + return [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"Contents/Resources/modules"]; +} + +#pragma mark Menus +- (IBAction)showPreferences:(id)sender +{ + [[[NSWindowController alloc] initWithWindowNibName:@"Settings"] window]; +} + +- (IBAction)basicEvent:(id)sender +{ + if (apple_is_running) + apple_frontend_post_event(&apple_event_basic_command, (void*)((NSMenuItem*)sender).tag); +} + +- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo +{ + [NSApplication.sharedApplication stopModal]; +} + +@end + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +} + diff --git a/apple/RetroArch/RetroArch_Apple.h b/apple/RetroArch/RetroArch_Apple.h index 39096304d5..a714bb70c1 100644 --- a/apple/RetroArch/RetroArch_Apple.h +++ b/apple/RetroArch/RetroArch_Apple.h @@ -21,7 +21,8 @@ #import #import "RAModuleInfo.h" -void apple_run_core(RAModuleInfo* core, const char* file); +#define GSEVENT_TYPE_KEYDOWN 10 +#define GSEVENT_TYPE_KEYUP 11 @protocol RetroArch_Platform - (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; @@ -30,59 +31,29 @@ void apple_run_core(RAModuleInfo* core, const char* file); - (NSString*)corePath; @end +#ifdef IOS +#import "../iOS/platform.h" +#elif defined(OSX) +#import "../OSX/platform.h" +#endif + +extern bool apple_is_paused; +extern bool apple_is_running; +extern bool apple_use_tv_mode; +extern RAModuleInfo* apple_core; + extern id apple_platform; -#ifdef IOS +// main.m +enum basic_event_t { RESET = 1, LOAD_STATE = 2, SAVE_STATE = 3, QUIT = 4 }; +extern void apple_event_basic_command(void* userdata); +extern void apple_event_set_state_slot(void* userdata); +extern void apple_event_show_rgui(void* userdata); -// RAGameView.m -@interface RAGameView : UIViewController -+ (RAGameView*)get; -- (void)openPauseMenu; -- (void)closePauseMenu; -@end - -@interface RetroArch_iOS : UINavigationController - -+ (RetroArch_iOS*)get; - -- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; -- (void)unloadingCore:(RAModuleInfo*)core; -- (NSString*)retroarchConfigPath; - -- (void)refreshConfig; -- (void)refreshSystemConfig; - -@property (strong, nonatomic) NSString* documentsDirectory; // e.g. /var/mobile/Documents -@property (strong, nonatomic) NSString* systemDirectory; // e.g. /var/mobile/Documents/.RetroArch -@property (strong, nonatomic) NSString* systemConfigPath; // e.g. /var/mobile/Documents/.RetroArch/frontend.cfg - -@end - -#elif defined(OSX) - -#import - -@interface RAGameView : NSOpenGLView - -+ (RAGameView*)get; -- (void)display; - -@end - -@interface RetroArch_OSX : NSObject -{ -@public - NSWindow IBOutlet *window; -} - -+ (RetroArch_OSX*)get; - -- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; -- (void)unloadingCore:(RAModuleInfo*)core; - -@end - -#endif +extern void apple_refresh_config(); +extern void apple_enter_stasis(); +extern void apple_exit_stasis(); +extern void apple_run_core(RAModuleInfo* core, const char* file); // utility.m extern void apple_display_alert(NSString* message, NSString* title); @@ -90,4 +61,7 @@ extern void objc_clear_config_hack(); extern bool path_make_and_check_directory(const char* path, mode_t mode, int amode); extern NSString* objc_get_value_from_config(config_file_t* config, NSString* name, NSString* defaultValue); +// frontend/platform/platform_apple.c +extern void apple_frontend_post_event(void (*fn)(void*), void* userdata); + #endif diff --git a/apple/RetroArch/main.m b/apple/RetroArch/main.m index 3e00e3c919..7d9f392040 100644 --- a/apple/RetroArch/main.m +++ b/apple/RetroArch/main.m @@ -21,36 +21,14 @@ #include "apple_input.h" -// If USE_XATTR is defined any loaded file will get a com.RetroArch.Core extended attribute -// specifying which core was used to load. -//#define USE_XATTR - -#ifdef IOS -#import "views.h" -#include "../iOS/input/BTStack/btpad.h" -#include "../iOS/input/BTStack/btdynamic.h" -#include "../iOS/input/BTStack/btpad.h" -#elif defined(USE_XATTR) -#include "sys/xattr.h" -#endif - #include "file.h" -#define GSEVENT_TYPE_KEYDOWN 10 -#define GSEVENT_TYPE_KEYUP 11 - //#define HAVE_DEBUG_FILELOG static bool use_tv_mode; id apple_platform; -// From frontend/frontend_ios.c -extern void apple_frontend_post_event(void (*fn)(void*), void* userdata); - - -// These are based on the tag property of the button used to trigger the event -enum basic_event_t { RESET = 1, LOAD_STATE = 2, SAVE_STATE = 3, QUIT = 4 }; -static void event_basic_command(void* userdata) +void apple_event_basic_command(void* userdata) { switch ((enum basic_event_t)userdata) { @@ -61,12 +39,12 @@ static void event_basic_command(void* userdata) } } -static void event_set_state_slot(void* userdata) +void apple_event_set_state_slot(void* userdata) { g_extern.state_slot = (uint32_t)userdata; } -static void event_show_rgui(void* userdata) +void apple_event_show_rgui(void* userdata) { const bool in_menu = g_extern.lifecycle_mode_state & (1 << MODE_MENU); g_extern.lifecycle_mode_state &= ~(1ULL << (in_menu ? MODE_MENU : MODE_GAME)); @@ -82,7 +60,15 @@ static void event_reload_config(void* userdata) init_drivers(); } -static pthread_mutex_t stasis_mutex = PTHREAD_MUTEX_INITIALIZER; +void apple_refresh_config() +{ + if (apple_is_running) + apple_frontend_post_event(&event_reload_config, 0); + else + objc_clear_config_hack(); +} + +pthread_mutex_t stasis_mutex = PTHREAD_MUTEX_INITIALIZER; static void event_stasis(void* userdata) { @@ -94,18 +80,34 @@ static void event_stasis(void* userdata) init_drivers(); } +void apple_enter_stasis() +{ + if (apple_is_running) + { + pthread_mutex_lock(&stasis_mutex); + apple_frontend_post_event(event_stasis, 0); + } +} + +void apple_exit_stasis() +{ + if (apple_is_running) + pthread_mutex_unlock(&stasis_mutex); +} + #pragma mark EMULATION static pthread_t apple_retro_thread; -static bool apple_is_paused; -static bool apple_is_running; -static RAModuleInfo* apple_core; +bool apple_is_paused; +bool apple_is_running; +bool apple_use_tv_mode; +RAModuleInfo* apple_core; void* rarch_main_spring(void* args) { char** argv = args; uint32_t argc = 0; - while (argv && argv[argc ++]); + while (argv && argv[argc]) argc++; if (rarch_main(argc, argv)) { @@ -136,11 +138,15 @@ void apple_run_core(RAModuleInfo* core, const char* file) if (file && core) { argv[3] = "-L"; + argv[4] = core_path; strlcpy(core_path, apple_core.path.UTF8String, sizeof(core_path)); strlcpy(file_path, file, sizeof(file_path)); } else - argv[3] = 0; + { + argv[3] = "--menu"; + argv[4] = 0; + } if (pthread_create(&apple_retro_thread, 0, rarch_main_spring, argv)) { @@ -169,578 +175,3 @@ void apple_rarch_exited(void* result) if (use_tv_mode) apple_run_core(nil, 0); } - -// -// IOS -// -#pragma mark IOS -#ifdef IOS -// Input helpers: This is kept here because it needs objective-c -static void handle_touch_event(NSArray* touches) -{ - const int numTouches = [touches count]; - const float scale = [[UIScreen mainScreen] scale]; - - g_current_input_data.touch_count = 0; - - for(int i = 0; i != numTouches && g_current_input_data.touch_count < MAX_TOUCHES; i ++) - { - UITouch* touch = [touches objectAtIndex:i]; - const CGPoint coord = [touch locationInView:touch.view]; - - if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) - { - g_current_input_data.touches[g_current_input_data.touch_count ].screen_x = coord.x * scale; - g_current_input_data.touches[g_current_input_data.touch_count ++].screen_y = coord.y * scale; - } - } -} - -@interface RApplication : UIApplication -@end - -@implementation RApplication - -- (void)sendEvent:(UIEvent *)event -{ - [super sendEvent:event]; - - if ([[event allTouches] count]) - handle_touch_event(event.allTouches.allObjects); - else if ([event respondsToSelector:@selector(_gsEvent)]) - { - // Stolen from: http://nacho4d-nacho4d.blogspot.com/2012/01/catching-keyboard-events-in-ios.html - uint8_t* eventMem = (uint8_t*)(void*)CFBridgingRetain([event performSelector:@selector(_gsEvent)]); - int eventType = eventMem ? *(int*)&eventMem[8] : 0; - - if (eventType == GSEVENT_TYPE_KEYDOWN || eventType == GSEVENT_TYPE_KEYUP) - apple_input_handle_key_event(*(uint16_t*)&eventMem[0x3C], eventType == GSEVENT_TYPE_KEYDOWN); - - CFBridgingRelease(eventMem); - } -} - -int main(int argc, char *argv[]) -{ - @autoreleasepool { -#if defined(HAVE_DEBUG_FILELOG) && (TARGET_IPHONE_SIMULATOR == 0) - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console_stdout.log"]; - freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "a", stdout); - freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "a", stderr); -#endif - return UIApplicationMain(argc, argv, NSStringFromClass([RApplication class]), NSStringFromClass([RetroArch_iOS class])); - } -} - -@end - -@implementation RetroArch_iOS -{ - UIWindow* _window; - - bool _isGameTop, _isRomList; - uint32_t _settingMenusInBackStack; - uint32_t _enabledOrientations; - - RAModuleInfo* _module; -} - -+ (RetroArch_iOS*)get -{ - return (RetroArch_iOS*)[[UIApplication sharedApplication] delegate]; -} - -// UIApplicationDelegate -- (void)applicationDidFinishLaunching:(UIApplication *)application -{ - apple_platform = self; - self.delegate = self; - - // Setup window - _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - _window.rootViewController = self; - [_window makeKeyAndVisible]; - - // Build system paths and test permissions - self.documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; - self.systemDirectory = [self.documentsDirectory stringByAppendingPathComponent:@".RetroArch"]; - self.systemConfigPath = [self.systemDirectory stringByAppendingPathComponent:@"frontend.cfg"]; - - if (!path_make_and_check_directory(self.documentsDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) - apple_display_alert([NSString stringWithFormat:@"Failed to create or access base directory: %@", self.documentsDirectory], 0); - else if (!path_make_and_check_directory(self.systemDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) - apple_display_alert([NSString stringWithFormat:@"Failed to create or access system directory: %@", self.systemDirectory], 0); - else - { - [self pushViewController:[RADirectoryList directoryListAtBrowseRoot] animated:YES]; - [self refreshSystemConfig]; - - if (use_tv_mode) - apple_run_core(nil, 0); - } - - // Warn if there are no cores present - if ([RAModuleInfo getModules].count == 0) - apple_display_alert(@"No libretro cores were found. You will not be able to play any games.", 0); -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - if (apple_is_running) - pthread_mutex_unlock(&stasis_mutex); -} - -- (void)applicationWillResignActive:(UIApplication *)application -{ - if (apple_is_running) - { - pthread_mutex_lock(&stasis_mutex); - apple_frontend_post_event(event_stasis, 0); - } -} - -// UINavigationControllerDelegate -- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated -{ - _isGameTop = [viewController isKindOfClass:[RAGameView class]]; - _isRomList = [viewController isKindOfClass:[RADirectoryList class]]; - - [[UIApplication sharedApplication] setStatusBarHidden:_isGameTop withAnimation:UIStatusBarAnimationNone]; - [[UIApplication sharedApplication] setIdleTimerDisabled:_isGameTop]; - - self.navigationBarHidden = _isGameTop; - [self setToolbarHidden:!_isRomList animated:YES]; - self.topViewController.navigationItem.rightBarButtonItem = [self createSettingsButton]; -} - -// UINavigationController: Never animate when pushing onto, or popping, an RAGameView -- (void)pushViewController:(UIViewController*)theView animated:(BOOL)animated -{ - if ([theView respondsToSelector:@selector(isSettingsView)] && [(id)theView isSettingsView]) - _settingMenusInBackStack ++; - - [super pushViewController:theView animated:animated && !_isGameTop]; -} - -- (UIViewController*)popViewControllerAnimated:(BOOL)animated -{ - if ([self.topViewController respondsToSelector:@selector(isSettingsView)] && [(id)self.topViewController isSettingsView]) - _settingMenusInBackStack --; - - return [super popViewControllerAnimated:animated && !_isGameTop]; -} - -// NOTE: This version only runs on iOS6 -- (NSUInteger)supportedInterfaceOrientations -{ - return _isGameTop ? _enabledOrientations - : UIInterfaceOrientationMaskAll; -} - -// NOTE: This version runs on iOS2-iOS5, but not iOS6 -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - if (_isGameTop) - switch (interfaceOrientation) - { - case UIInterfaceOrientationPortrait: - return (_enabledOrientations & UIInterfaceOrientationMaskPortrait); - case UIInterfaceOrientationPortraitUpsideDown: - return (_enabledOrientations & UIInterfaceOrientationMaskPortraitUpsideDown); - case UIInterfaceOrientationLandscapeLeft: - return (_enabledOrientations & UIInterfaceOrientationMaskLandscapeLeft); - case UIInterfaceOrientationLandscapeRight: - return (_enabledOrientations & UIInterfaceOrientationMaskLandscapeRight); - } - - return YES; -} - - -#pragma mark RetroArch_Platform -- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file -{ - [self pushViewController:RAGameView.get animated:NO]; - [RASettingsList refreshModuleConfig:core]; - - btpad_set_inquiry_state(false); - - [self refreshSystemConfig]; -} - -- (void)unloadingCore:(RAModuleInfo*)core -{ - [self popToViewController:[RAGameView get] animated:NO]; - [self popViewControllerAnimated:NO]; - - btpad_set_inquiry_state(true); -} - -- (NSString*)retroarchConfigPath -{ - return [NSString stringWithFormat:@"%@/retroarch.cfg", self.systemDirectory]; -} - -- (NSString*)corePath -{ - return [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"modules"]; -} - -- (void)refreshConfig -{ - if (apple_is_running) - apple_frontend_post_event(&event_reload_config, 0); - else - objc_clear_config_hack(); -} - -- (void)refreshSystemConfig -{ - // Read load time settings - config_file_t* conf = config_file_new([self.systemConfigPath UTF8String]); - - if (conf) - { - // Get enabled orientations - static const struct { const char* setting; uint32_t orientation; } orientationSettings[4] = - { - { "ios_allow_portrait", UIInterfaceOrientationMaskPortrait }, - { "ios_allow_portrait_upside_down", UIInterfaceOrientationMaskPortraitUpsideDown }, - { "ios_allow_landscape_left", UIInterfaceOrientationMaskLandscapeLeft }, - { "ios_allow_landscape_right", UIInterfaceOrientationMaskLandscapeRight } - }; - - _enabledOrientations = 0; - - for (int i = 0; i < 4; i ++) - { - bool enabled = false; - bool found = config_get_bool(conf, orientationSettings[i].setting, &enabled); - - if (!found || enabled) - _enabledOrientations |= orientationSettings[i].orientation; - } - - // Setup bluetooth mode - NSString* btmode = objc_get_value_from_config(conf, @"ios_btmode", @"keyboard"); - apple_input_enable_icade([btmode isEqualToString:@"icade"]); - btstack_set_poweron([btmode isEqualToString:@"btstack"]); - - bool val; - use_tv_mode = config_get_bool(conf, "ios_tv_mode", & val) && val; - - config_file_free(conf); - } -} - -#pragma mark PAUSE MENU -- (UIBarButtonItem*)createSettingsButton -{ - if (_settingMenusInBackStack == 0) - return [[UIBarButtonItem alloc] - initWithTitle:@"Settings" - style:UIBarButtonItemStyleBordered - target:[RetroArch_iOS get] - action:@selector(showSystemSettings)]; - - else - return nil; -} - -- (IBAction)showPauseMenu:(id)sender -{ - if (apple_is_running && !apple_is_paused && _isGameTop) - { - apple_is_paused = true; - [[RAGameView get] openPauseMenu]; - - btpad_set_inquiry_state(true); - } -} - -- (IBAction)basicEvent:(id)sender -{ - if (apple_is_running) - apple_frontend_post_event(&event_basic_command, ((UIView*)sender).tag); - - [self closePauseMenu:sender]; -} - -- (IBAction)chooseState:(id)sender -{ - if (apple_is_running) - apple_frontend_post_event(event_set_state_slot, (void*)((UISegmentedControl*)sender).selectedSegmentIndex); -} - -- (IBAction)showRGUI:(id)sender -{ - if (apple_is_running) - apple_frontend_post_event(event_show_rgui, 0); - - [self closePauseMenu:sender]; -} - -- (IBAction)closePauseMenu:(id)sender -{ - [[RAGameView get] closePauseMenu]; - apple_is_paused = false; - - btpad_set_inquiry_state(false); -} - -- (IBAction)showSettings -{ - [self pushViewController:[[RASettingsList alloc] initWithModule:_module] animated:YES]; -} - -- (IBAction)showSystemSettings -{ - [self pushViewController:[RASystemSettingsList new] animated:YES]; -} - -@end - - -#endif - -// -// OSX -// -#pragma mark OSX -#ifdef OSX - -@interface RApplication : NSApplication -@end - -@implementation RApplication - -- (void)sendEvent:(NSEvent *)event -{ - [super sendEvent:event]; - - if (event.type == GSEVENT_TYPE_KEYDOWN || event.type == GSEVENT_TYPE_KEYUP) - apple_input_handle_key_event(event.keyCode, event.type == GSEVENT_TYPE_KEYDOWN); -} - -@end - -@implementation RetroArch_OSX -{ - NSWindow IBOutlet* _coreSelectSheet; - - bool _isTerminating; - bool _loaded; - bool _wantReload; - NSString* _file; - RAModuleInfo* _core; -} - -+ (RetroArch_OSX*)get -{ - return (RetroArch_OSX*)[[NSApplication sharedApplication] delegate]; -} - -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification -{ - apple_platform = self; - _loaded = true; - - [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - - RAGameView.get.frame = [window.contentView bounds]; - [window.contentView setAutoresizesSubviews:YES]; - [window.contentView addSubview:RAGameView.get]; - [window makeFirstResponder:RAGameView.get]; - - // Create core select list - NSComboBox* cb = (NSComboBox*)[_coreSelectSheet.contentView viewWithTag:1]; - - for (RAModuleInfo* i in RAModuleInfo.getModules) - [cb addItemWithObjectValue:i]; - - if (cb.numberOfItems) - [cb selectItemAtIndex:0]; - else - apple_display_alert(@"No libretro cores were found.", @"RetroArch"); - - // Run RGUI if needed - if (!_wantReload) - apple_run_core(nil, 0); - else - [self chooseCore]; - - _wantReload = false; - - extern void osx_pad_init(); - osx_pad_init(); -} - -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication -{ - return YES; -} - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender -{ - _isTerminating = true; - - if (apple_is_running) - apple_frontend_post_event(event_basic_command, (void*)QUIT); - - return apple_is_running ? NSTerminateCancel : NSTerminateNow; -} - - -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames -{ - if (filenames.count == 1 && filenames[0]) - { - _file = filenames[0]; - - if (!_loaded) - _wantReload = true; - else - [self chooseCore]; - - [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; - } - else - { - apple_display_alert(@"Cannot open multiple files", @"RetroArch"); - [sender replyToOpenOrPrint:NSApplicationDelegateReplyFailure]; - } -} - -- (void)openDocument:(id)sender -{ - NSOpenPanel* panel = [NSOpenPanel openPanel]; - [panel beginSheetModalForWindow:window completionHandler:^(NSInteger result) - { - [NSApplication.sharedApplication stopModal]; - - if (result == NSOKButton && panel.URL) - { - _file = panel.URL.path; - [self performSelector:@selector(chooseCore) withObject:nil afterDelay:.5f]; - } - }]; - [NSApplication.sharedApplication runModalForWindow:panel]; -} - -// This utility function will queue the _core and _file instance values for running. -// If the emulator thread is already running it will tell it to quit. -- (void)runCore -{ - _wantReload = apple_is_running; - - if (!apple_is_running) - apple_run_core(_core, _file.UTF8String); - else - apple_frontend_post_event(event_basic_command, (void*)QUIT); -} - -- (void)chooseCore -{ -#ifdef USE_XATTR - char stored_name[PATH_MAX]; - if (getxattr(_file.UTF8String, "com.RetroArch.Core", stored_name, PATH_MAX, 0, 0) > 0) - { - for (RAModuleInfo* i in RAModuleInfo.getModules) - { - const char* core_name = i.path.lastPathComponent.UTF8String; - if (strcmp(core_name, stored_name) == 0) - { - _core = i; - [self runCore]; - return; - } - } - } -#endif - - [NSApplication.sharedApplication beginSheet:_coreSelectSheet modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil]; - [NSApplication.sharedApplication runModalForWindow:_coreSelectSheet]; -} - -- (IBAction)coreWasChosen:(id)sender -{ - [NSApplication.sharedApplication stopModal]; - [NSApplication.sharedApplication endSheet:_coreSelectSheet returnCode:0]; - [_coreSelectSheet orderOut:self]; - - if (_isTerminating) - return; - - NSComboBox* cb = (NSComboBox*)[_coreSelectSheet.contentView viewWithTag:1]; - _core = (RAModuleInfo*)cb.objectValueOfSelectedItem; - - [self runCore]; -} - -#pragma mark RetroArch_Platform -- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file -{ - if (file) - { - [NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:file]]]; - -#ifdef USE_XATTR - const char* core_name = core.path.lastPathComponent.UTF8String; - setxattr(file, "com.RetroArch.Core", core_name, strlen(core_name) + 1, 0, 0); -#endif - } -} - -- (void)unloadingCore:(RAModuleInfo*)core -{ - if (_isTerminating) - [NSApplication.sharedApplication terminate:nil]; - - if (_wantReload) - apple_run_core(_core, _file.UTF8String); - else if(use_tv_mode) - apple_run_core(nil, 0); - else - [NSApplication.sharedApplication terminate:nil]; - - _wantReload = false; -} - -- (NSString*)retroarchConfigPath -{ - NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - return [paths[0] stringByAppendingPathComponent:@"RetroArch/retroarch.cfg"]; -} - -- (NSString*)corePath -{ - return [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"Contents/Resources/modules"]; -} - -#pragma mark Menus -- (IBAction)showPreferences:(id)sender -{ - [[[NSWindowController alloc] initWithWindowNibName:@"Settings"] window]; -} - -- (IBAction)basicEvent:(id)sender -{ - if (apple_is_running) - apple_frontend_post_event(&event_basic_command, (void*)((NSMenuItem*)sender).tag); -} - -- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo -{ - [NSApplication.sharedApplication stopModal]; -} - -@end - -int main(int argc, char *argv[]) -{ - return NSApplicationMain(argc, (const char **) argv); -} - -#endif diff --git a/apple/RetroArch_OSX.xcodeproj/project.pbxproj b/apple/RetroArch_OSX.xcodeproj/project.pbxproj index e4b16ceb4a..6a1101970c 100644 --- a/apple/RetroArch_OSX.xcodeproj/project.pbxproj +++ b/apple/RetroArch_OSX.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 9620F6651790004F001B3B81 /* Settings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9620F6641790004F001B3B81 /* Settings.xib */; }; 962EE0E2178B3DF6004224FF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 962EE0E1178B3DF6004224FF /* IOKit.framework */; }; 96355CE31788E72A0010DBFA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96355CE21788E72A0010DBFA /* Cocoa.framework */; }; + 9646869817BBC14E00C5EA69 /* platform.m in Sources */ = {isa = PBXBuildFile; fileRef = 9646869617BBC14E00C5EA69 /* platform.m */; }; 967894931788ECDB00D6CA69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9678948F1788ECDB00D6CA69 /* InfoPlist.strings */; }; 967894941788ECDB00D6CA69 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 967894911788ECDB00D6CA69 /* MainMenu.xib */; }; 967894961788ED1100D6CA69 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 967894951788ED1100D6CA69 /* main.m */; }; @@ -35,6 +36,8 @@ 96355CE51788E72A0010DBFA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 96355CE61788E72A0010DBFA /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 96355CE71788E72A0010DBFA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 9646869617BBC14E00C5EA69 /* platform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = platform.m; path = OSX/platform.m; sourceTree = SOURCE_ROOT; }; + 9646869717BBC14E00C5EA69 /* platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = platform.h; path = OSX/platform.h; sourceTree = SOURCE_ROOT; }; 9678948D1788ECCA00D6CA69 /* RetroArch-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "RetroArch-Info.plist"; path = "OSX/RetroArch-Info.plist"; sourceTree = SOURCE_ROOT; }; 967894901788ECDB00D6CA69 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = OSX/en.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; 967894921788ECDB00D6CA69 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = OSX/en.lproj/MainMenu.xib; sourceTree = SOURCE_ROOT; }; @@ -109,6 +112,8 @@ 96355CE81788E72A0010DBFA /* RetroArch */ = { isa = PBXGroup; children = ( + 9646869617BBC14E00C5EA69 /* platform.m */, + 9646869717BBC14E00C5EA69 /* platform.h */, 9620F662178FD4D3001B3B81 /* settings.m */, 967894A01788F07D00D6CA69 /* griffin.c */, 967894971788F02600D6CA69 /* RAGameView.m */, @@ -214,6 +219,7 @@ 9678949F1788F02600D6CA69 /* utility.m in Sources */, 967894A11788F07D00D6CA69 /* griffin.c in Sources */, 9620F663178FD4D3001B3B81 /* settings.m in Sources */, + 9646869817BBC14E00C5EA69 /* platform.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/apple/RetroArch_iOS.xcodeproj/project.pbxproj b/apple/RetroArch_iOS.xcodeproj/project.pbxproj index 15081fd2eb..f94d43b805 100644 --- a/apple/RetroArch_iOS.xcodeproj/project.pbxproj +++ b/apple/RetroArch_iOS.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 96366C5516C9AC3300D64A22 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96366C5416C9AC3300D64A22 /* CoreAudio.framework */; }; 96366C5916C9ACF500D64A22 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96366C5816C9ACF500D64A22 /* AudioToolbox.framework */; }; 963F5AC816CC523B009BBD19 /* RAGameView.m in Sources */ = {isa = PBXBuildFile; fileRef = 963F5AC516CC523B009BBD19 /* RAGameView.m */; }; + 9646869517BBBEAE00C5EA69 /* platform.m in Sources */ = {isa = PBXBuildFile; fileRef = 9646869417BBBEAE00C5EA69 /* platform.m */; }; 966B9CBD16E41E7A005B61E1 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 966B9CB816E41E7A005B61E1 /* Default-568h@2x.png */; }; 966B9CBF16E41E7A005B61E1 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 966B9CB916E41E7A005B61E1 /* Default.png */; }; 966B9CC116E41E7A005B61E1 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 966B9CBA16E41E7A005B61E1 /* Default@2x.png */; }; @@ -49,6 +50,7 @@ 96366C5416C9AC3300D64A22 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 96366C5816C9ACF500D64A22 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 963F5AC516CC523B009BBD19 /* RAGameView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RAGameView.m; sourceTree = ""; }; + 9646869417BBBEAE00C5EA69 /* platform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = platform.m; path = iOS/platform.m; sourceTree = SOURCE_ROOT; }; 966B9CB816E41E7A005B61E1 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 966B9CB916E41E7A005B61E1 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 966B9CBA16E41E7A005B61E1 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; @@ -152,6 +154,7 @@ 96AFAE3316C1D4EA009DE44C /* RetroArch */ = { isa = PBXGroup; children = ( + 9646869417BBBEAE00C5EA69 /* platform.m */, 967894571788EAAE00D6CA69 /* browser.m */, 967894581788EAAE00D6CA69 /* RALogView.m */, 967894591788EAAE00D6CA69 /* settings.m */, @@ -305,6 +308,7 @@ 9678945B1788EAAE00D6CA69 /* browser.m in Sources */, 9678945C1788EAAE00D6CA69 /* RALogView.m in Sources */, 9678945D1788EAAE00D6CA69 /* settings.m in Sources */, + 9646869517BBBEAE00C5EA69 /* platform.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/apple/iOS/platform.h b/apple/iOS/platform.h new file mode 100644 index 0000000000..42c168884f --- /dev/null +++ b/apple/iOS/platform.h @@ -0,0 +1,42 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __RARCH_IOS_PLATFORM_H +#define __RARCH_IOS_PLATFORM_H + +@interface RAGameView : UIViewController ++ (RAGameView*)get; +- (void)openPauseMenu; +- (void)closePauseMenu; +@end + +@interface RetroArch_iOS : UINavigationController + ++ (RetroArch_iOS*)get; + +- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; +- (void)unloadingCore:(RAModuleInfo*)core; +- (NSString*)retroarchConfigPath; + +- (void)refreshSystemConfig; + +@property (strong, nonatomic) NSString* documentsDirectory; // e.g. /var/mobile/Documents +@property (strong, nonatomic) NSString* systemDirectory; // e.g. /var/mobile/Documents/.RetroArch +@property (strong, nonatomic) NSString* systemConfigPath; // e.g. /var/mobile/Documents/.RetroArch/frontend.cfg + +@end + +#endif \ No newline at end of file diff --git a/apple/iOS/platform.m b/apple/iOS/platform.m new file mode 100644 index 0000000000..74b6d5f16c --- /dev/null +++ b/apple/iOS/platform.m @@ -0,0 +1,346 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include + +#import "RetroArch_Apple.h" +#include "rarch_wrapper.h" + +#include "../RetroArch/apple_input.h" + +#import "views.h" +#include "input/BTStack/btpad.h" +#include "input/BTStack/btdynamic.h" +#include "input/BTStack/btpad.h" + +#include "file.h" + +//#define HAVE_DEBUG_FILELOG + +// Input helpers: This is kept here because it needs objective-c +static void handle_touch_event(NSArray* touches) +{ + const int numTouches = [touches count]; + const float scale = [[UIScreen mainScreen] scale]; + + g_current_input_data.touch_count = 0; + + for(int i = 0; i != numTouches && g_current_input_data.touch_count < MAX_TOUCHES; i ++) + { + UITouch* touch = [touches objectAtIndex:i]; + const CGPoint coord = [touch locationInView:touch.view]; + + if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) + { + g_current_input_data.touches[g_current_input_data.touch_count ].screen_x = coord.x * scale; + g_current_input_data.touches[g_current_input_data.touch_count ++].screen_y = coord.y * scale; + } + } +} + +@interface RApplication : UIApplication +@end + +@implementation RApplication + +- (void)sendEvent:(UIEvent *)event +{ + [super sendEvent:event]; + + if ([[event allTouches] count]) + handle_touch_event(event.allTouches.allObjects); + else if ([event respondsToSelector:@selector(_gsEvent)]) + { + // Stolen from: http://nacho4d-nacho4d.blogspot.com/2012/01/catching-keyboard-events-in-ios.html + uint8_t* eventMem = (uint8_t*)(void*)CFBridgingRetain([event performSelector:@selector(_gsEvent)]); + int eventType = eventMem ? *(int*)&eventMem[8] : 0; + + if (eventType == GSEVENT_TYPE_KEYDOWN || eventType == GSEVENT_TYPE_KEYUP) + apple_input_handle_key_event(*(uint16_t*)&eventMem[0x3C], eventType == GSEVENT_TYPE_KEYDOWN); + + CFBridgingRelease(eventMem); + } +} + +@end + +@implementation RetroArch_iOS +{ + UIWindow* _window; + + bool _isGameTop, _isRomList; + uint32_t _settingMenusInBackStack; + uint32_t _enabledOrientations; + + RAModuleInfo* _module; +} + ++ (RetroArch_iOS*)get +{ + return (RetroArch_iOS*)[[UIApplication sharedApplication] delegate]; +} + +#pragma mark LIFECYCLE (UIApplicationDelegate) +- (void)applicationDidFinishLaunching:(UIApplication *)application +{ + apple_platform = self; + self.delegate = self; + + // Setup window + _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + _window.rootViewController = self; + [_window makeKeyAndVisible]; + + // Build system paths and test permissions + self.documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; + self.systemDirectory = [self.documentsDirectory stringByAppendingPathComponent:@".RetroArch"]; + self.systemConfigPath = [self.systemDirectory stringByAppendingPathComponent:@"frontend.cfg"]; + + if (!path_make_and_check_directory(self.documentsDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) + apple_display_alert([NSString stringWithFormat:@"Failed to create or access base directory: %@", self.documentsDirectory], 0); + else if (!path_make_and_check_directory(self.systemDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) + apple_display_alert([NSString stringWithFormat:@"Failed to create or access system directory: %@", self.systemDirectory], 0); + else + { + [self pushViewController:[RADirectoryList directoryListAtBrowseRoot] animated:YES]; + [self refreshSystemConfig]; + + if (apple_use_tv_mode) + apple_run_core(nil, 0); + } + + // Warn if there are no cores present + if ([RAModuleInfo getModules].count == 0) + apple_display_alert(@"No libretro cores were found. You will not be able to play any games.", 0); +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + apple_enter_stasis(); +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + apple_exit_stasis(); +} + +// UINavigationControllerDelegate +- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated +{ + _isGameTop = [viewController isKindOfClass:[RAGameView class]]; + _isRomList = [viewController isKindOfClass:[RADirectoryList class]]; + + [[UIApplication sharedApplication] setStatusBarHidden:_isGameTop withAnimation:UIStatusBarAnimationNone]; + [[UIApplication sharedApplication] setIdleTimerDisabled:_isGameTop]; + + self.navigationBarHidden = _isGameTop; + [self setToolbarHidden:!_isRomList animated:YES]; + self.topViewController.navigationItem.rightBarButtonItem = [self createSettingsButton]; +} + +// UINavigationController: Never animate when pushing onto, or popping, an RAGameView +- (void)pushViewController:(UIViewController*)theView animated:(BOOL)animated +{ + if ([theView respondsToSelector:@selector(isSettingsView)] && [(id)theView isSettingsView]) + _settingMenusInBackStack ++; + + [super pushViewController:theView animated:animated && !_isGameTop]; +} + +- (UIViewController*)popViewControllerAnimated:(BOOL)animated +{ + if ([self.topViewController respondsToSelector:@selector(isSettingsView)] && [(id)self.topViewController isSettingsView]) + _settingMenusInBackStack --; + + return [super popViewControllerAnimated:animated && !_isGameTop]; +} + +// NOTE: This version only runs on iOS6 +- (NSUInteger)supportedInterfaceOrientations +{ + return _isGameTop ? _enabledOrientations + : UIInterfaceOrientationMaskAll; +} + +// NOTE: This version runs on iOS2-iOS5, but not iOS6 +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + if (_isGameTop) + switch (interfaceOrientation) + { + case UIInterfaceOrientationPortrait: + return (_enabledOrientations & UIInterfaceOrientationMaskPortrait); + case UIInterfaceOrientationPortraitUpsideDown: + return (_enabledOrientations & UIInterfaceOrientationMaskPortraitUpsideDown); + case UIInterfaceOrientationLandscapeLeft: + return (_enabledOrientations & UIInterfaceOrientationMaskLandscapeLeft); + case UIInterfaceOrientationLandscapeRight: + return (_enabledOrientations & UIInterfaceOrientationMaskLandscapeRight); + } + + return YES; +} + + +#pragma mark RetroArch_Platform +- (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file +{ + [self pushViewController:RAGameView.get animated:NO]; + [RASettingsList refreshModuleConfig:core]; + + btpad_set_inquiry_state(false); + + [self refreshSystemConfig]; +} + +- (void)unloadingCore:(RAModuleInfo*)core +{ + [self popToViewController:[RAGameView get] animated:NO]; + [self popViewControllerAnimated:NO]; + + btpad_set_inquiry_state(true); +} + +- (NSString*)retroarchConfigPath +{ + return [NSString stringWithFormat:@"%@/retroarch.cfg", self.systemDirectory]; +} + +- (NSString*)corePath +{ + return [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"modules"]; +} + +#pragma mark FRONTEND CONFIG +- (void)refreshSystemConfig +{ + // Read load time settings + config_file_t* conf = config_file_new([self.systemConfigPath UTF8String]); + + if (conf) + { + // Get enabled orientations + static const struct { const char* setting; uint32_t orientation; } orientationSettings[4] = + { + { "ios_allow_portrait", UIInterfaceOrientationMaskPortrait }, + { "ios_allow_portrait_upside_down", UIInterfaceOrientationMaskPortraitUpsideDown }, + { "ios_allow_landscape_left", UIInterfaceOrientationMaskLandscapeLeft }, + { "ios_allow_landscape_right", UIInterfaceOrientationMaskLandscapeRight } + }; + + _enabledOrientations = 0; + + for (int i = 0; i < 4; i ++) + { + bool enabled = false; + bool found = config_get_bool(conf, orientationSettings[i].setting, &enabled); + + if (!found || enabled) + _enabledOrientations |= orientationSettings[i].orientation; + } + + // Setup bluetooth mode + NSString* btmode = objc_get_value_from_config(conf, @"ios_btmode", @"keyboard"); + apple_input_enable_icade([btmode isEqualToString:@"icade"]); + btstack_set_poweron([btmode isEqualToString:@"btstack"]); + + bool val; + apple_use_tv_mode = config_get_bool(conf, "ios_tv_mode", & val) && val; + + config_file_free(conf); + } +} + +#pragma mark PAUSE MENU +- (UIBarButtonItem*)createSettingsButton +{ + if (_settingMenusInBackStack == 0) + return [[UIBarButtonItem alloc] + initWithTitle:@"Settings" + style:UIBarButtonItemStyleBordered + target:[RetroArch_iOS get] + action:@selector(showSystemSettings)]; + + else + return nil; +} + +- (IBAction)showPauseMenu:(id)sender +{ + if (apple_is_running && !apple_is_paused && _isGameTop) + { + apple_is_paused = true; + [[RAGameView get] openPauseMenu]; + + btpad_set_inquiry_state(true); + } +} + +- (IBAction)basicEvent:(id)sender +{ + if (apple_is_running) + apple_frontend_post_event(&apple_event_basic_command, ((UIView*)sender).tag); + + [self closePauseMenu:sender]; +} + +- (IBAction)chooseState:(id)sender +{ + if (apple_is_running) + apple_frontend_post_event(apple_event_set_state_slot, (void*)((UISegmentedControl*)sender).selectedSegmentIndex); +} + +- (IBAction)showRGUI:(id)sender +{ + if (apple_is_running) + apple_frontend_post_event(apple_event_show_rgui, 0); + + [self closePauseMenu:sender]; +} + +- (IBAction)closePauseMenu:(id)sender +{ + [[RAGameView get] closePauseMenu]; + apple_is_paused = false; + + btpad_set_inquiry_state(false); +} + +- (IBAction)showSettings +{ + [self pushViewController:[[RASettingsList alloc] initWithModule:_module] animated:YES]; +} + +- (IBAction)showSystemSettings +{ + [self pushViewController:[RASystemSettingsList new] animated:YES]; +} + +@end + +int main(int argc, char *argv[]) +{ + @autoreleasepool { +#if defined(HAVE_DEBUG_FILELOG) && (TARGET_IPHONE_SIMULATOR == 0) + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console_stdout.log"]; + freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "a", stdout); + freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding], "a", stderr); +#endif + return UIApplicationMain(argc, argv, NSStringFromClass([RApplication class]), NSStringFromClass([RetroArch_iOS class])); + } +} diff --git a/apple/iOS/settings.m b/apple/iOS/settings.m index 797fe4c7ce..b386204a4c 100644 --- a/apple/iOS/settings.m +++ b/apple/iOS/settings.m @@ -345,7 +345,7 @@ static NSArray* build_input_port_group(config_file_t* config, uint32_t player) config_file_free(config); } - [[RetroArch_iOS get] refreshConfig]; + apple_refresh_config(); } }