Mercurial > sdl-ios-xcode
diff src/main/macos/SDL_main.c @ 0:74212992fb08
Initial revision
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Thu, 26 Apr 2001 16:45:43 +0000 |
parents | |
children | e8157fcb3114 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/macos/SDL_main.c Thu Apr 26 16:45:43 2001 +0000 @@ -0,0 +1,562 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +/* This file takes care of command line argument parsing, and stdio redirection + in the MacOS environment. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#if TARGET_API_MAC_CARBON +#include <Carbon.h> +#else +#include <Dialogs.h> +#include <Fonts.h> +#include <Events.h> +#include <Resources.h> +#include <Folders.h> +#endif + +/* Include the SDL main definition header */ +#include "SDL.h" +#include "SDL_main.h" +#ifdef main +#undef main +#endif + +/* The standard output files */ +#define STDOUT_FILE "stdout.txt" +#define STDERR_FILE "stderr.txt" + +#if !defined(__MWERKS__) && !TARGET_API_MAC_CARBON + /* In MPW, the qd global has been removed from the libraries */ + QDGlobals qd; +#endif + +/* Structure for keeping prefs in 1 variable */ +typedef struct { + Str255 command_line; + Str255 video_driver_name; + Boolean output_to_file; +} PrefsRecord; + +/* See if the command key is held down at startup */ +static Boolean CommandKeyIsDown(void) +{ + KeyMap theKeyMap; + + GetKeys(theKeyMap); + + if (((unsigned char *) theKeyMap)[6] & 0x80) { + return(true); + } + return(false); +} + +/* Parse a command line buffer into arguments */ +static int ParseCommandLine(char *cmdline, char **argv) +{ + char *bufp; + int argc; + + argc = 0; + for ( bufp = cmdline; *bufp; ) { + /* Skip leading whitespace */ + while ( isspace(*bufp) ) { + ++bufp; + } + /* Skip over argument */ + if ( *bufp == '"' ) { + ++bufp; + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while ( *bufp && (*bufp != '"') ) { + ++bufp; + } + } else { + if ( *bufp ) { + if ( argv ) { + argv[argc] = bufp; + } + ++argc; + } + /* Skip over word */ + while ( *bufp && ! isspace(*bufp) ) { + ++bufp; + } + } + if ( *bufp ) { + if ( argv ) { + *bufp = '\0'; + } + ++bufp; + } + } + if ( argv ) { + argv[argc] = NULL; + } + return(argc); +} + +/* Remove the output files if there was no output written */ +static void cleanup_output(void) +{ + FILE *file; + int empty; + + /* Flush the output in case anything is queued */ + fclose(stdout); + fclose(stderr); + + /* See if the files have any output in them */ + file = fopen(STDOUT_FILE, "rb"); + if ( file ) { + empty = (fgetc(file) == EOF) ? 1 : 0; + fclose(file); + if ( empty ) { + remove(STDOUT_FILE); + } + } + file = fopen(STDERR_FILE, "rb"); + if ( file ) { + empty = (fgetc(file) == EOF) ? 1 : 0; + fclose(file); + if ( empty ) { + remove(STDERR_FILE); + } + } +} + +static int getCurrentAppName (StrFileName name) { + + ProcessSerialNumber process; + ProcessInfoRec process_info; + FSSpec process_fsp; + + process.highLongOfPSN = 0; + process.lowLongOfPSN = kCurrentProcess; + process_info.processInfoLength = sizeof (process_info); + process_info.processName = NULL; + process_info.processAppSpec = &process_fsp; + + if ( noErr != GetProcessInformation (&process, &process_info) ) + return 0; + + memcpy (name, process_fsp.name, process_fsp.name[0] + 1); + return 1; +} + +static int getPrefsFile (FSSpec *prefs_fsp, int create) { + + /* The prefs file name is the application name, possibly truncated, */ + /* plus " Preferences */ + + #define SUFFIX " Preferences" + #define MAX_NAME 19 /* 31 - strlen (SUFFIX) */ + + short volume_ref_number; + long directory_id; + StrFileName prefs_name; + StrFileName app_name; + + /* Get Preferences folder - works with Multiple Users */ + if ( noErr != FindFolder ( kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, + &volume_ref_number, &directory_id) ) + exit (-1); + + if ( ! getCurrentAppName (app_name) ) + exit (-1); + + /* Truncate if name is too long */ + if (app_name[0] > MAX_NAME ) + app_name[0] = MAX_NAME; + + memcpy (prefs_name + 1, app_name + 1, app_name[0]); + memcpy (prefs_name + app_name[0] + 1, SUFFIX, strlen (SUFFIX)); + prefs_name[0] = app_name[0] + strlen (SUFFIX); + + /* Make the file spec for prefs file */ + if ( noErr != FSMakeFSSpec (volume_ref_number, directory_id, prefs_name, prefs_fsp) ) + if ( !create ) + return 0; + else { + /* Create the prefs file */ + memcpy (prefs_fsp->name, prefs_name, prefs_name[0] + 1); + prefs_fsp->parID = directory_id; + prefs_fsp->vRefNum = volume_ref_number; + + FSpCreateResFile (prefs_fsp, '????', 'pref', 0); + + if ( noErr != ResError () ) + return 0; + } + + return 1; +} + +static int readPrefsResource (PrefsRecord *prefs) { + + Handle prefs_handle; + + prefs_handle = Get1Resource( 'CLne', 128 ); + + if (prefs_handle != NULL) { + int offset = 0; + int j = 0; + + HLock(prefs_handle); + + /* Get command line string */ + memcpy (prefs->command_line, *prefs_handle, (*prefs_handle)[0]+1); + + /* Get video driver name */ + offset += (*prefs_handle)[0] + 1; + memcpy (prefs->video_driver_name, *prefs_handle + offset, (*prefs_handle)[offset] + 1); + + /* Get save-to-file option (1 or 0) */ + offset += (*prefs_handle)[offset] + 1; + prefs->output_to_file = (*prefs_handle)[offset]; + + ReleaseResource( prefs_handle ); + + return ResError() == noErr; + } + + return 0; +} + +static int writePrefsResource (PrefsRecord *prefs, short resource_file) { + + Handle prefs_handle; + + UseResFile (resource_file); + + prefs_handle = Get1Resource ( 'CLne', 128 ); + if (prefs_handle != NULL) + RemoveResource (prefs_handle); + + prefs_handle = NewHandle ( prefs->command_line[0] + prefs->video_driver_name[0] + 4 ); + if (prefs_handle != NULL) { + + int offset; + + HLock (prefs_handle); + + /* Command line text */ + offset = 0; + memcpy (*prefs_handle, prefs->command_line, prefs->command_line[0] + 1); + + /* Video driver name */ + offset += prefs->command_line[0] + 1; + memcpy (*prefs_handle + offset, prefs->video_driver_name, prefs->video_driver_name[0] + 1); + + /* Output-to-file option */ + offset += prefs->video_driver_name[0] + 1; + *( *((char**)prefs_handle) + offset) = (char)prefs->output_to_file; + *( *((char**)prefs_handle) + offset + 1) = 0; + + AddResource (prefs_handle, 'CLne', 128, "\pCommand Line"); + WriteResource (prefs_handle); + UpdateResFile (resource_file); + DisposeHandle (prefs_handle); + + return ResError() == noErr; + } + + return 0; +} + +static int readPreferences (PrefsRecord *prefs) { + + int no_error = 1; + FSSpec prefs_fsp; + + /* Check for prefs file first */ + if ( getPrefsFile (&prefs_fsp, 0) ) { + + short prefs_resource; + + prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdPerm); + if ( prefs_resource == -1 ) /* this shouldn't happen, but... */ + return 0; + + UseResFile (prefs_resource); + no_error = readPrefsResource (prefs); + CloseResFile (prefs_resource); + } + + /* Fall back to application's resource fork (reading only, so this is safe) */ + else { + + no_error = readPrefsResource (prefs); + } + + return no_error; +} + +static int writePreferences (PrefsRecord *prefs) { + + int no_error = 1; + FSSpec prefs_fsp; + + /* Get prefs file, create if it doesn't exist */ + if ( getPrefsFile (&prefs_fsp, 1) ) { + + short prefs_resource; + + prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdWrPerm); + if (prefs_resource == -1) + return 0; + no_error = writePrefsResource (prefs, prefs_resource); + CloseResFile (prefs_resource); + } + + return no_error; +} + +/* This is where execution begins */ +int main(int argc, char *argv[]) +{ + +#pragma unused(argc, argv) + +#define DEFAULT_ARGS "\p" /* pascal string for default args */ +#define DEFAULT_VIDEO_DRIVER "\ptoolbox" /* pascal string for default video driver name */ +#define DEFAULT_OUTPUT_TO_FILE 1 /* 1 == output to file, 0 == no output */ + +#define VIDEO_ID_DRAWSPROCKET 1 /* these correspond to popup menu choices */ +#define VIDEO_ID_TOOLBOX 2 + + PrefsRecord prefs = { DEFAULT_ARGS, DEFAULT_VIDEO_DRIVER, DEFAULT_OUTPUT_TO_FILE }; + + int nargs; + char **args; + char *commandLine; + + StrFileName appNameText; + int videodriver = VIDEO_ID_TOOLBOX; + int settingsChanged = 0; + + long i; + + /* Kyle's SDL command-line dialog code ... */ +#if !TARGET_API_MAC_CARBON + InitGraf (&qd.thePort); + InitFonts (); + InitWindows (); + InitMenus (); + InitDialogs (nil); +#endif + InitCursor (); + FlushEvents(everyEvent,0); +#if !TARGET_API_MAC_CARBON + MaxApplZone (); +#endif + MoreMasters (); + MoreMasters (); +#if 0 + /* Intialize SDL, and put up a dialog if we fail */ + if ( SDL_Init (0) < 0 ) { + +#define kErr_OK 1 +#define kErr_Text 2 + + DialogPtr errorDialog; + short dummyType; + Rect dummyRect; + Handle dummyHandle; + short itemHit; + + errorDialog = GetNewDialog (1001, nil, (WindowPtr)-1); + DrawDialog (errorDialog); + + GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle, &dummyRect); + SetDialogItemText (dummyHandle, "\pError Initializing SDL"); + + SetPort (errorDialog); + do { + ModalDialog (nil, &itemHit); + } while (itemHit != kErr_OK); + + DisposeDialog (errorDialog); + exit (-1); + } + atexit(cleanup_output); + atexit(SDL_Quit); +#endif + +/* Set up SDL's QuickDraw environment */ +#if !TARGET_API_MAC_CARBON + SDL_InitQuickDraw(&qd); +#endif + + if ( readPreferences (&prefs) ) { + + if (memcmp (prefs.video_driver_name+1, "DSp", 3) == 0) + videodriver = 1; + else if (memcmp (prefs.video_driver_name+1, "toolbox", 7) == 0) + videodriver = 2; + } + + if ( CommandKeyIsDown() ) { + +#define kCL_OK 1 +#define kCL_Cancel 2 +#define kCL_Text 3 +#define kCL_File 4 +#define kCL_Video 6 + + DialogPtr commandDialog; + short dummyType; + Rect dummyRect; + Handle dummyHandle; + short itemHit; + + /* Assume that they will change settings, rather than do exhaustive check */ + settingsChanged = 1; + + /* Create dialog and display it */ + commandDialog = GetNewDialog (1000, nil, (DialogPtr)-1); + SetPort (commandDialog); + + /* Setup controls */ + GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */ + SetControlValue ((ControlHandle)dummyHandle, prefs.output_to_file ); + + GetDialogItem (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); + SetDialogItemText (dummyHandle, prefs.command_line); + + GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect); + SetControlValue ((ControlRef)dummyHandle, videodriver); + + SetDialogDefaultItem (commandDialog, kCL_OK); + SetDialogCancelItem (commandDialog, kCL_Cancel); + + do { + + ModalDialog(nil, &itemHit); /* wait for user response */ + + /* Toggle command-line output checkbox */ + if ( itemHit == kCL_File ) { + GetDialogItem(commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */ + SetControlValue((ControlHandle)dummyHandle, !GetControlValue((ControlHandle)dummyHandle) ); + } + + } while (itemHit != kCL_OK && itemHit != kCL_Cancel); + + /* Get control values, even if they did not change */ + GetDialogItem (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); /* MJS */ + GetDialogItemText (dummyHandle, prefs.command_line); + + GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */ + prefs.output_to_file = GetControlValue ((ControlHandle)dummyHandle); + + GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect); + videodriver = GetControlValue ((ControlRef)dummyHandle); + + DisposeDialog (commandDialog); + + if (itemHit == kCL_Cancel ) { + exit (0); + } + } + + /* Set pseudo-environment variables for video driver, update prefs */ + switch ( videodriver ) { + case VIDEO_ID_DRAWSPROCKET: + putenv ("SDL_VIDEODRIVER=DSp"); + memcpy (prefs.video_driver_name, "\pDSp", 4); + break; + case VIDEO_ID_TOOLBOX: + putenv ("SDL_VIDEODRIVER=toolbox"); + memcpy (prefs.video_driver_name, "\ptoolbox", 8); + break; + } + + /* Redirect standard I/O to files */ + if ( prefs.output_to_file ) { + freopen (STDOUT_FILE, "w", stdout); + freopen (STDERR_FILE, "w", stderr); + } else { + fclose (stdout); + fclose (stderr); + } + + if (settingsChanged) { + /* Save the prefs, even if they might not have changed (but probably did) */ + if ( ! writePreferences (&prefs) ) + fprintf (stderr, "WARNING: Could not save preferences!\n"); + } + + getCurrentAppName (appNameText); /* check for error here ? */ + + commandLine = (char*) malloc (appNameText[0] + prefs.command_line[0] + 2); + if ( commandLine == NULL ) { + exit(-1); + } + + /* Rather than rewrite ParseCommandLine method, let's replace */ + /* any spaces in application name with underscores, */ + /* so that the app name is only 1 argument */ + for (i = 1; i < 1+appNameText[0]; i++) + if ( appNameText[i] == ' ' ) appNameText[i] = '_'; + + /* Copy app name & full command text to command-line C-string */ + memcpy (commandLine, appNameText + 1, appNameText[0]); + commandLine[appNameText[0]] = ' '; + memcpy (commandLine + appNameText[0] + 1, prefs.command_line + 1, prefs.command_line[0]); + commandLine[ appNameText[0] + 1 + prefs.command_line[0] ] = '\0'; + + /* Parse C-string into argv and argc */ + nargs = ParseCommandLine (commandLine, NULL); + args = (char **)malloc((nargs+1)*(sizeof *args)); + if ( args == NULL ) { + exit(-1); + } + ParseCommandLine (commandLine, args); + + /* Run the main application code */ + SDL_main(nargs, args); + free (args); + free (commandLine); + + /* Remove useless stdout.txt and stderr.txt */ + cleanup_output (); + + /* Exit cleanly, calling atexit() functions */ + exit (0); + + /* Never reached, but keeps the compiler quiet */ + return (0); +}