Home | History | Annotate | Download | only in macos
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 
     23 /* This file takes care of command line argument parsing, and stdio redirection
     24    in the MacOS environment. (stdio/stderr is *not* directed for Mach-O builds)
     25  */
     26 
     27 #if defined(__APPLE__) && defined(__MACH__)
     28 #include <Carbon/Carbon.h>
     29 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
     30 #include <Carbon.h>
     31 #else
     32 #include <Dialogs.h>
     33 #include <Fonts.h>
     34 #include <Events.h>
     35 #include <Resources.h>
     36 #include <Folders.h>
     37 #endif
     38 
     39 /* Include the SDL main definition header */
     40 #include "SDL.h"
     41 #include "SDL_main.h"
     42 #ifdef main
     43 #undef main
     44 #endif
     45 
     46 #if !(defined(__APPLE__) && defined(__MACH__))
     47 /* The standard output files */
     48 #define STDOUT_FILE	"stdout.txt"
     49 #define STDERR_FILE	"stderr.txt"
     50 #endif
     51 
     52 #if !defined(__MWERKS__) && !TARGET_API_MAC_CARBON
     53 	/* In MPW, the qd global has been removed from the libraries */
     54 	QDGlobals qd;
     55 #endif
     56 
     57 /* Structure for keeping prefs in 1 variable */
     58 typedef struct {
     59     Str255  command_line;
     60     Str255  video_driver_name;
     61     Boolean output_to_file;
     62 }  PrefsRecord;
     63 
     64 /* See if the command key is held down at startup */
     65 static Boolean CommandKeyIsDown(void)
     66 {
     67 	KeyMap  theKeyMap;
     68 
     69 	GetKeys(theKeyMap);
     70 
     71 	if (((unsigned char *) theKeyMap)[6] & 0x80) {
     72 		return(true);
     73 	}
     74 	return(false);
     75 }
     76 
     77 #if !(defined(__APPLE__) && defined(__MACH__))
     78 
     79 /* Parse a command line buffer into arguments */
     80 static int ParseCommandLine(char *cmdline, char **argv)
     81 {
     82 	char *bufp;
     83 	int argc;
     84 
     85 	argc = 0;
     86 	for ( bufp = cmdline; *bufp; ) {
     87 		/* Skip leading whitespace */
     88 		while ( SDL_isspace(*bufp) ) {
     89 			++bufp;
     90 		}
     91 		/* Skip over argument */
     92 		if ( *bufp == '"' ) {
     93 			++bufp;
     94 			if ( *bufp ) {
     95 				if ( argv ) {
     96 					argv[argc] = bufp;
     97 				}
     98 				++argc;
     99 			}
    100 			/* Skip over word */
    101 			while ( *bufp && (*bufp != '"') ) {
    102 				++bufp;
    103 			}
    104 		} else {
    105 			if ( *bufp ) {
    106 				if ( argv ) {
    107 					argv[argc] = bufp;
    108 				}
    109 				++argc;
    110 			}
    111 			/* Skip over word */
    112 			while ( *bufp && ! SDL_isspace(*bufp) ) {
    113 				++bufp;
    114 			}
    115 		}
    116 		if ( *bufp ) {
    117 			if ( argv ) {
    118 				*bufp = '\0';
    119 			}
    120 			++bufp;
    121 		}
    122 	}
    123 	if ( argv ) {
    124 		argv[argc] = NULL;
    125 	}
    126 	return(argc);
    127 }
    128 
    129 /* Remove the output files if there was no output written */
    130 static void cleanup_output(void)
    131 {
    132 	FILE *file;
    133 	int empty;
    134 
    135 	/* Flush the output in case anything is queued */
    136 	fclose(stdout);
    137 	fclose(stderr);
    138 
    139 	/* See if the files have any output in them */
    140 	file = fopen(STDOUT_FILE, "rb");
    141 	if ( file ) {
    142 		empty = (fgetc(file) == EOF) ? 1 : 0;
    143 		fclose(file);
    144 		if ( empty ) {
    145 			remove(STDOUT_FILE);
    146 		}
    147 	}
    148 	file = fopen(STDERR_FILE, "rb");
    149 	if ( file ) {
    150 		empty = (fgetc(file) == EOF) ? 1 : 0;
    151 		fclose(file);
    152 		if ( empty ) {
    153 			remove(STDERR_FILE);
    154 		}
    155 	}
    156 }
    157 
    158 #endif //!(defined(__APPLE__) && defined(__MACH__))
    159 
    160 static int getCurrentAppName (StrFileName name) {
    161 
    162     ProcessSerialNumber process;
    163     ProcessInfoRec      process_info;
    164     FSSpec              process_fsp;
    165 
    166     process.highLongOfPSN = 0;
    167     process.lowLongOfPSN  = kCurrentProcess;
    168     process_info.processInfoLength = sizeof (process_info);
    169     process_info.processName    = NULL;
    170     process_info.processAppSpec = &process_fsp;
    171 
    172     if ( noErr != GetProcessInformation (&process, &process_info) )
    173        return 0;
    174 
    175     SDL_memcpy(name, process_fsp.name, process_fsp.name[0] + 1);
    176     return 1;
    177 }
    178 
    179 static int getPrefsFile (FSSpec *prefs_fsp, int create) {
    180 
    181     /* The prefs file name is the application name, possibly truncated, */
    182     /* plus " Preferences */
    183 
    184     #define  SUFFIX   " Preferences"
    185     #define  MAX_NAME 19             /* 31 - strlen (SUFFIX) */
    186 
    187     short  volume_ref_number;
    188     long   directory_id;
    189     StrFileName  prefs_name;
    190     StrFileName  app_name;
    191 
    192     /* Get Preferences folder - works with Multiple Users */
    193     if ( noErr != FindFolder ( kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
    194                                &volume_ref_number, &directory_id) )
    195         exit (-1);
    196 
    197     if ( ! getCurrentAppName (app_name) )
    198         exit (-1);
    199 
    200     /* Truncate if name is too long */
    201     if (app_name[0] > MAX_NAME )
    202         app_name[0] = MAX_NAME;
    203 
    204     SDL_memcpy(prefs_name + 1, app_name + 1, app_name[0]);
    205     SDL_memcpy(prefs_name + app_name[0] + 1, SUFFIX, strlen (SUFFIX));
    206     prefs_name[0] = app_name[0] + strlen (SUFFIX);
    207 
    208     /* Make the file spec for prefs file */
    209     if ( noErr != FSMakeFSSpec (volume_ref_number, directory_id, prefs_name, prefs_fsp) ) {
    210         if ( !create )
    211             return 0;
    212         else {
    213             /* Create the prefs file */
    214             SDL_memcpy(prefs_fsp->name, prefs_name, prefs_name[0] + 1);
    215             prefs_fsp->parID   = directory_id;
    216             prefs_fsp->vRefNum = volume_ref_number;
    217 
    218             FSpCreateResFile (prefs_fsp, 0x3f3f3f3f, 'pref', 0); // '????' parsed as trigraph
    219 
    220             if ( noErr != ResError () )
    221                 return 0;
    222         }
    223      }
    224     return 1;
    225 }
    226 
    227 static int readPrefsResource (PrefsRecord *prefs) {
    228 
    229     Handle prefs_handle;
    230 
    231     prefs_handle = Get1Resource( 'CLne', 128 );
    232 
    233 	if (prefs_handle != NULL) {
    234 		int offset = 0;
    235 //		int j      = 0;
    236 
    237 		HLock(prefs_handle);
    238 
    239 		/* Get command line string */
    240 		SDL_memcpy(prefs->command_line, *prefs_handle, (*prefs_handle)[0]+1);
    241 
    242 		/* Get video driver name */
    243 		offset += (*prefs_handle)[0] + 1;
    244 		SDL_memcpy(prefs->video_driver_name, *prefs_handle + offset, (*prefs_handle)[offset] + 1);
    245 
    246 		/* Get save-to-file option (1 or 0) */
    247 		offset += (*prefs_handle)[offset] + 1;
    248 		prefs->output_to_file = (*prefs_handle)[offset];
    249 
    250 		ReleaseResource( prefs_handle );
    251 
    252         return ResError() == noErr;
    253     }
    254 
    255     return 0;
    256 }
    257 
    258 static int writePrefsResource (PrefsRecord *prefs, short resource_file) {
    259 
    260     Handle prefs_handle;
    261 
    262     UseResFile (resource_file);
    263 
    264     prefs_handle = Get1Resource ( 'CLne', 128 );
    265     if (prefs_handle != NULL)
    266         RemoveResource (prefs_handle);
    267 
    268     prefs_handle = NewHandle ( prefs->command_line[0] + prefs->video_driver_name[0] + 4 );
    269     if (prefs_handle != NULL) {
    270 
    271         int offset;
    272 
    273         HLock (prefs_handle);
    274 
    275         /* Command line text */
    276         offset = 0;
    277         SDL_memcpy(*prefs_handle, prefs->command_line, prefs->command_line[0] + 1);
    278 
    279         /* Video driver name */
    280         offset += prefs->command_line[0] + 1;
    281         SDL_memcpy(*prefs_handle + offset, prefs->video_driver_name, prefs->video_driver_name[0] + 1);
    282 
    283         /* Output-to-file option */
    284         offset += prefs->video_driver_name[0] + 1;
    285         *( *((char**)prefs_handle) + offset)     = (char)prefs->output_to_file;
    286         *( *((char**)prefs_handle) + offset + 1) = 0;
    287 
    288         AddResource   (prefs_handle, 'CLne', 128, "\pCommand Line");
    289         WriteResource (prefs_handle);
    290         UpdateResFile (resource_file);
    291         DisposeHandle (prefs_handle);
    292 
    293         return ResError() == noErr;
    294     }
    295 
    296     return 0;
    297 }
    298 
    299 static int readPreferences (PrefsRecord *prefs) {
    300 
    301     int    no_error = 1;
    302     FSSpec prefs_fsp;
    303 
    304     /* Check for prefs file first */
    305     if ( getPrefsFile (&prefs_fsp, 0) ) {
    306 
    307         short  prefs_resource;
    308 
    309         prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdPerm);
    310         if ( prefs_resource == -1 ) /* this shouldn't happen, but... */
    311             return 0;
    312 
    313         UseResFile   (prefs_resource);
    314         no_error = readPrefsResource (prefs);
    315         CloseResFile (prefs_resource);
    316     }
    317 
    318     /* Fall back to application's resource fork (reading only, so this is safe) */
    319     else {
    320 
    321           no_error = readPrefsResource (prefs);
    322      }
    323 
    324     return no_error;
    325 }
    326 
    327 static int writePreferences (PrefsRecord *prefs) {
    328 
    329     int    no_error = 1;
    330     FSSpec prefs_fsp;
    331 
    332     /* Get prefs file, create if it doesn't exist */
    333     if ( getPrefsFile (&prefs_fsp, 1) ) {
    334 
    335         short  prefs_resource;
    336 
    337         prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdWrPerm);
    338         if (prefs_resource == -1)
    339             return 0;
    340         no_error = writePrefsResource (prefs, prefs_resource);
    341         CloseResFile (prefs_resource);
    342     }
    343 
    344     return no_error;
    345 }
    346 
    347 /* This is where execution begins */
    348 int main(int argc, char *argv[])
    349 {
    350 
    351 #if !(defined(__APPLE__) && defined(__MACH__))
    352 #pragma unused(argc, argv)
    353 #endif
    354 
    355 #define DEFAULT_ARGS "\p"                /* pascal string for default args */
    356 #define DEFAULT_VIDEO_DRIVER "\ptoolbox" /* pascal string for default video driver name */
    357 #define DEFAULT_OUTPUT_TO_FILE 1         /* 1 == output to file, 0 == no output */
    358 
    359 #define VIDEO_ID_DRAWSPROCKET 1          /* these correspond to popup menu choices */
    360 #define VIDEO_ID_TOOLBOX      2
    361 
    362     PrefsRecord prefs = { DEFAULT_ARGS, DEFAULT_VIDEO_DRIVER, DEFAULT_OUTPUT_TO_FILE };
    363 
    364 #if !(defined(__APPLE__) && defined(__MACH__))
    365 	int     nargs;
    366 	char   **args;
    367 	char   *commandLine;
    368 
    369 	StrFileName  appNameText;
    370 #endif
    371 	int     videodriver     = VIDEO_ID_TOOLBOX;
    372     int     settingsChanged = 0;
    373 
    374     long	i;
    375 
    376 	/* Kyle's SDL command-line dialog code ... */
    377 #if !TARGET_API_MAC_CARBON
    378 	InitGraf    (&qd.thePort);
    379 	InitFonts   ();
    380 	InitWindows ();
    381 	InitMenus   ();
    382 	InitDialogs (nil);
    383 #endif
    384 	InitCursor ();
    385 	FlushEvents(everyEvent,0);
    386 #if !TARGET_API_MAC_CARBON
    387 	MaxApplZone ();
    388 #endif
    389 	MoreMasters ();
    390 	MoreMasters ();
    391 #if 0
    392 	/* Intialize SDL, and put up a dialog if we fail */
    393 	if ( SDL_Init (0) < 0 ) {
    394 
    395 #define kErr_OK		1
    396 #define kErr_Text	2
    397 
    398         DialogPtr errorDialog;
    399         short	  dummyType;
    400     	Rect	  dummyRect;
    401 	    Handle    dummyHandle;
    402 	    short     itemHit;
    403 
    404 		errorDialog = GetNewDialog (1001, nil, (WindowPtr)-1);
    405 		if (errorDialog == NULL)
    406 		    return -1;
    407 		DrawDialog (errorDialog);
    408 
    409 		GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle, &dummyRect);
    410 		SetDialogItemText (dummyHandle, "\pError Initializing SDL");
    411 
    412 #if TARGET_API_MAC_CARBON
    413 		SetPort (GetDialogPort(errorDialog));
    414 #else
    415 		SetPort (errorDialog);
    416 #endif
    417 		do {
    418 			ModalDialog (nil, &itemHit);
    419 		} while (itemHit != kErr_OK);
    420 
    421 		DisposeDialog (errorDialog);
    422 		exit (-1);
    423 	}
    424 	atexit(cleanup_output);
    425 	atexit(SDL_Quit);
    426 #endif
    427 
    428 /* Set up SDL's QuickDraw environment  */
    429 #if !TARGET_API_MAC_CARBON
    430 	SDL_InitQuickDraw(&qd);
    431 #endif
    432 
    433 	 if ( readPreferences (&prefs) ) {
    434 
    435         if (SDL_memcmp(prefs.video_driver_name+1, "DSp", 3) == 0)
    436             videodriver = 1;
    437         else if (SDL_memcmp(prefs.video_driver_name+1, "toolbox", 7) == 0)
    438             videodriver = 2;
    439 	 }
    440 
    441 	if ( CommandKeyIsDown() ) {
    442 
    443 #define kCL_OK		1
    444 #define kCL_Cancel	2
    445 #define kCL_Text	3
    446 #define kCL_File	4
    447 #define kCL_Video   6
    448 
    449         DialogPtr commandDialog;
    450         short	  dummyType;
    451         Rect	  dummyRect;
    452         Handle    dummyHandle;
    453         short     itemHit;
    454    #if TARGET_API_MAC_CARBON
    455         ControlRef control;
    456    #endif
    457 
    458         /* Assume that they will change settings, rather than do exhaustive check */
    459         settingsChanged = 1;
    460 
    461         /* Create dialog and display it */
    462         commandDialog = GetNewDialog (1000, nil, (WindowPtr)-1);
    463     #if TARGET_API_MAC_CARBON
    464         SetPort ( GetDialogPort(commandDialog) );
    465     #else
    466         SetPort (commandDialog);
    467      #endif
    468 
    469         /* Setup controls */
    470     #if TARGET_API_MAC_CARBON
    471         GetDialogItemAsControl(commandDialog, kCL_File, &control);
    472         SetControlValue (control, prefs.output_to_file);
    473     #else
    474         GetDialogItem   (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
    475         SetControlValue ((ControlHandle)dummyHandle, prefs.output_to_file );
    476     #endif
    477 
    478         GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect);
    479         SetDialogItemText (dummyHandle, prefs.command_line);
    480 
    481     #if TARGET_API_MAC_CARBON
    482         GetDialogItemAsControl(commandDialog, kCL_Video, &control);
    483         SetControlValue (control, videodriver);
    484    #else
    485         GetDialogItem   (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
    486         SetControlValue ((ControlRef)dummyHandle, videodriver);
    487      #endif
    488 
    489         SetDialogDefaultItem (commandDialog, kCL_OK);
    490         SetDialogCancelItem  (commandDialog, kCL_Cancel);
    491 
    492         do {
    493 
    494         	ModalDialog(nil, &itemHit); /* wait for user response */
    495 
    496             /* Toggle command-line output checkbox */
    497         	if ( itemHit == kCL_File ) {
    498         #if TARGET_API_MAC_CARBON
    499         		GetDialogItemAsControl(commandDialog, kCL_File, &control);
    500         		SetControlValue (control, !GetControlValue(control));
    501         #else
    502         		GetDialogItem(commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
    503         		SetControlValue((ControlHandle)dummyHandle, !GetControlValue((ControlHandle)dummyHandle) );
    504         #endif
    505         	}
    506 
    507         } while (itemHit != kCL_OK && itemHit != kCL_Cancel);
    508 
    509         /* Get control values, even if they did not change */
    510         GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); /* MJS */
    511         GetDialogItemText (dummyHandle, prefs.command_line);
    512 
    513     #if TARGET_API_MAC_CARBON
    514         GetDialogItemAsControl(commandDialog, kCL_File, &control);
    515         prefs.output_to_file = GetControlValue(control);
    516 	#else
    517         GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
    518         prefs.output_to_file = GetControlValue ((ControlHandle)dummyHandle);
    519  	#endif
    520 
    521     #if TARGET_API_MAC_CARBON
    522         GetDialogItemAsControl(commandDialog, kCL_Video, &control);
    523         videodriver = GetControlValue(control);
    524     #else
    525         GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
    526         videodriver = GetControlValue ((ControlRef)dummyHandle);
    527      #endif
    528 
    529         DisposeDialog (commandDialog);
    530 
    531         if (itemHit == kCL_Cancel ) {
    532         	exit (0);
    533         }
    534 	}
    535 
    536     /* Set pseudo-environment variables for video driver, update prefs */
    537 	switch ( videodriver ) {
    538 	   case VIDEO_ID_DRAWSPROCKET:
    539 	      SDL_putenv("SDL_VIDEODRIVER=DSp");
    540 	      SDL_memcpy(prefs.video_driver_name, "\pDSp", 4);
    541 	      break;
    542 	   case VIDEO_ID_TOOLBOX:
    543 	      SDL_putenv("SDL_VIDEODRIVER=toolbox");
    544 	      SDL_memcpy(prefs.video_driver_name, "\ptoolbox", 8);
    545 	      break;
    546 	}
    547 
    548 #if !(defined(__APPLE__) && defined(__MACH__))
    549     /* Redirect standard I/O to files */
    550 	if ( prefs.output_to_file ) {
    551 		freopen (STDOUT_FILE, "w", stdout);
    552 		freopen (STDERR_FILE, "w", stderr);
    553 	} else {
    554 		fclose (stdout);
    555 		fclose (stderr);
    556 	}
    557 #endif
    558 
    559     if (settingsChanged) {
    560         /* Save the prefs, even if they might not have changed (but probably did) */
    561         if ( ! writePreferences (&prefs) )
    562             fprintf (stderr, "WARNING: Could not save preferences!\n");
    563     }
    564 
    565 #if !(defined(__APPLE__) && defined(__MACH__))
    566     appNameText[0] = 0;
    567     getCurrentAppName (appNameText); /* check for error here ? */
    568 
    569     commandLine = (char*) malloc (appNameText[0] + prefs.command_line[0] + 2);
    570     if ( commandLine == NULL ) {
    571        exit(-1);
    572     }
    573 
    574     /* Rather than rewrite ParseCommandLine method, let's replace  */
    575     /* any spaces in application name with underscores,            */
    576     /* so that the app name is only 1 argument                     */
    577     for (i = 1; i < 1+appNameText[0]; i++)
    578         if ( appNameText[i] == ' ' ) appNameText[i] = '_';
    579 
    580     /* Copy app name & full command text to command-line C-string */
    581     SDL_memcpy(commandLine, appNameText + 1, appNameText[0]);
    582     commandLine[appNameText[0]] = ' ';
    583     SDL_memcpy(commandLine + appNameText[0] + 1, prefs.command_line + 1, prefs.command_line[0]);
    584     commandLine[ appNameText[0] + 1 + prefs.command_line[0] ] = '\0';
    585 
    586     /* Parse C-string into argv and argc */
    587     nargs = ParseCommandLine (commandLine, NULL);
    588     args = (char **)malloc((nargs+1)*(sizeof *args));
    589     if ( args == NULL ) {
    590 		exit(-1);
    591 	}
    592 	ParseCommandLine (commandLine, args);
    593 
    594 	/* Run the main application code */
    595 	SDL_main(nargs, args);
    596 	free (args);
    597 	free (commandLine);
    598 
    599    	/* Remove useless stdout.txt and stderr.txt */
    600    	cleanup_output ();
    601 #else // defined(__APPLE__) && defined(__MACH__)
    602 	SDL_main(argc, argv);
    603 #endif
    604 
    605 	/* Exit cleanly, calling atexit() functions */
    606 	exit (0);
    607 
    608 	/* Never reached, but keeps the compiler quiet */
    609 	return (0);
    610 }
    611