view src/main/macosx/SDLMain.m @ 1663:11775724e3fe SDL-1.3

fine tuning indent output
author Sam Lantinga <slouken@libsdl.org>
date Sun, 28 May 2006 13:29:03 +0000
parents 782fd950bd46
children cd3db072ba8a
line wrap: on
line source

/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
       Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
       Non-NIB-Code & other changes: Max Horn <max@quendi.de>

    Feel free to customize this file to suit your needs
*/

#import "SDL.h"
#import "SDLMain.h"
#import <sys/param.h>           /* for MAXPATHLEN */
#import <unistd.h>

/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
 but the method still is there and works. To avoid warnings, we declare
 it ourselves here. */
@ interface NSApplication (SDL_Missing_Methods) - (void) setAppleMenu:(NSMenu
 *)
    menu;
@end
/* Use this flag to determine whether we use SDLMain.nib or not */
#define		SDL_USE_NIB_FILE	0
/* Use this flag to determine whether we use CPS (docking) or not */
#define		SDL_USE_CPS		1
#ifdef SDL_USE_CPS
/* Portions of CPS.h */
     typedef struct CPSProcessSerNum
     {
         UInt32
             lo;
         UInt32
             hi;
     } CPSProcessSerNum;

     extern
         OSErr
     CPSGetCurrentProcess (CPSProcessSerNum * psn);
     extern
         OSErr
     CPSEnableForegroundOperation (CPSProcessSerNum * psn, UInt32 _arg2,
                                   UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
     extern
         OSErr
     CPSSetFrontProcess (CPSProcessSerNum * psn);

#endif /* SDL_USE_CPS */

     static int
         gArgc;
     static char **
         gArgv;
     static
         BOOL
         gFinderLaunch;
     static
         BOOL
         gCalledAppMainline = FALSE;

     static NSString *
     getApplicationName (void)
{
    NSDictionary *dict;
    NSString *appName = 0;

    /* Determine the application name */
    dict =
        (NSDictionary *) CFBundleGetInfoDictionary (CFBundleGetMainBundle ());
    if (dict)
      appName =[dict objectForKey:@"CFBundleName"];

    if (![appName length])
        appName =[[NSProcessInfo processInfo] processName];

    return appName;
}

#if SDL_USE_NIB_FILE
/* A helper category for NSString */
@interface NSString (ReplaceSubString) - (NSString *) stringByReplacingRange:(NSRange)
     aRange
     with:(NSString *)
    aString;
@end
#endif
@ interface SDLApplication:NSApplication
    @ end @ implementation SDLApplication
/* Invoked from the Quit menu item */
- (void) terminate:(id) sender
{
    /* Post a SDL_QUIT event */
    SDL_Event event;
    event.type = SDL_QUIT;
    SDL_PushEvent (&event);
}

@end
/* The main class of the application, the application's delegate */
    @ implementation SDLMain
/* Set the working directory to the .app's parent directory */
- (void) setupWorkingDirectory:(BOOL) shouldChdir {
    if (shouldChdir) {
        char parentdir[MAXPATHLEN];
        CFURLRef url = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
        CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent (0, url);
        if (CFURLGetFileSystemRepresentation
            (url2, true, (UInt8 *) parentdir, MAXPATHLEN)) {
            assert (chdir (parentdir) == 0);    /* chdir to the binary app's parent */
        }
        CFRelease (url);
        CFRelease (url2);
    }

}

#if SDL_USE_NIB_FILE

/* Fix menu to contain the real app name instead of "SDL App" */
-(void) fixMenu:(NSMenu *)
     aMenu
     withAppName:(NSString *)
    appName
{
    NSRange aRange;
    NSEnumerator *enumerator;
    NSMenuItem *menuItem;

  aRange =[[aMenu title] rangeOfString:@"SDL App"];
    if (aRange.length != 0)
      [aMenu setTitle: [[aMenu title] stringByReplacingRange: aRange with:appName]];

    enumerator =[[aMenu itemArray] objectEnumerator];
    while ((menuItem =[enumerator nextObject])) {
      aRange =[[menuItem title] rangeOfString:@"SDL App"];
        if (aRange.length != 0)
          [menuItem setTitle: [[menuItem title] stringByReplacingRange: aRange with:appName]];
        if ([menuItem hasSubmenu])
          [self fixMenu: [menuItem submenu] withAppName:appName];
    }
    [aMenu sizeToFit];
}

#else

static void
setApplicationMenu (void)
{
    /* warning: this code is very odd */
    NSMenu *appleMenu;
    NSMenuItem *menuItem;
    NSString *title;
    NSString *appName;

    appName = getApplicationName ();
  appleMenu =[[NSMenu alloc] initWithTitle:@""];

    /* Add menu items */
  title =[@"About " stringByAppendingString:appName];
  [appleMenu addItemWithTitle: title action: @selector (orderFrontStandardAboutPanel: )keyEquivalent:@""];

  [appleMenu addItem:[NSMenuItem separatorItem]];

  title =[@"Hide " stringByAppendingString:appName];
  [appleMenu addItemWithTitle: title action: @selector (hide: )keyEquivalent:@"h"];

  menuItem = (NSMenuItem *)[appleMenu addItemWithTitle: @"Hide Others" action: @selector (hideOtherApplications: )keyEquivalent:@"h"];
  [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask |
     NSCommandKeyMask)];

  [appleMenu addItemWithTitle: @"Show All" action: @selector (unhideAllApplications: )keyEquivalent:@""];

  [appleMenu addItem:[NSMenuItem separatorItem]];

  title =[@"Quit " stringByAppendingString:appName];
  [appleMenu addItemWithTitle: title action: @selector (terminate: )keyEquivalent:@"q"];


    /* Put menu into the menubar */
  menuItem =[[NSMenuItem alloc] initWithTitle: @"" action: nil keyEquivalent:@""];
  [menuItem setSubmenu:appleMenu];
  [[NSApp mainMenu] addItem:menuItem];

    /* Tell the application object that this is now the application menu */
  [NSApp setAppleMenu:appleMenu];

    /* Finally give up our references to the objects */
    [appleMenu release];
    [menuItem release];
}

