view src/main/macos/SDL_main.c @ 1895:c121d94672cb

SDL 1.2 is moving to a branch, and SDL 1.3 is becoming the head.
author Sam Lantinga <slouken@libsdl.org>
date Mon, 10 Jul 2006 21:04:37 +0000
parents 7a610f25c12f
children
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: */