SDLMain.m

Go to the documentation of this file.
00001 /* ES40 emulator.
00002  * Copyright (C) 2007-2008 by the ES40 Emulator Project
00003  *
00004  * WWW    : http://es40.org
00005  * E-mail : camiel@camicom.com
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020  *
00021  * Although this is not required, the author would appreciate being notified of, 
00022  * and receiving any modifications you may make to the source code that might serve
00023  * the general public.
00024  */
00025 
00036 /*   SDLMain.m - main entry point for our Cocoa-ized SDL app
00037        Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
00038        Non-NIB-Code & other changes: Max Horn <max@quendi.de>
00039 
00040     Feel free to customize this file to suit your needs
00041 */
00042 
00043 #import "SDL.h"
00044 #import "SDLMain.h"
00045 #import <sys/param.h> /* for MAXPATHLEN */
00046 #import <unistd.h>
00047 
00048 /* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
00049  but the method still is there and works. To avoid warnings, we declare
00050  it ourselves here. */
00051 @interface NSApplication(SDL_Missing_Methods)
00052 - (void)setAppleMenu:(NSMenu *)menu;
00053 @end
00054 
00055 /* Use this flag to determine whether we use SDLMain.nib or not */
00056 #define         SDL_USE_NIB_FILE        0
00057 
00058 /* Use this flag to determine whether we use CPS (docking) or not */
00059 #define         SDL_USE_CPS             1
00060 #ifdef SDL_USE_CPS
00061 /* Portions of CPS.h */
00062 typedef struct CPSProcessSerNum
00063 {
00064         UInt32          lo;
00065         UInt32          hi;
00066 } CPSProcessSerNum;
00067 
00068 extern OSErr    CPSGetCurrentProcess( CPSProcessSerNum *psn);
00069 extern OSErr    CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
00070 extern OSErr    CPSSetFrontProcess( CPSProcessSerNum *psn);
00071 
00072 #endif /* SDL_USE_CPS */
00073 
00074 static int    gArgc;
00075 static char  **gArgv;
00076 static BOOL   gFinderLaunch;
00077 static BOOL   gCalledAppMainline = FALSE;
00078 
00079 static NSString *getApplicationName(void)
00080 {
00081     NSDictionary *dict;
00082     NSString *appName = 0;
00083 
00084     /* Determine the application name */
00085     dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
00086     if (dict)
00087         appName = [dict objectForKey: @"CFBundleName"];
00088     
00089     if (![appName length])
00090         appName = [[NSProcessInfo processInfo] processName];
00091 
00092     return appName;
00093 }
00094 
00095 #if SDL_USE_NIB_FILE
00096 /* A helper category for NSString */
00097 @interface NSString (ReplaceSubString)
00098 - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
00099 @end
00100 #endif
00101 
00102 @interface SDLApplication : NSApplication
00103 @end
00104 
00105 @implementation SDLApplication
00106 /* Invoked from the Quit menu item */
00107 - (void)terminate:(id)sender
00108 {
00109     /* Post a SDL_QUIT event */
00110     SDL_Event event;
00111     event.type = SDL_QUIT;
00112     SDL_PushEvent(&event);
00113 }
00114 @end
00115 
00116 /* The main class of the application, the application's delegate */
00117 @implementation SDLMain
00118 
00119 - (IBAction)prefsMenu:(id)sender
00120 {
00121     printf ("prefs menu\n");
00122 }
00123 
00124 - (IBAction)newGame:(id)sender
00125 {
00126     printf ("new game\n");
00127     
00128     NSRunAlertPanel (@"Get ready to blow up some... stuff!", 
00129         @"Click OK to begin total carnage. Click Cancel to prevent total carnage.",                             @"OK", @"Cancel", nil);
00130 }
00131 
00132 - (IBAction)openGame:(id)sender
00133 {
00134     NSString *path = nil;
00135     NSOpenPanel *openPanel = [ NSOpenPanel openPanel ];
00136     
00137     if ( [ openPanel runModalForDirectory:nil
00138              file:@"SavedGame" types:nil ] ) {
00139              
00140         path = [ [ openPanel filenames ] objectAtIndex:0 ];
00141     }
00142     
00143     /* printf ("open game: %s\n", [ path cString ]); */
00144     NSLog (@"open game: %@", path);
00145 }
00146 
00147 - (IBAction)saveGame:(id)sender
00148 {
00149     NSString *path = nil;
00150     NSSavePanel *savePanel = [ NSSavePanel savePanel ];
00151     
00152     if ( [ savePanel runModalForDirectory:nil
00153            file:@"SaveGameFile" ] ) {
00154             
00155         path = [ savePanel filename ];
00156     }
00157     
00158     /* printf ("save game: %s\n", [ path cString ]); */
00159     NSLog (@"save game: %@", path);
00160 }
00161 
00162 - (IBAction)saveGameAs:(id)sender
00163 {
00164     printf ("save game as\n");
00165 }
00166 
00167 - (IBAction)help:(id)sender
00168 {
00169     NSRunAlertPanel (@"Oh help, where have ye gone?", 
00170         @"Sorry, there is no help available.\n\nThis message brought to you by We Don't Document, Inc.\n\n", @"Rats", @"Good, I never read it anyway", nil);
00171 }
00172 
00173 
00174 /* Set the working directory to the .app's parent directory */
00175 - (void) setupWorkingDirectory:(BOOL)shouldChdir
00176 {
00177     if (shouldChdir)
00178     {
00179         char parentdir;
00180                 CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
00181                 CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
00182                 if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
00183                 assert ( chdir (parentdir) == 0 );   /* chdir to the binary app's parent */
00184                 }
00185                 CFRelease(url);
00186                 CFRelease(url2);
00187         }
00188 
00189 }
00190 
00191 #if SDL_USE_NIB_FILE
00192 
00193 /* Fix menu to contain the real app name instead of "SDL App" */
00194 - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
00195 {
00196     NSRange aRange;
00197     NSEnumerator *enumerator;
00198     NSMenuItem *menuItem;
00199 
00200     aRange = [[aMenu title] rangeOfString:@"SDL App"];
00201     if (aRange.length != 0)
00202         [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
00203 
00204     enumerator = [[aMenu itemArray] objectEnumerator];
00205     while ((menuItem = [enumerator nextObject]))
00206     {
00207         aRange = [[menuItem title] rangeOfString:@"SDL App"];
00208         if (aRange.length != 0)
00209             [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
00210         if ([menuItem hasSubmenu])
00211             [self fixMenu:[menuItem submenu] withAppName:appName];
00212     }
00213     [ aMenu sizeToFit ];
00214 }
00215 
00216 #else
00217 
00218 static void setApplicationMenu(void)
00219 {
00220     /* warning: this code is very odd */
00221     NSMenu *appleMenu;
00222     NSMenuItem *menuItem;
00223     NSString *title;
00224     NSString *appName;
00225     
00226     appName = getApplicationName();
00227     appleMenu = [[NSMenu alloc] initWithTitle:@""];
00228     
00229     /* Add menu items */
00230     title = [@"About " stringByAppendingString:appName];
00231     [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
00232 
00233     [appleMenu addItem:[NSMenuItem separatorItem]];
00234 
00235     title = [@"Hide " stringByAppendingString:appName];
00236     [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
00237 
00238     menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
00239     [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
00240 
00241     [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
00242 
00243     [appleMenu addItem:[NSMenuItem separatorItem]];
00244 
00245     title = [@"Quit " stringByAppendingString:appName];
00246     [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
00247 
00248     
00249     /* Put menu into the menubar */
00250     menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
00251     [menuItem setSubmenu:appleMenu];
00252     [[NSApp mainMenu] addItem:menuItem];
00253 
00254     /* Tell the application object that this is now the application menu */
00255     [NSApp setAppleMenu:appleMenu];
00256 
00257     /* Finally give up our references to the objects */
00258     [appleMenu release];
00259     [menuItem release];
00260 }
00261 
00262 /* Create a window menu */
00263 static void setupWindowMenu(void)
00264 {
00265     NSMenu      *windowMenu;
00266     NSMenuItem  *windowMenuItem;
00267     NSMenuItem  *menuItem;
00268 
00269     windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
00270     
00271     /* "Minimize" item */
00272     menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
00273     [windowMenu addItem:menuItem];
00274     [menuItem release];
00275     
00276     /* Put menu into the menubar */
00277     windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
00278     [windowMenuItem setSubmenu:windowMenu];
00279     [[NSApp mainMenu] addItem:windowMenuItem];
00280     
00281     /* Tell the application object that this is now the window menu */
00282     [NSApp setWindowsMenu:windowMenu];
00283 
00284     /* Finally give up our references to the objects */
00285     [windowMenu release];
00286     [windowMenuItem release];
00287 }
00288 
00289 /* Replacement for NSApplicationMain */
00290 static void CustomApplicationMain (int argc, char **argv)
00291 {
00292     NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];
00293     SDLMain                             *sdlMain;
00294 
00295     /* Ensure the application object is initialised */
00296     [SDLApplication sharedApplication];
00297     
00298 #ifdef SDL_USE_CPS
00299     {
00300         CPSProcessSerNum PSN;
00301         /* Tell the dock about us */
00302         if (!CPSGetCurrentProcess(&PSN))
00303             if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
00304                 if (!CPSSetFrontProcess(&PSN))
00305                     [SDLApplication sharedApplication];
00306     }
00307 #endif /* SDL_USE_CPS */
00308 
00309     /* Set up the menubar */
00310     [NSApp setMainMenu:[[NSMenu alloc] init]];
00311     setApplicationMenu();
00312     setupWindowMenu();
00313 
00314     /* Create SDLMain and make it the app delegate */
00315     sdlMain = [[SDLMain alloc] init];
00316     [NSApp setDelegate:sdlMain];
00317     
00318     /* Start the main event loop */
00319     [NSApp run];
00320     
00321     [sdlMain release];
00322     [pool release];
00323 }
00324 
00325 #endif
00326 
00327 
00328 /*
00329  * Catch document open requests...this lets us notice files when the app
00330  *  was launched by double-clicking a document, or when a document was
00331  *  dragged/dropped on the app's icon. You need to have a
00332  *  CFBundleDocumentsType section in your Info.plist to get this message,
00333  *  apparently.
00334  *
00335  * Files are added to gArgv, so to the app, they'll look like command line
00336  *  arguments. Previously, apps launched from the finder had nothing but
00337  *  an argv[0].
00338  *
00339  * This message may be received multiple times to open several docs on launch.
00340  *
00341  * This message is ignored once the app's mainline has been called.
00342  */
00343 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
00344 {
00345     const char *temparg;
00346     size_t arglen;
00347     char *arg;
00348     char **newargv;
00349 
00350     if (!gFinderLaunch)  /* MacOS is passing command line args. */
00351         return FALSE;
00352 
00353     if (gCalledAppMainline)  /* app has started, ignore this document. */
00354         return FALSE;
00355 
00356     temparg = [filename UTF8String];
00357     arglen = SDL_strlen(temparg) + 1;
00358     arg = (char *) SDL_malloc(arglen);
00359     if (arg == NULL)
00360         return FALSE;
00361 
00362     newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
00363     if (newargv == NULL)
00364     {
00365         SDL_free(arg);
00366         return FALSE;
00367     }
00368     gArgv = newargv;
00369 
00370     SDL_strlcpy(arg, temparg, arglen);
00371     gArgv = arg;
00372     gArgv = NULL;
00373     return TRUE;
00374 }
00375 
00376 
00377 /* Called when the internal event loop has just started running */
00378 - (void) applicationDidFinishLaunching: (NSNotification *) note
00379 {
00380     int status;
00381 
00382     /* Set the working directory to the .app's parent directory */
00383     [self setupWorkingDirectory:gFinderLaunch];
00384 
00385 #if SDL_USE_NIB_FILE
00386     /* Set the main menu to contain the real app name instead of "SDL App" */
00387     [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
00388 #endif
00389 
00390     /* Hand off to main application code */
00391     gCalledAppMainline = TRUE;
00392     status = SDL_main (gArgc, gArgv);
00393 
00394     /* We're done, thank you for playing */
00395     exit(status);
00396 }
00397 @end
00398 
00399 
00400 @implementation NSString (ReplaceSubString)
00401 
00402 - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
00403 {
00404     unsigned int bufferSize;
00405     unsigned int selfLen = [self length];
00406     unsigned int aStringLen = [aString length];
00407     unichar *buffer;
00408     NSRange localRange;
00409     NSString *result;
00410 
00411     bufferSize = selfLen + aStringLen - aRange.length;
00412     buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
00413     
00414     /* Get first part into buffer */
00415     localRange.location = 0;
00416     localRange.length = aRange.location;
00417     [self getCharacters:buffer range:localRange];
00418     
00419     /* Get middle part into buffer */
00420     localRange.location = 0;
00421     localRange.length = aStringLen;
00422     [aString getCharacters:(buffer+aRange.location) range:localRange];
00423      
00424     /* Get last part into buffer */
00425     localRange.location = aRange.location + aRange.length;
00426     localRange.length = selfLen - localRange.location;
00427     [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
00428     
00429     /* Build output string */
00430     result = [NSString stringWithCharacters:buffer length:bufferSize];
00431     
00432     NSDeallocateMemoryPages(buffer, bufferSize);
00433     
00434     return result;
00435 }
00436 
00437 @end
00438 
00439 
00440 
00441 #ifdef main
00442 #  undef main
00443 #endif
00444 
00445 
00446 /* Main entry point to executable - should *not* be SDL_main! */
00447 int main (int argc, char **argv)
00448 {
00449     /* Copy the arguments into a global variable */
00450     /* This is passed if we are launched by double-clicking */
00451     if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
00452         gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
00453         gArgv = argv;
00454         gArgv = NULL;
00455         gArgc = 1;
00456         gFinderLaunch = YES;
00457     } else {
00458         int i;
00459         gArgc = argc;
00460         gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
00461         for (i = 0; i <= argc; i++)
00462             gArgv[i] = argv[i];
00463         gFinderLaunch = NO;
00464     }
00465 
00466 #if SDL_USE_NIB_FILE
00467     [SDLApplication poseAsClass:[NSApplication class]];
00468     NSApplicationMain (argc, argv);
00469 #else
00470     CustomApplicationMain (argc, argv);
00471 #endif
00472     return 0;
00473 }

SourceForge.net Logo
Project space on SourceForge.net