SDLMain.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * Copyright © 2011-2020 Frictional Games
  3. *
  4. * This file is part of Amnesia: A Machine For Pigs.
  5. *
  6. * Amnesia: A Machine For Pigs is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. * Amnesia: A Machine For Pigs is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with Amnesia: A Machine For Pigs. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. /* SDLMain.m - main entry point for our Cocoa-ized SDL app
  19. Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
  20. Non-NIB-Code & other changes: Max Horn <max@quendi.de>
  21. Feel free to customize this file to suit your needs
  22. */
  23. #include <SDL/SDL.h>
  24. #include "SDLMain.h"
  25. #include <sys/param.h> /* for MAXPATHLEN */
  26. #include <unistd.h>
  27. /* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
  28. but the method still is there and works. To avoid warnings, we declare
  29. it ourselves here. */
  30. @interface NSApplication(SDL_Missing_Methods)
  31. - (void)setAppleMenu:(NSMenu *)menu;
  32. @end
  33. /* Use this flag to determine whether we use SDLMain.nib or not */
  34. #define SDL_USE_NIB_FILE 0
  35. /* Use this flag to determine whether we use CPS (docking) or not */
  36. #define SDL_USE_CPS 1
  37. #ifdef SDL_USE_CPS
  38. /* Portions of CPS.h */
  39. typedef struct CPSProcessSerNum
  40. {
  41. UInt32 lo;
  42. UInt32 hi;
  43. } CPSProcessSerNum;
  44. extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
  45. extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
  46. extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
  47. #endif /* SDL_USE_CPS */
  48. static int gArgc;
  49. static char **gArgv;
  50. static BOOL gFinderLaunch;
  51. static BOOL gSetCWD;
  52. static BOOL gCalledAppMainline = FALSE;
  53. static NSString *getApplicationName(void)
  54. {
  55. const NSDictionary *dict;
  56. NSString *appName = 0;
  57. /* Determine the application name */
  58. dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
  59. if (dict)
  60. appName = [dict objectForKey: @"CFBundleName"];
  61. if (![appName length])
  62. appName = [[NSProcessInfo processInfo] processName];
  63. return appName;
  64. }
  65. #if SDL_USE_NIB_FILE
  66. /* A helper category for NSString */
  67. @interface NSString (ReplaceSubString)
  68. - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
  69. @end
  70. #endif
  71. @interface NSApplication (SDLApplication)
  72. @end
  73. @implementation NSApplication (SDLApplication)
  74. /* Invoked from the Quit menu item */
  75. - (void)terminate:(id)sender
  76. {
  77. /* Post a SDL_QUIT event */
  78. SDL_Event event;
  79. event.type = SDL_QUIT;
  80. SDL_PushEvent(&event);
  81. }
  82. @end
  83. /* The main class of the application, the application's delegate */
  84. @implementation SDLMain
  85. /* Set the working directory to the .app's parent directory */
  86. - (void) setupWorkingDirectory:(BOOL)shouldChdir
  87. {
  88. NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
  89. if (shouldChdir || [cwd isEqualToString:@"/"])
  90. {
  91. NSBundle *bundle = [NSBundle mainBundle];
  92. NSString *startPath = [[bundle infoDictionary] objectForKey:@"StartFolder"];
  93. if (startPath==nil || [startPath isEqualToString:@"PARENT"]) {
  94. NSString *path = [bundle bundlePath];
  95. NSString *parentdir = [path stringByDeletingLastPathComponent];
  96. [[NSFileManager defaultManager] changeCurrentDirectoryPath:parentdir];
  97. } else if ([startPath isEqualToString:@"RESOURCE"]) {
  98. NSString *path = [bundle resourcePath];
  99. [[NSFileManager defaultManager] changeCurrentDirectoryPath:path];
  100. } else {
  101. NSLog(@"Unknown Startup Folder");
  102. }
  103. }
  104. }
  105. #if SDL_USE_NIB_FILE
  106. /* Fix menu to contain the real app name instead of "SDL App" */
  107. - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
  108. {
  109. NSRange aRange;
  110. NSEnumerator *enumerator;
  111. NSMenuItem *menuItem;
  112. aRange = [[aMenu title] rangeOfString:@"SDL App"];
  113. if (aRange.length != 0)
  114. [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
  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. }
  125. #else
  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. appName = getApplicationName();
  134. appleMenu = [[NSMenu alloc] initWithTitle:@""];
  135. /* Add menu items */
  136. title = [@"About " stringByAppendingString:appName];
  137. [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
  138. [appleMenu addItem:[NSMenuItem separatorItem]];
  139. title = [@"Hide " stringByAppendingString:appName];
  140. [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
  141. menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
  142. [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
  143. [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
  144. [appleMenu addItem:[NSMenuItem separatorItem]];
  145. title = [@"Quit " stringByAppendingString:appName];
  146. [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
  147. /* Put menu into the menubar */
  148. menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
  149. [menuItem setSubmenu:appleMenu];
  150. [[NSApp mainMenu] addItem:menuItem];
  151. /* Tell the application object that this is now the application menu */
  152. [NSApp setAppleMenu:appleMenu];
  153. /* Finally give up our references to the objects */
  154. [appleMenu release];
  155. [menuItem release];
  156. }
  157. /* Create a window menu */
  158. static void setupWindowMenu(void)
  159. {
  160. NSMenu *windowMenu;
  161. NSMenuItem *windowMenuItem;
  162. NSMenuItem *menuItem;
  163. windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
  164. /* "Minimize" item */
  165. menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
  166. [windowMenu addItem:menuItem];
  167. [menuItem release];
  168. /* Put menu into the menubar */
  169. windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
  170. [windowMenuItem setSubmenu:windowMenu];
  171. [[NSApp mainMenu] addItem:windowMenuItem];
  172. /* Tell the application object that this is now the window menu */
  173. [NSApp setWindowsMenu:windowMenu];
  174. /* Finally give up our references to the objects */
  175. [windowMenu release];
  176. [windowMenuItem release];
  177. }
  178. /* Replacement for NSApplicationMain */
  179. static void CustomApplicationMain (int argc, char **argv)
  180. {
  181. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  182. SDLMain *sdlMain;
  183. /* Ensure the application object is initialised */
  184. [NSApplication sharedApplication];
  185. #ifdef SDL_USE_CPS
  186. {
  187. CPSProcessSerNum PSN;
  188. /* Tell the dock about us */
  189. if (!CPSGetCurrentProcess(&PSN))
  190. if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
  191. if (!CPSSetFrontProcess(&PSN))
  192. [NSApplication sharedApplication];
  193. }
  194. #endif /* SDL_USE_CPS */
  195. /* Set up the menubar */
  196. [NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]];
  197. setApplicationMenu();
  198. setupWindowMenu();
  199. /* Create SDLMain and make it the app delegate */
  200. sdlMain = [[SDLMain alloc] init];
  201. [NSApp setDelegate:sdlMain];
  202. /* Start the main event loop */
  203. [NSApp run];
  204. [sdlMain release];
  205. [pool release];
  206. }
  207. #endif
  208. /*
  209. * Catch document open requests...this lets us notice files when the app
  210. * was launched by double-clicking a document, or when a document was
  211. * dragged/dropped on the app's icon. You need to have a
  212. * CFBundleDocumentsType section in your Info.plist to get this message,
  213. * apparently.
  214. *
  215. * Files are added to gArgv, so to the app, they'll look like command line
  216. * arguments. Previously, apps launched from the finder had nothing but
  217. * an argv[0].
  218. *
  219. * This message may be received multiple times to open several docs on launch.
  220. *
  221. * This message is ignored once the app's mainline has been called.
  222. */
  223. - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
  224. {
  225. const char *temparg;
  226. size_t arglen;
  227. char *arg;
  228. char **newargv;
  229. if (!gFinderLaunch) /* MacOS is passing command line args. */
  230. return FALSE;
  231. if (gCalledAppMainline) /* app has started, ignore this document. */
  232. return FALSE;
  233. temparg = [filename UTF8String];
  234. arglen = SDL_strlen(temparg) + 1;
  235. arg = (char *) SDL_malloc(arglen);
  236. if (arg == NULL)
  237. return FALSE;
  238. newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
  239. if (newargv == NULL)
  240. {
  241. SDL_free(arg);
  242. return FALSE;
  243. }
  244. gArgv = newargv;
  245. SDL_strlcpy(arg, temparg, arglen);
  246. gArgv[gArgc++] = arg;
  247. gArgv[gArgc] = NULL;
  248. return TRUE;
  249. }
  250. /* Called when the internal event loop has just started running */
  251. - (void) applicationDidFinishLaunching: (NSNotification *) note
  252. {
  253. int status;
  254. /* Set the working directory to the .app's parent directory */
  255. [self setupWorkingDirectory:gSetCWD];
  256. #if SDL_USE_NIB_FILE
  257. /* Set the main menu to contain the real app name instead of "SDL App" */
  258. [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
  259. #endif
  260. /* Hand off to main application code */
  261. gCalledAppMainline = TRUE;
  262. status = SDL_main (gArgc, gArgv);
  263. /* We're done, thank you for playing */
  264. exit(status);
  265. }
  266. @end
  267. @implementation NSString (ReplaceSubString)
  268. - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
  269. {
  270. unsigned int bufferSize;
  271. unsigned int selfLen = [self length];
  272. unsigned int aStringLen = [aString length];
  273. unichar *buffer;
  274. NSRange localRange;
  275. NSString *result;
  276. bufferSize = selfLen + aStringLen - aRange.length;
  277. buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
  278. /* Get first part into buffer */
  279. localRange.location = 0;
  280. localRange.length = aRange.location;
  281. [self getCharacters:buffer range:localRange];
  282. /* Get middle part into buffer */
  283. localRange.location = 0;
  284. localRange.length = aStringLen;
  285. [aString getCharacters:(buffer+aRange.location) range:localRange];
  286. /* Get last part into buffer */
  287. localRange.location = aRange.location + aRange.length;
  288. localRange.length = selfLen - localRange.location;
  289. [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
  290. /* Build output string */
  291. result = [NSString stringWithCharacters:buffer length:bufferSize];
  292. NSDeallocateMemoryPages(buffer, bufferSize);
  293. return result;
  294. }
  295. @end
  296. #ifdef main
  297. # undef main
  298. #endif
  299. /* Main entry point to executable - should *not* be SDL_main! */
  300. int main (int argc, char **argv)
  301. {
  302. int l;
  303. /* Copy the arguments into a global variable */
  304. /* This is passed if we are launched by double-clicking */
  305. for (l=0; l<argc; ++l) {
  306. NSLog(@"Arg %i: %s", l, argv[l]);
  307. }
  308. if ( argc >= 3 && strncmp (argv[1], "-psn", 4) == 0 ) {
  309. gArgv = (char **) SDL_malloc(sizeof (char *) * (argc - 2) );
  310. gArgv[0] = argv[0];
  311. int i;
  312. for (i=3; i<argc; ++i) {
  313. gArgv[i-2] = argv[i];
  314. }
  315. gArgc = argc - 2;
  316. gFinderLaunch = YES;
  317. gSetCWD = YES;
  318. } else if ( argc >= 2 && strncmp( argv[1], "-cwd", 4) == 0 ) {
  319. gArgv = (char **) SDL_malloc(sizeof (char *) * (argc - 1) );
  320. gArgv[0] = argv[0];
  321. int i;
  322. for (i=2; i<argc; ++i) {
  323. gArgv[i-1] = argv[i];
  324. }
  325. gArgc = argc - 1;
  326. gFinderLaunch = NO;
  327. gSetCWD = NO;
  328. } else {
  329. int i;
  330. gArgc = argc;
  331. gArgv = (char **) SDL_malloc(sizeof (char *) * (argc));
  332. for (i = 0; i < argc; i++)
  333. gArgv[i] = argv[i];
  334. gFinderLaunch = NO;
  335. gSetCWD = YES;
  336. }
  337. for (l=0; l<gArgc; ++l) {
  338. NSLog(@"gArg %i: %s", l, gArgv[l]);
  339. }
  340. #if SDL_USE_NIB_FILE
  341. NSApplicationMain (argc, argv);
  342. #else
  343. CustomApplicationMain (argc, argv);
  344. #endif
  345. return 0;
  346. }