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