SDLMain.m 11 KB

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