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