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#import "SDL.h"
9#import "SDLMain.h"
10#import <sys/param.h> /* for MAXPATHLEN */
11#import <unistd.h>
12#import <Carbon/Carbon.h>
13
14/* Use this flag to determine whether we use SDLMain.nib or not */
15#define		SDL_USE_NIB_FILE	0
16
17
18static int    gArgc;
19static char  **gArgv;
20static BOOL   gFinderLaunch;
21
22//extern NSAutoreleasePool *pool;
23//void PreInitMac();
24
25void MacMessageBox(const char *msg, const char *caption, unsigned int flags){
26	NSAlert *alert = [[[NSAlert alloc] init] autorelease];
27	[alert addButtonWithTitle:@"OK"];
28	[alert setMessageText:[NSString stringWithCString:caption]];
29	[alert setInformativeText:[NSString stringWithCString:msg]];
30	[alert setAlertStyle:NSWarningAlertStyle];
31	[alert runModal];
32}
33
34#if SDL_USE_NIB_FILE
35/* A helper category for NSString */
36@interface NSString (ReplaceSubString)
37- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
38@end
39#else
40/* An internal Apple class used to setup Apple menus */
41@interface NSAppleMenuController:NSObject {}
42- (void)controlMenu:(NSMenu *)aMenu;
43@end
44#endif
45
46@interface SDLApplication : NSApplication
47@end
48
49@implementation SDLApplication
50/* Invoked from the Quit menu item */
51- (void)terminate:(id)sender
52{
53    /* Post a SDL_QUIT event */
54    SDL_Event event;
55    event.type = SDL_QUIT;
56    SDL_PushEvent(&event);
57}
58@end
59
60
61/* The main class of the application, the application's delegate */
62@implementation SDLMain
63
64/* Set the working directory to the .app's parent directory */
65- (void) setupWorkingDirectory:(BOOL)shouldChdir
66{
67    char parentdir[MAXPATHLEN];
68    char *c;
69
70    strncpy ( parentdir, gArgv[0], sizeof(parentdir) );
71    c = (char*) parentdir;
72
73    while (*c != '\0')     /* go to end */
74        c++;
75
76    while (*c != '/')      /* back up to parent */
77        c--;
78
79    *c++ = '\0';             /* cut off last part (binary name) */
80
81    if (shouldChdir)
82    {
83      assert ( chdir (parentdir) == 0 );   /* chdir to the binary app's parent */
84      assert ( chdir ("../../../") == 0 ); /* chdir to the .app's parent */
85    }
86}
87
88#if SDL_USE_NIB_FILE
89
90/* Fix menu to contain the real app name instead of "SDL App" */
91- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
92{
93    NSRange aRange;
94    NSEnumerator *enumerator;
95    NSMenuItem *menuItem;
96
97    aRange = [[aMenu title] rangeOfString:@"SDL App"];
98    if (aRange.length != 0)
99        [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
100
101    enumerator = [[aMenu itemArray] objectEnumerator];
102    while ((menuItem = [enumerator nextObject]))
103    {
104        aRange = [[menuItem title] rangeOfString:@"SDL App"];
105        if (aRange.length != 0)
106            [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
107        if ([menuItem hasSubmenu])
108            [self fixMenu:[menuItem submenu] withAppName:appName];
109    }
110    [ aMenu sizeToFit ];
111}
112
113#else
114
115void setupAppleMenu(void)
116{
117    /* warning: this code is very odd */
118    NSAppleMenuController *appleMenuController;
119    NSMenu *appleMenu;
120    NSMenuItem *appleMenuItem;
121
122    appleMenuController = [[NSAppleMenuController alloc] init];
123    appleMenu = [[NSMenu alloc] initWithTitle:@""];
124    appleMenuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
125
126    [appleMenuItem setSubmenu:appleMenu];
127
128    /* yes, we do need to add it and then remove it --
129       if you don't add it, it doesn't get displayed
130       if you don't remove it, you have an extra, titleless item in the menubar
131       when you remove it, it appears to stick around
132       very, very odd */
133    [[NSApp mainMenu] addItem:appleMenuItem];
134    [appleMenuController controlMenu:appleMenu];
135    [[NSApp mainMenu] removeItem:appleMenuItem];
136    [appleMenu release];
137    [appleMenuItem release];
138}
139
140/* Create a window menu */
141void setupWindowMenu(void)
142{
143    NSMenu		*windowMenu;
144    NSMenuItem	*windowMenuItem;
145    NSMenuItem	*menuItem;
146
147
148    windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
149
150    /* "Minimize" item */
151    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
152    [windowMenu addItem:menuItem];
153    [menuItem release];
154
155    /* Put menu into the menubar */
156    windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
157    [windowMenuItem setSubmenu:windowMenu];
158    [[NSApp mainMenu] addItem:windowMenuItem];
159
160    /* Tell the application object that this is now the window menu */
161    [NSApp setWindowsMenu:windowMenu];
162
163    /* Finally give up our references to the objects */
164    [windowMenu release];
165    [windowMenuItem release];
166}
167
168/* Replacement for NSApplicationMain */
169void CustomApplicationMain ()
170{
171	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
172    SDLMain				*sdlMain;
173	//PreInitMac();
174
175    /* Ensure the application object is initialised */
176    [SDLApplication sharedApplication];
177
178    /* Set up the menubar */
179    [NSApp setMainMenu:[[NSMenu alloc] init]];
180    setupAppleMenu();
181    setupWindowMenu();
182
183    /* Create SDLMain and make it the app delegate */
184    sdlMain = [[SDLMain alloc] init];
185    [NSApp setDelegate:sdlMain];
186
187    /* Bring the app to foreground */
188    ProcessSerialNumber psn;
189    GetCurrentProcess(&psn);
190    TransformProcessType(&psn,kProcessTransformToForegroundApplication);
191
192    /* Start the main event loop */
193    [NSApp run];
194
195    [sdlMain release];
196    [pool release];
197}
198
199#endif
200
201/* Called when the internal event loop has just started running */
202- (void) applicationDidFinishLaunching: (NSNotification *) note
203{
204    int status;
205
206    /* Set the working directory to the .app's parent directory */
207    [self setupWorkingDirectory:gFinderLaunch];
208
209#if SDL_USE_NIB_FILE
210    /* Set the main menu to contain the real app name instead of "SDL App" */
211    [self fixMenu:[NSApp mainMenu] withAppName:[[NSProcessInfo processInfo] processName]];
212#endif
213
214    /* Hand off to main application code */
215    status = SDL_main (gArgc, gArgv);
216
217    /* We're done, thank you for playing */
218    exit(status);
219}
220@end
221
222
223@implementation NSString (ReplaceSubString)
224
225- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
226{
227    unsigned int bufferSize;
228    unsigned int selfLen = [self length];
229    unsigned int aStringLen = [aString length];
230    unichar *buffer;
231    NSRange localRange;
232    NSString *result;
233
234    bufferSize = selfLen + aStringLen - aRange.length;
235    buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
236
237    /* Get first part into buffer */
238    localRange.location = 0;
239    localRange.length = aRange.location;
240    [self getCharacters:buffer range:localRange];
241
242    /* Get middle part into buffer */
243    localRange.location = 0;
244    localRange.length = aStringLen;
245    [aString getCharacters:(buffer+aRange.location) range:localRange];
246
247    /* Get last part into buffer */
248    localRange.location = aRange.location + aRange.length;
249    localRange.length = selfLen - localRange.location;
250    [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
251
252    /* Build output string */
253    result = [NSString stringWithCharacters:buffer length:bufferSize];
254
255    NSDeallocateMemoryPages(buffer, bufferSize);
256
257    return result;
258}
259
260@end
261
262
263
264#ifdef main
265#  undef main
266#endif
267
268
269/* Main entry point to executable - should *not* be SDL_main! */
270int main (int argc, char **argv)
271{
272
273    /* Copy the arguments into a global variable */
274    int i;
275
276    /* This is passed if we are launched by double-clicking */
277    if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
278        gArgc = 1;
279	gFinderLaunch = YES;
280    } else {
281        gArgc = argc;
282	gFinderLaunch = NO;
283    }
284    gArgv = (char**) malloc (sizeof(*gArgv) * (gArgc+1));
285    assert (gArgv != NULL);
286    for (i = 0; i < gArgc; i++)
287        gArgv[i] = argv[i];
288    gArgv[i] = NULL;
289
290#if SDL_USE_NIB_FILE
291    [SDLApplication poseAsClass:[NSApplication class]];
292    NSApplicationMain ();
293#else
294    CustomApplicationMain ();
295#endif
296    return 0;
297}
298
299
300