Skip to content

Instantly share code, notes, and snippets.

@nickoneill
Created April 20, 2011 18:04

Revisions

  1. @invalid-email-address Anonymous created this gist Apr 20, 2011.
    16 changes: 16 additions & 0 deletions SDLMain.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    /* SDLMain.m - main entry point for our Cocoa-ized SDL app
    Initial Version: Darrell Walisser <[email protected]>
    Non-NIB-Code & other changes: Max Horn <[email protected]>
    Feel free to customize this file to suit your needs
    */

    #ifndef _SDLMain_h_
    #define _SDLMain_h_

    #import <Cocoa/Cocoa.h>

    @interface SDLMain : NSObject
    @end

    #endif /* _SDLMain_h_ */
    382 changes: 382 additions & 0 deletions SDLMain.m
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,382 @@
    /* SDLMain.m - main entry point for our Cocoa-ized SDL app
    Initial Version: Darrell Walisser <[email protected]>
    Non-NIB-Code & other changes: Max Horn <[email protected]>
    Feel free to customize this file to suit your needs
    */

    #include "SDL.h"
    #include "SDLMain.h"
    #include <sys/param.h> /* for MAXPATHLEN */
    #include <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)
    {
    const NSDictionary *dict;
    NSString *appName = 0;

    /* Determine the application name */
    dict = (const 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, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
    chdir(parentdir); /* 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 = (unichar *)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;
    }