/* Create a window menu */
static void
setupWindowMenu (void)
{
    NSMenu *windowMenu;
    NSMenuItem *windowMenuItem;
    NSMenuItem *menuItem;

  windowMenu =[[NSMenu alloc] initWithTitle:@"Window"];

    /* "Minimize" item */
  menuItem =[[NSMenuItem alloc] initWithTitle: @"Minimize" action: @selector (performMiniaturize: )keyEquivalent:@"m"];
  [windowMenu addItem:menuItem];
    [menuItem release];

    /* Put menu into the menubar */
  windowMenuItem =[[NSMenuItem alloc] initWithTitle: @"Window" action: nil keyEquivalent:@""];
  [windowMenuItem setSubmenu:windowMenu];
  [[NSApp mainMenu] addItem:windowMenuItem];

    /* Tell the application object that this is now the window menu */
  [NSApp setWindowsMenu:windowMenu];

    /* Finally give up our references to the objects */
    [windowMenu release];
    [windowMenuItem release];
}

/* Replacement for NSApplicationMain */
static void
CustomApplicationMain (int argc, char **argv)
{
    NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
    SDLMain *sdlMain;

    /* Ensure the application object is initialised */
    [SDLApplication sharedApplication];

#ifdef SDL_USE_CPS
    {
        CPSProcessSerNum PSN;
        /* Tell the dock about us */
        if (!CPSGetCurrentProcess (&PSN))
            if (!CPSEnableForegroundOperation
                (&PSN, 0x03, 0x3C, 0x2C, 0x1103))
                if (!CPSSetFrontProcess (&PSN))
                    [SDLApplication sharedApplication];
    }
#endif /* SDL_USE_CPS */

    /* Set up the menubar */
  [NSApp setMainMenu:[[NSMenu alloc] init]];
    setApplicationMenu ();
    setupWindowMenu ();

    /* Create SDLMain and make it the app delegate */
    sdlMain =[[SDLMain alloc] init];
  [NSApp setDelegate:sdlMain];

    /* Start the main event loop */
    [NSApp run];

    [sdlMain release];
    [pool release];
}

#endif


