view src/main/macos/SDL_main.c @ 1662:782fd950bd46 SDL-1.3

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.
author Sam Lantinga <slouken@libsdl.org>
date Sun, 28 May 2006 13:04:16 +0000
parents 7a610f25c12f
children 4da1ee79c9af
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/

/* This file takes care of command line argument parsing, and stdio redirection
   in the MacOS environment. (stdio/stderr is *not* directed for Mach-O builds)
 */

#if defined(__APPLE__) && defined(__MACH__)
#include <Carbon/Carbon.h>
#elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
#include <Carbon.h>
#else
#include <Dialogs.h>
#include <Fonts.h>
#include <Events.h>
#include <Resources.h>
#include <Folders.h>
#endif

/* Include the SDL main definition header */
#include "SDL.h"
#include "SDL_main.h"
#ifdef main
#undef main
#endif

#if !(defined(__APPLE__) && defined(__MACH__))
/* The standard output files */
#define STDOUT_FILE	"stdout.txt"
#define STDERR_FILE	"stderr.txt"
#endif

#if !defined(__MWERKS__) && !TARGET_API_MAC_CARBON
        /* In MPW, the qd global has been removed from the libraries */
QDGlobals qd;
#endif

/* Structure for keeping prefs in 1 variable */
typedef struct
{
    Str255 command_line;
    Str255 video_driver_name;
    Boolean output_to_file;
} PrefsRecord;

/* See if the command key is held down at startup */
static Boolean
CommandKeyIsDown (void)
{
    KeyMap theKeyMap;

    GetKeys (theKeyMap);

    if (((unsigned char *) theKeyMap)[6] & 0x80) {
        return (true);
    }
    return (false);
}

#if !(defined(__APPLE__) && defined(__MACH__))

/* Parse a command line buffer into arguments */
static int
ParseCommandLine (char *cmdline, char **argv)
{
    char *bufp;
    int argc;

    argc = 0;
    for (bufp = cmdline; *bufp;) {
        /* Skip leading whitespace */
        while (SDL_isspace (*bufp)) {
            ++bufp;
        }
        /* Skip over argument */
        if (*bufp == '"') {
            ++bufp;
            if (*bufp) {
                if (argv) {
                    argv[argc] = bufp;
                }
                ++argc;
            }
            /* Skip over word */
            while (*bufp && (*bufp != '"')) {
                ++bufp;
            }
        } else {
            if (*bufp) {
                if (argv) {
                    argv[argc] = bufp;
                }
                ++argc;
            }
            /* Skip over word */
            while (*bufp && !SDL_isspace (*bufp)) {
                ++bufp;
            }
        }
        if (*bufp) {
            if (argv) {
                *bufp = '\0';
            }
            ++bufp;
        }
    }
    if (argv) {
        argv[argc] = NULL;
    }
    return (argc);
}

/* Remove the output files if there was no output written */
static void
cleanup_output (void)
{
    FILE *file;
    int empty;

    /* Flush the output in case anything is queued */
    fclose (stdout);
    fclose (stderr);

    /* See if the files have any output in them */
    file = fopen (STDOUT_FILE, "rb");
    if (file) {
        empty = (fgetc (file) == EOF) ? 1 : 0;
        fclose (file);
        if (empty) {
            remove (STDOUT_FILE);
        }
    }
    file = fopen (STDERR_FILE, "rb");
    if (file) {
        empty = (fgetc (file) == EOF) ? 1 : 0;
        fclose (file);
        if (empty) {
            remove (STDERR_FILE);
        }
    }
}

#endif //!(defined(__APPLE__) && defined(__MACH__))

static int
getCurrentAppName (StrFileName name)
{

    ProcessSerialNumber process;
    ProcessInfoRec process_info;
    FSSpec process_fsp;

    process.highLongOfPSN = 0;
    process.lowLongOfPSN = kCurrentProcess;
    process_info.processInfoLength = sizeof (process_info);
    process_info.processName = NULL;
    process_info.processAppSpec = &process_fsp;

    if (noErr != GetProcessInformation (&process, &process_info))
        return 0;

    SDL_memcpy (name, process_fsp.name, process_fsp.name[0] + 1);
    return 1;
}

