/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ /* This file takes care of command line argument parsing, and stdio redirection in the MacOS environment. (stdio/stderr is *not* directed for Mach-O builds) */ #if defined(__APPLE__) && defined(__MACH__) #include #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335) #include #else #include #include #include #include #include #endif /* Include the SDL main definition header */ #include "SDL.h" #include "SDL_main.h" #ifdef main #undef main #endif #if !(defined(__APPLE__) && defined(__MACH__)) /* The standard output files */ #define STDOUT_FILE "stdout.txt" #define STDERR_FILE "stderr.txt" #endif #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); } #if !(defined(__APPLE__) && defined(__MACH__)) /* 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 (SDL_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 && !SDL_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); } } } #endif //!(defined(__APPLE__) && defined(__MACH__)) 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; SDL_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; SDL_memcpy (prefs_name + 1, app_name + 1, app_name[0]); SDL_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 */ SDL_memcpy (prefs_fsp->name, prefs_name, prefs_name[0] + 1); prefs_fsp->parID = directory_id; prefs_fsp->vRefNum = volume_ref_number; FSpCreateResFile (prefs_fsp, 0x3f3f3f3f, 'pref', 0); // '????' parsed as trigraph 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 */ SDL_memcpy (prefs->command_line, *prefs_handle, (*prefs_handle)[0] + 1); /* Get video driver name */ offset += (*prefs_handle)[0] + 1; SDL_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; SDL_memcpy (*prefs_handle, prefs->command_line, prefs->command_line[0] + 1); /* Video driver name */ offset += prefs->command_line[0] + 1; SDL_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[]) { #if !(defined(__APPLE__) && defined(__MACH__)) #pragma unused(argc, argv) #endif #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 }; #if !(defined(__APPLE__) && defined(__MACH__)) int nargs; char **args; char *commandLine; StrFileName appNameText; #endif 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); if (errorDialog == NULL) return -1; DrawDialog (errorDialog); GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle, &dummyRect); SetDialogItemText (dummyHandle, "\pError Initializing SDL"); #if TARGET_API_MAC_CARBON SetPort (GetDialogPort (errorDialog)); #else SetPort (errorDialog); #endif 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 (SDL_memcmp (prefs.video_driver_name + 1, "DSp", 3) == 0) videodriver = 1; else if (SDL_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; #if TARGET_API_MAC_CARBON ControlRef control; #endif /* Assume that they will change settings, rather than do exhaustive check */ settingsChanged = 1; /* Create dialog and display it */ commandDialog = GetNewDialog (1000, nil, (WindowPtr) - 1); #if TARGET_API_MAC_CARBON SetPort (GetDialogPort (commandDialog)); #else SetPort (commandDialog); #endif /* Setup controls */ #if TARGET_API_MAC_CARBON GetDialogItemAsControl (commandDialog, kCL_File, &control); SetControlValue (control, prefs.output_to_file); #else GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */ SetControlValue ((ControlHandle) dummyHandle, prefs.output_to_file); #endif GetDialogItem (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); SetDialogItemText (dummyHandle, prefs.command_line); #if TARGET_API_MAC_CARBON GetDialogItemAsControl (commandDialog, kCL_Video, &control); SetControlValue (control, videodriver); #else GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect); SetControlValue ((ControlRef) dummyHandle, videodriver); #endif 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) { #if TARGET_API_MAC_CARBON GetDialogItemAsControl (commandDialog, kCL_File, &control); SetControlValue (control, !GetControlValue (control)); #else GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */ SetControlValue ((ControlHandle) dummyHandle, !GetControlValue ((ControlHandle) dummyHandle)); #endif } } 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); #if TARGET_API_MAC_CARBON GetDialogItemAsControl (commandDialog, kCL_File, &control); prefs.output_to_file = GetControlValue (control); #else GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */ prefs.output_to_file = GetControlValue ((ControlHandle) dummyHandle); #endif #if TARGET_API_MAC_CARBON GetDialogItemAsControl (commandDialog, kCL_Video, &control); videodriver = GetControlValue (control); #else GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect); videodriver = GetControlValue ((ControlRef) dummyHandle); #endif DisposeDialog (commandDialog); if (itemHit == kCL_Cancel) { exit (0); } } /* Set pseudo-environment variables for video driver, update prefs */ switch (videodriver) { case VIDEO_ID_DRAWSPROCKET: SDL_putenv ("SDL_VIDEODRIVER=DSp"); SDL_memcpy (prefs.video_driver_name, "\pDSp", 4); break; case VIDEO_ID_TOOLBOX: SDL_putenv ("SDL_VIDEODRIVER=toolbox"); SDL_memcpy (prefs.video_driver_name, "\ptoolbox", 8); break; } #if !(defined(__APPLE__) && defined(__MACH__)) /* 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); } #endif 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"); } #if !(defined(__APPLE__) && defined(__MACH__)) appNameText[0] = 0; 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 */ SDL_memcpy (commandLine, appNameText + 1, appNameText[0]); commandLine[appNameText[0]] = ' '; SDL_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 (); #else // defined(__APPLE__) && defined(__MACH__) SDL_main (argc, argv); #endif /* Exit cleanly, calling atexit() functions */ exit (0); /* Never reached, but keeps the compiler quiet */ return (0); } /* vi: set ts=4 sw=4 expandtab: */