SDLMain.m 12 KB

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