diff options
Diffstat (limited to 'delegateuitask_darwin.m')
| -rw-r--r-- | delegateuitask_darwin.m | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/delegateuitask_darwin.m b/delegateuitask_darwin.m new file mode 100644 index 0000000..4d38e3a --- /dev/null +++ b/delegateuitask_darwin.m @@ -0,0 +1,178 @@ +// 13 may 2014 + +#include "objc_darwin.h" +#include "_cgo_export.h" +#import <Foundation/NSObject.h> +#import <Foundation/NSValue.h> +#import <Foundation/NSNotification.h> +#import <AppKit/NSApplication.h> +#import <AppKit/NSWindow.h> +#import <Foundation/NSAutoreleasePool.h> +#import <AppKit/NSEvent.h> +#import <AppKit/NSAlert.h> + +extern NSRect dummyRect; + +@interface ourApplication : NSApplication +@end + +@implementation ourApplication + +// by default, NSApplication eats some key events +// this prevents that from happening with Area +// see http://stackoverflow.com/questions/24099063/how-do-i-detect-keyup-in-my-nsview-with-the-command-key-held and http://lists.apple.com/archives/cocoa-dev/2003/Oct/msg00442.html +- (void)sendEvent:(NSEvent *)e +{ + NSEventType type; + + type = [e type]; + if (type == NSKeyDown || type == NSKeyUp || type == NSFlagsChanged) { + id focused; + + focused = [[e window] firstResponder]; + // TODO can focused be nil? the isKindOfClass: docs don't say if it handles nil receivers + if ([focused isKindOfClass:areaClass]) + switch (type) { + case NSKeyDown: + [focused keyDown:e]; + return; + case NSKeyUp: + [focused keyUp:e]; + return; + case NSFlagsChanged: + [focused flagsChanged:e]; + return; + } + // else fall through + } + // otherwise, let NSApplication do it + [super sendEvent:e]; +} + +@end + +@interface appDelegate : NSObject +@end + +@implementation appDelegate + +// these are the uitask actions + +- (void)createWindow:(NSValue *)fp +{ + uitask_createWindow([fp pointerValue]); +} + +// these are the other delegate functions + +- (BOOL)windowShouldClose:(id)win +{ + return appDelegate_windowShouldClose(win); +} + +- (void)windowDidResize:(NSNotification *)n +{ + appDelegate_windowDidResize([n object]); +} + +- (void)buttonClicked:(id)button +{ + appDelegate_buttonClicked(button); +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app +{ + NSArray *windows; + NSUInteger i; + + // try to close all windows + windows = [NSApp windows]; + for (i = 0; i < [windows count]; i++) + [[windows objectAtIndex:i] performClose:self]; + // if any windows are left, cancel + if ([[NSApp windows] count] != 0) + return NSTerminateCancel; + // no windows are left; we're good + return NSTerminateNow; +} + +- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)data +{ + NSInteger *ret = (NSInteger *) data; + + *ret = returnCode; +} + +@end + +id makeAppDelegate(void) +{ + return [appDelegate new]; +} + +id windowGetContentView(id window) +{ + return [((NSWindow *) window) contentView]; +} + +// these are for douitask() but are here because @selector() is not a constant expression +SEL createWindow; + +BOOL initCocoa(id appDelegate) +{ + // on 10.6 the -[NSApplication setDelegate:] method complains if we don't have one + NSAutoreleasePool *pool; + + pool = [NSAutoreleasePool new]; + dummyRect = NSMakeRect(0, 0, 100, 100); + initAreaClass(); + [ourApplication sharedApplication]; // makes NSApp an object of type ourApplication + if ([NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] != YES) + return NO; + [NSApp activateIgnoringOtherApps:YES]; // TODO actually do C.NO here? Russ Cox does YES in his devdraw; the docs say the Finder does NO + [NSApp setDelegate:appDelegate]; + // uitask selectors + createWindow = @selector(createWindow:); + [pool release]; + return YES; +} + +void douitask(id appDelegate, SEL sel, void *p) +{ + NSAutoreleasePool *pool; + NSValue *fp; + + // we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr + pool = [NSAutoreleasePool new]; + fp = [NSValue valueWithPointer:p]; + [appDelegate performSelectorOnMainThread:sel + withObject:fp + waitUntilDone:YES]; // wait so we can properly drain the autorelease pool; on other platforms we wind up waiting anyway (since the main thread can only handle one thing at a time) so + [pool release]; +} + +void breakMainLoop(void) +{ + NSEvent *e; + + // -[NSApplication stop:] stops the event loop; it won't do a clean termination, but we're not too concerned with that (at least not on the other platforms either so) + // we can't call -[NSApplication terminate:] because that will just quit the program, ensuring we never leave ui.Go() + [NSApp stop:NSApp]; + // simply calling -[NSApplication stop:] is not good enough, as the stop flag is only checked when an event comes in + // we have to create a "proper" event; a blank event will just throw an exception + e = [NSEvent otherEventWithType:NSApplicationDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:e atStart:NO]; // not at start, just in case there are other events pending (TODO is this correct?) +} + +void cocoaMainLoop(void) +{ + [NSApp run]; +} |
