mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-26 00:29:27 +00:00
Suite
This commit is contained in:
parent
5d86735a69
commit
746dcb2e3d
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
typedef void (^DiskCompletionHandler)(NSOpenPanel *openPanel, NSInteger result);
|
||||
|
||||
@interface EmulatorDiskController : NSWindowController
|
||||
|
||||
+ (NSSet *)emulatorFileTypes;
|
||||
+ (NSString *)extensionForPath:(NSString *)path;
|
||||
+ (void)chooseDiskForWindow:(NSWindow *)window completionHandler:(DiskCompletionHandler)handler;
|
||||
|
||||
@end
|
|
@ -1,324 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import "EmulatorDiskController.h"
|
||||
#import "EmulatorPrefsController.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 kApple2DisksURL @"kApple2DisksURL"
|
||||
|
||||
@interface EmulatorDiskController ()
|
||||
|
||||
@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 (assign) IBOutlet NSButton *startupLoadDiskA;
|
||||
@property (assign) IBOutlet NSButton *startupLoadDiskB;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EmulatorDiskController
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
#if CRASH_APP_ON_LOAD_BECAUSE_YAY_GJ_APPLE
|
||||
glGetError();
|
||||
#endif
|
||||
|
||||
[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];
|
||||
[self.startupLoadDiskA setState:NSOffState];
|
||||
[self.startupLoadDiskB setState:NSOffState];
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
NSString *startupDiskA = [defaults stringForKey:kApple2PrefStartupDiskA];
|
||||
BOOL readOnlyA = [defaults boolForKey:kApple2PrefStartupDiskAProtected];
|
||||
if (startupDiskA)
|
||||
{
|
||||
const char *err = disk6_insert(0, [[NSString stringWithFormat:@"%@.gz", startupDiskA] UTF8String], readOnlyA);
|
||||
if (!err)
|
||||
{
|
||||
[self.diskInA setStringValue:[[startupDiskA pathComponents] lastObject]];
|
||||
[self.startupLoadDiskA setState:NSOnState];
|
||||
[self.diskAProtection setState:(readOnlyA ? NSOnState : NSOffState) atRow:0 column:0];
|
||||
[self.diskAProtection setState:(!readOnlyA ? NSOnState : NSOffState) atRow:0 column:1];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *startupDiskB = [defaults stringForKey:kApple2PrefStartupDiskB];
|
||||
BOOL readOnlyB = [defaults boolForKey:kApple2PrefStartupDiskBProtected];
|
||||
if (startupDiskB)
|
||||
{
|
||||
const char *err = disk6_insert(1, [[NSString stringWithFormat:@"%@.gz", startupDiskB] UTF8String], readOnlyB);
|
||||
if (!err)
|
||||
{
|
||||
[self.diskInB setStringValue:[[startupDiskB pathComponents] lastObject]];
|
||||
[self.startupLoadDiskB setState:NSOnState];
|
||||
[self.diskBProtection setState:(readOnlyB ? NSOnState : NSOffState) atRow:0 column:0];
|
||||
[self.diskBProtection setState:(!readOnlyB ? NSOnState : NSOffState) atRow:0 column:1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_savePrefs
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
[defaults removeObjectForKey:kApple2PrefStartupDiskA];
|
||||
[defaults removeObjectForKey:kApple2PrefStartupDiskB];
|
||||
[defaults removeObjectForKey:kApple2PrefStartupDiskAProtected];
|
||||
[defaults removeObjectForKey:kApple2PrefStartupDiskBProtected];
|
||||
|
||||
if ([self.startupLoadDiskA state] == NSOnState)
|
||||
{
|
||||
if (disk6.disk[0].fd >= 0)
|
||||
{
|
||||
NSString *diskA = [NSString stringWithUTF8String:disk6.disk[0].file_name];
|
||||
[defaults setObject:diskA forKey:kApple2PrefStartupDiskA];
|
||||
NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskAProtection] cells] firstObject];
|
||||
[defaults setBool:([readOnlyChoice state] == NSOnState) forKey:kApple2PrefStartupDiskAProtected];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self.startupLoadDiskB state] == NSOnState)
|
||||
{
|
||||
if (disk6.disk[1].fd >= 0)
|
||||
{
|
||||
NSString *diskB = [NSString stringWithUTF8String:disk6.disk[1].file_name];
|
||||
[defaults setObject:diskB forKey:kApple2PrefStartupDiskB];
|
||||
NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskBProtection] cells] firstObject];
|
||||
[defaults setBool:([readOnlyChoice state] == NSOnState) forKey:kApple2PrefStartupDiskBProtected];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_protectionChangedForDrive:(int)drive
|
||||
{
|
||||
if (disk6.disk[drive].fd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// HACK NOTE : dispatch so that state of outlet property is set properly
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSButtonCell *readOnlyChoice = (NSButtonCell *)[[(drive == 0 ? [self diskAProtection] : [self diskBProtection]) cells] firstObject];
|
||||
NSString *path = [NSString stringWithUTF8String:disk6.disk[drive].file_name];
|
||||
[self _insertDisketteInDrive:drive path:path type:[EmulatorDiskController extensionForPath:path] readOnly:([readOnlyChoice state] == NSOnState)];
|
||||
[self _savePrefs];
|
||||
});
|
||||
}
|
||||
|
||||
- (IBAction)diskAProtectionChanged:(id)sender
|
||||
{
|
||||
[self _protectionChangedForDrive:0];
|
||||
}
|
||||
|
||||
- (IBAction)diskBProtectionChanged:(id)sender
|
||||
{
|
||||
[self _protectionChangedForDrive:1];
|
||||
}
|
||||
|
||||
- (BOOL)_insertDisketteInDrive:(int)drive path:(NSString *)path type:(NSString *)type readOnly:(BOOL)readOnly
|
||||
{
|
||||
disk6_eject(drive);
|
||||
|
||||
const char *errMsg = disk6_insert(drive, [path UTF8String], readOnly);
|
||||
if (errMsg)
|
||||
{
|
||||
path = [NSString stringWithFormat:@"%@.gz", path];
|
||||
errMsg = disk6_insert(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;
|
||||
}
|
||||
}
|
||||
path = [NSString stringWithUTF8String:disk6.disk[drive].file_name];
|
||||
NSString *imageName = [[path pathComponents] lastObject];
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
if (drive == 0)
|
||||
{
|
||||
[[self diskInA] setStringValue:imageName];
|
||||
if ([[defaults stringForKey:kApple2PrefStartupDiskA] isEqualToString:path])
|
||||
{
|
||||
[self.startupLoadDiskA setState:NSOnState];
|
||||
//[self.diskAProtection setState:(readOnly ? NSOnState : NSOffState) atRow:0 column:0];
|
||||
//[self.diskAProtection setState:(!readOnly ? NSOnState : NSOffState) atRow:0 column:1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self diskInB] setStringValue:imageName];
|
||||
if ([[defaults stringForKey:kApple2PrefStartupDiskB] isEqualToString:path])
|
||||
{
|
||||
[self.startupLoadDiskB setState:NSOnState];
|
||||
//[self.diskBProtection setState:(readOnly ? NSOnState : NSOffState) atRow:0 column:0];
|
||||
//[self.diskBProtection setState:(!readOnly ? NSOnState : NSOffState) atRow:0 column:1];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
+ (NSSet *)emulatorFileTypes
|
||||
{
|
||||
static NSSet *set = nil;
|
||||
static dispatch_once_t onceToken = 0L;
|
||||
dispatch_once(&onceToken, ^{
|
||||
set = [[NSSet alloc] initWithObjects:@"dsk", @"nib", @"do", @"po", @"gz", nil];
|
||||
});
|
||||
return [[set retain] autorelease];
|
||||
}
|
||||
|
||||
+ (NSString *)extensionForPath:(NSString *)path
|
||||
{
|
||||
NSString *extension0 = [path pathExtension];
|
||||
NSString *extension1 = [[path stringByDeletingPathExtension] pathExtension];
|
||||
if ([extension0 isEqualToString:@"gz"])
|
||||
{
|
||||
extension0 = extension1;
|
||||
}
|
||||
return extension0;
|
||||
}
|
||||
|
||||
+ (void)chooseDiskForWindow:(NSWindow *)window completionHandler:(DiskCompletionHandler)handler
|
||||
{
|
||||
NSOpenPanel *openPanel = [[NSOpenPanel openPanel] retain];
|
||||
[openPanel setTitle:@"Choose a disk image"];
|
||||
NSURL *url = [[NSUserDefaults standardUserDefaults] URLForKey:kApple2DisksURL];
|
||||
if (!url)
|
||||
{
|
||||
url = [[NSBundle mainBundle] URLForResource:@"images" withExtension:nil];
|
||||
}
|
||||
[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 = [EmulatorDiskController emulatorFileTypes];
|
||||
[openPanel setAllowedFileTypes:[fileTypes allObjects]];
|
||||
handler = Block_copy(handler);
|
||||
[openPanel beginSheetModalForWindow:window completionHandler:^(NSInteger result) {
|
||||
handler(openPanel, result);
|
||||
Block_release(handler);
|
||||
[openPanel autorelease];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_chooseDisk:(int)drive readOnly:(BOOL)readOnly
|
||||
{
|
||||
[EmulatorDiskController chooseDiskForWindow:[self disksWindow] completionHandler:^(NSOpenPanel *openPanel, NSInteger result) {
|
||||
if (result == NSOKButton)
|
||||
{
|
||||
NSURL *selection = [[openPanel URLs] firstObject];
|
||||
NSString *path = [[selection path] stringByResolvingSymlinksInPath];
|
||||
NSString *extension = [EmulatorDiskController extensionForPath:path];
|
||||
NSSet *fileTypes = [EmulatorDiskController emulatorFileTypes];
|
||||
|
||||
NSString *directory = [path stringByDeletingLastPathComponent];
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setURL:[NSURL URLWithString:directory] forKey:kApple2DisksURL];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
[(drive == 0) ? self.startupLoadDiskA : self.startupLoadDiskB setState:NSOffState];
|
||||
[self _insertDisketteInDrive:drive path:path type:extension readOnly:readOnly];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)chooseDriveA:(id)sender
|
||||
{
|
||||
NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskAProtection] cells] firstObject];
|
||||
[self _chooseDisk:0 readOnly:([readOnlyChoice state] == NSOnState)];
|
||||
}
|
||||
|
||||
- (IBAction)chooseDriveB:(id)sender
|
||||
{
|
||||
NSButtonCell *readOnlyChoice = (NSButtonCell *)[[[self diskBProtection] cells] firstObject];
|
||||
[self _chooseDisk:1 readOnly:([readOnlyChoice state] == NSOnState)];
|
||||
}
|
||||
|
||||
- (IBAction)startupDiskAChoiceChanged:(id)sender
|
||||
{
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)startupDiskBChoiceChanged:(id)sender
|
||||
{
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)disksOK:(id)sender
|
||||
{
|
||||
[[self disksWindow] close];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on sample code from https://developer.apple.com/library/mac/samplecode/GLEssentials/Introduction/Intro.html
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface EmulatorFullscreenWindow : NSWindow
|
||||
|
||||
@end
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on sample code from https://developer.apple.com/library/mac/samplecode/GLEssentials/Introduction/Intro.html
|
||||
|
||||
#import "EmulatorFullscreenWindow.h"
|
||||
|
||||
@implementation EmulatorFullscreenWindow
|
||||
|
||||
- (id)init
|
||||
{
|
||||
// Create a screen-sized window on the display you want to take over
|
||||
NSRect screenRect = [[NSScreen mainScreen] frame];
|
||||
|
||||
// Initialize the window making it size of the screen and borderless
|
||||
self = [super initWithContentRect:screenRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
|
||||
|
||||
// Set the window level to be above the menu bar to cover everything else
|
||||
[self setLevel:NSMainMenuWindowLevel+1];
|
||||
|
||||
// Set opaque
|
||||
[self setOpaque:YES];
|
||||
|
||||
// Hide this when user switches to another window (or app)
|
||||
[self setHidesOnDeactivate:YES];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeKeyWindow
|
||||
{
|
||||
// Return yes so that this borderless window can receive input
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
[[self windowController] keyUp:event];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
[[self windowController] keyDown:event];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on sample code from https://developer.apple.com/library/mac/samplecode/GLEssentials/Introduction/Intro.html
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CVDisplayLink.h>
|
||||
|
||||
#import "modelUtil.h"
|
||||
#import "imageUtil.h"
|
||||
|
||||
@interface EmulatorGLView : NSOpenGLView
|
||||
@end
|
|
@ -1,321 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on sample code from https://developer.apple.com/library/mac/samplecode/GLEssentials/Introduction/Intro.html
|
||||
|
||||
#import "EmulatorGLView.h"
|
||||
|
||||
// Apple //e common routines
|
||||
#import "common.h"
|
||||
|
||||
#if TARGET_OS_MAC
|
||||
#define USE_DISPLAYLINK 0
|
||||
#define BROKEN_DISPLAYLINK 1
|
||||
#else
|
||||
// iOS uses CADisplayLink
|
||||
#define USE_DISPLAYLINK 1
|
||||
#endif
|
||||
|
||||
#define SUPPORT_RETINA_RESOLUTION 1
|
||||
|
||||
const NSString *kDrawTimerNotification = @"kDrawTimerNotification";
|
||||
|
||||
@interface EmulatorGLView ()
|
||||
|
||||
#if USE_DISPLAYLINK
|
||||
@property (nonatomic, assign) CVDisplayLinkRef displayLink;
|
||||
#else
|
||||
@property (nonatomic, retain) NSTimer *timer;
|
||||
#endif
|
||||
|
||||
- (void)initGL;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation EmulatorGLView
|
||||
|
||||
#if USE_DISPLAYLINK
|
||||
@synthesize displayLink = _displayLink;
|
||||
#else
|
||||
@synthesize timer = _timer;
|
||||
#endif
|
||||
|
||||
#pragma mark CVDisplayLink / NSTimer stuff
|
||||
|
||||
#if USE_DISPLAYLINK
|
||||
- (CVReturn)getFrameForTime:(const CVTimeStamp *)outputTime
|
||||
{
|
||||
// There is no autorelease pool when this method is called
|
||||
// because it will be called from a background thread.
|
||||
// It's important to create one or app can leak objects.
|
||||
@autoreleasepool {
|
||||
// We draw on a secondary thread through the display link
|
||||
// When resizing the view, -reshape is called automatically on the main
|
||||
// thread. Add a mutex around to avoid the threads accessing the context
|
||||
// simultaneously when resizing
|
||||
|
||||
#if BROKEN_DISPLAYLINK
|
||||
#warning ASC NOTE 2014/09/27 multi-threaded OpenGL on Mac considered harmful to developer sanity
|
||||
// 2014/09/27 Kinda defeats the purpose of using CVDisplayLink ... but keeps it from crashing to dispatch to main queue
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
#endif
|
||||
[[self openGLContext] makeCurrentContext];
|
||||
[self drawView];
|
||||
#if BROKEN_DISPLAYLINK
|
||||
});
|
||||
#endif
|
||||
}
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
// This is the renderer output callback function
|
||||
static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
|
||||
{
|
||||
CVReturn result = [(EmulatorGLView *)displayLinkContext getFrameForTime:outputTime];
|
||||
return result;
|
||||
}
|
||||
|
||||
#else // use NSTimer
|
||||
- (void)targetMethod:(NSTimer *)theTimer
|
||||
{
|
||||
NSAssert([NSThread isMainThread], @"Timer fired on non-main thread!");
|
||||
[[self openGLContext] makeCurrentContext];
|
||||
[self drawView];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
NSOpenGLPixelFormatAttribute attrs[] =
|
||||
{
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFADepthSize, 24,
|
||||
// Must specify the 3.2 Core Profile to use OpenGL 3.2
|
||||
NSOpenGLPFAOpenGLProfile,
|
||||
NSOpenGLProfileVersion3_2Core,
|
||||
0
|
||||
};
|
||||
|
||||
NSOpenGLPixelFormat *pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
|
||||
|
||||
if (!pf)
|
||||
{
|
||||
NSLog(@"No OpenGL pixel format");
|
||||
}
|
||||
|
||||
NSOpenGLContext* context = [[[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil] autorelease];
|
||||
|
||||
#if defined(DEBUG)
|
||||
// When we're using a CoreProfile context, crash if we call a legacy OpenGL function
|
||||
// This will make it much more obvious where and when such a function call is made so
|
||||
// that we can remove such calls.
|
||||
// Without this we'd simply get GL_INVALID_OPERATION error for calling legacy functions
|
||||
// but it would be more difficult to see where that function was called.
|
||||
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
|
||||
#endif
|
||||
|
||||
[self setPixelFormat:pf];
|
||||
[self setOpenGLContext:context];
|
||||
|
||||
#if SUPPORT_RETINA_RESOLUTION
|
||||
// Opt-In to Retina resolution
|
||||
[self setWantsBestResolutionOpenGLSurface:YES];
|
||||
#endif // SUPPORT_RETINA_RESOLUTION
|
||||
}
|
||||
|
||||
- (void)prepareOpenGL
|
||||
{
|
||||
[super prepareOpenGL];
|
||||
|
||||
// Make all the OpenGL calls to setup rendering
|
||||
// and build the necessary rendering objects
|
||||
[self initGL];
|
||||
|
||||
#if USE_DISPLAYLINK
|
||||
// Create a display link capable of being used with all active displays
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
||||
|
||||
// Set the renderer output callback function
|
||||
CVDisplayLinkSetOutputCallback(_displayLink, &displayLinkCallback, self);
|
||||
|
||||
// Set the display link for the current renderer
|
||||
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
|
||||
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
|
||||
|
||||
// Activate the display link
|
||||
CVDisplayLinkStart(_displayLink);
|
||||
#else
|
||||
[self.timer invalidate];
|
||||
self.timer = nil;
|
||||
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(targetMethod:) userInfo:nil repeats:YES];
|
||||
#endif
|
||||
|
||||
// Register to be notified when the window closes so we can stop the displaylink
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:[self window]];
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)notification
|
||||
{
|
||||
// Stop the display link when the window is closing because default
|
||||
// OpenGL render buffers will be destroyed. If display link continues to
|
||||
// fire without renderbuffers, OpenGL draw calls will set errors.
|
||||
#if USE_DISPLAYLINK
|
||||
CVDisplayLinkStop(_displayLink);
|
||||
#else
|
||||
[self.timer invalidate];
|
||||
self.timer = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)initGL
|
||||
{
|
||||
// The reshape function may have changed the thread to which our OpenGL
|
||||
// context is attached before prepareOpenGL and initGL are called. So call
|
||||
// makeCurrentContext to ensure that our OpenGL context current to this
|
||||
// thread (i.e. makeCurrentContext directs all OpenGL calls on this thread
|
||||
// to [self openGLContext])
|
||||
[[self openGLContext] makeCurrentContext];
|
||||
|
||||
emulator_start();
|
||||
|
||||
// Synchronize buffer swaps with vertical refresh rate
|
||||
GLint swapInt = 1;
|
||||
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
|
||||
// Init our renderer. Use 0 for the defaultFBO which is appropriate for
|
||||
// OSX (but not iOS since iOS apps must create their own FBO)
|
||||
#if TARGET_OS_MAC
|
||||
video_backend->init(0);
|
||||
#elif TARGET_OS_IPHONE
|
||||
# error "FBO FIXME TODO"
|
||||
video_backend->init(otherFBO);
|
||||
#else
|
||||
# error "unknown/unsupported Apple platform
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)reshape
|
||||
{
|
||||
[super reshape];
|
||||
|
||||
// We draw on a secondary thread through the display link. However, when
|
||||
// resizing the view, -drawRect is called on the main thread.
|
||||
// Add a mutex around to avoid the threads accessing the context
|
||||
// simultaneously when resizing.
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
|
||||
// Get the view size in Points
|
||||
NSRect viewRectPoints = [self bounds];
|
||||
|
||||
#if SUPPORT_RETINA_RESOLUTION
|
||||
|
||||
// Rendering at retina resolutions will reduce aliasing, but at the potential
|
||||
// cost of framerate and battery life due to the GPU needing to render more
|
||||
// pixels.
|
||||
|
||||
// Any calculations the renderer does which use pixel dimentions, must be
|
||||
// in "retina" space. [NSView convertRectToBacking] converts point sizes
|
||||
// to pixel sizes. Thus the renderer gets the size in pixels, not points,
|
||||
// so that it can set it's viewport and perform and other pixel based
|
||||
// calculations appropriately.
|
||||
// viewRectPixels will be larger (2x) than viewRectPoints for retina displays.
|
||||
// viewRectPixels will be the same as viewRectPoints for non-retina displays
|
||||
NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
|
||||
|
||||
#else //if !SUPPORT_RETINA_RESOLUTION
|
||||
|
||||
// App will typically render faster and use less power rendering at
|
||||
// non-retina resolutions since the GPU needs to render less pixels. There
|
||||
// is the cost of more aliasing, but it will be no-worse than on a Mac
|
||||
// without a retina display.
|
||||
|
||||
// Points:Pixels is always 1:1 when not supporting retina resolutions
|
||||
NSRect viewRectPixels = viewRectPoints;
|
||||
|
||||
#endif // !SUPPORT_RETINA_RESOLUTION
|
||||
|
||||
// Set the new dimensions in our renderer
|
||||
video_backend->reshape((int)viewRectPixels.size.width, (int)viewRectPixels.size.height);
|
||||
|
||||
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
}
|
||||
|
||||
- (void)renewGState
|
||||
{
|
||||
// Called whenever graphics state updated (such as window resize)
|
||||
|
||||
// OpenGL rendering is not synchronous with other rendering on the OSX.
|
||||
// Therefore, call disableScreenUpdatesUntilFlush so the window server
|
||||
// doesn't render non-OpenGL content in the window asynchronously from
|
||||
// OpenGL content, which could cause flickering. (non-OpenGL content
|
||||
// includes the title bar and drawing done by the app with other APIs)
|
||||
[[self window] disableScreenUpdatesUntilFlush];
|
||||
[super renewGState];
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)theRect
|
||||
{
|
||||
NSAssert([NSThread isMainThread], @"drawRect called on non-main thread!");
|
||||
// Called during resize operations
|
||||
// Avoid flickering during resize by drawing
|
||||
[[self openGLContext] makeCurrentContext];
|
||||
[self drawView];
|
||||
}
|
||||
|
||||
- (void)drawView
|
||||
{
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
video_backend->render();
|
||||
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
|
||||
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:(NSString *)kDrawTimerNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Stop the display link BEFORE releasing anything in the view
|
||||
// otherwise the display link thread may call into the view and crash
|
||||
// when it encounters something that has been release
|
||||
#if USE_DISPLAYLINK
|
||||
CVDisplayLinkStop(_displayLink);
|
||||
CVDisplayLinkRelease(_displayLink);
|
||||
#else
|
||||
[self.timer invalidate];
|
||||
self.timer = nil;
|
||||
#endif
|
||||
|
||||
// shut down common OpenGL stuff AFTER display link has been released
|
||||
emulator_shutdown();
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Application Delegate methods
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)application
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)application
|
||||
{
|
||||
disk6_eject(0);
|
||||
disk6_eject(1);
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface EmulatorJoystickCalibrationView : NSView
|
||||
|
||||
@end
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import "EmulatorJoystickCalibrationView.h"
|
||||
#import "common.h"
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
@implementation EmulatorJoystickCalibrationView
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
[super drawRect:dirtyRect];
|
||||
[self drawView];
|
||||
}
|
||||
|
||||
- (void)drawView
|
||||
{
|
||||
static int pulseSize = 1;
|
||||
|
||||
CGRect bounds = [self bounds];
|
||||
|
||||
NSAssert(bounds.size.height == JOY_RANGE, @"view should be 256pts high");
|
||||
NSAssert(bounds.size.width == JOY_RANGE, @"view should be 256pts wide");
|
||||
|
||||
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
|
||||
CGContextSaveGState(context);
|
||||
CGContextSetLineWidth(context, 1);
|
||||
|
||||
if (joy_clip_to_radius && (joy_mode == JOY_PCJOY))
|
||||
{
|
||||
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
|
||||
CGContextAddEllipseInRect(context, bounds);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
CGFloat x_val = joy_x;
|
||||
CGFloat y_val = JOY_RANGE-joy_y;
|
||||
|
||||
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
|
||||
CGRect cursor = CGRectMake(x_val-(pulseSize/2.f), y_val-(pulseSize/2.f), pulseSize, pulseSize);
|
||||
CGContextAddEllipseInRect(context, cursor);
|
||||
|
||||
CGContextStrokePath(context);
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
pulseSize = ((pulseSize+1) & 0x7) +1;
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
[[self nextResponder] keyUp:event];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
[[self nextResponder] keyDown:event];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface EmulatorJoystickController : NSObject
|
||||
|
||||
@end
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2014, 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import "EmulatorJoystickController.h"
|
||||
#import "EmulatorWindowController.h"
|
||||
#import "common.h"
|
||||
|
||||
#import <DDHidLib/DDHidJoystick.h>
|
||||
|
||||
@interface EmulatorJoystickController()
|
||||
@property (nonatomic, retain) NSDictionary *allJoysticks;
|
||||
+ (EmulatorJoystickController *)sharedInstance;
|
||||
- (void)resetJoysticks;
|
||||
@end
|
||||
|
||||
void gldriver_joystick_reset(void) {
|
||||
[EmulatorJoystickController sharedInstance];
|
||||
}
|
||||
|
||||
@implementation EmulatorJoystickController
|
||||
|
||||
@synthesize allJoysticks = _allJoysticks;
|
||||
|
||||
+ (EmulatorJoystickController *)sharedInstance
|
||||
{
|
||||
static dispatch_once_t onceToken = 0L;
|
||||
static EmulatorJoystickController *joystickController = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
joystickController = [[EmulatorJoystickController alloc] init];
|
||||
});
|
||||
return joystickController;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectivityPoll:) name:(NSString *)kDrawTimerNotification object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
self.allJoysticks = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)resetJoysticks
|
||||
{
|
||||
for (DDHidJoystick *joystick in [self.allJoysticks allValues])
|
||||
{
|
||||
@try {
|
||||
[joystick setDelegate:nil];
|
||||
[joystick stopListening];
|
||||
} @catch (NSException *e) {
|
||||
// hot-plugging joysticks can cause glitches
|
||||
NSLog(@"Joystick device library raised exception on close : %@", e);
|
||||
}
|
||||
}
|
||||
NSArray *joysticks = [DDHidJoystick allJoysticks];
|
||||
NSMutableDictionary *allJoysticks = [NSMutableDictionary dictionary];
|
||||
for (DDHidJoystick *joystick in joysticks) {
|
||||
NSString *key =[NSString stringWithFormat:@"%@-%@-%@", [joystick manufacturer], [joystick serialNumber], [joystick transport]];
|
||||
[allJoysticks setObject:joystick forKey:key];
|
||||
}
|
||||
self.allJoysticks = allJoysticks;
|
||||
dispatch_async(dispatch_get_main_queue(), ^ {
|
||||
for (DDHidJoystick *joystick in [self.allJoysticks allValues])
|
||||
{
|
||||
NSLog(@"found joystick : '%@' '%@' '%@' %@", [joystick manufacturer], [joystick serialNumber], [joystick transport], joystick);
|
||||
@try {
|
||||
[joystick setDelegate:self];
|
||||
[joystick startListening];
|
||||
} @catch (NSException *e) {
|
||||
// hot-plugging joystick can cause glitches
|
||||
NSLog(@"Joystick device library raised exception on start : %@", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Joystick connectivity polling
|
||||
|
||||
- (void)connectivityPoll:(NSNotification *)notification
|
||||
{
|
||||
static unsigned int counter = 0;
|
||||
counter = (counter+1) % 60;
|
||||
if (counter == 0)
|
||||
{
|
||||
NSArray *joysticks = [DDHidJoystick allJoysticks];
|
||||
BOOL changed = ([joysticks count] != [self.allJoysticks count]);
|
||||
for (DDHidJoystick *joystick in joysticks)
|
||||
{
|
||||
NSString *key =[NSString stringWithFormat:@"%@-%@-%@", [joystick manufacturer], [joystick serialNumber], [joystick transport]];
|
||||
if (![self.allJoysticks objectForKey:key])
|
||||
{
|
||||
changed = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
[self resetJoysticks];
|
||||
}
|
||||
}
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
if (joy_mode == JOY_KPAD)
|
||||
{
|
||||
c_keys_handle_input(-1, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark NSObject(DDHidJoystickDelegate)
|
||||
|
||||
#define DDHID_JOYSTICK_NORMALIZER ((float)JOY_RANGE/(DDHID_JOYSTICK_VALUE_MAX*2))
|
||||
#define QUARTER_JOY (HALF_JOY_RANGE>>1)
|
||||
|
||||
static inline void clampBeyondRadius(void) {
|
||||
CGFloat half_x = joy_x - HALF_JOY_RANGE;
|
||||
CGFloat half_y = joy_y - HALF_JOY_RANGE;
|
||||
CGFloat r = sqrtf(half_x*half_x + half_y*half_y);
|
||||
bool shouldClip = (r > HALF_JOY_RANGE);
|
||||
if (joy_clip_to_radius && shouldClip) {
|
||||
if (joy_x < HALF_JOY_RANGE) {
|
||||
joy_x = (joy_x < QUARTER_JOY) ? 0.f : joy_x;
|
||||
} else {
|
||||
joy_x = (joy_x < HALF_JOY_RANGE+QUARTER_JOY) ? joy_x : JOY_RANGE-1;
|
||||
}
|
||||
if (joy_y < HALF_JOY_RANGE) {
|
||||
joy_y = (joy_y < QUARTER_JOY) ? 0.f : joy_y;
|
||||
} else {
|
||||
joy_y = (joy_y < HALF_JOY_RANGE+QUARTER_JOY) ? joy_y : JOY_RANGE-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)ddhidJoystick:(DDHidJoystick *)joystick stick:(unsigned int)stick xChanged:(int)value
|
||||
{
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
if (joy_mode == JOY_KPAD) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
joy_x = (uint16_t)((value+DDHID_JOYSTICK_VALUE_MAX) * DDHID_JOYSTICK_NORMALIZER);
|
||||
if (joy_x > 0xFF) {
|
||||
joy_x = 0xFF;
|
||||
}
|
||||
|
||||
clampBeyondRadius();
|
||||
}
|
||||
|
||||
- (void)ddhidJoystick:(DDHidJoystick *)joystick stick:(unsigned int)stick yChanged:(int)value
|
||||
{
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
if (joy_mode == JOY_KPAD) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
joy_y = (uint16_t)((value+DDHID_JOYSTICK_VALUE_MAX) * DDHID_JOYSTICK_NORMALIZER);
|
||||
if (joy_y > 0xFF) {
|
||||
joy_y = 0xFF;
|
||||
}
|
||||
|
||||
clampBeyondRadius();
|
||||
}
|
||||
|
||||
- (void)ddhidJoystick:(DDHidJoystick *)joystick stick:(unsigned int)stick otherAxis:(unsigned)otherAxis valueChanged:(int)value
|
||||
{
|
||||
// NOOP ...
|
||||
}
|
||||
|
||||
- (void)ddhidJoystick:(DDHidJoystick *)joystick stick:(unsigned int)stick povNumber:(unsigned)povNumber valueChanged:(int)value
|
||||
{
|
||||
// NOOP ...
|
||||
}
|
||||
|
||||
- (void)ddhidJoystick:(DDHidJoystick *)joystick buttonDown:(unsigned int)buttonNumber
|
||||
{
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
if (joy_mode == JOY_KPAD) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// sample buttons only if apple keys aren't pressed. keys get set to 0xff, and js buttons are set to 0x80.
|
||||
if ((buttonNumber == 0x01) && !(joy_button0 & 0x7f)) {
|
||||
joy_button0 = 0x80;
|
||||
}
|
||||
if ((buttonNumber == 0x02) && !(joy_button1 & 0x7f)) {
|
||||
joy_button1 = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)ddhidJoystick:(DDHidJoystick *)joystick buttonUp:(unsigned int)buttonNumber
|
||||
{
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
if (joy_mode == JOY_KPAD) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// sample buttons only if apple keys aren't pressed. keys get set to 0xff, and js buttons are set to 0x80.
|
||||
if ((buttonNumber == 0x01) && !(joy_button0 & 0x7f)) {
|
||||
joy_button0 = 0x0;
|
||||
}
|
||||
if ((buttonNumber == 0x02) && !(joy_button1 & 0x7f)) {
|
||||
joy_button1 = 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define kApple2PrefStartupDiskA @"kApple2PrefStartupDiskA"
|
||||
#define kApple2PrefStartupDiskAProtected @"kApple2PrefStartupDiskAProtected"
|
||||
#define kApple2PrefStartupDiskB @"kApple2PrefStartupDiskB"
|
||||
#define kApple2PrefStartupDiskBProtected @"kApple2PrefStartupDiskBProtected"
|
||||
|
||||
@interface EmulatorPrefsController : NSWindowController
|
||||
|
||||
@end
|
|
@ -1,353 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import "EmulatorPrefsController.h"
|
||||
#import "EmulatorDiskController.h"
|
||||
#import "EmulatorJoystickCalibrationView.h"
|
||||
#import "EmulatorWindowController.h"
|
||||
#import "common.h"
|
||||
|
||||
#define kApple2SavedPrefs @"kApple2SavedPrefs"
|
||||
#define kApple2CPUSpeed @"kApple2CPUSpeed"
|
||||
#define kApple2CPUSpeedIsMax @"kApple2CPUSpeedIsMax"
|
||||
#define kApple2AltSpeed @"kApple2AltSpeed"
|
||||
#define kApple2AltSpeedIsMax @"kApple2AltSpeedIsMax"
|
||||
#define kApple2SoundcardConfig @"kApple2SoundcardConfig"
|
||||
#define kApple2ColorConfig @"kApple2ColorConfig"
|
||||
#define kApple2JoystickConfig @"kApple2JoystickConfig"
|
||||
#define kApple2JoystickAutoRecenter @"kApple2JoystickAutoRecenter"
|
||||
#define kApple2JoystickClipToRadius @"kApple2JoystickClipToRadius"
|
||||
#define kApple2JoystickStep @"kApple2JoystickStep"
|
||||
|
||||
@interface EmulatorPrefsController ()
|
||||
|
||||
@property (assign) IBOutlet NSSlider *cpuSlider;
|
||||
@property (assign) IBOutlet NSSlider *altSlider;
|
||||
@property (assign) IBOutlet NSTextField *cpuSliderLabel;
|
||||
@property (assign) IBOutlet NSTextField *altSliderLabel;
|
||||
@property (assign) IBOutlet NSButton *cpuMaxChoice;
|
||||
@property (assign) IBOutlet NSButton *altMaxChoice;
|
||||
|
||||
@property (assign) IBOutlet NSMatrix *soundCardChoice;
|
||||
|
||||
@property (assign) IBOutlet NSPopUpButton *colorChoice;
|
||||
|
||||
@property (assign) IBOutlet NSPopUpButton *joystickChoice;
|
||||
@property (assign) IBOutlet NSButton *joystickRecenter;
|
||||
@property (assign) IBOutlet NSButton *joystickClipToRadius;
|
||||
@property (assign) IBOutlet NSTextField *joystickStepLabel;
|
||||
@property (assign) IBOutlet NSStepper *joystickStepper;
|
||||
@property (assign) IBOutlet NSTextField *joystickStepperLabel;
|
||||
@property (assign) IBOutlet NSTextField *joystickKPadNotes;
|
||||
@property (assign) IBOutlet NSTextField *joystickDeviceNotes;
|
||||
|
||||
@property (assign) IBOutlet NSTextField *button0Pressed;
|
||||
@property (assign) IBOutlet NSTextField *button1Pressed;
|
||||
@property (assign) IBOutlet EmulatorJoystickCalibrationView *joystickCalibrationView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EmulatorPrefsController
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
BOOL firstTime = ![defaults boolForKey:kApple2SavedPrefs];
|
||||
if (firstTime)
|
||||
{
|
||||
[defaults setBool:YES forKey:kApple2SavedPrefs];
|
||||
[defaults setDouble:1.0 forKey:kApple2CPUSpeed];
|
||||
[defaults setDouble:CPU_SCALE_SLOWEST forKey:kApple2AltSpeed];
|
||||
[defaults setBool:NO forKey:kApple2CPUSpeedIsMax];
|
||||
[defaults setBool:NO forKey:kApple2AltSpeedIsMax];
|
||||
[defaults setInteger:COLOR_INTERP forKey:kApple2ColorConfig];
|
||||
[defaults setInteger:JOY_KPAD forKey:kApple2JoystickConfig];
|
||||
[defaults setBool:YES forKey:kApple2JoystickAutoRecenter];
|
||||
[defaults removeObjectForKey:kApple2PrefStartupDiskA];
|
||||
[defaults removeObjectForKey:kApple2PrefStartupDiskB];
|
||||
}
|
||||
|
||||
cpu_scale_factor = [defaults doubleForKey:kApple2CPUSpeed];
|
||||
[self.cpuSlider setDoubleValue:cpu_scale_factor];
|
||||
[self.cpuSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", cpu_scale_factor*100]];
|
||||
if ([defaults boolForKey:kApple2CPUSpeedIsMax])
|
||||
{
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
[self.cpuMaxChoice setState:NSOnState];
|
||||
[self.cpuSlider setEnabled:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.cpuMaxChoice setState:NSOffState];
|
||||
[self.cpuSlider setEnabled:YES];
|
||||
}
|
||||
|
||||
cpu_altscale_factor = [defaults doubleForKey:kApple2AltSpeed];
|
||||
[self.altSlider setDoubleValue:cpu_altscale_factor];
|
||||
[self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", cpu_altscale_factor*100]];
|
||||
if ([defaults boolForKey:kApple2AltSpeedIsMax])
|
||||
{
|
||||
cpu_altscale_factor = CPU_SCALE_FASTEST;
|
||||
[self.altMaxChoice setState:NSOnState];
|
||||
[self.altSlider setEnabled:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.altMaxChoice setState:NSOffState];
|
||||
[self.altSlider setEnabled:YES];
|
||||
}
|
||||
|
||||
#warning TODO : actually implement sound card choices
|
||||
[self.soundCardChoice deselectAllCells];
|
||||
[self.soundCardChoice selectCellAtRow:1 column:0];
|
||||
|
||||
NSInteger mode = [defaults integerForKey:kApple2ColorConfig];
|
||||
if (! ((mode >= COLOR_NONE) && (mode < NUM_COLOROPTS)) )
|
||||
{
|
||||
mode = COLOR_NONE;
|
||||
}
|
||||
[self.colorChoice selectItemAtIndex:mode];
|
||||
color_mode = (color_mode_t)mode;
|
||||
|
||||
mode = [defaults integerForKey:kApple2JoystickConfig];
|
||||
if (! ((mode >= JOY_PCJOY) && (mode < NUM_JOYOPTS)) )
|
||||
{
|
||||
mode = JOY_PCJOY;
|
||||
}
|
||||
joy_mode = (joystick_mode_t)mode;
|
||||
[self.joystickChoice selectItemAtIndex:mode];
|
||||
|
||||
#ifdef KEYPAD_JOYSTICK
|
||||
joy_auto_recenter = [defaults integerForKey:kApple2JoystickAutoRecenter];
|
||||
[self.joystickRecenter setState:joy_auto_recenter ? NSOnState : NSOffState];
|
||||
joy_step = [defaults integerForKey:kApple2JoystickStep];
|
||||
if (!joy_step)
|
||||
{
|
||||
joy_step = 1;
|
||||
}
|
||||
[self.joystickStepLabel setIntegerValue:joy_step];
|
||||
[self.joystickStepper setIntegerValue:joy_step];
|
||||
#endif
|
||||
|
||||
joy_clip_to_radius = [defaults boolForKey:kApple2JoystickClipToRadius];
|
||||
[self.joystickClipToRadius setState:joy_clip_to_radius ? NSOnState : NSOffState];
|
||||
|
||||
[self _setupJoystickUI];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(drawJoystickCalibration:) name:(NSString *)kDrawTimerNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)_savePrefs
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:YES forKey:kApple2SavedPrefs];
|
||||
[defaults setDouble:cpu_scale_factor forKey:kApple2CPUSpeed];
|
||||
[defaults setDouble:cpu_altscale_factor forKey:kApple2AltSpeed];
|
||||
[defaults setBool:([self.cpuMaxChoice state] == NSOnState) forKey:kApple2CPUSpeedIsMax];
|
||||
[defaults setBool:([self.altMaxChoice state] == NSOnState) forKey:kApple2AltSpeedIsMax];
|
||||
[defaults setInteger:color_mode forKey:kApple2ColorConfig];
|
||||
[defaults setInteger:joy_mode forKey:kApple2JoystickConfig];
|
||||
[defaults setInteger:joy_step forKey:kApple2JoystickStep];
|
||||
[defaults setBool:joy_auto_recenter forKey:kApple2JoystickAutoRecenter];
|
||||
[defaults setBool:joy_clip_to_radius forKey:kApple2JoystickClipToRadius];
|
||||
}
|
||||
|
||||
- (IBAction)sliderDidMove:(id)sender
|
||||
{
|
||||
NSSlider *slider = (NSSlider *)sender;
|
||||
double value = [slider doubleValue];
|
||||
if (slider == self.cpuSlider)
|
||||
{
|
||||
cpu_scale_factor = value;
|
||||
[self.cpuSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]];
|
||||
}
|
||||
else
|
||||
{
|
||||
cpu_altscale_factor = value;
|
||||
[self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]];
|
||||
}
|
||||
|
||||
timing_initialize();
|
||||
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)peggedChoiceChanged:(id)sender
|
||||
{
|
||||
NSButton *maxButton = (NSButton *)sender;
|
||||
if (maxButton == self.cpuMaxChoice)
|
||||
{
|
||||
[self.cpuSlider setEnabled:([maxButton state] != NSOnState)];
|
||||
cpu_scale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.cpuSlider doubleValue];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.altSlider setEnabled:([maxButton state] != NSOnState)];
|
||||
cpu_altscale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.altSlider doubleValue];
|
||||
}
|
||||
|
||||
timing_initialize();
|
||||
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)colorChoiceChanged:(id)sender
|
||||
{
|
||||
NSInteger mode = [self.colorChoice indexOfSelectedItem];
|
||||
if (! ((mode >= COLOR_NONE) && (mode < NUM_COLOROPTS)) )
|
||||
{
|
||||
mode = COLOR_NONE;
|
||||
}
|
||||
color_mode = (color_mode_t)mode;
|
||||
[self _savePrefs];
|
||||
|
||||
#warning HACK TODO FIXME need to refactor video resetting procedure
|
||||
video_reset();
|
||||
video_setpage(!!(softswitches & SS_SCREEN));
|
||||
video_redraw();
|
||||
}
|
||||
|
||||
- (IBAction)soundCardChoiceChanged:(id)sender
|
||||
{
|
||||
#warning TODO : make soundcard configurable at runtime
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Joystick preferences
|
||||
|
||||
- (void)_setupJoystickUI
|
||||
{
|
||||
[self.joystickRecenter setHidden:(joy_mode == JOY_PCJOY)];
|
||||
[self.joystickClipToRadius setHidden:(joy_mode != JOY_PCJOY)];
|
||||
[self.joystickStepLabel setHidden:(joy_mode == JOY_PCJOY)];
|
||||
[self.joystickStepper setHidden:(joy_mode == JOY_PCJOY)];
|
||||
[self.joystickStepperLabel setHidden:(joy_mode == JOY_PCJOY)];
|
||||
[self.joystickKPadNotes setHidden:(joy_mode == JOY_PCJOY)];
|
||||
[self.joystickDeviceNotes setHidden:(joy_mode != JOY_PCJOY)];
|
||||
}
|
||||
|
||||
- (IBAction)joystickChoiceChanged:(id)sender
|
||||
{
|
||||
NSInteger mode = [self.joystickChoice indexOfSelectedItem];
|
||||
if (! ((mode >= JOY_PCJOY) && (mode < NUM_JOYOPTS)) )
|
||||
{
|
||||
mode = JOY_PCJOY;
|
||||
}
|
||||
joy_mode = (joystick_mode_t)mode;
|
||||
[self _setupJoystickUI];
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)autoRecenterChoiceChanged:(id)sender
|
||||
{
|
||||
joy_auto_recenter = ([self.joystickRecenter state] == NSOnState);
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)clipToRadiusChoiceChanged:(id)sender
|
||||
{
|
||||
joy_clip_to_radius = ([self.joystickClipToRadius state] == NSOnState);
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
- (IBAction)stepValueChanged:(id)sender
|
||||
{
|
||||
joy_step = [self.joystickStepper intValue];
|
||||
[self.joystickStepLabel setIntegerValue:joy_step];
|
||||
[self _savePrefs];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma Joystick calibration
|
||||
|
||||
- (void)drawJoystickCalibration:(NSNotification *)notification
|
||||
{
|
||||
if (![self.joystickCalibrationView isHidden])
|
||||
{
|
||||
[self.joystickCalibrationView setNeedsDisplay:YES];
|
||||
[self.button0Pressed setHidden:!(joy_button0)];
|
||||
[self.button1Pressed setHidden:!(joy_button1)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)flagsChanged:(NSEvent*)event
|
||||
{
|
||||
// NOTE : yay, awesome! checking for ([event modifierFlags] & NSAlternateKeyMask) does not work properly if both were pressed and then one ALT key is unpressed ... kudoes to Apple for an excellent key-handling API /sarc
|
||||
static BOOL leftAltEngaged = NO;
|
||||
static BOOL rightAltEngaged = NO;
|
||||
switch ([event keyCode])
|
||||
{
|
||||
case ALT_LT:
|
||||
leftAltEngaged = !leftAltEngaged;
|
||||
c_keys_handle_input(SCODE_L_ALT, /*pressed:*/leftAltEngaged, /*cooked:*/0);
|
||||
break;
|
||||
case ALT_RT:
|
||||
rightAltEngaged = !rightAltEngaged;
|
||||
c_keys_handle_input(SCODE_R_ALT, /*pressed:*/rightAltEngaged, /*cooked:*/0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!([event modifierFlags] & NSAlternateKeyMask))
|
||||
{
|
||||
// But we can trust the system state if no alt modifier exists ... this resets b0rken edge cases
|
||||
leftAltEngaged = NO;
|
||||
rightAltEngaged = NO;
|
||||
}
|
||||
[self.button0Pressed setHidden:!(joy_button0)];
|
||||
[self.button1Pressed setHidden:!(joy_button1)];
|
||||
}
|
||||
|
||||
- (void)_handleKeyEvent:(NSEvent *)event pressed:(BOOL)pressed
|
||||
{
|
||||
unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
|
||||
int scode = (int)c;
|
||||
switch (scode)
|
||||
{
|
||||
case NSUpArrowFunctionKey:
|
||||
c_keys_handle_input(SCODE_U, pressed, /*cooked:*/0);
|
||||
break;
|
||||
case NSDownArrowFunctionKey:
|
||||
c_keys_handle_input(SCODE_D, pressed, /*cooked:*/0);
|
||||
break;
|
||||
case NSLeftArrowFunctionKey:
|
||||
c_keys_handle_input(SCODE_L, pressed, /*cooked:*/0);
|
||||
break;
|
||||
case NSRightArrowFunctionKey:
|
||||
c_keys_handle_input(SCODE_R, pressed, /*cooked:*/0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
[self _handleKeyEvent:event pressed:NO];
|
||||
|
||||
// Allow other character to be handled (or not and beep)
|
||||
//[super keyDown:event];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
[self _handleKeyEvent:event pressed:YES];
|
||||
|
||||
// Allow other character to be handled (or not and beep)
|
||||
//[super keyDown:event];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface EmulatorWindow : NSWindow
|
||||
|
||||
@end
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#import "EmulatorWindow.h"
|
||||
|
||||
@implementation EmulatorWindow
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
[[self windowController] keyUp:event];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
[[self windowController] keyDown:event];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on sample code from https://developer.apple.com/library/mac/samplecode/GLEssentials/Introduction/Intro.html
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "EmulatorGLView.h"
|
||||
|
||||
#define CAPS_LOCK 0x39
|
||||
#define SHIFT_LT 0x38
|
||||
#define SHIFT_RT 0x3c
|
||||
#define CTRL_LT 0x3b
|
||||
#define CTRL_RT 0x3e
|
||||
#define ALT_LT 0x3a
|
||||
#define ALT_RT 0x3d
|
||||
|
||||
extern const NSString *kDrawTimerNotification;
|
||||
|
||||
@interface EmulatorWindowController : NSWindowController
|
||||
|
||||
@end
|
|
@ -1,404 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2013-2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on sample code from https://developer.apple.com/library/mac/samplecode/GLEssentials/Introduction/Intro.html
|
||||
|
||||
#import "EmulatorWindowController.h"
|
||||
#import "EmulatorFullscreenWindow.h"
|
||||
#import "common.h"
|
||||
|
||||
@interface EmulatorWindowController ()
|
||||
|
||||
@property (nonatomic, assign) BOOL paused;
|
||||
|
||||
@property (assign) IBOutlet EmulatorGLView *view;
|
||||
@property (assign) IBOutlet NSWindow *disksWindow;
|
||||
@property (assign) IBOutlet NSWindow *prefsWindow;
|
||||
@property (assign) IBOutlet NSMenuItem *pauseMenuItem;
|
||||
@property (assign) IBOutlet NSToolbar *toolBar;
|
||||
@property (assign) IBOutlet NSToolbarItem *pauseItem;
|
||||
@property (assign) IBOutlet NSToolbarItem *disk1Item;
|
||||
@property (assign) IBOutlet NSToolbarItem *disk2Item;
|
||||
@property (nonatomic, retain) EmulatorFullscreenWindow *fullscreenWindow;
|
||||
@property (nonatomic, retain) NSWindow *standardWindow;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation EmulatorWindowController
|
||||
|
||||
@synthesize paused = _paused;
|
||||
@synthesize fullscreenWindow = _fullscreenWindow;
|
||||
@synthesize standardWindow = _standardWindow;
|
||||
|
||||
- (id)initWithWindow:(NSWindow *)window
|
||||
{
|
||||
self = [super initWithWindow:window];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Initialize to nil since it indicates app is not fullscreen
|
||||
self.fullscreenWindow = nil;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowWillOpen:) name:NSWindowDidBecomeKeyNotification object:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
#warning TODO FIXME ... probably should exit emulator if this gets invoked ...
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)reboot:(id)sender
|
||||
{
|
||||
[[self disksWindow] close];
|
||||
cpu65_reboot();
|
||||
}
|
||||
|
||||
- (IBAction)showDisksWindow:(id)sender
|
||||
{
|
||||
if (self.fullscreenWindow)
|
||||
{
|
||||
[self toggleFullScreen:nil];
|
||||
[self.standardWindow setFrame:[[NSScreen mainScreen] frame] display:YES];
|
||||
}
|
||||
[self.disksWindow makeKeyAndOrderFront:sender];
|
||||
}
|
||||
|
||||
- (IBAction)showPreferences:(id)sender
|
||||
{
|
||||
if (self.fullscreenWindow)
|
||||
{
|
||||
[self toggleFullScreen:nil];
|
||||
[self.standardWindow setFrame:[[NSScreen mainScreen] frame] display:YES];
|
||||
}
|
||||
[self.prefsWindow makeKeyAndOrderFront:sender];
|
||||
}
|
||||
|
||||
- (IBAction)toggleFullScreen:(id)sender
|
||||
{
|
||||
if (self.fullscreenWindow)
|
||||
{
|
||||
[NSCursor unhide];
|
||||
[self goWindow];
|
||||
}
|
||||
else
|
||||
{
|
||||
[NSCursor hide];
|
||||
[self goFullscreen];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)toggleCPUSpeed:(id)sender
|
||||
{
|
||||
cpu_pause();
|
||||
timing_toggleCPUSpeed();
|
||||
if (video_backend && video_backend->animation_showCPUSpeed)
|
||||
{
|
||||
video_backend->animation_showCPUSpeed();
|
||||
}
|
||||
cpu_resume();
|
||||
}
|
||||
|
||||
- (IBAction)togglePause:(id)sender
|
||||
{
|
||||
NSAssert(pthread_main_np(), @"Pause emulation called from non-main thread");
|
||||
self.paused = !_paused;
|
||||
}
|
||||
|
||||
- (void)setPaused:(BOOL)paused
|
||||
{
|
||||
if (_paused == paused)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = paused;
|
||||
if (paused)
|
||||
{
|
||||
[[self pauseMenuItem] setTitle:@"Resume Emulation"];
|
||||
[[self pauseItem] setImage:[NSImage imageNamed:@"Run"]];
|
||||
[[self pauseItem] setLabel:@"Paused"];
|
||||
cpu_pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self pauseMenuItem] setTitle:@"Pause Emulation"];
|
||||
[[self pauseItem] setImage:[NSImage imageNamed:@"Stop"]];
|
||||
[[self pauseItem] setLabel:@"Running"];
|
||||
cpu_resume();
|
||||
}
|
||||
if (video_backend && video_backend->animation_showPaused)
|
||||
{
|
||||
video_backend->animation_showPaused();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillOpen:(NSNotification *)notification
|
||||
{
|
||||
if ((self.prefsWindow == notification.object) || (self.disksWindow == notification.object))
|
||||
{
|
||||
self.paused = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
if (self.prefsWindow == notification.object)
|
||||
{
|
||||
self.paused = [self.disksWindow isVisible];
|
||||
}
|
||||
if (self.disksWindow == notification.object)
|
||||
{
|
||||
self.paused = [self.prefsWindow isVisible];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)goFullscreen
|
||||
{
|
||||
// If app is already fullscreen...
|
||||
if (self.fullscreenWindow)
|
||||
{
|
||||
//...don't do anything
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate a new fullscreen window
|
||||
self.fullscreenWindow = [[[EmulatorFullscreenWindow alloc] init] autorelease];
|
||||
|
||||
// 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 in the fullscreen window
|
||||
[self.fullscreenWindow setContentView:self.view];
|
||||
|
||||
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];
|
||||
|
||||
// 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];
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
// 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 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];
|
||||
|
||||
// Release/nilify fullscreenWindow
|
||||
self.fullscreenWindow = nil;
|
||||
}
|
||||
|
||||
- (void)flagsChanged:(NSEvent*)event
|
||||
{
|
||||
static BOOL modified_caps_lock = NO;
|
||||
switch ([event keyCode])
|
||||
{
|
||||
case CAPS_LOCK:
|
||||
{
|
||||
if ([event modifierFlags] & NSAlphaShiftKeyMask)
|
||||
{
|
||||
modified_caps_lock = YES;
|
||||
caps_lock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps_lock = false;
|
||||
}
|
||||
}
|
||||
|
||||
case SHIFT_LT:
|
||||
c_keys_handle_input(SCODE_L_SHIFT, ([event modifierFlags] & NSShiftKeyMask), 0);
|
||||
break;
|
||||
|
||||
case SHIFT_RT:
|
||||
c_keys_handle_input(SCODE_R_SHIFT, ([event modifierFlags] & NSShiftKeyMask), 0);
|
||||
break;
|
||||
|
||||
case CTRL_LT:
|
||||
c_keys_handle_input(SCODE_L_CTRL, ([event modifierFlags] & NSControlKeyMask), 0);
|
||||
break;
|
||||
|
||||
case CTRL_RT:
|
||||
c_keys_handle_input(SCODE_R_CTRL, ([event modifierFlags] & NSControlKeyMask), 0);
|
||||
break;
|
||||
|
||||
case ALT_LT:
|
||||
c_keys_handle_input(SCODE_L_ALT, ([event modifierFlags] & NSAlternateKeyMask), 0);
|
||||
break;
|
||||
|
||||
case ALT_RT:
|
||||
c_keys_handle_input(SCODE_R_ALT, ([event modifierFlags] & NSAlternateKeyMask), 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleKeyEvent:(NSEvent *)event pressed:(BOOL)pressed
|
||||
{
|
||||
unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
|
||||
int scode = (int)c;
|
||||
|
||||
int cooked = 0;
|
||||
switch (scode)
|
||||
{
|
||||
case 0x1b:
|
||||
scode = SCODE_ESC;
|
||||
break;
|
||||
|
||||
case NSUpArrowFunctionKey:
|
||||
scode = SCODE_U;
|
||||
break;
|
||||
case NSDownArrowFunctionKey:
|
||||
scode = SCODE_D;
|
||||
break;
|
||||
case NSLeftArrowFunctionKey:
|
||||
scode = SCODE_L;
|
||||
break;
|
||||
case NSRightArrowFunctionKey:
|
||||
scode = SCODE_R;
|
||||
break;
|
||||
|
||||
case NSF1FunctionKey:
|
||||
scode = SCODE_F1;
|
||||
break;
|
||||
case NSF2FunctionKey:
|
||||
scode = SCODE_F2;
|
||||
break;
|
||||
case NSF3FunctionKey:
|
||||
scode = SCODE_F3;
|
||||
break;
|
||||
case NSF4FunctionKey:
|
||||
scode = SCODE_F4;
|
||||
break;
|
||||
case NSF5FunctionKey:
|
||||
scode = SCODE_F5;
|
||||
break;
|
||||
case NSF6FunctionKey:
|
||||
scode = SCODE_F6;
|
||||
break;
|
||||
case NSF7FunctionKey:
|
||||
scode = SCODE_F7;
|
||||
break;
|
||||
case NSF8FunctionKey:
|
||||
scode = SCODE_F8;
|
||||
break;
|
||||
case NSF9FunctionKey:
|
||||
scode = SCODE_F9;
|
||||
break;
|
||||
case NSF10FunctionKey:
|
||||
scode = SCODE_F10;
|
||||
break;
|
||||
case NSF11FunctionKey:
|
||||
scode = SCODE_F11;
|
||||
break;
|
||||
case NSF12FunctionKey:
|
||||
scode = SCODE_F12;
|
||||
break;
|
||||
|
||||
case NSInsertFunctionKey:
|
||||
scode = SCODE_INS;
|
||||
break;
|
||||
case NSDeleteFunctionKey:
|
||||
scode = SCODE_DEL;
|
||||
break;
|
||||
case NSHomeFunctionKey:
|
||||
scode = SCODE_HOME;
|
||||
break;
|
||||
case NSEndFunctionKey:
|
||||
scode = SCODE_END;
|
||||
break;
|
||||
|
||||
case NSPageUpFunctionKey:
|
||||
scode = SCODE_PGUP;
|
||||
break;
|
||||
case NSPageDownFunctionKey:
|
||||
scode = SCODE_PGDN;
|
||||
break;
|
||||
|
||||
case NSPrintScreenFunctionKey:
|
||||
scode = SCODE_PRNT;
|
||||
break;
|
||||
case NSPauseFunctionKey:
|
||||
scode = SCODE_PAUSE;
|
||||
break;
|
||||
case NSBreakFunctionKey:
|
||||
scode = SCODE_BRK;
|
||||
break;
|
||||
|
||||
default:
|
||||
if ([event modifierFlags] & NSControlKeyMask)
|
||||
{
|
||||
scode = c_keys_ascii_to_scancode(scode);
|
||||
cooked = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cooked = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
c_keys_handle_input(scode, pressed, cooked);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event
|
||||
{
|
||||
[self _handleKeyEvent:event pressed:NO];
|
||||
|
||||
// Allow other character to be handled (or not and beep)
|
||||
//[super keyDown:event];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)event
|
||||
{
|
||||
[self _handleKeyEvent:event pressed:YES];
|
||||
|
||||
// Allow other character to be handled (or not and beep)
|
||||
//[super keyDown:event];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9060" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
|
@ -63,7 +62,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="640" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="U1j-AP-Luf" id="rJ2-oE-iBp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Emulator settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mbz-lM-DIP">
|
||||
|
@ -85,7 +84,7 @@
|
|||
<rect key="frame" x="0.0" y="44" width="640" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="oOH-E6-lMK" id="Fmr-bG-eyO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Load disk image" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gOe-OR-ycd">
|
||||
|
@ -107,7 +106,7 @@
|
|||
<rect key="frame" x="0.0" y="88" width="640" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ITu-uc-66C" id="gFu-ft-Q65">
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Save & restore" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fJA-S4-XoG">
|
||||
|
@ -129,7 +128,7 @@
|
|||
<rect key="frame" x="0.0" y="132" width="640" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3WS-Nv-AcH" id="4iZ-VH-juB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="640" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Reboot or quit emulator" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0WI-e2-xoP">
|
||||
|
|
|
@ -23,7 +23,10 @@
|
|||
@property (assign) IBOutlet NSWindow *disksWindow;
|
||||
@property (assign) IBOutlet NSWindow *prefsWindow;
|
||||
@property (assign) IBOutlet NSMenuItem *pauseMenuItem;
|
||||
|
||||
@property (assign) IBOutlet NSToolbar *toolBar;
|
||||
@property (assign) IBOutlet NSToolbarItem *pauseItem;
|
||||
@property (assign) IBOutlet NSToolbarItem *disk1Item;
|
||||
@property (assign) IBOutlet NSToolbarItem *disk2Item;
|
||||
@property (nonatomic, retain) EmulatorFullscreenWindow *fullscreenWindow;
|
||||
@property (nonatomic, retain) NSWindow *standardWindow;
|
||||
|
||||
|
@ -126,13 +129,21 @@
|
|||
if (paused)
|
||||
{
|
||||
[[self pauseMenuItem] setTitle:@"Resume Emulation"];
|
||||
[[self pauseItem] setImage:[NSImage imageNamed:@"Run"]];
|
||||
[[self pauseItem] setLabel:@"Paused"];
|
||||
cpu_pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self pauseMenuItem] setTitle:@"Pause Emulation"];
|
||||
[[self pauseItem] setImage:[NSImage imageNamed:@"Stop"]];
|
||||
[[self pauseItem] setLabel:@"Running"];
|
||||
cpu_resume();
|
||||
}
|
||||
if (video_backend && video_backend->animation_showPaused)
|
||||
{
|
||||
video_backend->animation_showPaused();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowWillOpen:(NSNotification *)notification
|
||||
|
|
Loading…
Reference in New Issue
Block a user