Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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 */
  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:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
  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:(id)sdlMain]; // Pierre-Marie Baty -- added type cast
  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. static int IsRootCwd()
  355. {
  356.     char buf[MAXPATHLEN];
  357.     char *cwd = getcwd(buf, sizeof (buf));
  358.     return (cwd && (strcmp(cwd, "/") == 0));
  359. }
  360.  
  361. static int IsFinderLaunch(const int argc, char **argv)
  362. {
  363.     if ((argc == 1) && IsRootCwd()) {
  364.         /* we might still be launched from the Finder; on 10.9+, you might not
  365.         get the -psn command line anymore. Check version, if there's no
  366.         command line, and if our current working directory is "/". */
  367.         return 1;
  368.     }
  369.     return 0;  /* not a Finder launch. */
  370. }
  371.  
  372. /* Main entry point to executable - should *not* be SDL_main! */
  373. int main (int argc, char **argv)
  374. {
  375.     /* Copy the arguments into a global variable */
  376.     /* This is passed if we are launched by double-clicking */
  377.     if ( IsFinderLaunch(argc, argv) ) {
  378.         gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
  379.         gArgv[0] = argv[0];
  380.         gArgv[1] = NULL;
  381.         gArgc = 1;
  382.         gFinderLaunch = YES;
  383.     } else {
  384.         int i;
  385.         gArgc = argc;
  386.         gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
  387.         for (i = 0; i <= argc; i++)
  388.             gArgv[i] = argv[i];
  389.         gFinderLaunch = NO;
  390.     }
  391.  
  392. #if SDL_USE_NIB_FILE
  393.     NSApplicationMain (argc, argv);
  394. #else
  395.     CustomApplicationMain (argc, argv);
  396. #endif
  397.     return 0;
  398. }
  399.  
  400.