From e427c8510d56058550bf516f637d07f39676012c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 29 Apr 2012 23:25:15 +0000 Subject: OSX implementation. --- pugl/pugl.h | 16 ++++ pugl/pugl_osx.m | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pugl/pugl_x11.c | 6 +- pugl_test.c | 9 -- wscript | 17 +++- 5 files changed, 327 insertions(+), 12 deletions(-) create mode 100644 pugl/pugl_osx.m diff --git a/pugl/pugl.h b/pugl/pugl.h index e5021f3..13f5efa 100644 --- a/pugl/pugl.h +++ b/pugl/pugl.h @@ -23,6 +23,22 @@ #include +/* + This API is pure portable C and contains no platform specific elements, or + even a GL dependency. However, unfortunately GL includes vary across + platforms so they are included here to allow for pure portable programs. +*/ +#ifdef __APPLE__ +# include "OpenGL/gl.h" +# include "OpenGL/glu.h" +#else +# ifdef _WIN32 +# include /* Broken Windows GL headers require this */ +# endif +# include "GL/gl.h" +# include "GL/glu.h" +#endif + #ifdef __cplusplus extern "C" { #else diff --git a/pugl/pugl_osx.m b/pugl/pugl_osx.m new file mode 100644 index 0000000..619e453 --- /dev/null +++ b/pugl/pugl_osx.m @@ -0,0 +1,291 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include + +#import + +#include "pugl_internal.h" + +@interface PuglOpenGLView : NSOpenGLView +{ + int colorBits; + int depthBits; +@public + PuglWindow* win; +} + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits; +- (void) reshape; +- (void) drawRect:(NSRect)rect; +- (void) mouseMoved:(NSEvent*)event; +- (void) mouseDown:(NSEvent*)event; +- (void) mouseUp:(NSEvent*)event; +- (void) rightMouseDown:(NSEvent*)event; +- (void) rightMouseUp:(NSEvent*)event; +- (void) keyDown:(NSEvent*)event; +- (void) keyUp:(NSEvent*)event; + +@end + +@implementation PuglOpenGLView + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits +{ + colorBits = numColorBits; + depthBits = numDepthBits; + + NSOpenGLPixelFormatAttribute pixelAttribs[16] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, + colorBits, + NSOpenGLPFADepthSize, + depthBits, + 0 + }; + + NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:pixelAttribs]; + + if (pixelFormat) { + self = [super initWithFrame:frame pixelFormat:pixelFormat]; + [pixelFormat release]; + if (self) { + [[self openGLContext] makeCurrentContext]; + [self reshape]; + } + } else { + self = nil; + } + + return self; +} + +- (void) reshape +{ + [[self openGLContext] update]; + + NSRect bounds = [self bounds]; + int width = bounds.size.width; + int height = bounds.size.height; + + if (win->reshapeFunc) { + // User provided a reshape function, defer to that + win->reshapeFunc(win, width, height); + } else { + // No custom reshape function, do something reasonable + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0f, width/(float)height, 1.0f, 10.0f); + glViewport(0, 0, width, height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + + win->width = width; + win->height = height; + win->redisplay = true; +} + +- (void) drawRect:(NSRect)rect +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (self->win->displayFunc) { + self->win->displayFunc(self->win); + } + + glFlush(); + glSwapAPPLE(); +} + +- (void) mouseMoved:(NSEvent*)event +{ + NSPoint loc = [event locationInWindow]; + if (win->motionFunc) { + win->motionFunc(win, loc.x, loc.y); + } +} + +- (void) mouseDown:(NSEvent*)event +{ + NSPoint loc = [event locationInWindow]; + if (win->mouseFunc) { + win->mouseFunc(win, 1, true, loc.x, loc.y); + } +} + +- (void) mouseUp:(NSEvent*)event +{ + NSPoint loc = [event locationInWindow]; + if (win->mouseFunc) { + win->mouseFunc(win, 1, false, loc.x, loc.y); + } +} + +- (void) rightMouseDown:(NSEvent*)event +{ + NSPoint loc = [event locationInWindow]; + if (win->mouseFunc) { + win->mouseFunc(win, 3, true, loc.x, loc.y); + } +} + +- (void) rightMouseUp:(NSEvent*)event +{ + NSPoint loc = [event locationInWindow]; + if (win->mouseFunc) { + win->mouseFunc(win, 3, false, loc.x, loc.y); + } +} + +- (void) scrollWheel:(NSEvent*)event +{ + if (win->scrollFunc) { + win->scrollFunc(win, [event deltaX], [event deltaY]); + } +} + +- (void) keyDown:(NSEvent*)event +{ + NSString* chars = [event characters];; + if (win->keyboardFunc) { + win->keyboardFunc(win, true, [chars characterAtIndex:0]); + } +} + +- (void) keyUp:(NSEvent*)event +{ + NSString* chars = [event characters];; + if (win->keyboardFunc) { + win->keyboardFunc(win, false, [chars characterAtIndex:0]); + } +} + +@end + +struct PuglPlatformDataImpl { + PuglOpenGLView* view; + NSModalSession session; + id window; +}; + +PuglWindow* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable) +{ + PuglWindow* win = (PuglWindow*)calloc(1, sizeof(PuglWindow)); + win->width = width; + win->height = height; + + win->impl = (PuglPlatformData*)calloc(1, sizeof(PuglPlatformData)); + + PuglPlatformData* impl = win->impl; + + [NSAutoreleasePool new]; + [NSApplication sharedApplication]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + id window = [[[NSWindow alloc] + initWithContentRect:NSMakeRect(0, 0, 512, 512) + styleMask:NSTitledWindowMask + backing:NSBackingStoreBuffered + defer:NO] + autorelease]; + + [window cascadeTopLeftFromPoint:NSMakePoint(20, 20)]; + [window setTitle:titleString]; + [window setAcceptsMouseMovedEvents:YES]; + + impl->view = [PuglOpenGLView new]; + impl->window = window; + impl->view->win = win; + + [window setContentView:impl->view]; + [NSApp activateIgnoringOtherApps:YES]; + [window makeFirstResponder:impl->view]; + + impl->session = [NSApp beginModalSessionForWindow:win->impl->window]; + + return win; +} + +void +puglDestroy(PuglWindow* win) +{ + [NSApp endModalSession:win->impl->session]; + [win->impl->view release]; + free(win->impl); + free(win); +} + +void +puglDisplay(PuglWindow* win) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (win->displayFunc) { + win->displayFunc(win); + } + + glFlush(); + win->redisplay = false; +} + +PuglStatus +puglProcessEvents(PuglWindow* win) +{ + NSInteger response = [NSApp runModalSession:win->impl->session]; + if (response != NSRunContinuesResponse) { + if (win->closeFunc) { + win->closeFunc(win); + } + } + + if (win->redisplay) { + puglDisplay(win); + } + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglWindow* win) +{ + win->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglWindow* win) +{ + return (PuglNativeWindow)win->impl->view; +} diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c index c6cc2b5..f4aa933 100644 --- a/pugl/pugl_x11.c +++ b/pugl/pugl_x11.c @@ -176,8 +176,8 @@ puglReshape(PuglWindow* win, int width, int height) // No custom reshape function, do something reasonable glMatrixMode(GL_PROJECTION); glLoadIdentity(); - gluPerspective(45.0f, win->width/(float)win->height, 1.0f, 10.0f); - glViewport(0, 0, win->width, win->height); + gluPerspective(45.0f, width/(float)height, 1.0f, 10.0f); + glViewport(0, 0, width, height); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -203,6 +203,8 @@ puglDisplay(PuglWindow* win) if (win->impl->doubleBuffered) { glXSwapBuffers(win->impl->display, win->impl->win); } + + win->redisplay = false; } PuglStatus diff --git a/pugl_test.c b/pugl_test.c index 05efa61..b1dd316 100644 --- a/pugl_test.c +++ b/pugl_test.c @@ -18,17 +18,8 @@ @file pugl_test.c A simple Pugl test that creates a top-level window. */ -/* - This program uses only the the Pugl and OpenGL APIs, but the Windows - gl.h is broken and requires windows.h to be included first... -*/ -#ifdef _WIN32 -# include -#endif - #include -#include "GL/gl.h" #include "pugl/pugl.h" static int quit = 0; diff --git a/wscript b/wscript index a76cf00..79c805e 100644 --- a/wscript +++ b/wscript @@ -77,12 +77,19 @@ def build(bld): autowaf.build_pc(bld, 'PUGL', PUGL_VERSION, PUGL_MAJOR_VERSION, [], {'PUGL_MAJOR_VERSION' : PUGL_MAJOR_VERSION}) - libflags = [ '-fvisibility=hidden' ] + libflags = [ '-fvisibility=hidden' ] + framework = [] + libs = [] if Options.platform == 'win32': lang = 'cxx' lib_source = ['pugl/pugl_win.cpp'] libs = ['opengl32', 'glu32', 'gdi32', 'user32'] defines = [] + elif Options.platform == 'darwin': + lang = 'c' # Objective C, actually + lib_source = ['pugl/pugl_osx.m'] + framework = ['Cocoa', 'OpenGL'] + defines = [] else: lang = 'c' lib_source = ['pugl/pugl_x11.c'] @@ -98,6 +105,7 @@ def build(bld): source = lib_source, includes = ['.', './src'], lib = libs, + framework = framework, name = 'libpugl', target = 'pugl-%s' % PUGL_MAJOR_VERSION, vnum = PUGL_LIB_VERSION, @@ -113,6 +121,7 @@ def build(bld): source = lib_source, includes = ['.', './src'], lib = libs, + framework = framework, name = 'libpugl_static', target = 'pugl-%s' % PUGL_MAJOR_VERSION, vnum = PUGL_LIB_VERSION, @@ -130,6 +139,7 @@ def build(bld): includes = ['.', './src'], use = 'libpugl_static', lib = test_libs, + framework = framework, target = 'pugl_test', install_path = '', defines = defines, @@ -138,3 +148,8 @@ def build(bld): def lint(ctx): subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* pugl/*', shell=True) +from waflib import TaskGen +@TaskGen.extension('.m') +def m_hook(self, node): + "Alias .m files to be compiled the same as .c files, gcc will do the right thing." + return self.create_compiled_task('c', node) -- cgit v1.2.1