1999
|
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 |
|
|
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 |
NSDictionary *dict;
|
|
47 |
NSString *appName = 0;
|
|
48 |
|
|
49 |
/* Determine the application name */
|
|
50 |
dict = (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 SDLApplication : NSApplication
|
|
68 |
@end
|
|
69 |
|
|
70 |
@implementation 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, true, (UInt8 *)parentdir, MAXPATHLEN)) {
|
|
93 |
assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
|
|
94 |
}
|
|
95 |
CFRelease(url);
|
|
96 |
CFRelease(url2);
|
|
97 |
}
|
|
98 |
|
|
99 |
}
|
|
100 |
|
|
101 |
#if SDL_USE_NIB_FILE
|
|
102 |
|
|
103 |
/* Fix menu to contain the real app name instead of "SDL App" */
|
|
104 |
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
|
|
105 |
{
|
|
106 |
NSRange aRange;
|
|
107 |
NSEnumerator *enumerator;
|
|
108 |
NSMenuItem *menuItem;
|
|
109 |
|
|
110 |
aRange = [[aMenu title] rangeOfString:@"SDL App"];
|
|
111 |
if (aRange.length != 0)
|
|
112 |
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
|
|
113 |
|
|
114 |
enumerator = [[aMenu itemArray] objectEnumerator];
|
|
115 |
while ((menuItem = [enumerator nextObject]))
|
|
116 |
{
|
|
117 |
aRange = [[menuItem title] rangeOfString:@"SDL App"];
|
|
118 |
if (aRange.length != 0)
|
|
119 |
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
|
|
120 |
if ([menuItem hasSubmenu])
|
|
121 |
[self fixMenu:[menuItem submenu] withAppName:appName];
|
|
122 |
}
|
|
123 |
[ aMenu sizeToFit ];
|
|
124 |
}
|
|
125 |
|
|
126 |
#else
|
|
127 |
|
|
128 |
static void setApplicationMenu(void)
|
|
129 |
{
|
|
130 |
/* warning: this code is very odd */
|
|
131 |
NSMenu *appleMenu;
|
|
132 |
NSMenuItem *menuItem;
|
|
133 |
NSString *title;
|
|
134 |
NSString *appName;
|
|
135 |
|
|
136 |
appName = getApplicationName();
|
|
137 |
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
|
138 |
|
|
139 |
/* Add menu items */
|
|
140 |
title = [@"About " stringByAppendingString:appName];
|
|
141 |
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
|
142 |
|
|
143 |
[appleMenu addItem:[NSMenuItem separatorItem]];
|
|
144 |
|
|
145 |
title = [@"Hide " stringByAppendingString:appName];
|
|
146 |
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
|
147 |
|
|
148 |
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
|
149 |
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
|
150 |
|
|
151 |
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
|
152 |
|
|
153 |
[appleMenu addItem:[NSMenuItem separatorItem]];
|
|
154 |
|
|
155 |
title = [@"Quit " stringByAppendingString:appName];
|
|
156 |
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
|
157 |
|
|
158 |
|
|
159 |
/* Put menu into the menubar */
|
|
160 |
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
|
161 |
[menuItem setSubmenu:appleMenu];
|
|
162 |
[[NSApp mainMenu] addItem:menuItem];
|
|
163 |
|
|
164 |
/* Tell the application object that this is now the application menu */
|
|
165 |
[NSApp setAppleMenu:appleMenu];
|
|
166 |
|
|
167 |
/* Finally give up our references to the objects */
|
|
168 |
[appleMenu release];
|
|
169 |
[menuItem release];
|
|
170 |
}
|
|
171 |
|
|
172 |
/* Create a window menu */
|
|
173 |
static void setupWindowMenu(void)
|
|
174 |
{
|
|
175 |
NSMenu *windowMenu;
|
|
176 |
NSMenuItem *windowMenuItem;
|
|
177 |
NSMenuItem *menuItem;
|
|
178 |
|
|
179 |
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
|
180 |
|
|
181 |
/* "Minimize" item */
|
|
182 |
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
|
183 |
[windowMenu addItem:menuItem];
|
|
184 |
[menuItem release];
|
|
185 |
|
|
186 |
/* Put menu into the menubar */
|
|
187 |
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
|
188 |
[windowMenuItem setSubmenu:windowMenu];
|
|
189 |
[[NSApp mainMenu] addItem:windowMenuItem];
|
|
190 |
|
|
191 |
/* Tell the application object that this is now the window menu */
|
|
192 |
[NSApp setWindowsMenu:windowMenu];
|
|
193 |
|
|
194 |
/* Finally give up our references to the objects */
|
|
195 |
[windowMenu release];
|
|
196 |
[windowMenuItem release];
|
|
197 |
}
|
|
198 |
|
|
199 |
/* Replacement for NSApplicationMain */
|
|
200 |
static void CustomApplicationMain (int argc, char **argv)
|
|
201 |
{
|
|
202 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
203 |
SDLMain *sdlMain;
|
|
204 |
|
|
205 |
/* Ensure the application object is initialised */
|
|
206 |
[SDLApplication sharedApplication];
|
|
207 |
|
|
208 |
#ifdef SDL_USE_CPS
|
|
209 |
{
|
|
210 |
CPSProcessSerNum PSN;
|
|
211 |
/* Tell the dock about us */
|
|
212 |
if (!CPSGetCurrentProcess(&PSN))
|
|
213 |
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
|
214 |
if (!CPSSetFrontProcess(&PSN))
|
|
215 |
[SDLApplication sharedApplication];
|
|
216 |
}
|
|
217 |
#endif /* SDL_USE_CPS */
|
|
218 |
|
|
219 |
/* Set up the menubar */
|
|
220 |
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
|
221 |
setApplicationMenu();
|
|
222 |
setupWindowMenu();
|
|
223 |
|
|
224 |
/* Create SDLMain and make it the app delegate */
|
|
225 |
sdlMain = [[SDLMain alloc] init];
|
|
226 |
[NSApp setDelegate:sdlMain];
|
|
227 |
|
|
228 |
/* Start the main event loop */
|
|
229 |
[NSApp run];
|
|
230 |
|
|
231 |
[sdlMain release];
|
|
232 |
[pool release];
|
|
233 |
}
|
|
234 |
|
|
235 |
#endif
|
|
236 |
|
|
237 |
|
|
238 |
/*
|
|
239 |
* Catch document open requests...this lets us notice files when the app
|
|
240 |
* was launched by double-clicking a document, or when a document was
|
|
241 |
* dragged/dropped on the app's icon. You need to have a
|
|
242 |
* CFBundleDocumentsType section in your Info.plist to get this message,
|
|
243 |
* apparently.
|
|
244 |
*
|
|
245 |
* Files are added to gArgv, so to the app, they'll look like command line
|
|
246 |
* arguments. Previously, apps launched from the finder had nothing but
|
|
247 |
* an argv[0].
|
|
248 |
*
|
|
249 |
* This message may be received multiple times to open several docs on launch.
|
|
250 |
*
|
|
251 |
* This message is ignored once the app's mainline has been called.
|
|
252 |
*/
|
|
253 |
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
|
254 |
{
|
|
255 |
const char *temparg;
|
|
256 |
size_t arglen;
|
|
257 |
char *arg;
|
|
258 |
char **newargv;
|
|
259 |
|
|
260 |
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
|
261 |
return FALSE;
|
|
262 |
|
|
263 |
if (gCalledAppMainline) /* app has started, ignore this document. */
|
|
264 |
return FALSE;
|
|
265 |
|
|
266 |
temparg = [filename UTF8String];
|
|
267 |
arglen = SDL_strlen(temparg) + 1;
|
|
268 |
arg = (char *) SDL_malloc(arglen);
|
|
269 |
if (arg == NULL)
|
|
270 |
return FALSE;
|
|
271 |
|
|
272 |
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
|
273 |
if (newargv == NULL)
|
|
274 |
{
|
|
275 |
SDL_free(arg);
|
|
276 |
return FALSE;
|
|
277 |
}
|
|
278 |
gArgv = newargv;
|
|
279 |
|
|
280 |
SDL_strlcpy(arg, temparg, arglen);
|
|
281 |
gArgv[gArgc++] = arg;
|
|
282 |
gArgv[gArgc] = NULL;
|
|
283 |
return TRUE;
|
|
284 |
}
|
|
285 |
|
|
286 |
|
|
287 |
/* Called when the internal event loop has just started running */
|
|
288 |
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
|
289 |
{
|
|
290 |
int status;
|
|
291 |
|
|
292 |
/* Set the working directory to the .app's parent directory */
|
|
293 |
[self setupWorkingDirectory:gFinderLaunch];
|
|
294 |
|
|
295 |
#if SDL_USE_NIB_FILE
|
|
296 |
/* Set the main menu to contain the real app name instead of "SDL App" */
|
|
297 |
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
|
|
298 |
#endif
|
|
299 |
|
|
300 |
/* Hand off to main application code */
|
|
301 |
gCalledAppMainline = TRUE;
|
|
302 |
status = SDL_main (gArgc, gArgv);
|
|
303 |
|
|
304 |
/* We're done, thank you for playing */
|
|
305 |
exit(status);
|
|
306 |
}
|
|
307 |
@end
|
|
308 |
|
|
309 |
|
|
310 |
@implementation NSString (ReplaceSubString)
|
|
311 |
|
|
312 |
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
|
313 |
{
|
|
314 |
unsigned int bufferSize;
|
|
315 |
unsigned int selfLen = [self length];
|
|
316 |
unsigned int aStringLen = [aString length];
|
|
317 |
unichar *buffer;
|
|
318 |
NSRange localRange;
|
|
319 |
NSString *result;
|
|
320 |
|
|
321 |
bufferSize = selfLen + aStringLen - aRange.length;
|
|
322 |
buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
|
323 |
|
|
324 |
/* Get first part into buffer */
|
|
325 |
localRange.location = 0;
|
|
326 |
localRange.length = aRange.location;
|
|
327 |
[self getCharacters:buffer range:localRange];
|
|
328 |
|
|
329 |
/* Get middle part into buffer */
|
|
330 |
localRange.location = 0;
|
|
331 |
localRange.length = aStringLen;
|
|
332 |
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
|
333 |
|
|
334 |
/* Get last part into buffer */
|
|
335 |
localRange.location = aRange.location + aRange.length;
|
|
336 |
localRange.length = selfLen - localRange.location;
|
|
337 |
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
|
338 |
|
|
339 |
/* Build output string */
|
|
340 |
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
|
341 |
|
|
342 |
NSDeallocateMemoryPages(buffer, bufferSize);
|
|
343 |
|
|
344 |
return result;
|
|
345 |
}
|
|
346 |
|
|
347 |
@end
|
|
348 |
|
|
349 |
|
|
350 |
|
|
351 |
#ifdef main
|
|
352 |
# undef main
|
|
353 |
#endif
|
|
354 |
|
|
355 |
|
|
356 |
/* Main entry point to executable - should *not* be SDL_main! */
|
|
357 |
int main (int argc, char **argv)
|
|
358 |
{
|
|
359 |
/* Copy the arguments into a global variable */
|
|
360 |
/* This is passed if we are launched by double-clicking */
|
|
361 |
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
|
362 |
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
|
363 |
gArgv[0] = argv[0];
|
|
364 |
gArgv[1] = NULL;
|
|
365 |
gArgc = 1;
|
|
366 |
gFinderLaunch = YES;
|
|
367 |
} else {
|
|
368 |
int i;
|
|
369 |
gArgc = argc;
|
|
370 |
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
|
371 |
for (i = 0; i <= argc; i++)
|
|
372 |
gArgv[i] = argv[i];
|
|
373 |
gFinderLaunch = NO;
|
|
374 |
}
|
|
375 |
|
|
376 |
#if SDL_USE_NIB_FILE
|
|
377 |
[SDLApplication poseAsClass:[NSApplication class]];
|
|
378 |
NSApplicationMain (argc, argv);
|
|
379 |
#else
|
|
380 |
CustomApplicationMain (argc, argv);
|
|
381 |
#endif
|
|
382 |
return 0;
|
|
383 |
}
|
|
384 |
|