static int
getPrefsFile (FSSpec * prefs_fsp, int create)
{

    /* The prefs file name is the application name, possibly truncated, */
    /* plus " Preferences */

#define  SUFFIX   " Preferences"
#define  MAX_NAME 19            /* 31 - strlen (SUFFIX) */

    short volume_ref_number;
    long directory_id;
    StrFileName prefs_name;
    StrFileName app_name;

    /* Get Preferences folder - works with Multiple Users */
    if (noErr !=
        FindFolder (kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
                    &volume_ref_number, &directory_id))
        exit (-1);

    if (!getCurrentAppName (app_name))
        exit (-1);

    /* Truncate if name is too long */
    if (app_name[0] > MAX_NAME)
        app_name[0] = MAX_NAME;

    SDL_memcpy (prefs_name + 1, app_name + 1, app_name[0]);
    SDL_memcpy (prefs_name + app_name[0] + 1, SUFFIX, strlen (SUFFIX));
    prefs_name[0] = app_name[0] + strlen (SUFFIX);

    /* Make the file spec for prefs file */
    if (noErr !=
        FSMakeFSSpec (volume_ref_number, directory_id, prefs_name, prefs_fsp))
    {
        if (!create)
            return 0;
        else {
            /* Create the prefs file */
            SDL_memcpy (prefs_fsp->name, prefs_name, prefs_name[0] + 1);
            prefs_fsp->parID = directory_id;
            prefs_fsp->vRefNum = volume_ref_number;

            FSpCreateResFile (prefs_fsp, 0x3f3f3f3f, 'pref', 0);        // '????' parsed as trigraph

            if (noErr != ResError ())
                return 0;
        }
    }
    return 1;
}

static int
readPrefsResource (PrefsRecord * prefs)
{

    Handle prefs_handle;

    prefs_handle = Get1Resource ('CLne', 128);

    if (prefs_handle != NULL) {
        int offset = 0;
//              int j      = 0;

        HLock (prefs_handle);

        /* Get command line string */
        SDL_memcpy (prefs->command_line, *prefs_handle,
                    (*prefs_handle)[0] + 1);

        /* Get video driver name */
        offset += (*prefs_handle)[0] + 1;
        SDL_memcpy (prefs->video_driver_name, *prefs_handle + offset,
                    (*prefs_handle)[offset] + 1);

        /* Get save-to-file option (1 or 0) */
        offset += (*prefs_handle)[offset] + 1;
        prefs->output_to_file = (*prefs_handle)[offset];

        ReleaseResource (prefs_handle);

        return ResError () == noErr;
    }

    return 0;
}

static int
writePrefsResource (PrefsRecord * prefs, short resource_file)
{

    Handle prefs_handle;

    UseResFile (resource_file);

    prefs_handle = Get1Resource ('CLne', 128);
    if (prefs_handle != NULL)
        RemoveResource (prefs_handle);

    prefs_handle =
        NewHandle (prefs->command_line[0] + prefs->video_driver_name[0] + 4);
    if (prefs_handle != NULL) {

        int offset;

        HLock (prefs_handle);

        /* Command line text */
        offset = 0;
        SDL_memcpy (*prefs_handle, prefs->command_line,
                    prefs->command_line[0] + 1);

        /* Video driver name */
        offset += prefs->command_line[0] + 1;
        SDL_memcpy (*prefs_handle + offset, prefs->video_driver_name,
                    prefs->video_driver_name[0] + 1);

        /* Output-to-file option */
        offset += prefs->video_driver_name[0] + 1;
        *(*((char **) prefs_handle) + offset) = (char) prefs->output_to_file;
        *(*((char **) prefs_handle) + offset + 1) = 0;

        AddResource (prefs_handle, 'CLne', 128, "\pCommand Line");
        WriteResource (prefs_handle);
        UpdateResFile (resource_file);
        DisposeHandle (prefs_handle);

        return ResError () == noErr;
    }

    return 0;
}

static int
readPreferences (PrefsRecord * prefs)
{

    int no_error = 1;
    FSSpec prefs_fsp;

    /* Check for prefs file first */
    if (getPrefsFile (&prefs_fsp, 0)) {

        short prefs_resource;

        prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdPerm);
        if (prefs_resource == -1)       /* this shouldn't happen, but... */
            return 0;

        UseResFile (prefs_resource);
        no_error = readPrefsResource (prefs);
        CloseResFile (prefs_resource);
    }

    /* Fall back to application's resource fork (reading only, so this is safe) */
    else {

        no_error = readPrefsResource (prefs);
    }

    return no_error;
}

static int
writePreferences (PrefsRecord * prefs)
{

    int no_error = 1;
    FSSpec prefs_fsp;

    /* Get prefs file, create if it doesn't exist */
    if (getPrefsFile (&prefs_fsp, 1)) {

        short prefs_resource;

        prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdWrPerm);
        if (prefs_resource == -1)
            return 0;
        no_error = writePrefsResource (prefs, prefs_resource);
        CloseResFile (prefs_resource);
    }

    return no_error;
}