/*
 * Catch document open requests...this lets us notice files when the app
 *  was launched by double-clicking a document, or when a document was
 *  dragged/dropped on the app's icon. You need to have a
 *  CFBundleDocumentsType section in your Info.plist to get this message,
 *  apparently.
 *
 * Files are added to gArgv, so to the app, they'll look like command line
 *  arguments. Previously, apps launched from the finder had nothing but
 *  an argv[0].
 *
 * This message may be received multiple times to open several docs on launch.
 *
 * This message is ignored once the app's mainline has been called.
 */
-(BOOL) application:(NSApplication *)
     theApplication openFile:(NSString *) filename
{
    const char *temparg;
    size_t arglen;
    char *arg;
    char **newargv;

    if (!gFinderLaunch)         /* MacOS is passing command line args. */
        return FALSE;

    if (gCalledAppMainline)     /* app has started, ignore this document. */
        return FALSE;

    temparg =[filename UTF8String];
    arglen = SDL_strlen (temparg) + 1;
    arg = (char *) SDL_malloc (arglen);
    if (arg == NULL)
        return FALSE;

    newargv = (char **) realloc (gArgv, sizeof (char *) * (gArgc + 2));
    if (newargv == NULL) {
        SDL_free (arg);
        return FALSE;
    }
    gArgv = newargv;

    SDL_strlcpy (arg, temparg, arglen);
    gArgv[gArgc++] = arg;
    gArgv[gArgc] = NULL;
    return TRUE;
}


/* Called when the internal event loop has just started running */
-(void) applicationDidFinishLaunching:(NSNotification *) note {
    int status;

    /* Set the working directory to the .app's parent directory */
  [self setupWorkingDirectory:gFinderLaunch];

#if SDL_USE_NIB_FILE
    /* Set the main menu to contain the real app name instead of "SDL App" */
  [self fixMenu: [NSApp mainMenu] withAppName:getApplicationName ()];
#endif

    /* Hand off to main application code */
    gCalledAppMainline = TRUE;
    status = SDL_main (gArgc, gArgv);

    /* We're done, thank you for playing */
    exit (status);
}

@end @ implementation NSString (ReplaceSubString) - (NSString *) stringByReplacingRange:(NSRange)
     aRange
     with:(NSString *)
    aString
{
    unsigned int bufferSize;
    unsigned int selfLen =[self length];
    unsigned int aStringLen =[aString length];
    unichar *buffer;
    NSRange localRange;
    NSString *result;

    bufferSize = selfLen + aStringLen - aRange.length;
    buffer = NSAllocateMemoryPages (bufferSize * sizeof (unichar));

    /* Get first part into buffer */
    localRange.location = 0;
    localRange.length = aRange.location;
  [self getCharacters: buffer range:localRange];

    /* Get middle part into buffer */
    localRange.location = 0;
    localRange.length = aStringLen;
  [aString getCharacters: (buffer + aRange.location) range:localRange];

    /* Get last part into buffer */
    localRange.location = aRange.location + aRange.length;
    localRange.length = selfLen - localRange.location;
  [self getCharacters: (buffer + aRange.location + aStringLen) range:localRange];

    /* Build output string */
  result =[NSString stringWithCharacters: buffer length:bufferSize];

    NSDeallocateMemoryPages (buffer, bufferSize);

    return result;
}

@end
#ifdef main
#  undef main
#endif
/* Main entry point to executable - should *not* be SDL_main! */
    int
main (int argc, char **argv)
{
    /* Copy the arguments into a global variable */
    /* This is passed if we are launched by double-clicking */
    if (argc >= 2 && strncmp (argv[1], "-psn", 4) == 0) {
        gArgv = (char **) SDL_malloc (sizeof (char *) * 2);
        gArgv[0] = argv[0];
        gArgv[1] = NULL;
        gArgc = 1;
        gFinderLaunch = YES;
    } else {
        int i;
        gArgc = argc;
        gArgv = (char **) SDL_malloc (sizeof (char *) * (argc + 1));
        for (i = 0; i <= argc; i++)
            gArgv[i] = argv[i];
        gFinderLaunch = NO;
    }

#if SDL_USE_NIB_FILE
  [SDLApplication poseAsClass:[NSApplication class]];
    NSApplicationMain (argc, argv);
#else
    CustomApplicationMain (argc, argv);
#endif
    return 0;
}