#include "allegro5/allegro.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_events.h"
#include "allegro5/internal/aintern_keyboard.h"
#include "allegro5/platform/aintosx.h"
#ifdef __LP64__
/* FIXME: the following prototype and enum definition appear to needed in
* 64 bit mode on 10.5 (Leopard). Apple's documentation indicates that
* "deprecated features" are not available in 64 bit, but
* UpdateSystemActivity is not deprecated and the documentation likewise
* suggests that all that should be required is to #include CoreServices.h
* or Power.h. However, this does not appear to work... for now, this
* should work ok.
* On 10.6 (Snow Leopard) these defines cause a problem, so they are
* disabled.
*/
extern OSErr UpdateSystemActivity(uint8_t activity);
#if 0
enum {
OverallAct = 0, /* Delays idle sleep by small amount */
UsrActivity = 1, /* Delays idle sleep and dimming by timeout time */
NetActivity = 2, /* Delays idle sleep and power cycling by small amount */
HDActivity = 3, /* Delays hard drive spindown and idle sleep by small amount */
IdleActivity = 4 /* Delays idle sleep by timeout time */
};
#endif
#endif
extern NSBundle *_al_osx_bundle;
/* For compatibility with the unix code */
static int __crt0_argc;
static char **__crt0_argv;
static int (*user_main)(int, char **);
static char *arg0, *arg1 = NULL;
static BOOL in_bundle(void)
{
/* This comes from the ADC tips & tricks section: how to detect if the app
* lives inside a bundle
*/
FSRef processRef;
ProcessSerialNumber psn = { 0, kCurrentProcess };
FSCatalogInfo processInfo;
GetProcessBundleLocation(&psn, &processRef);
FSGetCatalogInfo(&processRef, kFSCatInfoNodeFlags, &processInfo, NULL, NULL, NULL);
if (processInfo.nodeFlags & kFSNodeIsDirectoryMask)
return YES;
else
return NO;
}
@interface AllegroAppDelegate : NSObject <NSApplicationDelegate>
{
NSTimer* activity;
}
- (void) dealloc;
- (BOOL)application: (NSApplication *)theApplication openFile: (NSString *)filename;
- (void)applicationDidFinishLaunching: (NSNotification *)aNotification;
- (void)applicationDidChangeScreenParameters: (NSNotification *)aNotification;
+ (void)app_main: (id)arg;
- (NSApplicationTerminateReply) applicationShouldTerminate: (id)sender;
- (void) updateSystemActivity:(NSTimer*) timer;
- (void) setInhibitScreenSaver: (NSNumber*) inhibit;
@end
@interface AllegroWindowDelegate : NSObject
- (BOOL)windowShouldClose: (id)sender;
- (void)windowDidDeminiaturize: (NSNotification *)aNotification;
@end
@implementation AllegroAppDelegate
/* setInhibitScreenSaver:
* If inhibit is YES, set up an infrequent timer to call
* updateSystemActivity: to prevent the screen saver from activating
* Must be called from the main thread (osx_inhibit_screensaver ensures
* this)
* Has no effect if inhibit is YES and the timer is already active
* or if inhibit is NO and the timer is not active
*/
-(void) setInhibitScreenSaver: (NSNumber *) inhibit
{
if ([inhibit boolValue] == YES) {
if (activity == nil) {
// Schedule every 30 seconds
activity = [NSTimer scheduledTimerWithTimeInterval:30.0
target:self
selector:@selector(updateSystemActivity:)
userInfo:nil
repeats:YES];
[activity retain];
}
// else already active
}
else {
// OK to send message to nil if timer wasn't set.
[activity invalidate];
[activity release];
activity = nil;
}
}
/* updateSystemActivity:
* called by a timer to inform the system that there is still activity and
* therefore do not dim the screen/start the screensaver
*/
-(void) updateSystemActivity: (NSTimer*) timer
{
(void)timer;
UpdateSystemActivity(UsrActivity);
}
-(void) dealloc
{
[activity invalidate];
[activity release];
[super dealloc];
}
- (BOOL)application: (NSApplication *)theApplication openFile: (NSString *)filename
{
NSData* data = [filename dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
(void)theApplication;
if (data != nil) {
unsigned int len = 1 + [data length];
arg1 = al_malloc(len);
memset(arg1, 0, len);
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000
[data getBytes: arg1 length:len];
#else
[data getBytes: arg1];
#endif
return YES;
}
else {
return NO;
}
}
/* applicationDidFinishLaunching:
* Called when the app is ready to run.
*/
- (void)applicationDidFinishLaunching: (NSNotification *)aNotification
{
NSString* exename, *resdir;
NSFileManager* fm;
BOOL isDir;
(void)aNotification;
if (in_bundle() == YES)
{
/* In a bundle, so chdir to the containing directory,
* or to the 'magic' resource directory if it exists.
* (see the readme.osx file for more info)
*/
_al_osx_bundle = [NSBundle mainBundle];
exename = [[_al_osx_bundle executablePath] lastPathComponent];
resdir = [[_al_osx_bundle resourcePath] stringByAppendingPathComponent: exename];
fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath: resdir isDirectory: &isDir] && isDir) {
/* Yes, it exists inside the bundle */
[fm changeCurrentDirectoryPath: resdir];
}
else {
/* No, change to the 'standard' OSX resource directory if it exists*/
if ([fm fileExistsAtPath: [_al_osx_bundle resourcePath] isDirectory: &isDir] && isDir)
{
[fm changeCurrentDirectoryPath: [_al_osx_bundle resourcePath]];
}
/* It doesn't exist - this is unusual for a bundle. Don't chdir */
}
arg0 = strdup([[_al_osx_bundle bundlePath] fileSystemRepresentation]);
if (arg1) {
static char *args[2];
args[0] = arg0;
args[1] = arg1;
__crt0_argv = args;
__crt0_argc = 2;
}
else {
__crt0_argv = &arg0;
__crt0_argc = 1;
}
}
/* else: not in a bundle so don't chdir */
[NSThread detachNewThreadSelector: @selector(app_main:)
toTarget: [AllegroAppDelegate class]
withObject: nil];
return;
}
/* applicationDidChangeScreenParameters:
* Invoked when the screen did change resolution/color depth.
*/
- (void)applicationDidChangeScreenParameters: (NSNotification *)aNotification
{
/* no-op */
(void)aNotification;
}
/* Call the user main() */
static void call_user_main(void)
{
exit(user_main(__crt0_argc, __crt0_argv));
}
/* app_main:
* Thread dedicated to the user program; real main() gets called here.
*/
+ (void)app_main: (id)arg
{
(void)arg;
call_user_main();
}
/* applicationShouldTerminate:
* Called upon Command-Q or "Quit" menu item selection.
* Post a message but do not quit directly
*/
- (NSApplicationTerminateReply) applicationShouldTerminate: (id)sender
{
(void)sender;
_al_osx_post_quit();
return NSTerminateCancel;
}
/* end of AllegroAppDelegate implementation */
@end
/* This prevents warnings that 'NSApplication might not
* respond to setAppleMenu' on OS X 10.4+
*/
@interface NSApplication(AllegroOSX)
- (void)setAppleMenu:(NSMenu *)menu;
@end
/* Helper macro to add entries to the menu */
#define add_menu(name, sel, eq) \
[menu addItem: [[[NSMenuItem alloc] \
initWithTitle: name \
action: @selector(sel) \
keyEquivalent: eq] autorelease]] \
int _al_osx_run_main(int argc, char **argv,
int (*real_main)(int, char **))
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
AllegroAppDelegate *app_delegate = [[AllegroAppDelegate alloc] init];
NSMenu *menu;
NSMenuItem *temp_item;
user_main = real_main;
__crt0_argc = argc;
__crt0_argv = argv;
#ifdef OSX_BOOTSTRAP_DETECTION
if (!_al_osx_bootstrap_ok()) /* not safe to use NSApplication */
call_user_main();
#endif
[NSApplication sharedApplication];
/* Load the main menu nib if possible */
if ((!in_bundle()) || ([NSBundle loadNibNamed: @"MainMenu"
owner: NSApp] == NO))
{
/* Didn't load the nib; create a default menu programmatically */
NSString* title = nil;
NSDictionary* app_dictionary = [[NSBundle mainBundle] infoDictionary];
if (app_dictionary)
{
title = [app_dictionary objectForKey: @"CFBundleName"];
}
if (title == nil)
{
title = [[NSProcessInfo processInfo] processName];
}
NSMenu* main_menu = [[NSMenu alloc] initWithTitle: @""];
[NSApp setMainMenu: main_menu];
/* Add application ("Apple") menu */
menu = [[NSMenu alloc] initWithTitle: @"Apple menu"];
temp_item = [[NSMenuItem alloc]
initWithTitle: @""
action: NULL
keyEquivalent: @""];
[main_menu addItem: temp_item];
[main_menu setSubmenu: menu forItem: temp_item];
[temp_item release];
add_menu([@"Hide " stringByAppendingString: title], hide:, @"h");
add_menu(@"Hide Others", hideOtherApplications:, @"");
add_menu(@"Show All", unhideAllApplications:, @"");
[menu addItem: [NSMenuItem separatorItem]];
add_menu([@"Quit " stringByAppendingString: title], terminate:, @"q");
[NSApp setAppleMenu: menu];
[menu release];
/* Add "Window" menu */
menu = [[NSMenu alloc]
initWithTitle: @"Window"];
temp_item = [[NSMenuItem alloc]
initWithTitle: @""
action: NULL
keyEquivalent: @""];
[main_menu addItem: temp_item];
[main_menu setSubmenu: menu forItem: temp_item];
[temp_item release];
/* Add menu entries */
add_menu(@"Minimize", performMiniaturize:, @"M");
add_menu(@"Bring All to Front", arrangeInFront:, @"");
[NSApp setWindowsMenu:menu];
[menu release];
[main_menu release];
}
// setDelegate: doesn't retain the delegate here (a Cocoa convention)
// therefore we don't release it.
[NSApp setDelegate: app_delegate];
[pool drain];
[NSApp run];
/* Can never get here */
[app_delegate release];
return 0;
}