]>
Commit | Line | Data |
---|---|---|
10a0e290 BB |
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/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:(NSAlternateKeyMask|NSCommandKeyMask)]; | |
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:sdlMain]; | |
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 | /* Main entry point to executable - should *not* be SDL_main! */ | |
355 | int main (int argc, char **argv) | |
356 | { | |
357 | /* Copy the arguments into a global variable */ | |
358 | /* This is passed if we are launched by double-clicking */ | |
359 | if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { | |
360 | gArgv = (char **) SDL_malloc(sizeof (char *) * 2); | |
361 | gArgv[0] = argv[0]; | |
362 | gArgv[1] = NULL; | |
363 | gArgc = 1; | |
364 | gFinderLaunch = YES; | |
365 | } else { | |
366 | int i; | |
367 | gArgc = argc; | |
368 | gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); | |
369 | for (i = 0; i <= argc; i++) | |
370 | gArgv[i] = argv[i]; | |
371 | gFinderLaunch = NO; | |
372 | } | |
373 | ||
374 | #if SDL_USE_NIB_FILE | |
375 | NSApplicationMain (argc, argv); | |
376 | #else | |
377 | CustomApplicationMain (argc, argv); | |
378 | #endif | |
379 | return 0; | |
380 | } | |
381 |