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