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