From 4a9545317ee5a8ffb07f48d5b36a0308fd40391f Mon Sep 17 00:00:00 2001
From: Camilla Berglund
Date: Wed, 15 Feb 2012 01:44:55 +0100
Subject: [PATCH] Postponed AppKit init to first window creation.
---
readme.html | 1 +
src/cocoa_init.m | 175 +++++---------------------------------------
src/cocoa_window.m | 176 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+), 158 deletions(-)
diff --git a/readme.html b/readme.html
index 36c5add2..5ab86c64 100644
--- a/readme.html
+++ b/readme.html
@@ -310,6 +310,7 @@ version of GLFW.
Bugfix: The FSAA test did not check for the availability of GL_ARB_multisample
[Cocoa] Added support for OpenGL 3.2 core profile in 10.7 Lion and above
[Cocoa] Added support for joysticks
+ [Cocoa] Postponed menu creation to first window creation
[Cocoa] Replaced NSDate
time source with mach_absolute_time
[Cocoa] Bugfix: The loop condition for saving video modes used the wrong index variable
[Cocoa] Bugfix: The OpenGL framework was not retrieved, making glfwGetProcAddress crash
diff --git a/src/cocoa_init.m b/src/cocoa_init.m
index 41362044..45088a4e 100644
--- a/src/cocoa_init.m
+++ b/src/cocoa_init.m
@@ -27,168 +27,39 @@
//
//========================================================================
-// Needed for _NSGetProgname
-#include
-
#include "internal.h"
-//========================================================================
-// GLFW application class
-//========================================================================
-
-@interface GLFWApplication : NSApplication
-@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] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask))
- [[self keyWindow] sendEvent:event];
- else
- [super sendEvent:event];
-}
-
-@end
-
-
-// Prior to Snow Leopard, we need to use this oddly-named semi-private API
-// to get the application menu working properly. Need to be careful in
-// case it goes away in a future OS update.
-@interface NSApplication (NSAppleMenu)
-- (void)setAppleMenu:(NSMenu*)m;
-@end
-
-// Keys to search for as potential application names
-NSString* GLFWNameKeys[] =
-{
- @"CFBundleDisplayName",
- @"CFBundleName",
- @"CFBundleExecutable",
-};
-
-
//========================================================================
// Change to our application bundle's resources directory, if present
//========================================================================
static void changeToResourcesDirectory(void)
{
- char* resourcePath = [[[NSBundle mainBundle] resourcePath] UTF8String];
-
- if (access(resourcePath, R_OK) == 0)
- chdir(resourcePath);
-}
-
-
-//========================================================================
-// Try to figure out what the calling application is called
-//========================================================================
-static NSString* findAppName(void)
-{
- unsigned int i;
- NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
+ CFBundleRef bundle = CFBundleGetMainBundle();
+ if (!bundle)
+ return;
- for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++)
- {
- id name = [infoDictionary objectForKey:GLFWNameKeys[i]];
- if (name &&
- [name isKindOfClass:[NSString class]] &&
- ![@"" isEqualToString:name])
- {
- _glfwLibrary.NS.bundled = GL_TRUE;
- return name;
- }
- }
+ CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
+ char resourcesPath[MAXPATHLEN];
- // If we get here, we're unbundled
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ CFStringRef name = CFURLCopyLastPathComponent(resourcesURL);
+ if (CFStringCompare(CFSTR("Resources"), name, 0) != kCFCompareEqualTo)
+ return;
- // Having the app in front of the terminal window is also generally
- // handy. There is an NSApplication API to do this, but...
- SetFrontProcess(&psn);
+ CFRelease(name);
- char** progname = _NSGetProgname();
- if (progname && *progname)
+ if (!CFURLGetFileSystemRepresentation(resourcesURL,
+ TRUE,
+ (UInt8*) resourcesPath,
+ MAXPATHLEN));
{
- // TODO: UTF-8?
- return [NSString stringWithUTF8String:*progname];
+ CFRelease(resourcesURL);
+ return;
}
- // Really shouldn't get here
- return @"GLFW Application";
-}
-
-//========================================================================
-// 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
-// localize(d|able), etc. Loading a nib would save us this horror, but that
-// doesn't seem like a good thing to require of GLFW's clients.
-//========================================================================
-static void setUpMenuBar(void)
-{
- NSString* appName = findAppName();
+ CFRelease(resourcesURL);
- NSMenu* bar = [[NSMenu alloc] init];
- [NSApp setMainMenu:bar];
-
- NSMenuItem* appMenuItem =
- [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
- NSMenu* appMenu = [[NSMenu alloc] init];
- [appMenuItem setSubmenu:appMenu];
-
- [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
- action:@selector(orderFrontStandardAboutPanel:)
- keyEquivalent:@""];
- [appMenu addItem:[NSMenuItem separatorItem]];
- NSMenu* servicesMenu = [[NSMenu alloc] init];
- [NSApp setServicesMenu:servicesMenu];
- [[appMenu addItemWithTitle:@"Services"
- action:NULL
- keyEquivalent:@""] setSubmenu:servicesMenu];
- [appMenu addItem:[NSMenuItem separatorItem]];
- [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
- action:@selector(hide:)
- keyEquivalent:@"h"];
- [[appMenu addItemWithTitle:@"Hide Others"
- action:@selector(hideOtherApplications:)
- keyEquivalent:@"h"]
- setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
- [appMenu addItemWithTitle:@"Show All"
- action:@selector(unhideAllApplications:)
- keyEquivalent:@""];
- [appMenu addItem:[NSMenuItem separatorItem]];
- [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
- action:@selector(terminate:)
- keyEquivalent:@"q"];
-
- NSMenuItem* windowMenuItem =
- [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
- NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
- [NSApp setWindowsMenu:windowMenu];
- [windowMenuItem setSubmenu:windowMenu];
-
- [windowMenu addItemWithTitle:@"Miniaturize"
- action:@selector(performMiniaturize:)
- keyEquivalent:@"m"];
- [windowMenu addItemWithTitle:@"Zoom"
- action:@selector(performZoom:)
- keyEquivalent:@""];
- [windowMenu addItem:[NSMenuItem separatorItem]];
- [windowMenu addItemWithTitle:@"Bring All to Front"
- action:@selector(arrangeInFront:)
- keyEquivalent:@""];
-
- // At least guard the call to private API to avoid an exception if it
- // goes away. Hopefully that means the worst we'll break in future is to
- // look ugly...
- if ([NSApp respondsToSelector:@selector(setAppleMenu:)])
- [NSApp setAppleMenu:appMenu];
+ chdir(resourcesPath);
}
@@ -202,11 +73,6 @@ static void setUpMenuBar(void)
int _glfwPlatformInit(void)
{
- _glfwLibrary.NS.autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- // Implicitly create shared NSApplication instance
- [GLFWApplication sharedApplication];
-
_glfwLibrary.NS.OpenGLFramework =
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
if (_glfwLibrary.NS.OpenGLFramework == NULL)
@@ -216,13 +82,6 @@ int _glfwPlatformInit(void)
return GL_FALSE;
}
- // Setting up the menu bar must go between sharedApplication
- // above and finishLaunching below, in order to properly emulate the
- // behavior of NSApplicationMain
- setUpMenuBar();
-
- [NSApp finishLaunching];
-
if (_glfwLibrary.NS.bundled)
changeToResourcesDirectory();
diff --git a/src/cocoa_window.m b/src/cocoa_window.m
index b5dfb436..6bfd0971 100644
--- a/src/cocoa_window.m
+++ b/src/cocoa_window.m
@@ -29,6 +29,9 @@
#include "internal.h"
+// Needed for _NSGetProgname
+#include
+
//========================================================================
// Delegate for window related notifications
@@ -443,6 +446,176 @@ static int convertMacKeyCode(unsigned int macKeyCode)
@end
+//========================================================================
+// GLFW application class
+//========================================================================
+
+@interface GLFWApplication : NSApplication
+@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] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask))
+ [[self keyWindow] sendEvent:event];
+ else
+ [super sendEvent:event];
+}
+
+@end
+
+
+// Prior to Snow Leopard, we need to use this oddly-named semi-private API
+// to get the application menu working properly. Need to be careful in
+// case it goes away in a future OS update.
+@interface NSApplication (NSAppleMenu)
+- (void)setAppleMenu:(NSMenu*)m;
+@end
+
+//========================================================================
+// Try to figure out what the calling application is called
+//========================================================================
+
+static NSString* findAppName(void)
+{
+ unsigned int i;
+ NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
+
+ // Keys to search for as potential application names
+ NSString* GLFWNameKeys[] =
+ {
+ @"CFBundleDisplayName",
+ @"CFBundleName",
+ @"CFBundleExecutable",
+ };
+
+ for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++)
+ {
+ id name = [infoDictionary objectForKey:GLFWNameKeys[i]];
+ if (name &&
+ [name isKindOfClass:[NSString class]] &&
+ ![@"" isEqualToString:name])
+ {
+ _glfwLibrary.NS.bundled = GL_TRUE;
+ return name;
+ }
+ }
+
+ // If we get here, we're unbundled
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+
+ // Having the app in front of the terminal window is also generally
+ // handy. There is an NSApplication API to do this, but...
+ SetFrontProcess(&psn);
+
+ char** progname = _NSGetProgname();
+ if (progname && *progname)
+ {
+ // TODO: UTF-8?
+ return [NSString stringWithUTF8String:*progname];
+ }
+
+ // Really shouldn't get here
+ return @"GLFW Application";
+}
+
+//========================================================================
+// 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
+// localize(d|able), etc. Loading a nib would save us this horror, but that
+// doesn't seem like a good thing to require of GLFW's clients.
+//========================================================================
+static void setUpMenuBar(void)
+{
+ NSString* appName = findAppName();
+
+ NSMenu* bar = [[NSMenu alloc] init];
+ [NSApp setMainMenu:bar];
+
+ NSMenuItem* appMenuItem =
+ [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
+ NSMenu* appMenu = [[NSMenu alloc] init];
+ [appMenuItem setSubmenu:appMenu];
+
+ [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
+ action:@selector(orderFrontStandardAboutPanel:)
+ keyEquivalent:@""];
+ [appMenu addItem:[NSMenuItem separatorItem]];
+ NSMenu* servicesMenu = [[NSMenu alloc] init];
+ [NSApp setServicesMenu:servicesMenu];
+ [[appMenu addItemWithTitle:@"Services"
+ action:NULL
+ keyEquivalent:@""] setSubmenu:servicesMenu];
+ [appMenu addItem:[NSMenuItem separatorItem]];
+ [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
+ action:@selector(hide:)
+ keyEquivalent:@"h"];
+ [[appMenu addItemWithTitle:@"Hide Others"
+ action:@selector(hideOtherApplications:)
+ keyEquivalent:@"h"]
+ setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask];
+ [appMenu addItemWithTitle:@"Show All"
+ action:@selector(unhideAllApplications:)
+ keyEquivalent:@""];
+ [appMenu addItem:[NSMenuItem separatorItem]];
+ [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
+ action:@selector(terminate:)
+ keyEquivalent:@"q"];
+
+ NSMenuItem* windowMenuItem =
+ [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
+ NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+ [NSApp setWindowsMenu:windowMenu];
+ [windowMenuItem setSubmenu:windowMenu];
+
+ [windowMenu addItemWithTitle:@"Miniaturize"
+ action:@selector(performMiniaturize:)
+ keyEquivalent:@"m"];
+ [windowMenu addItemWithTitle:@"Zoom"
+ action:@selector(performZoom:)
+ keyEquivalent:@""];
+ [windowMenu addItem:[NSMenuItem separatorItem]];
+ [windowMenu addItemWithTitle:@"Bring All to Front"
+ action:@selector(arrangeInFront:)
+ keyEquivalent:@""];
+
+ // At least guard the call to private API to avoid an exception if it
+ // goes away. Hopefully that means the worst we'll break in future is to
+ // look ugly...
+ if ([NSApp respondsToSelector:@selector(setAppleMenu:)])
+ [NSApp setAppleMenu:appMenu];
+}
+
+
+//========================================================================
+// Initialize the Cocoa Application Kit
+//========================================================================
+static GLboolean initializeCocoa(void)
+{
+ if (NSApp)
+ return GL_TRUE;
+
+ _glfwLibrary.NS.autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ // Implicitly create shared NSApplication instance
+ [GLFWApplication sharedApplication];
+
+ // Setting up the menu bar must go between sharedApplication
+ // above and finishLaunching below, in order to properly emulate the
+ // behavior of NSApplicationMain
+ setUpMenuBar();
+
+ [NSApp finishLaunching];
+
+ return GL_TRUE;
+}
+
//========================================================================
// Create the Cocoa window
//========================================================================
@@ -641,6 +814,9 @@ int _glfwPlatformOpenWindow(_GLFWwindow* window,
const _GLFWwndconfig* wndconfig,
const _GLFWfbconfig* fbconfig)
{
+ if (!initializeCocoa())
+ return GL_FALSE;
+
// We can only have one application delegate, but we only allocate it the
// first time we create a window to keep all window code in this file
if (_glfwLibrary.NS.delegate == nil)