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@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 */ 58typedef 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 */ 65static 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 */ 80static 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 */ 130static 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 160static 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 179static 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 227static 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 258static 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 299static 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 327static 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 */ 348int 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