/* This is where execution begins */
int
main (int argc, char *argv[])
{

#if !(defined(__APPLE__) && defined(__MACH__))
#pragma unused(argc, argv)
#endif

#define DEFAULT_ARGS "\p"       /* pascal string for default args */
#define DEFAULT_VIDEO_DRIVER "\ptoolbox"        /* pascal string for default video driver name */
#define DEFAULT_OUTPUT_TO_FILE 1        /* 1 == output to file, 0 == no output */

#define VIDEO_ID_DRAWSPROCKET 1 /* these correspond to popup menu choices */
#define VIDEO_ID_TOOLBOX      2

    PrefsRecord prefs =
        { DEFAULT_ARGS, DEFAULT_VIDEO_DRIVER, DEFAULT_OUTPUT_TO_FILE };

#if !(defined(__APPLE__) && defined(__MACH__))
    int nargs;
    char **args;
    char *commandLine;

    StrFileName appNameText;
#endif
    int videodriver = VIDEO_ID_TOOLBOX;
    int settingsChanged = 0;

    long i;

    /* Kyle's SDL command-line dialog code ... */
#if !TARGET_API_MAC_CARBON
    InitGraf (&qd.thePort);
    InitFonts ();
    InitWindows ();
    InitMenus ();
    InitDialogs (nil);
#endif
    InitCursor ();
    FlushEvents (everyEvent, 0);
#if !TARGET_API_MAC_CARBON
    MaxApplZone ();
#endif
    MoreMasters ();
    MoreMasters ();
#if 0
    /* Intialize SDL, and put up a dialog if we fail */
    if (SDL_Init (0) < 0) {

#define kErr_OK		1
#define kErr_Text	2

        DialogPtr errorDialog;
        short dummyType;
        Rect dummyRect;
        Handle dummyHandle;
        short itemHit;

        errorDialog = GetNewDialog (1001, nil, (WindowPtr) - 1);
        if (errorDialog == NULL)
            return -1;
        DrawDialog (errorDialog);

        GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle,
                       &dummyRect);
        SetDialogItemText (dummyHandle, "\pError Initializing SDL");

#if TARGET_API_MAC_CARBON
        SetPort (GetDialogPort (errorDialog));
#else
        SetPort (errorDialog);
#endif
        do {
            ModalDialog (nil, &itemHit);
        }
        while (itemHit != kErr_OK);

        DisposeDialog (errorDialog);
        exit (-1);
    }
    atexit (cleanup_output);
    atexit (SDL_Quit);
#endif

/* Set up SDL's QuickDraw environment  */
#if !TARGET_API_MAC_CARBON
    SDL_InitQuickDraw (&qd);
#endif

    if (readPreferences (&prefs)) {

        if (SDL_memcmp (prefs.video_driver_name + 1, "DSp", 3) == 0)
            videodriver = 1;
        else if (SDL_memcmp (prefs.video_driver_name + 1, "toolbox", 7) == 0)
            videodriver = 2;
    }

    if (CommandKeyIsDown ()) {

#define kCL_OK		1
#define kCL_Cancel	2
#define kCL_Text	3
#define kCL_File	4
#define kCL_Video   6

        DialogPtr commandDialog;
        short dummyType;
        Rect dummyRect;
        Handle dummyHandle;
        short itemHit;
#if TARGET_API_MAC_CARBON
        ControlRef control;
#endif

        /* Assume that they will change settings, rather than do exhaustive check */
        settingsChanged = 1;

        /* Create dialog and display it */
        commandDialog = GetNewDialog (1000, nil, (WindowPtr) - 1);
#if TARGET_API_MAC_CARBON
        SetPort (GetDialogPort (commandDialog));
#else
        SetPort (commandDialog);
#endif

        /* Setup controls */
#if TARGET_API_MAC_CARBON
        GetDialogItemAsControl (commandDialog, kCL_File, &control);
        SetControlValue (control, prefs.output_to_file);
#else
        GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect);  /* MJS */
        SetControlValue ((ControlHandle) dummyHandle, prefs.output_to_file);
#endif

        GetDialogItem (commandDialog, kCL_Text, &dummyType, &dummyHandle,
                       &dummyRect);
        SetDialogItemText (dummyHandle, prefs.command_line);

#if TARGET_API_MAC_CARBON
        GetDialogItemAsControl (commandDialog, kCL_Video, &control);
        SetControlValue (control, videodriver);
#else
        GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle,
                       &dummyRect);
        SetControlValue ((ControlRef) dummyHandle, videodriver);
