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#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 */ 27typedef struct CPSProcessSerNum 28{ 29 UInt32 lo; 30 UInt32 hi; 31} CPSProcessSerNum; 32 33extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); 34extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); 35extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); 36 37#endif /* SDL_USE_CPS */ 38 39static int gArgc; 40static char **gArgv; 41static BOOL gFinderLaunch; 42static BOOL gCalledAppMainline = FALSE; 43 44static 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 126static 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 */ 171static 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 */ 198static 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! */ 355int 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