From 88c5edb40925bc0c987e8cfc7d306174f8094453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Mon, 24 Dec 2018 23:31:58 +0100 Subject: [PATCH] Cocoa: Remove subclassing of NSApplication This removes the GLFW NSApplication subclass as a step towards better coexistence with other libraries that touch Cocoa. This moves application object creation to platform init to allow event processing before window creation. Related to #1317. --- src/cocoa_init.m | 49 +++++++++++++++----- src/cocoa_platform.h | 4 +- src/cocoa_window.m | 105 +++++++++++-------------------------------- 3 files changed, 67 insertions(+), 91 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 01a746ba..f3c47957 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -27,6 +27,10 @@ #include "internal.h" #include // For MAXPATHLEN +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSEventMaskKeyUp NSKeyUpMask + #define NSEventModifierFlagCommand NSCommandKeyMask +#endif // Change to our application bundle's resources directory, if present // @@ -271,17 +275,21 @@ static GLFWbool initializeTIS(void) return updateUnicodeDataNS(); } -@interface GLFWLayoutListener : NSObject +@interface GLFWHelper : NSObject @end -@implementation GLFWLayoutListener +@implementation GLFWHelper - (void)selectedKeyboardInputSourceChanged:(NSObject* )object { updateUnicodeDataNS(); } -@end +- (void)doNothing:(id)object +{ +} + +@end // GLFWHelper ////////////////////////////////////////////////////////////////////////// @@ -291,13 +299,31 @@ static GLFWbool initializeTIS(void) int _glfwPlatformInit(void) { _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + _glfw.ns.helper = [[GLFWHelper alloc] init]; + + [NSThread detachNewThreadSelector:@selector(doNothing:) + toTarget:_glfw.ns.helper + withObject:nil]; + + [NSApplication sharedApplication]; + + NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) + { + if ([event modifierFlags] & NSEventModifierFlagCommand) + [[NSApp keyWindow] sendEvent:event]; + + return event; + }; + + _glfw.ns.keyUpMonitor = + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp + handler:block]; if (_glfw.hints.init.ns.chdir) changeToResourcesDirectory(); - _glfw.ns.listener = [[GLFWLayoutListener alloc] init]; [[NSNotificationCenter defaultCenter] - addObserver:_glfw.ns.listener + addObserver:_glfw.ns.helper selector:@selector(selectedKeyboardInputSourceChanged:) name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; @@ -342,18 +368,21 @@ void _glfwPlatformTerminate(void) _glfw.ns.delegate = nil; } - if (_glfw.ns.listener) + if (_glfw.ns.helper) { [[NSNotificationCenter defaultCenter] - removeObserver:_glfw.ns.listener + removeObserver:_glfw.ns.helper name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] - removeObserver:_glfw.ns.listener]; - [_glfw.ns.listener release]; - _glfw.ns.listener = nil; + removeObserver:_glfw.ns.helper]; + [_glfw.ns.helper release]; + _glfw.ns.helper = nil; } + if (_glfw.ns.keyUpMonitor) + [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; + free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 282f7cc8..13adaa97 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -109,7 +109,9 @@ typedef struct _GLFWlibraryNS TISInputSourceRef inputSource; IOHIDManagerRef hidManager; id unicodeData; - id listener; + id helper; + id keyUpMonitor; + id nibObjects; char keyName[64]; short int keycodes[256]; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 9c018eb8..f450eda0 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -46,7 +46,6 @@ #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask #define NSEventMaskAny NSAnyEventMask #define NSEventTypeApplicationDefined NSApplicationDefined - #define NSEventTypeKeyUp NSKeyUp #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat #endif @@ -856,52 +855,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; @end -//------------------------------------------------------------------------ -// GLFW application class -//------------------------------------------------------------------------ - -@interface GLFWApplication : NSApplication -{ - NSArray* nibObjects; -} - -@end - -@implementation GLFWApplication - -// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost -// This works around an AppKit bug, where key up events while holding -// down the command key don't get sent to the key window. -- (void)sendEvent:(NSEvent *)event -{ - if ([event type] == NSEventTypeKeyUp && - ([event modifierFlags] & NSEventModifierFlagCommand)) - { - [[self keyWindow] sendEvent:event]; - } - else - [super sendEvent:event]; -} - - -// No-op thread entry point -// -- (void)doNothing:(id)object -{ -} - -- (void)loadMainMenu -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 - [[NSBundle mainBundle] loadNibNamed:@"MainMenu" - owner:NSApp - topLevelObjects:&nibObjects]; -#else - [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp]; -#endif -} -@end - // Set up the menu bar (manually) // This is nasty, nasty stuff -- calls to undocumented semi-private APIs that // could go away at any moment, lots of stuff that really should be @@ -1011,32 +964,9 @@ static void createMenuBar(void) // static GLFWbool initializeAppKit(void) { - if (NSApp) + if (_glfw.ns.delegate) return GLFW_TRUE; - // Implicitly create shared NSApplication instance - [GLFWApplication sharedApplication]; - - // Make Cocoa enter multi-threaded mode - [NSThread detachNewThreadSelector:@selector(doNothing:) - toTarget:NSApp - withObject:nil]; - - if (_glfw.hints.init.ns.menubar) - { - // In case we are unbundled, make us a proper UI application - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - - // Menu bar setup must go between sharedApplication above and - // finishLaunching below, in order to properly emulate the behavior - // of NSApplicationMain - - if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) - [NSApp loadMainMenu]; - else - createMenuBar(); - } - // There can only be one application delegate, but we allocate it the // first time a window is created to keep all window code in this file _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; @@ -1048,6 +978,30 @@ static GLFWbool initializeAppKit(void) } [NSApp setDelegate:_glfw.ns.delegate]; + + if (_glfw.hints.init.ns.menubar) + { + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + + if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" + owner:NSApp + topLevelObjects:&_glfw.ns.nibObjects]; +#else + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp]; +#endif + } + else + createMenuBar(); + } + [NSApp run]; // Press and Hold prevents some keys from emitting repeated characters @@ -1554,9 +1508,6 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) void _glfwPlatformPollEvents(void) { - if (!initializeAppKit()) - return; - for (;;) { NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny @@ -1707,9 +1658,6 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, NSImage* native; NSBitmapImageRep* rep; - if (!initializeAppKit()) - return GLFW_FALSE; - rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:image->width @@ -1745,9 +1693,6 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - if (!initializeAppKit()) - return GLFW_FALSE; - if (shape == GLFW_ARROW_CURSOR) cursor->ns.object = [NSCursor arrowCursor]; else if (shape == GLFW_IBEAM_CURSOR)