Mercurial > sdl-ios-xcode
comparison Xcode/TemplatesForXcode/SDL Custom Cocoa Application/SDLMain.m @ 2207:d63e9f5944ae
Unpacked project archives to get individual file history in subversion
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 21 Jul 2007 17:09:01 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2206:ca7d2227d630 | 2207:d63e9f5944ae |
---|---|
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 "MyController.h" | |
11 #import <sys/param.h> /* for MAXPATHLEN */ | |
12 #import <unistd.h> | |
13 | |
14 /* For some reaon, Apple removed setAppleMenu from the headers in 10.4, | |
15 but the method still is there and works. To avoid warnings, we declare | |
16 it ourselves here. */ | |
17 @interface NSApplication(SDL_Missing_Methods) | |
18 - (void)setAppleMenu:(NSMenu *)menu; | |
19 @end | |
20 | |
21 /* Use this flag to determine whether we use SDLMain.nib or not */ | |
22 #define SDL_USE_NIB_FILE 1 | |
23 | |
24 /* Use this flag to determine whether we use CPS (docking) or not */ | |
25 #define SDL_USE_CPS 1 | |
26 #ifdef SDL_USE_CPS | |
27 /* Portions of CPS.h */ | |
28 typedef struct CPSProcessSerNum | |
29 { | |
30 UInt32 lo; | |
31 UInt32 hi; | |
32 } CPSProcessSerNum; | |
33 | |
34 extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); | |
35 extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); | |
36 extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); | |
37 | |
38 #endif /* SDL_USE_CPS */ | |
39 | |
40 static int gArgc; | |
41 static char **gArgv; | |
42 static BOOL gFinderLaunch; | |
43 static BOOL gCalledAppMainline = FALSE; | |
44 | |
45 static NSString *getApplicationName(void) | |
46 { | |
47 NSDictionary *dict; | |
48 NSString *appName = 0; | |
49 | |
50 /* Determine the application name */ | |
51 dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); | |
52 if (dict) | |
53 appName = [dict objectForKey: @"CFBundleName"]; | |
54 | |
55 if (![appName length]) | |
56 appName = [[NSProcessInfo processInfo] processName]; | |
57 | |
58 return appName; | |
59 } | |
60 | |
61 #if SDL_USE_NIB_FILE | |
62 /* A helper category for NSString */ | |
63 @interface NSString (ReplaceSubString) | |
64 - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; | |
65 @end | |
66 #endif | |
67 | |
68 @interface SDLApplication : NSApplication | |
69 @end | |
70 | |
71 @implementation SDLApplication | |
72 /* Invoked from the Quit menu item */ | |
73 - (void)terminate:(id)sender | |
74 { | |
75 /* Post a SDL_QUIT event */ | |
76 SDL_Event event; | |
77 event.type = SDL_QUIT; | |
78 SDL_PushEvent(&event); | |
79 } | |
80 @end | |
81 | |
82 /* The main class of the application, the application's delegate */ | |
83 @implementation SDLMain | |
84 | |
85 /* Set the working directory to the .app's parent directory */ | |
86 - (void) setupWorkingDirectory:(BOOL)shouldChdir | |
87 { | |
88 if (shouldChdir) | |
89 { | |
90 char parentdir[MAXPATHLEN]; | |
91 CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); | |
92 CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); | |
93 if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) { | |
94 assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ | |
95 } | |
96 CFRelease(url); | |
97 CFRelease(url2); | |
98 } | |
99 | |
100 } | |
101 | |
102 #if SDL_USE_NIB_FILE | |
103 | |
104 /* Fix menu to contain the real app name instead of "SDL App" */ | |
105 - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName | |
106 { | |
107 NSRange aRange; | |
108 NSEnumerator *enumerator; | |
109 NSMenuItem *menuItem; | |
110 | |
111 aRange = [[aMenu title] rangeOfString:@"SDL App"]; | |
112 if (aRange.length != 0) | |
113 [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; | |
114 | |
115 enumerator = [[aMenu itemArray] objectEnumerator]; | |
116 while ((menuItem = [enumerator nextObject])) | |
117 { | |
118 aRange = [[menuItem title] rangeOfString:@"SDL App"]; | |
119 if (aRange.length != 0) | |
120 [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; | |
121 if ([menuItem hasSubmenu]) | |
122 [self fixMenu:[menuItem submenu] withAppName:appName]; | |
123 } | |
124 [ aMenu sizeToFit ]; | |
125 } | |
126 | |
127 #else | |
128 | |
129 static void setApplicationMenu(void) | |
130 { | |
131 /* warning: this code is very odd */ | |
132 NSMenu *appleMenu; | |
133 NSMenuItem *menuItem; | |
134 NSString *title; | |
135 NSString *appName; | |
136 | |
137 appName = getApplicationName(); | |
138 appleMenu = [[NSMenu alloc] initWithTitle:@""]; | |
139 | |
140 /* Add menu items */ | |
141 title = [@"About " stringByAppendingString:appName]; | |
142 [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; | |
143 | |
144 [appleMenu addItem:[NSMenuItem separatorItem]]; | |
145 | |
146 title = [@"Hide " stringByAppendingString:appName]; | |
147 [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; | |
148 | |
149 menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; | |
150 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; | |
151 | |
152 [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; | |
153 | |
154 [appleMenu addItem:[NSMenuItem separatorItem]]; | |
155 | |
156 title = [@"Quit " stringByAppendingString:appName]; | |
157 [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; | |
158 | |
159 | |
160 /* Put menu into the menubar */ | |
161 menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; | |
162 [menuItem setSubmenu:appleMenu]; | |
163 [[NSApp mainMenu] addItem:menuItem]; | |
164 | |
165 /* Tell the application object that this is now the application menu */ | |
166 [NSApp setAppleMenu:appleMenu]; | |
167 | |
168 /* Finally give up our references to the objects */ | |
169 [appleMenu release]; | |
170 [menuItem release]; | |
171 } | |
172 | |
173 /* Create a window menu */ | |
174 static void setupWindowMenu(void) | |
175 { | |
176 NSMenu *windowMenu; | |
177 NSMenuItem *windowMenuItem; | |
178 NSMenuItem *menuItem; | |
179 | |
180 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; | |
181 | |
182 /* "Minimize" item */ | |
183 menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; | |
184 [windowMenu addItem:menuItem]; | |
185 [menuItem release]; | |
186 | |
187 /* Put menu into the menubar */ | |
188 windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; | |
189 [windowMenuItem setSubmenu:windowMenu]; | |
190 [[NSApp mainMenu] addItem:windowMenuItem]; | |
191 | |
192 /* Tell the application object that this is now the window menu */ | |
193 [NSApp setWindowsMenu:windowMenu]; | |
194 | |
195 /* Finally give up our references to the objects */ | |
196 [windowMenu release]; | |
197 [windowMenuItem release]; | |
198 } | |
199 | |
200 /* Replacement for NSApplicationMain */ | |
201 static void CustomApplicationMain (int argc, char **argv) | |
202 { | |
203 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
204 SDLMain *sdlMain; | |
205 | |
206 /* Ensure the application object is initialised */ | |
207 [SDLApplication sharedApplication]; | |
208 | |
209 #ifdef SDL_USE_CPS | |
210 { | |
211 CPSProcessSerNum PSN; | |
212 /* Tell the dock about us */ | |
213 if (!CPSGetCurrentProcess(&PSN)) | |
214 if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) | |
215 if (!CPSSetFrontProcess(&PSN)) | |
216 [SDLApplication sharedApplication]; | |
217 } | |
218 #endif /* SDL_USE_CPS */ | |
219 | |
220 /* Set up the menubar */ | |
221 [NSApp setMainMenu:[[NSMenu alloc] init]]; | |
222 setApplicationMenu(); | |
223 setupWindowMenu(); | |
224 | |
225 /* Create SDLMain and make it the app delegate */ | |
226 sdlMain = [[SDLMain alloc] init]; | |
227 [NSApp setDelegate:sdlMain]; | |
228 | |
229 /* Start the main event loop */ | |
230 [NSApp run]; | |
231 | |
232 [sdlMain release]; | |
233 [pool release]; | |
234 } | |
235 | |
236 #endif | |
237 | |
238 | |
239 /* | |
240 * Catch document open requests...this lets us notice files when the app | |
241 * was launched by double-clicking a document, or when a document was | |
242 * dragged/dropped on the app's icon. You need to have a | |
243 * CFBundleDocumentsType section in your Info.plist to get this message, | |
244 * apparently. | |
245 * | |
246 * Files are added to gArgv, so to the app, they'll look like command line | |
247 * arguments. Previously, apps launched from the finder had nothing but | |
248 * an argv[0]. | |
249 * | |
250 * This message may be received multiple times to open several docs on launch. | |
251 * | |
252 * This message is ignored once the app's mainline has been called. | |
253 */ | |
254 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename | |
255 { | |
256 const char *temparg; | |
257 size_t arglen; | |
258 char *arg; | |
259 char **newargv; | |
260 | |
261 if (!gFinderLaunch) /* MacOS is passing command line args. */ | |
262 return FALSE; | |
263 | |
264 if (gCalledAppMainline) /* app has started, ignore this document. */ | |
265 return FALSE; | |
266 | |
267 temparg = [filename UTF8String]; | |
268 arglen = SDL_strlen(temparg) + 1; | |
269 arg = (char *) SDL_malloc(arglen); | |
270 if (arg == NULL) | |
271 return FALSE; | |
272 | |
273 newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); | |
274 if (newargv == NULL) | |
275 { | |
276 SDL_free(arg); | |
277 return FALSE; | |
278 } | |
279 gArgv = newargv; | |
280 | |
281 SDL_strlcpy(arg, temparg, arglen); | |
282 gArgv[gArgc++] = arg; | |
283 gArgv[gArgc] = NULL; | |
284 return TRUE; | |
285 } | |
286 | |
287 | |
288 /* Called when the internal event loop has just started running */ | |
289 - (void) applicationDidFinishLaunching: (NSNotification *) note | |
290 { | |
291 int status; | |
292 | |
293 gController = _controller; | |
294 | |
295 /* Set the working directory to the .app's parent directory */ | |
296 [self setupWorkingDirectory:gFinderLaunch]; | |
297 | |
298 #if SDL_USE_NIB_FILE | |
299 /* Set the main menu to contain the real app name instead of "SDL App" */ | |
300 [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; | |
301 #endif | |
302 | |
303 /* Hand off to main application code */ | |
304 gCalledAppMainline = TRUE; | |
305 status = SDL_main (gArgc, gArgv); | |
306 | |
307 /* We're done, thank you for playing */ | |
308 exit(status); | |
309 } | |
310 @end | |
311 | |
312 | |
313 @implementation NSString (ReplaceSubString) | |
314 | |
315 - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString | |
316 { | |
317 unsigned int bufferSize; | |
318 unsigned int selfLen = [self length]; | |
319 unsigned int aStringLen = [aString length]; | |
320 unichar *buffer; | |
321 NSRange localRange; | |
322 NSString *result; | |
323 | |
324 bufferSize = selfLen + aStringLen - aRange.length; | |
325 buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar)); | |
326 | |
327 /* Get first part into buffer */ | |
328 localRange.location = 0; | |
329 localRange.length = aRange.location; | |
330 [self getCharacters:buffer range:localRange]; | |
331 | |
332 /* Get middle part into buffer */ | |
333 localRange.location = 0; | |
334 localRange.length = aStringLen; | |
335 [aString getCharacters:(buffer+aRange.location) range:localRange]; | |
336 | |
337 /* Get last part into buffer */ | |
338 localRange.location = aRange.location + aRange.length; | |
339 localRange.length = selfLen - localRange.location; | |
340 [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; | |
341 | |
342 /* Build output string */ | |
343 result = [NSString stringWithCharacters:buffer length:bufferSize]; | |
344 | |
345 NSDeallocateMemoryPages(buffer, bufferSize); | |
346 | |
347 return result; | |
348 } | |
349 | |
350 @end | |
351 | |
352 | |
353 | |
354 #ifdef main | |
355 # undef main | |
356 #endif | |
357 | |
358 | |
359 /* Main entry point to executable - should *not* be SDL_main! */ | |
360 int main (int argc, char **argv) | |
361 { | |
362 /* Copy the arguments into a global variable */ | |
363 /* This is passed if we are launched by double-clicking */ | |
364 if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { | |
365 gArgv = (char **) SDL_malloc(sizeof (char *) * 2); | |
366 gArgv[0] = argv[0]; | |
367 gArgv[1] = NULL; | |
368 gArgc = 1; | |
369 gFinderLaunch = YES; | |
370 } else { | |
371 int i; | |
372 gArgc = argc; | |
373 gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); | |
374 for (i = 0; i <= argc; i++) | |
375 gArgv[i] = argv[i]; | |
376 gFinderLaunch = NO; | |
377 } | |
378 | |
379 #if SDL_USE_NIB_FILE | |
380 [SDLApplication poseAsClass:[NSApplication class]]; | |
381 NSApplicationMain (argc, argv); | |
382 #else | |
383 CustomApplicationMain (argc, argv); | |
384 #endif | |
385 return 0; | |
386 } |