Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 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 |