#endif

        SetDialogDefaultItem (commandDialog, kCL_OK);
        SetDialogCancelItem (commandDialog, kCL_Cancel);

        do {

            ModalDialog (nil, &itemHit);        /* wait for user response */

            /* Toggle command-line output checkbox */
            if (itemHit == kCL_File) {
#if TARGET_API_MAC_CARBON
                GetDialogItemAsControl (commandDialog, kCL_File, &control);
                SetControlValue (control, !GetControlValue (control));
#else
                GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect);  /* MJS */
                SetControlValue ((ControlHandle) dummyHandle,
                                 !GetControlValue ((ControlHandle)
                                                   dummyHandle));
#endif
            }

        }
        while (itemHit != kCL_OK && itemHit != kCL_Cancel);

        /* Get control values, even if they did not change */
        GetDialogItem (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect);  /* MJS */
        GetDialogItemText (dummyHandle, prefs.command_line);

#if TARGET_API_MAC_CARBON
        GetDialogItemAsControl (commandDialog, kCL_File, &control);
        prefs.output_to_file = GetControlValue (control);
#else
        GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect);  /* MJS */
        prefs.output_to_file = GetControlValue ((ControlHandle) dummyHandle);
#endif

#if TARGET_API_MAC_CARBON
        GetDialogItemAsControl (commandDialog, kCL_Video, &control);
        videodriver = GetControlValue (control);
#else
        GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle,
                       &dummyRect);
        videodriver = GetControlValue ((ControlRef) dummyHandle);
#endif

        DisposeDialog (commandDialog);

        if (itemHit == kCL_Cancel) {
            exit (0);
        }
    }

    /* Set pseudo-environment variables for video driver, update prefs */
    switch (videodriver) {
    case VIDEO_ID_DRAWSPROCKET:
        SDL_putenv ("SDL_VIDEODRIVER=DSp");
        SDL_memcpy (prefs.video_driver_name, "\pDSp", 4);
        break;
    case VIDEO_ID_TOOLBOX:
        SDL_putenv ("SDL_VIDEODRIVER=toolbox");
        SDL_memcpy (prefs.video_driver_name, "\ptoolbox", 8);
        break;
    }

#if !(defined(__APPLE__) && defined(__MACH__))
    /* Redirect standard I/O to files */
    if (prefs.output_to_file) {
        freopen (STDOUT_FILE, "w", stdout);
        freopen (STDERR_FILE, "w", stderr);
    } else {
        fclose (stdout);
        fclose (stderr);
    }
#endif

    if (settingsChanged) {
        /* Save the prefs, even if they might not have changed (but probably did) */
        if (!writePreferences (&prefs))
            fprintf (stderr, "WARNING: Could not save preferences!\n");
    }
#if !(defined(__APPLE__) && defined(__MACH__))
    appNameText[0] = 0;
    getCurrentAppName (appNameText);    /* check for error here ? */

    commandLine =
        (char *) malloc (appNameText[0] + prefs.command_line[0] + 2);
    if (commandLine == NULL) {
        exit (-1);
    }

    /* Rather than rewrite ParseCommandLine method, let's replace  */
    /* any spaces in application name with underscores,            */
    /* so that the app name is only 1 argument                     */
    for (i = 1; i < 1 + appNameText[0]; i++)
        if (appNameText[i] == ' ')
            appNameText[i] = '_';

    /* Copy app name & full command text to command-line C-string */
    SDL_memcpy (commandLine, appNameText + 1, appNameText[0]);
    commandLine[appNameText[0]] = ' ';
    SDL_memcpy (commandLine + appNameText[0] + 1, prefs.command_line + 1,
                prefs.command_line[0]);
    commandLine[appNameText[0] + 1 + prefs.command_line[0]] = '\0';

    /* Parse C-string into argv and argc */
    nargs = ParseCommandLine (commandLine, NULL);
    args = (char **) malloc ((nargs + 1) * (sizeof *args));
    if (args == NULL) {
        exit (-1);
    }
    ParseCommandLine (commandLine, args);

    /* Run the main application code */
    SDL_main (nargs, args);
    free (args);
    free (commandLine);

    /* Remove useless stdout.txt and stderr.txt */
    cleanup_output ();
#else // defined(__APPLE__) && defined(__MACH__)
    SDL_main (argc, argv);
#endif

    /* Exit cleanly, calling atexit() functions */
    exit (0);

    /* Never reached, but keeps the compiler quiet */
    return (0);
}

/* vi: set ts=4 sw=4 expandtab: */