aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pugl/pugl.h16
-rw-r--r--pugl/pugl_osx.m291
-rw-r--r--pugl/pugl_x11.c6
-rw-r--r--pugl_test.c9
-rw-r--r--wscript17
5 files changed, 327 insertions, 12 deletions
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 <stdint.h>
+/*
+ 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 <windows.h> /* 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 <http://drobilla.net>
+
+ 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 <stdlib.h>
+
+#import <Cocoa/Cocoa.h>
+
+#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 <windows.h>
-#endif
-
#include <stdio.h>
-#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)