From 9dc86c37514d3e104f71707529595d3a98aeee18 Mon Sep 17 00:00:00 2001 From: Andy Somogyi Date: Mon, 30 Oct 2017 16:07:20 -0400 Subject: [PATCH] fixed bug with GLFW running in a Cocoa app, added Cocoa test app --- examples/CMakeLists.txt | 30 ++ examples/cocoa/AppDelegate.h | 19 + examples/cocoa/AppDelegate.m | 242 ++++++++++++ examples/cocoa/MainMenu.xib | 725 +++++++++++++++++++++++++++++++++++ examples/cocoa/main.m | 5 + examples/cocoa/plist.in | 47 +++ src/cocoa_init.m | 45 ++- src/cocoa_platform.h | 9 + src/cocoa_window.m | 6 +- 9 files changed, 1121 insertions(+), 7 deletions(-) create mode 100644 examples/cocoa/AppDelegate.h create mode 100644 examples/cocoa/AppDelegate.m create mode 100644 examples/cocoa/MainMenu.xib create mode 100644 examples/cocoa/main.m create mode 100644 examples/cocoa/plist.in diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f7a2ebe43..b470383b0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -73,3 +73,33 @@ if (APPLE) MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in") endif() +if (APPLE) + + add_executable( + Cocoa + MACOSX_BUNDLE + cocoa/AppDelegate.h + cocoa/AppDelegate.m + cocoa/main.m + cocoa/MainMenu.xib + glfw.icns + ${GLAD} + ) + + set_source_files_properties( + cocoa/MainMenu.xib + PROPERTIES + MACOSX_PACKAGE_LOCATION Resources + ) + + set_target_properties( + Cocoa + PROPERTIES + RESOURCE glfw.icns + MACOSX_BUNDLE_ICON_FILE glfw.icns + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/cocoa/plist.in + ) + + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit") + +endif() diff --git a/examples/cocoa/AppDelegate.h b/examples/cocoa/AppDelegate.h new file mode 100644 index 000000000..387c77343 --- /dev/null +++ b/examples/cocoa/AppDelegate.h @@ -0,0 +1,19 @@ +#import // NSApplicationDelegate + +@interface AppDelegate : NSObject + +@property (assign, nonatomic) IBOutlet NSWindow *window; + +@property (assign, atomic) NSTimer *stepTimer; + +-(IBAction)createGlfwWindow:(id)sender; + +-(IBAction)singleStep:(id)sender; + +-(IBAction)closeGlfwWindow:(id)sender; + +-(IBAction)run:(id)sender; + +-(IBAction)stop:(id)sender; + +@end diff --git a/examples/cocoa/AppDelegate.m b/examples/cocoa/AppDelegate.m new file mode 100644 index 000000000..3109594d1 --- /dev/null +++ b/examples/cocoa/AppDelegate.m @@ -0,0 +1,242 @@ + +//======================================================================== +// Simple GLFW in Cocoa app example +// Copyright (c) Andy Somogyi +// +// This is a port of the GLFW basic example, originaly +// Copyright (c) Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#import "AppDelegate.h" +#include +#include +#include + +#define GLFW_EXPOSE_NATIVE_COCOA +#include + +#include "linmath.h" + +static void createCocoaGlfwWindow(int width, int height, int xpos, int ypos); +static void cocoaGlfwStep(); +static void cocoaGlfwCloseWindow(); + +@implementation AppDelegate + +@synthesize window = _windows; + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +-(IBAction)createGlfwWindow:(id)sender { + NSRect frame = self.window.frame; + + createCocoaGlfwWindow(700, 400, frame.size.width, 0); +} + +-(IBAction)singleStep:(id)sender { + printf("step message \n"); + fflush(stdout); + cocoaGlfwStep(); +} + +-(IBAction)closeGlfwWindow:(id)sender { + printf("close window message \n"); + fflush(stdout); + cocoaGlfwCloseWindow(); +} + +-(IBAction)run:(id)sender { + + if(self.stepTimer) { + return; + } + + [self.stepTimer invalidate]; + + NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 + target:self selector:@selector(singleStep:) + userInfo:nil repeats:YES]; + self.stepTimer = timer; + +} + +-(IBAction)stop:(id)sender { + [self.stepTimer invalidate]; + self.stepTimer = nil; +} + +@end + + +static const struct +{ + float x, y; + float r, g, b; +} vertices[3] = +{ + { -0.6f, -0.4f, 1.f, 0.f, 0.f }, + { 0.6f, -0.4f, 0.f, 1.f, 0.f }, + { 0.f, 0.6f, 0.f, 0.f, 1.f } +}; + +static const char* vertex_shader_text = +"#version 110\n" +"uniform mat4 MVP;\n" +"attribute vec3 vCol;\n" +"attribute vec2 vPos;\n" +"varying vec3 color;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +" color = vCol;\n" +"}\n"; + +static const char* fragment_shader_text = +"#version 110\n" +"varying vec3 color;\n" +"void main()\n" +"{\n" +" gl_FragColor = vec4(color, 1.0);\n" +"}\n"; + +static GLuint vertex_buffer, vertex_shader, fragment_shader, program; +static GLint mvp_location, vpos_location, vcol_location; +static GLFWwindow *win; +static float angle = 0; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static void window_refresh_callback(GLFWwindow* window) +{ + float ratio; + int width, height; + mat4x4 m, p, mvp; + + glfwGetFramebufferSize(window, &width, &height); + ratio = width / (float) height; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + + mat4x4_identity(m); + mat4x4_rotate_Z(m, m, angle); + mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f); + mat4x4_mul(mvp, p, m); + + glUseProgram(program); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glfwSwapBuffers(window); +} + +static void window_close_callback(GLFWwindow* window) +{ + glfwDestroyWindow(window); + win = NULL; +} + +static void createCocoaGlfwWindow(int width, int height, int xpos, int ypos) { + glfwSetErrorCallback(error_callback); + + if(win) { + printf("window is already open\n"); + fflush(stdout); + return; + } + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + win = glfwCreateWindow(width, height, "Simple example", NULL, NULL); + if (!win) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(win, key_callback); + + glfwSetWindowRefreshCallback(win, window_refresh_callback); + + glfwSetWindowCloseCallback(win, window_close_callback); + + glfwMakeContextCurrent(win); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + // NOTE: OpenGL error checks have been omitted for brevity + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + vcol_location = glGetAttribLocation(program, "vCol"); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + glEnableVertexAttribArray(vcol_location); + glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) (sizeof(float) * 2)); + +} + +static void cocoaGlfwCloseWindow() { + glfwDestroyWindow(win); + win = NULL; +} + +static void cocoaGlfwStep() { + angle += 0.1; + window_refresh_callback(win); +} diff --git a/examples/cocoa/MainMenu.xib b/examples/cocoa/MainMenu.xib new file mode 100644 index 000000000..edbc1cfc8 --- /dev/null +++ b/examples/cocoa/MainMenu.xib @@ -0,0 +1,725 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/cocoa/main.m b/examples/cocoa/main.m new file mode 100644 index 000000000..23327f9ef --- /dev/null +++ b/examples/cocoa/main.m @@ -0,0 +1,5 @@ +#import // NSApplicationMain + +int main(int argc, const char *argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/examples/cocoa/plist.in b/examples/cocoa/plist.in new file mode 100644 index 000000000..271473ea2 --- /dev/null +++ b/examples/cocoa/plist.in @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 01a746bae..5c46b1fc8 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -290,7 +290,7 @@ static GLFWbool initializeTIS(void) int _glfwPlatformInit(void) { - _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + _glfwCreateAutoreleasePool(); if (_glfw.hints.init.ns.chdir) changeToResourcesDirectory(); @@ -359,8 +359,7 @@ void _glfwPlatformTerminate(void) _glfwTerminateNSGL(); _glfwTerminateJoysticksNS(); - [_glfw.ns.autoreleasePool release]; - _glfw.ns.autoreleasePool = nil; + _glfwDestroyAutorealeasePool(); } const char* _glfwPlatformGetVersionString(void) @@ -372,3 +371,43 @@ const char* _glfwPlatformGetVersionString(void) ; } +static BOOL isNSApplicationRunning() +{ + BOOL isRunning = NO; + NSApplication *sharedNSApp = [NSApplication sharedApplication]; + if(sharedNSApp) + { + isRunning = [sharedNSApp isRunning]; + } + return isRunning; +} + +void _glfwCreateAutoreleasePool() +{ + if(!isNSApplicationRunning()) + { + _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + } +} + +// Clears and resets the autorealease pool if GLFW is managing it's own even loop, +// and not using the built-in NSApplication event loop. GLFW creates it's own +// autorelease pool only if it is running it's own event loop +void _glfwResetAutoreleasePool() +{ + if(!isNSApplicationRunning()) + { + [_glfw.ns.autoreleasePool drain]; + _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + } +} + +void _glfwDestroyAutorealeasePool() +{ + if(!isNSApplicationRunning()) + { + [_glfw.ns.autoreleasePool release]; + _glfw.ns.autoreleasePool = nil; + } +} + diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 61d0ee916..d4fd14ed0 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -162,3 +162,12 @@ void _glfwPollMonitorsNS(void); GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); +// If GLFW is running as a stand-alone application, we create a global autorealease +// pool. If GLFW is running in an existing native Cocoa app, the underlying +// NSApplication creates it's own autorelease pool. These functions check if GLFW +// is running in a Cocoa app or not, and creates the autorealease pool accordingly. +// If GLFW is in a Cocoa app, these functions don't do anything +void _glfwCreateAutoreleasePool(); +void _glfwResetAutoreleasePool(); +void _glfwDestroyAutorealeasePool(); + diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 1ee85bc60..4d68a037f 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1176,8 +1176,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) [window->ns.object close]; window->ns.object = nil; - [_glfw.ns.autoreleasePool drain]; - _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + _glfwResetAutoreleasePool(); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) @@ -1507,8 +1506,7 @@ void _glfwPlatformPollEvents(void) [NSApp sendEvent:event]; } - [_glfw.ns.autoreleasePool drain]; - _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + _glfwResetAutoreleasePool(); } void _glfwPlatformWaitEvents(void)