diff --git a/Mini vMac/AppDelegate.m b/Mini vMac/AppDelegate.m index c0748f6..1f6a54c 100644 --- a/Mini vMac/AppDelegate.m +++ b/Mini vMac/AppDelegate.m @@ -6,6 +6,7 @@ // Copyright © 2016 namedfork. All rights reserved. // +@import AVFoundation; #import "AppDelegate.h" #import "SettingsViewController.h" #import "InsertDiskViewController.h" @@ -36,6 +37,7 @@ static NSObject *sharedEmulator = nil; [self loadEmulator:@"MacPlus4M"]; } [self initDefaults]; + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:NULL]; [sharedEmulator performSelector:@selector(run) withObject:nil afterDelay:0.1]; return YES; } @@ -48,7 +50,10 @@ static NSObject *sharedEmulator = nil; NSString *firstLanguage = [NSBundle preferredLocalizationsFromArray:layoutForLanguage.allKeys].firstObject; NSDictionary *defaultValues = @{@"trackpad": @([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad), @"keyboardLayout": layoutForLanguage[firstLanguage], - @"machine": @"MacPlus4M" + @"machine": @"MacPlus4M", + @"speedValue": @(sharedEmulator.initialSpeed), + @"runInBackground": @NO, + @"autoSlow": @(sharedEmulator.initialAutoSlow) }; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; @@ -59,8 +64,11 @@ static NSObject *sharedEmulator = nil; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == [NSUserDefaults standardUserDefaults]) { + NSUserDefaults *defaults = object; if ([keyPath isEqualToString:@"speedValue"]) { - sharedEmulator.speed = [[NSUserDefaults standardUserDefaults] integerForKey:@"speedValue"]; + sharedEmulator.speed = [defaults integerForKey:@"speedValue"]; + } else if ([keyPath isEqualToString:@"autoSlow"]) { + sharedEmulator.autoSlow = [defaults integerForKey:@"autoSlow"]; } } } @@ -87,15 +95,26 @@ static NSObject *sharedEmulator = nil; } - (void)applicationDidEnterBackground:(UIApplication *)application { - [[NSUserDefaults standardUserDefaults] synchronize]; - sharedEmulator.running = NO; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults synchronize]; + if ([defaults boolForKey:@"runInBackground"]) { + // slow down to 1x when in background + sharedEmulator.speed = EmulatorSpeed1x; + } else { + sharedEmulator.running = NO; + } if (sharedEmulator.anyDiskInserted == NO) { exit(0); } } - (void)applicationWillEnterForeground:(UIApplication *)application { - sharedEmulator.running = YES; + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (sharedEmulator.running) { + sharedEmulator.speed = [defaults integerForKey:@"speedValue"]; + } else { + sharedEmulator.running = YES; + } } - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message { diff --git a/Mini vMac/Base.lproj/Main.storyboard b/Mini vMac/Base.lproj/Main.storyboard index 86cebb3..be2ccdd 100644 --- a/Mini vMac/Base.lproj/Main.storyboard +++ b/Mini vMac/Base.lproj/Main.storyboard @@ -64,6 +64,9 @@ + + + @@ -169,6 +172,23 @@ + + + + + + + + + + + diff --git a/Mini vMac/EmulatorProtocol.h b/Mini vMac/EmulatorProtocol.h index cd572a6..c8399e3 100644 --- a/Mini vMac/EmulatorProtocol.h +++ b/Mini vMac/EmulatorProtocol.h @@ -10,11 +10,24 @@ @import CoreGraphics; @import QuartzCore; +typedef enum : NSInteger { + EmulatorSpeedAllOut = -1, + EmulatorSpeed1x = 0, + EmulatorSpeed2x = 1, + EmulatorSpeed4x = 2, + EmulatorSpeed8x = 3, + EmulatorSpeed16x = 4, + EmulatorSpeed32x = 5 +} EmulatorSpeed; + @protocol Emulator @property (nonatomic, strong) NSString *dataPath; @property (nonatomic, assign, getter=isRunning) BOOL running; -@property (nonatomic, assign) NSInteger speed; +@property (nonatomic, assign) EmulatorSpeed speed; +@property (nonatomic, assign) BOOL autoSlow; +@property (nonatomic, readonly) BOOL initialAutoSlow; +@property (nonatomic, readonly) BOOL autoSlowSupported; @property (nonatomic, weak) CALayer *screenLayer; @property (nonatomic, readonly) NSBundle *bundle; diff --git a/Mini vMac/Info.plist b/Mini vMac/Info.plist index ad8ef84..90339c0 100644 --- a/Mini vMac/Info.plist +++ b/Mini vMac/Info.plist @@ -77,6 +77,10 @@ 3 LSRequiresIPhoneOS + UIBackgroundModes + + audio + UIFileSharingEnabled UILaunchStoryboardName diff --git a/Mini vMac/MYOSGLUE.m b/Mini vMac/MYOSGLUE.m index b5984bf..ef969b9 100644 --- a/Mini vMac/MYOSGLUE.m +++ b/Mini vMac/MYOSGLUE.m @@ -1757,14 +1757,40 @@ static dispatch_once_t onceToken; return [NSBundle bundleForClass:self.class]; } -- (NSInteger)speed { +- (EmulatorSpeed)speed { return SpeedValue; } -- (void)setSpeed:(NSInteger)speed { +- (void)setSpeed:(EmulatorSpeed)speed { SpeedValue = speed; } +#if EnableAutoSlow +- (BOOL)autoSlow { + return !WantNotAutoSlow; +} + +- (void)setAutoSlow:(BOOL)autoSlow { + WantNotAutoSlow = !autoSlow; +} +#else +- (BOOL)autoSlow { + return NO; +} + +- (void)setAutoSlow:(BOOL)autoSlow { + +} +#endif + +- (BOOL)autoSlowSupported { + return EnableAutoSlow; +} + +- (BOOL)initialAutoSlow { + return !WantInitNotAutoSlow; +} + - (BOOL)isRunning { return !SpeedStopped; } @@ -1803,7 +1829,9 @@ static dispatch_once_t onceToken; } - (void)updateScreen:(CGImageRef)screenImage { - screenLayer.contents = (__bridge id)screenImage; + if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) { + screenLayer.contents = (__bridge id)screenImage; + } } #pragma mark - Disk diff --git a/Mini vMac/SettingsViewController.m b/Mini vMac/SettingsViewController.m index e9f56f4..ffe9c3f 100644 --- a/Mini vMac/SettingsViewController.m +++ b/Mini vMac/SettingsViewController.m @@ -25,6 +25,7 @@ typedef enum : NSInteger { { NSArray *keyboardLayouts; NSArray *emulatorBundles; + NSBundle *selectedEmulatorBundle; NSString *aboutTitle; NSArray*> *aboutItems; UITextView *footerView; @@ -33,10 +34,21 @@ typedef enum : NSInteger { - (void)viewDidLoad { [super viewDidLoad]; keyboardLayouts = [[NSBundle mainBundle] pathsForResourcesOfType:@"nfkeyboardlayout" inDirectory:@"Keyboard Layouts"]; - emulatorBundles = [AppDelegate sharedInstance].emulatorBundles; + [self loadEmulatorBundles]; [self loadCredits]; } +- (void)loadEmulatorBundles { + emulatorBundles = [AppDelegate sharedInstance].emulatorBundles; + NSString *selectedBundleName = [[NSUserDefaults standardUserDefaults] stringForKey:@"machine"]; + for (NSBundle *bundle in emulatorBundles) { + NSString *bundleName = bundle.bundlePath.lastPathComponent.stringByDeletingPathExtension; + if ([selectedBundleName isEqualToString:bundleName]) { + selectedEmulatorBundle = bundle; + } + } +} + - (void)loadCredits { NSDictionary *aboutData = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"about" ofType:@"plist"]]; aboutTitle = aboutData[@"title"]; @@ -76,7 +88,8 @@ typedef enum : NSInteger { - (IBAction)changeSpeed:(UISegmentedControl*)sender { if ([sender isKindOfClass:[UISegmentedControl class]]) { - [[NSUserDefaults standardUserDefaults] setInteger:sender.selectedSegmentIndex forKey:@"speedValue"]; + EmulatorSpeed speedValue = sender.selectedSegmentIndex == sender.numberOfSegments - 1 ? EmulatorSpeedAllOut : sender.selectedSegmentIndex; + [[NSUserDefaults standardUserDefaults] setInteger:speedValue forKey:@"speedValue"]; } } @@ -86,6 +99,18 @@ typedef enum : NSInteger { } } +- (void)changeRunInBackground:(UISwitch*)sender { + if ([sender isKindOfClass:[UISwitch class]]) { + [[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:@"runInBackground"]; + } +} + +- (void)changeAutoSlow:(UISwitch*)sender { + if ([sender isKindOfClass:[UISwitch class]]) { + [[NSUserDefaults standardUserDefaults] setBool:sender.on forKey:@"autoSlow"]; + } +} + #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { @@ -94,6 +119,11 @@ typedef enum : NSInteger { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { switch (section) { + case SettingsSectionSpeed: { + NSDictionary *capabilities = [selectedEmulatorBundle objectForInfoDictionaryKey:@"MNVMCapabilities"]; + BOOL hasAutoSlow = [capabilities[@"AutoSlow"] boolValue]; + return hasAutoSlow ? 3 : 2; + } case SettingsSectionKeyboard: return keyboardLayouts.count; case SettingsSectionMachine: @@ -130,7 +160,9 @@ typedef enum : NSInteger { } - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { - if (section == SettingsSectionMachine) { + if (section == SettingsSectionSpeed) { + return NSLocalizedString(@"Faster speeds and running in background drain the battery faster", nil); + } else if (section == SettingsSectionMachine) { return NSLocalizedString(@"Changing the emulated machine requires to relaunch Mini vMac", nil); } else { return nil; @@ -150,9 +182,18 @@ typedef enum : NSInteger { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSInteger section = indexPath.section; if (section == SettingsSectionSpeed) { - cell = [tableView dequeueReusableCellWithIdentifier:@"speed" forIndexPath:indexPath]; - UISegmentedControl *speedControl = (UISegmentedControl*)[cell viewWithTag:128]; - speedControl.selectedSegmentIndex = [defaults integerForKey:@"speedValue"]; + if (indexPath.row == 0) { + cell = [tableView dequeueReusableCellWithIdentifier:@"speed" forIndexPath:indexPath]; + UISegmentedControl *speedControl = (UISegmentedControl*)[cell viewWithTag:128]; + EmulatorSpeed speedValue = [defaults integerForKey:@"speedValue"]; + speedControl.selectedSegmentIndex = speedValue == EmulatorSpeedAllOut ? speedControl.numberOfSegments - 1 : speedValue; + } else if (indexPath.row == 1) { + cell = [self switchCellForTableView:tableView indexPath:indexPath action:@selector(changeRunInBackground:) on:[defaults boolForKey:@"runInBackground"]]; + cell.textLabel.text = NSLocalizedString(@"Run in background", nil); + } else if (indexPath.row == 2) { + cell = [self switchCellForTableView:tableView indexPath:indexPath action:@selector(changeAutoSlow:) on:[defaults boolForKey:@"autoSlow"]]; + cell.textLabel.text = NSLocalizedString(@"AutoSlow", nil); + } } else if (section == SettingsSectionMouse) { cell = [tableView dequeueReusableCellWithIdentifier:@"mouse" forIndexPath:indexPath]; UISegmentedControl *mouseControl = (UISegmentedControl*)[cell viewWithTag:128]; @@ -171,8 +212,7 @@ typedef enum : NSInteger { cell.detailTextLabel.text = [bundle objectForInfoDictionaryKey:@"CFBundleGetInfoString"]; NSString *iconName = [NSString stringWithFormat:@"PlugIns/%@.mnvm/Icon", bundleName]; cell.imageView.image = [UIImage imageNamed:iconName]; - BOOL selected = [[defaults stringForKey:@"machine"] isEqualToString:bundleName]; - cell.accessoryType = selected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; + cell.accessoryType = bundle == selectedEmulatorBundle ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone; } else if (section == SettingsSectionAbout) { cell = [tableView dequeueReusableCellWithIdentifier:@"about" forIndexPath:indexPath]; NSDictionary *item = aboutItems[indexPath.row]; @@ -203,6 +243,8 @@ typedef enum : NSInteger { NSBundle *bundle = emulatorBundles[indexPath.row]; NSString *bundleName = bundle.bundlePath.lastPathComponent.stringByDeletingPathExtension; [defaults setValue:bundleName forKey:@"machine"]; + selectedEmulatorBundle = bundle; + [tableView reloadSections:[NSIndexSet indexSetWithIndex:SettingsSectionSpeed] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic]; } else if (indexPath.section == SettingsSectionAbout) { // links in about @@ -213,4 +255,18 @@ typedef enum : NSInteger { } } +- (UITableViewCell*)switchCellForTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath action:(SEL)action on:(BOOL)on { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"toggle" forIndexPath:indexPath]; + UISwitch *cellSwitch = (UISwitch*)cell.accessoryView; + if (cellSwitch == nil) { + cellSwitch = [UISwitch new]; + cell.accessoryView = cellSwitch; + } else { + [cellSwitch removeTarget:nil action:nil forControlEvents:UIControlEventAllEvents]; + } + cellSwitch.on = on; + [cellSwitch addTarget:self action:action forControlEvents:UIControlEventValueChanged]; + return cell; +} + @end