src/main/macosx/SDLMain.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:29:03 +0000
branchSDL-1.3
changeset 1663 11775724e3fe
parent 1662 782fd950bd46
child 1664 cd3db072ba8a
permissions -rw-r--r--
fine tuning indent output
     1 /*   SDLMain.m - main entry point for our Cocoa-ized SDL app
     2        Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
     3        Non-NIB-Code & other changes: Max Horn <max@quendi.de>
     4 
     5     Feel free to customize this file to suit your needs
     6 */
     7 
     8 #import "SDL.h"
     9 #import "SDLMain.h"
    10 #import <sys/param.h>           /* for MAXPATHLEN */
    11 #import <unistd.h>
    12 
    13 /* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
    14  but the method still is there and works. To avoid warnings, we declare
    15  it ourselves here. */
    16 @ interface NSApplication (SDL_Missing_Methods) - (void) setAppleMenu:(NSMenu
    17  *)
    18     menu;
    19 @end
    20 /* Use this flag to determine whether we use SDLMain.nib or not */
    21 #define		SDL_USE_NIB_FILE	0
    22 /* Use this flag to determine whether we use CPS (docking) or not */
    23 #define		SDL_USE_CPS		1
    24 #ifdef SDL_USE_CPS
    25 /* Portions of CPS.h */
    26      typedef struct CPSProcessSerNum
    27      {
    28          UInt32
    29              lo;
    30          UInt32
    31              hi;
    32      } CPSProcessSerNum;
    33 
    34      extern
    35          OSErr
    36      CPSGetCurrentProcess (CPSProcessSerNum * psn);
    37      extern
    38          OSErr
    39      CPSEnableForegroundOperation (CPSProcessSerNum * psn, UInt32 _arg2,
    40                                    UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
    41      extern
    42          OSErr
    43      CPSSetFrontProcess (CPSProcessSerNum * psn);
    44 
    45 #endif /* SDL_USE_CPS */
    46 
    47      static int
    48          gArgc;
    49      static char **
    50          gArgv;
    51      static
    52          BOOL
    53          gFinderLaunch;
    54      static
    55          BOOL
    56          gCalledAppMainline = FALSE;
    57 
    58      static NSString *
    59      getApplicationName (void)
    60 {
    61     NSDictionary *dict;
    62     NSString *appName = 0;
    63 
    64     /* Determine the application name */
    65     dict =
    66         (NSDictionary *) CFBundleGetInfoDictionary (CFBundleGetMainBundle ());
    67     if (dict)
    68       appName =[dict objectForKey:@"CFBundleName"];
    69 
    70     if (![appName length])
    71         appName =[[NSProcessInfo processInfo] processName];
    72 
    73     return appName;
    74 }
    75 
    76 #if SDL_USE_NIB_FILE
    77 /* A helper category for NSString */
    78 @interface NSString (ReplaceSubString) - (NSString *) stringByReplacingRange:(NSRange)
    79      aRange
    80      with:(NSString *)
    81     aString;
    82 @end
    83 #endif
    84 @ interface SDLApplication:NSApplication
    85     @ end @ implementation SDLApplication
    86 /* Invoked from the Quit menu item */
    87 - (void) terminate:(id) sender
    88 {
    89     /* Post a SDL_QUIT event */
    90     SDL_Event event;
    91     event.type = SDL_QUIT;
    92     SDL_PushEvent (&event);
    93 }
    94 
    95 @end
    96 /* The main class of the application, the application's delegate */
    97     @ implementation SDLMain
    98 /* Set the working directory to the .app's parent directory */
    99 - (void) setupWorkingDirectory:(BOOL) shouldChdir {
   100     if (shouldChdir) {
   101         char parentdir[MAXPATHLEN];
   102         CFURLRef url = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
   103         CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent (0, url);
   104         if (CFURLGetFileSystemRepresentation
   105             (url2, true, (UInt8 *) parentdir, MAXPATHLEN)) {
   106             assert (chdir (parentdir) == 0);    /* chdir to the binary app's parent */
   107         }
   108         CFRelease (url);
   109         CFRelease (url2);
   110     }
   111 
   112 }
   113 
   114 #if SDL_USE_NIB_FILE
   115 
   116 /* Fix menu to contain the real app name instead of "SDL App" */
   117 -(void) fixMenu:(NSMenu *)
   118      aMenu
   119      withAppName:(NSString *)
   120     appName
   121 {
   122     NSRange aRange;
   123     NSEnumerator *enumerator;
   124     NSMenuItem *menuItem;
   125 
   126   aRange =[[aMenu title] rangeOfString:@"SDL App"];
   127     if (aRange.length != 0)
   128       [aMenu setTitle: [[aMenu title] stringByReplacingRange: aRange with:appName]];
   129 
   130     enumerator =[[aMenu itemArray] objectEnumerator];
   131     while ((menuItem =[enumerator nextObject])) {
   132       aRange =[[menuItem title] rangeOfString:@"SDL App"];
   133         if (aRange.length != 0)
   134           [menuItem setTitle: [[menuItem title] stringByReplacingRange: aRange with:appName]];
   135         if ([menuItem hasSubmenu])
   136           [self fixMenu: [menuItem submenu] withAppName:appName];
   137     }
   138     [aMenu sizeToFit];
   139 }
   140 
   141 #else
   142 
   143 static void
   144 setApplicationMenu (void)
   145 {
   146     /* warning: this code is very odd */
   147     NSMenu *appleMenu;
   148     NSMenuItem *menuItem;
   149     NSString *title;
   150     NSString *appName;
   151 
   152     appName = getApplicationName ();
   153   appleMenu =[[NSMenu alloc] initWithTitle:@""];
   154 
   155     /* Add menu items */
   156   title =[@"About " stringByAppendingString:appName];
   157   [appleMenu addItemWithTitle: title action: @selector (orderFrontStandardAboutPanel: )keyEquivalent:@""];
   158 
   159   [appleMenu addItem:[NSMenuItem separatorItem]];
   160 
   161   title =[@"Hide " stringByAppendingString:appName];
   162   [appleMenu addItemWithTitle: title action: @selector (hide: )keyEquivalent:@"h"];
   163 
   164   menuItem = (NSMenuItem *)[appleMenu addItemWithTitle: @"Hide Others" action: @selector (hideOtherApplications: )keyEquivalent:@"h"];
   165   [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask |
   166      NSCommandKeyMask)];
   167 
   168   [appleMenu addItemWithTitle: @"Show All" action: @selector (unhideAllApplications: )keyEquivalent:@""];
   169 
   170   [appleMenu addItem:[NSMenuItem separatorItem]];
   171 
   172   title =[@"Quit " stringByAppendingString:appName];
   173   [appleMenu addItemWithTitle: title action: @selector (terminate: )keyEquivalent:@"q"];
   174 
   175 
   176     /* Put menu into the menubar */
   177   menuItem =[[NSMenuItem alloc] initWithTitle: @"" action: nil keyEquivalent:@""];
   178   [menuItem setSubmenu:appleMenu];
   179   [[NSApp mainMenu] addItem:menuItem];
   180 
   181     /* Tell the application object that this is now the application menu */
   182   [NSApp setAppleMenu:appleMenu];
   183 
   184     /* Finally give up our references to the objects */
   185     [appleMenu release];
   186     [menuItem release];
   187 }
   188 
   189 /* Create a window menu */
   190 static void
   191 setupWindowMenu (void)
   192 {
   193     NSMenu *windowMenu;
   194     NSMenuItem *windowMenuItem;
   195     NSMenuItem *menuItem;
   196 
   197   windowMenu =[[NSMenu alloc] initWithTitle:@"Window"];
   198 
   199     /* "Minimize" item */
   200   menuItem =[[NSMenuItem alloc] initWithTitle: @"Minimize" action: @selector (performMiniaturize: )keyEquivalent:@"m"];
   201   [windowMenu addItem:menuItem];
   202     [menuItem release];
   203 
   204     /* Put menu into the menubar */
   205   windowMenuItem =[[NSMenuItem alloc] initWithTitle: @"Window" action: nil keyEquivalent:@""];
   206   [windowMenuItem setSubmenu:windowMenu];
   207   [[NSApp mainMenu] addItem:windowMenuItem];
   208 
   209     /* Tell the application object that this is now the window menu */
   210   [NSApp setWindowsMenu:windowMenu];
   211 
   212     /* Finally give up our references to the objects */
   213     [windowMenu release];
   214     [windowMenuItem release];
   215 }
   216 
   217 /* Replacement for NSApplicationMain */
   218 static void
   219 CustomApplicationMain (int argc, char **argv)
   220 {
   221     NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
   222     SDLMain *sdlMain;
   223 
   224     /* Ensure the application object is initialised */
   225     [SDLApplication sharedApplication];
   226 
   227 #ifdef SDL_USE_CPS
   228     {
   229         CPSProcessSerNum PSN;
   230         /* Tell the dock about us */
   231         if (!CPSGetCurrentProcess (&PSN))
   232             if (!CPSEnableForegroundOperation
   233                 (&PSN, 0x03, 0x3C, 0x2C, 0x1103))
   234                 if (!CPSSetFrontProcess (&PSN))
   235                     [SDLApplication sharedApplication];
   236     }
   237 #endif /* SDL_USE_CPS */
   238 
   239     /* Set up the menubar */
   240   [NSApp setMainMenu:[[NSMenu alloc] init]];
   241     setApplicationMenu ();
   242     setupWindowMenu ();
   243 
   244     /* Create SDLMain and make it the app delegate */
   245     sdlMain =[[SDLMain alloc] init];
   246   [NSApp setDelegate:sdlMain];
   247 
   248     /* Start the main event loop */
   249     [NSApp run];
   250 
   251     [sdlMain release];
   252     [pool release];
   253 }
   254 
   255 #endif
   256 
   257 
   258 /*
   259  * Catch document open requests...this lets us notice files when the app
   260  *  was launched by double-clicking a document, or when a document was
   261  *  dragged/dropped on the app's icon. You need to have a
   262  *  CFBundleDocumentsType section in your Info.plist to get this message,
   263  *  apparently.
   264  *
   265  * Files are added to gArgv, so to the app, they'll look like command line
   266  *  arguments. Previously, apps launched from the finder had nothing but
   267  *  an argv[0].
   268  *
   269  * This message may be received multiple times to open several docs on launch.
   270  *
   271  * This message is ignored once the app's mainline has been called.
   272  */
   273 -(BOOL) application:(NSApplication *)
   274      theApplication openFile:(NSString *) filename
   275 {
   276     const char *temparg;
   277     size_t arglen;
   278     char *arg;
   279     char **newargv;
   280 
   281     if (!gFinderLaunch)         /* MacOS is passing command line args. */
   282         return FALSE;
   283 
   284     if (gCalledAppMainline)     /* app has started, ignore this document. */
   285         return FALSE;
   286 
   287     temparg =[filename UTF8String];
   288     arglen = SDL_strlen (temparg) + 1;
   289     arg = (char *) SDL_malloc (arglen);
   290     if (arg == NULL)
   291         return FALSE;
   292 
   293     newargv = (char **) realloc (gArgv, sizeof (char *) * (gArgc + 2));
   294     if (newargv == NULL) {
   295         SDL_free (arg);
   296         return FALSE;
   297     }
   298     gArgv = newargv;
   299 
   300     SDL_strlcpy (arg, temparg, arglen);
   301     gArgv[gArgc++] = arg;
   302     gArgv[gArgc] = NULL;
   303     return TRUE;
   304 }
   305 
   306 
   307 /* Called when the internal event loop has just started running */
   308 -(void) applicationDidFinishLaunching:(NSNotification *) note {
   309     int status;
   310 
   311     /* Set the working directory to the .app's parent directory */
   312   [self setupWorkingDirectory:gFinderLaunch];
   313 
   314 #if SDL_USE_NIB_FILE
   315     /* Set the main menu to contain the real app name instead of "SDL App" */
   316   [self fixMenu: [NSApp mainMenu] withAppName:getApplicationName ()];
   317 #endif
   318 
   319     /* Hand off to main application code */
   320     gCalledAppMainline = TRUE;
   321     status = SDL_main (gArgc, gArgv);
   322 
   323     /* We're done, thank you for playing */
   324     exit (status);
   325 }
   326 
   327 @end @ implementation NSString (ReplaceSubString) - (NSString *) stringByReplacingRange:(NSRange)
   328      aRange
   329      with:(NSString *)
   330     aString
   331 {
   332     unsigned int bufferSize;
   333     unsigned int selfLen =[self length];
   334     unsigned int aStringLen =[aString length];
   335     unichar *buffer;
   336     NSRange localRange;
   337     NSString *result;
   338 
   339     bufferSize = selfLen + aStringLen - aRange.length;
   340     buffer = NSAllocateMemoryPages (bufferSize * sizeof (unichar));
   341 
   342     /* Get first part into buffer */
   343     localRange.location = 0;
   344     localRange.length = aRange.location;
   345   [self getCharacters: buffer range:localRange];
   346 
   347     /* Get middle part into buffer */
   348     localRange.location = 0;
   349     localRange.length = aStringLen;
   350   [aString getCharacters: (buffer + aRange.location) range:localRange];
   351 
   352     /* Get last part into buffer */
   353     localRange.location = aRange.location + aRange.length;
   354     localRange.length = selfLen - localRange.location;
   355   [self getCharacters: (buffer + aRange.location + aStringLen) range:localRange];
   356 
   357     /* Build output string */
   358   result =[NSString stringWithCharacters: buffer length:bufferSize];
   359 
   360     NSDeallocateMemoryPages (buffer, bufferSize);
   361 
   362     return result;
   363 }
   364 
   365 @end
   366 #ifdef main
   367 #  undef main
   368 #endif
   369 /* Main entry point to executable - should *not* be SDL_main! */
   370     int
   371 main (int argc, char **argv)
   372 {
   373     /* Copy the arguments into a global variable */
   374     /* This is passed if we are launched by double-clicking */
   375     if (argc >= 2 && strncmp (argv[1], "-psn", 4) == 0) {
   376         gArgv = (char **) SDL_malloc (sizeof (char *) * 2);
   377         gArgv[0] = argv[0];
   378         gArgv[1] = NULL;
   379         gArgc = 1;
   380         gFinderLaunch = YES;
   381     } else {
   382         int i;
   383         gArgc = argc;
   384         gArgv = (char **) SDL_malloc (sizeof (char *) * (argc + 1));
   385         for (i = 0; i <= argc; i++)
   386             gArgv[i] = argv[i];
   387         gFinderLaunch = NO;
   388     }
   389 
   390 #if SDL_USE_NIB_FILE
   391   [SDLApplication poseAsClass:[NSApplication class]];
   392     NSApplicationMain (argc, argv);
   393 #else
   394     CustomApplicationMain (argc, argv);
   395 #endif
   396     return 0;
   397 }