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