From 6c63c49d096e541c9d666bf3554eb419109dd4a5 Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Sun, 12 Oct 2014 16:37:21 -0700 Subject: [PATCH] Improvements to Mac GUI * Reboot... menu option * Disk image selection interface * Whitespace code changes --- Apple2Mac/Apple2Mac/Base.lproj/MainMenu.xib | 198 +++++++++++- .../Classes/OSX/EmulatorWindowController.m | 293 +++++++++++++++--- src/cpu-supp.c | 8 + src/cpu.h | 1 + src/interface.c | 6 +- 5 files changed, 450 insertions(+), 56 deletions(-) diff --git a/Apple2Mac/Apple2Mac/Base.lproj/MainMenu.xib b/Apple2Mac/Apple2Mac/Base.lproj/MainMenu.xib index bf2b4396..948faf9d 100644 --- a/Apple2Mac/Apple2Mac/Base.lproj/MainMenu.xib +++ b/Apple2Mac/Apple2Mac/Base.lproj/MainMenu.xib @@ -23,15 +23,36 @@ + + +CA + + + + + + + + + + + + + + + + + + + - @@ -85,19 +106,190 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Apple2Mac/Apple2Mac/Classes/OSX/EmulatorWindowController.m b/Apple2Mac/Apple2Mac/Classes/OSX/EmulatorWindowController.m index 38b78b65..a5f6956d 100644 --- a/Apple2Mac/Apple2Mac/Classes/OSX/EmulatorWindowController.m +++ b/Apple2Mac/Apple2Mac/Classes/OSX/EmulatorWindowController.m @@ -10,6 +10,13 @@ #import "EmulatorWindowController.h" #import "EmulatorFullscreenWindow.h" +#import "common.h" + +#define READONLY_CHOICE_INDEX 0 +#define NO_DISK_INSERTED @"(No Disk Inserted)" +#define DSK_PROPERTIES @".dsk 143360 bytes" +#define NIB_PROPERTIES @".nib 232960 bytes" +#define GZ_EXTENSION @"gz" @interface EmulatorWindowController () @@ -17,6 +24,19 @@ @property (nonatomic, retain) EmulatorFullscreenWindow *fullscreenWindow; @property (nonatomic, retain) NSWindow *standardWindow; +@property (assign) IBOutlet NSWindow *disksWindow; +@property (assign) IBOutlet NSTextField *diskInA; +@property (assign) IBOutlet NSTextField *diskInB; +@property (assign) IBOutlet NSTextField *diskAProperties; +@property (assign) IBOutlet NSTextField *diskBProperties; +@property (assign) IBOutlet NSMatrix *diskAProtection; +@property (assign) IBOutlet NSMatrix *diskBProtection; +@property (assign) IBOutlet NSButton *chooseDiskA; +@property (assign) IBOutlet NSButton *chooseDiskB; + +@property (nonatomic, copy) NSString *diskAPath; +@property (nonatomic, copy) NSString *diskBPath; + @end @@ -25,18 +45,45 @@ @synthesize view = _view; @synthesize fullscreenWindow = _fullscreenWindow; @synthesize standardWindow = _standardWindow; +@synthesize diskAPath = _diskAPath; +@synthesize diskBPath = _diskBPath; - (id)initWithWindow:(NSWindow *)window { self = [super initWithWindow:window]; - if (self) - { - // Initialize to nil since it indicates app is not fullscreen - self.fullscreenWindow = nil; + if (self) + { + // Initialize to nil since it indicates app is not fullscreen + self.fullscreenWindow = nil; } - return self; + return self; +} + +- (void)dealloc +{ +#warning TODO FIXME ... probably should exit emulator if this gets invoked ... + self.diskAPath = nil; + self.diskBPath = nil; + [super dealloc]; +} + +- (void)awakeFromNib +{ + [self.diskInA setStringValue:NO_DISK_INSERTED]; + [self.diskAProperties setStringValue:@""]; + [self.diskInB setStringValue:NO_DISK_INSERTED]; + [self.diskBProperties setStringValue:@""]; + + [self.chooseDiskA setKeyEquivalent:@"\r"]; + [self.chooseDiskA setBezelStyle:NSRoundedBezelStyle]; +} + +- (IBAction)reboot:(id)sender +{ + [[self disksWindow] close]; + cpu65_reboot(); } - (IBAction)toggleFullScreen:(id)sender @@ -51,68 +98,218 @@ } } +- (IBAction)diskAProtectionChanged:(id)sender +{ + if ([[[self diskInA] stringValue] isEqualToString:NO_DISK_INSERTED]) + { + return; + } + // HACK NOTE : dispatch so that state of outlet property is set properly + dispatch_async(dispatch_get_main_queue(), ^{ + NSButtonCell *readOnlyChoice = [[[self diskAProtection] cells] firstObject]; + NSString *path = [self diskAPath]; + [self _insertDisketteInDrive:0 path:path type:[self _extensionForPath:path] readOnly:([readOnlyChoice state] == NSOnState)]; + }); +} + +- (IBAction)diskBProtectionChanged:(id)sender +{ + if ([[[self diskInB] stringValue] isEqualToString:NO_DISK_INSERTED]) + { + return; + } + // HACK NOTE : dispatch so that state of outlet property is set properly + dispatch_async(dispatch_get_main_queue(), ^{ + NSButtonCell *readOnlyChoice = [[[self diskBProtection] cells] firstObject]; + NSString *path = [self diskBPath]; + [self _insertDisketteInDrive:1 path:path type:[self _extensionForPath:path] readOnly:([readOnlyChoice state] == NSOnState)]; + }); +} + +- (BOOL)_insertDisketteInDrive:(int)drive path:(NSString *)path type:(NSString *)type readOnly:(BOOL)readOnly +{ + c_eject_6(drive); + + const char *errMsg = c_new_diskette_6(drive, [path UTF8String], readOnly); + if (errMsg) + { + NSAlert *alert = [NSAlert alertWithError:[NSError errorWithDomain:[NSString stringWithUTF8String:errMsg] code:-1 userInfo:nil]]; + [alert beginSheetModalForWindow:[self disksWindow] completionHandler:nil]; + if (!drive) + { + [[self diskInA] setStringValue:NO_DISK_INSERTED]; + [[self diskAProperties] setStringValue:@""]; + } + else + { + [[self diskInB] setStringValue:NO_DISK_INSERTED]; + [[self diskBProperties] setStringValue:@""]; + } + + return NO; + } + + NSString *imageName = [[path pathComponents] lastObject]; + + if (!drive) + { + self.diskAPath = path; + [[self diskInA] setStringValue:imageName]; + } + else + { + self.diskBPath = path; + [[self diskInB] setStringValue:imageName]; + } + + if ([type isEqualToString:@"dsk"] || [type isEqualToString:@"do"] || [type isEqualToString:@"po"]) + { + if (!drive) + { + [[self diskAProperties] setStringValue:DSK_PROPERTIES]; + } + else + { + [[self diskBProperties] setStringValue:DSK_PROPERTIES]; + } + } + else + { + if (!drive) + { + [[self diskAProperties] setStringValue:NIB_PROPERTIES]; + } + else + { + [[self diskBProperties] setStringValue:NIB_PROPERTIES]; + } + } + + return YES; +} + +- (NSString *)_extensionForPath:(NSString *)path +{ + NSString *extension0 = [path pathExtension]; + NSString *extension1 = [[path stringByDeletingPathExtension] pathExtension]; + if ([extension0 isEqualToString:GZ_EXTENSION]) + { + extension0 = extension1; + } + return extension0; +} + +- (void)_chooseDisk:(int)drive readOnly:(BOOL)readOnly +{ + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + [openPanel setTitle:@"Choose a disk image"]; +#warning FIXME TODO : installation default should be what it bundled, but should not always choose this ... + NSURL *url = [[NSBundle mainBundle] URLForResource:@"blank" withExtension:@"dsk.gz"]; + url = [url URLByDeletingLastPathComponent]; + [openPanel setDirectoryURL:url]; + [openPanel setShowsResizeIndicator:YES]; + [openPanel setShowsHiddenFiles:NO]; + [openPanel setCanChooseFiles:YES]; + [openPanel setCanChooseDirectories:NO]; + [openPanel setCanCreateDirectories:NO]; + [openPanel setAllowsMultipleSelection:NO]; + + // NOTE : Doesn't appear to be a way to specify ".dsk.gz" ... so we may inadvertently allow files of ".foo.gz" here + NSSet *fileTypes = [NSSet setWithObjects:@"dsk", @"nib", @"do", @"po", GZ_EXTENSION, nil]; + [openPanel setAllowedFileTypes:[fileTypes allObjects]]; + [openPanel beginSheetModalForWindow:[self disksWindow] completionHandler:^(NSInteger result) { + if (result == NSOKButton) + { + NSURL *selection = [[openPanel URLs] firstObject]; + NSString *path = [[selection path] stringByResolvingSymlinksInPath]; + NSString *extension = [self _extensionForPath:path]; + + if (![fileTypes containsObject:extension]) + { + NSAlert *alert = [NSAlert alertWithError:[NSError errorWithDomain:@"Disk image must have file extension of .dsk, .do, .po, or .nib only" code:-1 userInfo:nil]]; + [alert beginSheetModalForWindow:[self disksWindow] completionHandler:nil]; + return; + } + + [self _insertDisketteInDrive:drive path:path type:extension readOnly:readOnly]; + } + }]; +} + +- (IBAction)chooseDriveA:(id)sender +{ + NSButtonCell *readOnlyChoice = [[[self diskAProtection] cells] firstObject]; + [self _chooseDisk:0 readOnly:([readOnlyChoice state] == NSOnState)]; +} + +- (IBAction)chooseDriveB:(id)sender +{ + NSButtonCell *readOnlyChoice = [[[self diskBProtection] cells] firstObject]; + [self _chooseDisk:1 readOnly:([readOnlyChoice state] == NSOnState)]; +} + - (void)goFullscreen { - // If app is already fullscreen... - if (self.fullscreenWindow) - { - //...don't do anything - return; - } + // If app is already fullscreen... + if (self.fullscreenWindow) + { + //...don't do anything + return; + } - // Allocate a new fullscreen window - self.fullscreenWindow = [[[EmulatorFullscreenWindow alloc] init] autorelease]; + // Allocate a new fullscreen window + self.fullscreenWindow = [[[EmulatorFullscreenWindow alloc] init] autorelease]; - // Resize the view to screensize - NSRect viewRect = [self.fullscreenWindow frame]; + // Resize the view to screensize + NSRect viewRect = [self.fullscreenWindow frame]; - // Set the view to the size of the fullscreen window - [self.view setFrameSize: viewRect.size]; + // Set the view to the size of the fullscreen window + [self.view setFrameSize: viewRect.size]; - // Set the view in the fullscreen window - [self.fullscreenWindow setContentView:self.view]; + // Set the view in the fullscreen window + [self.fullscreenWindow setContentView:self.view]; - self.standardWindow = [self window]; + self.standardWindow = [self window]; - // Hide non-fullscreen window so it doesn't show up when switching out - // of this app (i.e. with CMD-TAB) - [self.standardWindow orderOut:self]; + // Hide non-fullscreen window so it doesn't show up when switching out + // of this app (i.e. with CMD-TAB) + [self.standardWindow orderOut:self]; - // Set controller to the fullscreen window so that all input will go to - // this controller (self) - [self setWindow:self.fullscreenWindow]; + // Set controller to the fullscreen window so that all input will go to + // this controller (self) + [self setWindow:self.fullscreenWindow]; - // Show the window and make it the key window for input - [self.fullscreenWindow makeKeyAndOrderFront:self]; + // Show the window and make it the key window for input + [self.fullscreenWindow makeKeyAndOrderFront:self]; } - (void)goWindow { - // If controller doesn't have a full screen window... - if (self.fullscreenWindow == nil) - { - //...app is already windowed so don't do anything - return; - } + // If controller doesn't have a full screen window... + if (self.fullscreenWindow == nil) + { + //...app is already windowed so don't do anything + return; + } - // Get the rectangle of the original window - NSRect viewRect = [self.standardWindow frame]; - - // Set the view rect to the new size - [self.view setFrame:viewRect]; + // Get the rectangle of the original window + NSRect viewRect = [self.standardWindow frame]; + + // Set the view rect to the new size + [self.view setFrame:viewRect]; - // Set controller to the standard window so that all input will go to - // this controller (self) - [self setWindow:self.standardWindow]; + // Set controller to the standard window so that all input will go to + // this controller (self) + [self setWindow:self.standardWindow]; - // Set the content of the orginal window to the view - [[self window] setContentView:self.view]; + // Set the content of the orginal window to the view + [[self window] setContentView:self.view]; - // Show the window and make it the key window for input - [[self window] makeKeyAndOrderFront:self]; + // Show the window and make it the key window for input + [[self window] makeKeyAndOrderFront:self]; - // Release/nilify fullscreenWindow - self.fullscreenWindow = nil; + // Release/nilify fullscreenWindow + self.fullscreenWindow = nil; } - (void)keyDown:(NSEvent *)event diff --git a/src/cpu-supp.c b/src/cpu-supp.c index 7646fad6..956d0460 100644 --- a/src/cpu-supp.c +++ b/src/cpu-supp.c @@ -646,6 +646,14 @@ void cpu65_uninterrupt(int reason) pthread_mutex_unlock(&irq_mutex); } +void cpu65_reboot(void) { + timing_initialize(); + video_set(0); + joy_button0 = 0xff; // OpenApple + cpu65_interrupt(ResetSig); + c_initialize_sound_hooks(); +} + #if CPU_TRACING /* ------------------------------------------------------------------------- diff --git a/src/cpu.h b/src/cpu.h index 7f5a730c..1fbf74e5 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -44,6 +44,7 @@ extern void cpu65_interrupt(int reason); extern void cpu65_uninterrupt(int reason); extern void cpu65_run(void); +extern void cpu65_reboot(void); extern void cpu65_direct_write(int ea,int data); diff --git a/src/interface.c b/src/interface.c index 53f12a45..3d35a1f8 100644 --- a/src/interface.c +++ b/src/interface.c @@ -1315,11 +1315,7 @@ void c_interface_parameters() ch = toupper(ch); if (ch == 'Y') { - timing_initialize(); - video_set(0); - joy_button0 = 0xff; // OpenApple - cpu65_interrupt(ResetSig); - c_initialize_sound_hooks(); + cpu65_reboot(); c_interface_exit(ch); return; }