HatariWii/src/gui-osx/SDLMain.m
2018-05-25 20:45:09 +02:00

500 lines
14 KiB
Objective-C

/*
SDLMain.m - main entry point for our Cocoa-ized SDL app
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
Modifications for Hatari by Miguel Saro and Jerome Vernet
Feel free to customize this file to suit your needs
*/
/* Use this flag to determine whether we use SDLMain.nib or not */
#define SDL_USE_NIB_FILE 1
/* Use this flag to determine whether we use CPS (docking) or not */
#define SDL_USE_CPS 1
#import "SDL.h"
#import "SDLMain.h"
#import <sys/param.h> // for MAXPATHLEN
#import <unistd.h>
// for Hatari
#import "dialog.h"
#import "floppy.h"
#import "reset.h"
#import "screenSnapShot.h"
#import "memorySnapShot.h"
#import "sound.h"
#import "screen.h"
#import "PrefsController.h"
#import "Shared.h"
#import "video.h"
#import "avi_record.h"
#import "debugui.h"
#import "clocks_timings.h"
#import "change.h"
#ifdef SDL_USE_CPS
// Portions of CPS.h
typedef struct CPSProcessSerNum
{
UInt32 lo;
UInt32 hi;
} CPSProcessSerNum;
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
#endif // SDL_USE_CPS
static int gArgc;
static char **gArgv;
static BOOL gFinderLaunch = NO ;
static BOOL gCalledAppMainline = NO ;
// The main class of the application, the application's delegate
//
@implementation HatariAppDelegate
char szPath[FILENAME_MAX] ; // for general use
// Set the working directory to the .app's parent directory
- (void) setupWorkingDirectory:(BOOL)shouldChdir
{
if (shouldChdir)
chdir([[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] cStringUsingEncoding:NSASCIIStringEncoding]) ;
}
/*
* Catch document open requests...this lets us notice files when the app
* was launched by double-clicking a document, or when a document was
* dragged/dropped on the app's icon. You need to have a
* CFBundleDocumentsType section in your Info.plist to get this message,
* apparently.
*
* Files are added to gArgv, so to the app, they'll look like command line
* arguments. Previously, apps launched from the finder had nothing but
* an argv[0].
*
* This message may be received multiple times to open several docs on launch.
*
* This message is ignored once the app's mainline has been called.
*/
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
const char *temparg;
size_t arglen;
char *arg;
char **newargv;
if (!gFinderLaunch) // MacOS is passing command line args.
return FALSE;
if (gCalledAppMainline) // app has started, ignore this document.
return FALSE;
temparg = [filename UTF8String] ;
arglen = SDL_strlen(temparg) + 1 ;
arg = (char *) SDL_malloc(arglen) ;
if (arg == NULL)
return FALSE;
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)) ;
if (newargv == NULL)
{
SDL_free(arg);
return FALSE;
}
gArgv = newargv ;
SDL_strlcpy(arg, temparg, arglen) ;
gArgv[gArgc++] = arg ;
gArgv[gArgc] = NULL ;
return TRUE;
}
// Called when the internal event loop has just started running
//
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
int status;
// Set the working directory to the .app's parent directory
[self setupWorkingDirectory:gFinderLaunch];
//setenv ("SDL_ENABLEAPPEVENTS", "1", 1) ;
// Hand off to main application code
gCalledAppMainline = TRUE;
status = SDL_main (gArgc, gArgv) ;
// We're done, thank you for playing
exit(status) ;
}
// Hatari Stuff
- (IBAction)prefsMenu:(id)sender
{
static int in_propdialog = 0;
if (in_propdialog)
return ;
++in_propdialog ;
Dialog_DoProperty() ;
--in_propdialog;
}
/*- (IBAction) openPreferences:(id)sender
{
[[PrefsController prefs] loadPrefs:sender];
} // */
- (IBAction)debugUI:(id)sender
{
DebugUI(REASON_USER);
}
- (IBAction)warmReset:(id)sender
{
if ([NSApp myAlerte:NSInformationalAlertStyle Txt:localize(@"Warm reset!") firstB:localize(@"OK") alternateB:localize(@"Cancel")
otherB:nil informativeTxt:localize(@"Really reset the emulator?")] == NSAlertDefaultReturn)
Reset_Warm();
}
- (IBAction)coldReset:(id)sender
{
if ([NSApp myAlerte:NSInformationalAlertStyle Txt:localize(@"Cold reset") firstB:localize(@"OK") alternateB:localize(@"Cancel")
otherB:nil informativeTxt:localize(@"Really reset the emulator?")] == NSAlertDefaultReturn)
Reset_Cold();
}
- (IBAction)insertDiskA:(id)sender
{
[self insertDisk:0] ;
}
- (IBAction)insertDiskB:(id)sender
{
[self insertDisk:1] ;
}
- (void)insertDisk:(int)disque
{
NSString *aDisk ;
aDisk = [NSApp hopenfile:NO defoDir:nil defoFile:@"" types:[NSArray arrayWithObjects:allF,nil]] ;
if ([aDisk length] == 0) return ; // user canceled
[aDisk getCString:szPath maxLength:FILENAME_MAX-1 encoding:NSASCIIStringEncoding] ;
Floppy_SetDiskFileName(disque, szPath, NULL) ;
Floppy_InsertDiskIntoDrive(disque) ;
}
/*-----------------------------------------------------------------------*/
/*
Controls the enabled state of the menu items
*/
- (BOOL)validateMenuItem:(NSMenuItem*)item
{
if (item == beginCaptureAnim)
{
return !Avi_AreWeRecording() ;
}
if (item == endCaptureAnim)
{
return Avi_AreWeRecording() ;
}
if (item == beginCaptureSound)
{
return !Sound_AreWeRecording() ;
}
if (item == endCaptureSound)
{
return Sound_AreWeRecording() ;
}
return YES;
}
- (NSString*)displayFileSelection:(const char*)pathInParams preferredFileName:(NSString*)preferredFileName allowedExtensions:(NSArray*)allowedExtensions
{
// BOOL test ;
NSString *directoryToOpen;
NSString *fileToPreselect;
NSString *preferredPath;
NSString *extensionText;
NSString *selectFile;
// Get the path from the user settings
preferredPath = [[NSString stringWithCString:pathInParams encoding:NSASCIIStringEncoding] stringByAbbreviatingWithTildeInPath];
if ((preferredPath != nil) && ([preferredPath length] > 0)) // Determine the directory and filename
{
directoryToOpen = [preferredPath stringByDeletingLastPathComponent]; // Existing path: we use it
fileToPreselect = [preferredPath lastPathComponent];
}
else
{
directoryToOpen = [@"~" stringByExpandingTildeInPath]; // No path: we use the user's directory
fileToPreselect = preferredFileName;
} ;
if(bInFullScreen)
Screen_ReturnFromFullScreen();
// SavePanel for choosing what file to write
extensionText = [NSString stringWithFormat:localize(@"Please specify a .%@ file"), [allowedExtensions componentsJoinedByString:localize(@" or a .")] ];
selectFile = [NSApp hsavefile:YES defoDir:directoryToOpen defoFile:fileToPreselect types:allowedExtensions titre:extensionText ] ;
if ([selectFile length] != 0 )
return selectFile ;
return nil;
}
- (IBAction)captureScreen:(id)sender
{
GuiOsx_Pause();
ScreenSnapShot_SaveScreen();
GuiOsx_Resume();
}
- (IBAction)captureAnimation:(id)sender
{
GuiOsx_Pause();
if(!Avi_AreWeRecording()) {
NSString* path = [self displayFileSelection:ConfigureParams.Video.AviRecordFile preferredFileName:@"hatari.avi"
allowedExtensions:[NSArray arrayWithObject:@"avi"]];
if(path) {
GuiOsx_ExportPathString(path, ConfigureParams.Video.AviRecordFile, sizeof(ConfigureParams.Video.AviRecordFile));
Avi_StartRecording ( ConfigureParams.Video.AviRecordFile , ConfigureParams.Screen.bCrop ,
ConfigureParams.Video.AviRecordFps == 0 ?
ClocksTimings_GetVBLPerSec ( ConfigureParams.System.nMachineType , nScreenRefreshRate ) :
(Uint32)ConfigureParams.Video.AviRecordFps << CLOCKS_TIMINGS_SHIFT_VBL ,
1 << CLOCKS_TIMINGS_SHIFT_VBL ,
ConfigureParams.Video.AviRecordVcodec );
}
} else {
Avi_StopRecording();
}
GuiOsx_Resume();
}
- (IBAction)endCaptureAnimation:(id)sender
{
GuiOsx_Pause();
Avi_StopRecording();
GuiOsx_Resume();
}
- (IBAction)captureSound:(id)sender
{
GuiOsx_Pause();
NSString* path = [self displayFileSelection:ConfigureParams.Sound.szYMCaptureFileName preferredFileName:@"hatari.wav"
allowedExtensions:[NSArray arrayWithObjects:@"ym", @"wav", nil]];
if(path) {
GuiOsx_ExportPathString(path, ConfigureParams.Sound.szYMCaptureFileName, sizeof(ConfigureParams.Sound.szYMCaptureFileName));
Sound_BeginRecording(ConfigureParams.Sound.szYMCaptureFileName);
}
GuiOsx_Resume();
}
- (IBAction)endCaptureSound:(id)sender
{
GuiOsx_Pause();
Sound_EndRecording();
GuiOsx_Resume();
}
- (IBAction)saveMemorySnap:(id)sender
{
GuiOsx_Pause();
NSString* path = [self displayFileSelection:ConfigureParams.Memory.szMemoryCaptureFileName preferredFileName:@"hatari.sav"
allowedExtensions:[NSArray arrayWithObject:@"sav"]];
if(path) {
GuiOsx_ExportPathString(path, ConfigureParams.Memory.szMemoryCaptureFileName, sizeof(ConfigureParams.Memory.szMemoryCaptureFileName));
MemorySnapShot_Capture(ConfigureParams.Memory.szMemoryCaptureFileName, TRUE);
}
GuiOsx_Resume();
}
- (IBAction)restoreMemorySnap:(id)sender
{
NSString *directoryToOpen;
NSString *fileToPreselect;
NSString *oldPath ;
NSString *newPath ;
GuiOsx_Pause();
// Get the path from the user settings
oldPath = [NSString stringWithCString:(ConfigureParams.Memory.szMemoryCaptureFileName) encoding:NSASCIIStringEncoding];
if ((oldPath != nil) && ([oldPath length] > 0)) // Determine directory and filename
{ directoryToOpen = [oldPath stringByDeletingLastPathComponent]; // existing path: we use it.
fileToPreselect = [oldPath lastPathComponent]; }
else
{ directoryToOpen = [@"~" stringByExpandingTildeInPath]; // Currently no path: we use user's directory
fileToPreselect = nil; } ;
newPath = [NSApp hopenfile:NO defoDir:directoryToOpen defoFile:fileToPreselect types:[NSArray arrayWithObject:@"sav"] ] ;
if ([newPath length] != 0) // Perform the memory snapshot load
MemorySnapShot_Restore([newPath cStringUsingEncoding:NSASCIIStringEncoding], TRUE);
GuiOsx_Resume();
}
- (IBAction)doFullScreen:(id)sender
{
// A call to Screen_EnterFullScreen() would be required, but this causes a crash when using
// SDL runtime 1.2.11, probably due to conflicts between Cocoa and SDL.
// Therefore we simulate the fullscreen key press instead
SDL_KeyboardEvent event;
memset(&event, 0, sizeof(event));
event.type = SDL_KEYDOWN;
event.state = SDL_PRESSED;
event.keysym.sym = SDLK_F11;
SDL_PushEvent((SDL_Event*)&event); // Send the F11 key press
event.type = SDL_KEYUP;
event.state = SDL_RELEASED;
SDL_PushEvent((SDL_Event*)&event); // Send the F11 key release
}
- (IBAction)help:(id)sender
{
NSString *the_help;
the_help = [[NSBundle mainBundle] pathForResource:@"manual" ofType:@"html" inDirectory:@"HatariHelp"];
if (![[NSWorkspace sharedWorkspace] openFile:the_help withApplication:@"HelpViewer"])
if (![[NSWorkspace sharedWorkspace] openFile:the_help withApplication:@"Help Viewer"])
[[NSWorkspace sharedWorkspace] openFile:the_help];
}
- (IBAction)compat:(id)sender
{
NSString *C_aide ;
C_aide = [[NSBundle mainBundle] pathForResource:@"compatibility" ofType:@"html" inDirectory:@"HatariHelp"] ;
if (![[NSWorkspace sharedWorkspace] openFile:C_aide withApplication:@"HelpViewer"])
if (![[NSWorkspace sharedWorkspace] openFile:C_aide withApplication:@"Help Viewer"])
[[NSWorkspace sharedWorkspace] openFile:C_aide] ;
}
- (IBAction)openConfig:(id)sender
{
BOOL applyChanges ;
NSString *ConfigFile, *newCfg ;
CNF_PARAMS CurrentParams;
applyChanges = true ;
ConfigFile = [NSString stringWithCString:(sConfigFileName) encoding:NSASCIIStringEncoding];
// Backup of configuration settings to CurrentParams (which we will only
// commit back to the configuration settings if choosing user confirm)
CurrentParams = ConfigureParams;
GuiOsx_Pause();
newCfg = [NSApp hopenfile:NO defoDir:nil defoFile:ConfigFile types:[NSArray arrayWithObject:@"cfg"] ] ;
if ([newCfg length] != 0)
{
[newCfg getCString:szPath maxLength:FILENAME_MAX-1 encoding:NSASCIIStringEncoding] ; // get Cstring szPath
Configuration_Load(szPath) ; // Load the config into ConfigureParams
strcpy(sConfigFileName,szPath) ;
// Refresh all the controls to match ConfigureParams
if (Change_DoNeedReset(&CurrentParams, &ConfigureParams))
applyChanges = [NSApp myAlerte:NSInformationalAlertStyle Txt:localize(@"Reset the emulator") firstB:localize(@"Don't reset")
alternateB:localize(@"Reset") otherB:nil informativeTxt:@"" ] == NSAlertAlternateReturn ;
if (applyChanges)
Change_CopyChangedParamsToConfiguration(&CurrentParams, &ConfigureParams, true); // Ok with Reset
else
ConfigureParams = CurrentParams; //Restore previous Params.
} ;
GuiOsx_Resume();
}
- (IBAction)saveConfig:(id)sender {
}
@end
static int IsRootCwd()
{
char buf[MAXPATHLEN];
char *cwd = getcwd(buf, sizeof (buf));
return (cwd && (strcmp(cwd, "/") == 0));
}
static int IsTenPointNineOrLater()
{
/* Gestalt() is deprecated in 10.8 ... TODO: replace with better test? */
SInt32 major, minor;
Gestalt(gestaltSystemVersionMajor, &major);
Gestalt(gestaltSystemVersionMinor, &minor);
return ( ((major << 16) | minor) >= ((10 << 16) | 9) );
}
static int IsFinderLaunch(const int argc, char **argv)
{
/* -psn_XXX is passed if we are launched from Finder in 10.8 and earlier */
if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) {
return 1;
}
if (IsTenPointNineOrLater() && argc == 1 && IsRootCwd()) {
/* we might still be launched from the Finder; on 10.9+, you might not
get the -psn command line anymore. Check version, if there's no
command line, and if our current working directory is "/". */
return 1;
}
return 0; /* not a Finder launch. */
}
#ifdef main
# undef main
#endif
// Main entry point to executable - should *not* be SDL_main!
int main (int argc, char **argv)
{
// Copy the arguments into a global variable
if (IsFinderLaunch(argc, argv)) {
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
gArgv[0] = argv[0];
gArgv[1] = NULL;
gArgc = 1;
gFinderLaunch = YES;
} else {
int i;
gArgc = argc;
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
for (i = 0; i <= argc; i++)
gArgv[i] = argv[i];
gFinderLaunch = NO;
}
#if SDL_USE_NIB_FILE
NSApplicationMain (argc, (const char**)argv);
#else
CustomApplicationMain (argc, argv);
#endif
return 0;
}