src/main/macos/SDL_main.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1424 7a610f25c12f
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
     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 =
   603         (char *) malloc (appNameText[0] + prefs.command_line[0] + 2);
   604     if (commandLine == NULL) {
   605         exit (-1);
   606     }
   607 
   608     /* Rather than rewrite ParseCommandLine method, let's replace  */
   609     /* any spaces in application name with underscores,            */
   610     /* so that the app name is only 1 argument                     */
   611     for (i = 1; i < 1 + appNameText[0]; i++)
   612         if (appNameText[i] == ' ')
   613             appNameText[i] = '_';
   614 
   615     /* Copy app name & full command text to command-line C-string */
   616     SDL_memcpy (commandLine, appNameText + 1, appNameText[0]);
   617     commandLine[appNameText[0]] = ' ';
   618     SDL_memcpy (commandLine + appNameText[0] + 1, prefs.command_line + 1,
   619                 prefs.command_line[0]);
   620     commandLine[appNameText[0] + 1 + prefs.command_line[0]] = '\0';
   621 
   622     /* Parse C-string into argv and argc */
   623     nargs = ParseCommandLine (commandLine, NULL);
   624     args = (char **) malloc ((nargs + 1) * (sizeof *args));
   625     if (args == NULL) {
   626         exit (-1);
   627     }
   628     ParseCommandLine (commandLine, args);
   629 
   630     /* Run the main application code */
   631     SDL_main (nargs, args);
   632     free (args);
   633     free (commandLine);
   634 
   635     /* Remove useless stdout.txt and stderr.txt */
   636     cleanup_output ();
   637 #else // defined(__APPLE__) && defined(__MACH__)
   638     SDL_main (argc, argv);
   639 #endif
   640 
   641     /* Exit cleanly, calling atexit() functions */
   642     exit (0);
   643 
   644     /* Never reached, but keeps the compiler quiet */
   645     return (0);
   646 }
   647 
   648 /* vi: set ts=4 sw=4 expandtab: */