summaryrefslogtreecommitdiff
path: root/pugl
diff options
context:
space:
mode:
authorJonas Suhr Christensen <jsc@umbraculum.org>2014-01-18 10:17:58 +0100
committerJonas Suhr Christensen <jsc@umbraculum.org>2014-01-18 10:17:58 +0100
commitfe9e38995f5a0abc196e9600c38d95385d6bf84f (patch)
treed106ff875aba7289296f5ed47626f50fc0930d2c /pugl
parent5f1e301d951c10533b101bcddc7a0262780743a3 (diff)
Added PuGl window drawing. Enable with './configure -with-pugl'.
Diffstat (limited to 'pugl')
-rw-r--r--pugl/.svn/entries1
-rw-r--r--pugl/.svn/format1
-rw-r--r--pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base10
-rw-r--r--pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base376
-rw-r--r--pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base155
-rw-r--r--pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-basebin0 -> 92192 bytes
-rw-r--r--pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base400
-rw-r--r--pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base357
-rw-r--r--pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base5
-rw-r--r--pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base143
-rw-r--r--pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base386
-rw-r--r--pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base13
-rw-r--r--pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base193
-rw-r--r--pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base27
-rw-r--r--pugl/.svn/wc.dbbin0 -> 39936 bytes
-rw-r--r--pugl/AUTHORS5
-rw-r--r--pugl/COPYING13
-rw-r--r--pugl/README27
-rw-r--r--pugl/pugl.pc.in10
-rw-r--r--pugl/pugl/pugl.h357
-rw-r--r--pugl/pugl/pugl_internal.h143
-rw-r--r--pugl/pugl/pugl_osx.m400
-rw-r--r--pugl/pugl/pugl_win.cpp376
-rw-r--r--pugl/pugl/pugl_x11.c386
-rw-r--r--pugl/pugl_test.c193
-rwxr-xr-xpugl/wafbin0 -> 92192 bytes
-rw-r--r--pugl/wscript155
27 files changed, 4132 insertions, 0 deletions
diff --git a/pugl/.svn/entries b/pugl/.svn/entries
new file mode 100644
index 0000000..48082f7
--- /dev/null
+++ b/pugl/.svn/entries
@@ -0,0 +1 @@
+12
diff --git a/pugl/.svn/format b/pugl/.svn/format
new file mode 100644
index 0000000..48082f7
--- /dev/null
+++ b/pugl/.svn/format
@@ -0,0 +1 @@
+12
diff --git a/pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base b/pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base
new file mode 100644
index 0000000..d3606cd
--- /dev/null
+++ b/pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base
@@ -0,0 +1,10 @@
+prefix=@PREFIX@
+exec_prefix=@EXEC_PREFIX@
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: Pugl
+Version: @PUGL_VERSION@
+Description: Lightweight portable OpenGL API
+Libs: -L${libdir} -l@LIB_PUGL@
+Cflags: -I${includedir}/pugl-@PUGL_MAJOR_VERSION@
diff --git a/pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base b/pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base
new file mode 100644
index 0000000..5f85d12
--- /dev/null
+++ b/pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base
@@ -0,0 +1,376 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_win.cpp Windows/WGL Pugl Implementation.
+*/
+
+#include <windows.h>
+#include <windowsx.h>
+#include <GL/gl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pugl_internal.h"
+
+#ifndef WM_MOUSEWHEEL
+# define WM_MOUSEWHEEL 0x020A
+#endif
+#ifndef WM_MOUSEHWHEEL
+# define WM_MOUSEHWHEEL 0x020E
+#endif
+#ifndef WHEEL_DELTA
+# define WHEEL_DELTA 120
+#endif
+
+const int LOCAL_CLOSE_MSG = WM_USER + 50;
+
+struct PuglInternalsImpl {
+ HWND hwnd;
+ HDC hdc;
+ HGLRC hglrc;
+ WNDCLASS wc;
+};
+
+LRESULT CALLBACK
+wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = width;
+ view->height = height;
+
+ // FIXME: This is nasty, and pugl should not have static anything.
+ // Should class be a parameter? Does this make sense on other platforms?
+ static int wc_count = 0;
+ char classNameBuf[256];
+ _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++);
+
+ impl->wc.style = CS_OWNDC;
+ impl->wc.lpfnWndProc = wndProc;
+ impl->wc.cbClsExtra = 0;
+ impl->wc.cbWndExtra = 0;
+ impl->wc.hInstance = 0;
+ impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ impl->wc.lpszMenuName = NULL;
+ impl->wc.lpszClassName = classNameBuf;
+ RegisterClass(&impl->wc);
+
+ int winFlags = WS_POPUPWINDOW | WS_CAPTION;
+ if (resizable) {
+ winFlags |= WS_SIZEBOX;
+ }
+
+ // Adjust the overall window size to accomodate our requested client size
+ RECT wr = { 0, 0, width, height };
+ AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
+
+ impl->hwnd = CreateWindowEx(
+ WS_EX_TOPMOST,
+ classNameBuf, title,
+ (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags),
+ 0, 0, wr.right-wr.left, wr.bottom-wr.top,
+ (HWND)parent, NULL, NULL, NULL);
+
+ if (!impl->hwnd) {
+ free(impl);
+ free(view);
+ return NULL;
+ }
+
+ SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view);
+
+ impl->hdc = GetDC(impl->hwnd);
+
+ PIXELFORMATDESCRIPTOR pfd;
+ ZeroMemory(&pfd, sizeof(pfd));
+ pfd.nSize = sizeof(pfd);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.cDepthBits = 16;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+
+ int format = ChoosePixelFormat(impl->hdc, &pfd);
+ SetPixelFormat(impl->hdc, format, &pfd);
+
+ impl->hglrc = wglCreateContext(impl->hdc);
+ wglMakeCurrent(impl->hdc, impl->hglrc);
+
+ view->width = width;
+ view->height = height;
+
+ return view;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(view->impl->hglrc);
+ ReleaseDC(view->impl->hwnd, view->impl->hdc);
+ DestroyWindow(view->impl->hwnd);
+ UnregisterClass(view->impl->wc.lpszClassName, NULL);
+ free(view->impl);
+ free(view);
+}
+
+static void
+puglReshape(PuglView* view, int width, int height)
+{
+ wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
+
+ if (view->reshapeFunc) {
+ view->reshapeFunc(view, width, height);
+ } else {
+ puglDefaultReshape(view, width, height);
+ }
+
+ view->width = width;
+ view->height = height;
+}
+
+void
+puglDisplay(PuglView* view)
+{
+ wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+
+ glFlush();
+ SwapBuffers(view->impl->hdc);
+ view->redisplay = false;
+}
+
+static PuglKey
+keySymToSpecial(int sym)
+{
+ switch (sym) {
+ case VK_F1: return PUGL_KEY_F1;
+ case VK_F2: return PUGL_KEY_F2;
+ case VK_F3: return PUGL_KEY_F3;
+ case VK_F4: return PUGL_KEY_F4;
+ case VK_F5: return PUGL_KEY_F5;
+ case VK_F6: return PUGL_KEY_F6;
+ case VK_F7: return PUGL_KEY_F7;
+ case VK_F8: return PUGL_KEY_F8;
+ case VK_F9: return PUGL_KEY_F9;
+ case VK_F10: return PUGL_KEY_F10;
+ case VK_F11: return PUGL_KEY_F11;
+ case VK_F12: return PUGL_KEY_F12;
+ case VK_LEFT: return PUGL_KEY_LEFT;
+ case VK_UP: return PUGL_KEY_UP;
+ case VK_RIGHT: return PUGL_KEY_RIGHT;
+ case VK_DOWN: return PUGL_KEY_DOWN;
+ case VK_PRIOR: return PUGL_KEY_PAGE_UP;
+ case VK_NEXT: return PUGL_KEY_PAGE_DOWN;
+ case VK_HOME: return PUGL_KEY_HOME;
+ case VK_END: return PUGL_KEY_END;
+ case VK_INSERT: return PUGL_KEY_INSERT;
+ case VK_SHIFT: return PUGL_KEY_SHIFT;
+ case VK_CONTROL: return PUGL_KEY_CTRL;
+ case VK_MENU: return PUGL_KEY_ALT;
+ case VK_LWIN: return PUGL_KEY_SUPER;
+ case VK_RWIN: return PUGL_KEY_SUPER;
+ }
+ return (PuglKey)0;
+}
+
+static void
+processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam)
+{
+ view->event_timestamp_ms = GetMessageTime();
+ if (press) {
+ SetCapture(view->impl->hwnd);
+ } else {
+ ReleaseCapture();
+ }
+
+ if (view->mouseFunc) {
+ view->mouseFunc(view, button, press,
+ GET_X_LPARAM(lParam),
+ GET_Y_LPARAM(lParam));
+ }
+}
+
+static void
+setModifiers(PuglView* view)
+{
+ view->mods = 0;
+ view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0;
+ view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0;
+ view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0;
+ view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0;
+ view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0;
+}
+
+static LRESULT
+handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ PuglKey key;
+
+ setModifiers(view);
+ switch (message) {
+ case WM_CREATE:
+ case WM_SHOWWINDOW:
+ case WM_SIZE:
+ RECT rect;
+ GetClientRect(view->impl->hwnd, &rect);
+ puglReshape(view, rect.right, rect.bottom);
+ view->width = rect.right;
+ view->height = rect.bottom;
+ break;
+ case WM_PAINT:
+ BeginPaint(view->impl->hwnd, &ps);
+ puglDisplay(view);
+ EndPaint(view->impl->hwnd, &ps);
+ break;
+ case WM_MOUSEMOVE:
+ if (view->motionFunc) {
+ view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ processMouseEvent(view, 1, true, lParam);
+ break;
+ case WM_MBUTTONDOWN:
+ processMouseEvent(view, 2, true, lParam);
+ break;
+ case WM_RBUTTONDOWN:
+ processMouseEvent(view, 3, true, lParam);
+ break;
+ case WM_LBUTTONUP:
+ processMouseEvent(view, 1, false, lParam);
+ break;
+ case WM_MBUTTONUP:
+ processMouseEvent(view, 2, false, lParam);
+ break;
+ case WM_RBUTTONUP:
+ processMouseEvent(view, 3, false, lParam);
+ break;
+ case WM_MOUSEWHEEL:
+ if (view->scrollFunc) {
+ view->scrollFunc(
+ view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+ (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA);
+ }
+ break;
+ case WM_MOUSEHWHEEL:
+ if (view->scrollFunc) {
+ view->scrollFunc(
+ view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+ (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0);
+ }
+ break;
+ case WM_KEYDOWN:
+ view->event_timestamp_ms = (GetMessageTime());
+ if (view->ignoreKeyRepeat && (lParam & (1 << 30))) {
+ break;
+ } // else nobreak
+ case WM_KEYUP:
+ if ((key = keySymToSpecial(wParam))) {
+ if (view->specialFunc) {
+ view->specialFunc(view, message == WM_KEYDOWN, key);
+ }
+ } else if (view->keyboardFunc) {
+ view->keyboardFunc(view, message == WM_KEYDOWN, wParam);
+ }
+ break;
+ case WM_QUIT:
+ case LOCAL_CLOSE_MSG:
+ if (view->closeFunc) {
+ view->closeFunc(view);
+ }
+ break;
+ default:
+ return DefWindowProc(
+ view->impl->hwnd, message, wParam, lParam);
+ }
+
+ return 0;
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ MSG msg;
+ while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) {
+ handleMessage(view, msg.message, msg.wParam, msg.lParam);
+ }
+
+
+ if (view->redisplay) {
+ InvalidateRect(view->impl->hwnd, NULL, FALSE);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+LRESULT CALLBACK
+wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA);
+ switch (message) {
+ case WM_CREATE:
+ PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
+ return 0;
+ case WM_CLOSE:
+ PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam);
+ return 0;
+ case WM_DESTROY:
+ return 0;
+ default:
+ if (view) {
+ return handleMessage(view, message, wParam, lParam);
+ } else {
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ }
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return (PuglNativeWindow)view->impl->hwnd;
+}
diff --git a/pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base b/pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base
new file mode 100644
index 0000000..3682d14
--- /dev/null
+++ b/pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+import glob
+import os
+import sys
+
+from waflib.extras import autowaf as autowaf
+import waflib.Logs as Logs, waflib.Options as Options
+
+# Version of this package (even if built as a child)
+PUGL_VERSION = '0.2.0'
+PUGL_MAJOR_VERSION = '0'
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+# Pugl uses the same version number for both library and package
+PUGL_LIB_VERSION = PUGL_VERSION
+
+# Variables for 'waf dist'
+APPNAME = 'pugl'
+VERSION = PUGL_VERSION
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ opt.load('compiler_c')
+ if Options.platform == 'win32':
+ opt.load('compiler_cxx')
+ autowaf.set_options(opt)
+ opt.add_option('--test', action='store_true', default=False, dest='build_tests',
+ help="Build unit tests")
+ opt.add_option('--static', action='store_true', default=False, dest='static',
+ help="Build static library")
+ opt.add_option('--shared', action='store_true', default=False, dest='shared',
+ help="Build shared library")
+
+def configure(conf):
+ conf.load('compiler_c')
+ if Options.platform == 'win32':
+ conf.load('compiler_cxx')
+ autowaf.configure(conf)
+ autowaf.display_header('Pugl Configuration')
+
+ if conf.env['MSVC_COMPILER']:
+ conf.env.append_unique('CFLAGS', ['-TP', '-MD'])
+ else:
+ conf.env.append_unique('CFLAGS', '-std=c99')
+
+ # Shared library building is broken on win32 for some reason
+ conf.env['BUILD_TESTS'] = Options.options.build_tests
+ conf.env['BUILD_SHARED'] = (Options.platform != 'win32' or
+ Options.options.shared)
+ conf.env['BUILD_STATIC'] = (Options.options.build_tests or
+ Options.options.static)
+
+ autowaf.define(conf, 'PUGL_VERSION', PUGL_VERSION)
+ conf.write_config_header('pugl_config.h', remove=False)
+
+ conf.env['INCLUDES_PUGL'] = ['%s/pugl-%s' % (conf.env['INCLUDEDIR'],
+ PUGL_MAJOR_VERSION)]
+ conf.env['LIBPATH_PUGL'] = [conf.env['LIBDIR']]
+ conf.env['LIB_PUGL'] = ['pugl-%s' % PUGL_MAJOR_VERSION];
+
+ autowaf.display_msg(conf, "Static library", str(conf.env['BUILD_STATIC']))
+ autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS']))
+ print('')
+
+def build(bld):
+ # C Headers
+ includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION
+ bld.install_files(includedir, bld.path.ant_glob('pugl/*.h'))
+
+ # Pkgconfig file
+ autowaf.build_pc(bld, 'PUGL', PUGL_VERSION, PUGL_MAJOR_VERSION, [],
+ {'PUGL_MAJOR_VERSION' : PUGL_MAJOR_VERSION})
+
+ 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']
+ libs = ['X11', 'GL', 'GLU']
+ defines = []
+ if bld.env['MSVC_COMPILER']:
+ libflags = []
+
+ # Shared Library
+ if bld.env['BUILD_SHARED']:
+ obj = bld(features = '%s %sshlib' % (lang, lang),
+ export_includes = ['.'],
+ source = lib_source,
+ includes = ['.', './src'],
+ lib = libs,
+ framework = framework,
+ name = 'libpugl',
+ target = 'pugl-%s' % PUGL_MAJOR_VERSION,
+ vnum = PUGL_LIB_VERSION,
+ install_path = '${LIBDIR}',
+ defines = defines,
+ cflags = libflags + [ '-DPUGL_SHARED',
+ '-DPUGL_INTERNAL' ])
+
+ # Static library
+ if bld.env['BUILD_STATIC']:
+ obj = bld(features = '%s %sstlib' % (lang, lang),
+ export_includes = ['.'],
+ source = lib_source,
+ includes = ['.', './src'],
+ lib = libs,
+ framework = framework,
+ name = 'libpugl_static',
+ target = 'pugl-%s' % PUGL_MAJOR_VERSION,
+ vnum = PUGL_LIB_VERSION,
+ install_path = '${LIBDIR}',
+ defines = defines,
+ cflags = ['-DPUGL_INTERNAL'])
+
+ if bld.env['BUILD_TESTS']:
+ test_libs = libs
+ test_cflags = ['']
+
+ # Unit test program
+ obj = bld(features = 'c cprogram',
+ source = 'pugl_test.c',
+ includes = ['.', './src'],
+ use = 'libpugl_static',
+ lib = test_libs,
+ framework = framework,
+ target = 'pugl_test',
+ install_path = '',
+ defines = defines,
+ cflags = test_cflags)
+
+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)
diff --git a/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base b/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base
new file mode 100644
index 0000000..32ae229
--- /dev/null
+++ b/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base
Binary files differ
diff --git a/pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base b/pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base
new file mode 100644
index 0000000..c318d91
--- /dev/null
+++ b/pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base
@@ -0,0 +1,400 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_osx.m OSX/Cocoa Pugl Implementation.
+*/
+
+#include <stdlib.h>
+
+#import <Cocoa/Cocoa.h>
+
+#include "pugl_internal.h"
+
+@interface PuglWindow : NSWindow
+{
+@public
+ PuglView* puglview;
+}
+
+- (id) initWithContentRect:(NSRect)contentRect
+ styleMask:(unsigned int)aStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)flag;
+- (void) setPuglview:(PuglView*)view;
+- (BOOL) windowShouldClose:(id)sender;
+@end
+
+@implementation PuglWindow
+
+- (id)initWithContentRect:(NSRect)contentRect
+ styleMask:(unsigned int)aStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)flag
+{
+ NSWindow* result = [super initWithContentRect:contentRect
+ styleMask:(NSClosableWindowMask |
+ NSTitledWindowMask |
+ NSResizableWindowMask)
+ backing:NSBackingStoreBuffered defer:NO];
+
+ [result setAcceptsMouseMovedEvents:YES];
+ [result setLevel: CGShieldingWindowLevel() + 1];
+
+ return result;
+}
+
+- (void)setPuglview:(PuglView*)view
+{
+ puglview = view;
+ [self setContentSize:NSMakeSize(view->width, view->height) ];
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ if (puglview->closeFunc)
+ puglview->closeFunc(puglview);
+ return YES;
+}
+
+@end
+
+void
+puglDisplay(PuglView* view)
+{
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+}
+
+@interface PuglOpenGLView : NSOpenGLView
+{
+ int colorBits;
+ int depthBits;
+@public
+ PuglView* puglview;
+
+ NSTrackingArea* trackingArea;
+}
+
+- (id) initWithFrame:(NSRect)frame
+ colorBits:(int)numColorBits
+ depthBits:(int)numDepthBits;
+- (void) reshape;
+- (void) drawRect:(NSRect)rect;
+- (void) mouseMoved:(NSEvent*)event;
+- (void) mouseDragged:(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;
+- (void) flagsChanged:(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 (puglview) {
+ /* NOTE: Apparently reshape gets called when the GC gets around to
+ deleting the view (?), so we must have reset puglview to NULL when
+ this comes around.
+ */
+ if (puglview->reshapeFunc) {
+ puglview->reshapeFunc(puglview, width, height);
+ } else {
+ puglDefaultReshape(puglview, width, height);
+ }
+
+ puglview->width = width;
+ puglview->height = height;
+ }
+}
+
+- (void) drawRect:(NSRect)rect
+{
+ puglDisplay(puglview);
+ glFlush();
+ glSwapAPPLE();
+}
+
+static unsigned
+getModifiers(PuglView* view, NSEvent* ev)
+{
+ const unsigned modifierFlags = [ev modifierFlags];
+
+ view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX);
+
+ unsigned mods = 0;
+ mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
+ mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
+ mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0;
+ mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0;
+ return mods;
+}
+
+-(void)updateTrackingAreas
+{
+ if (trackingArea != nil) {
+ [self removeTrackingArea:trackingArea];
+ [trackingArea release];
+ }
+
+ const int opts = (NSTrackingMouseEnteredAndExited |
+ NSTrackingMouseMoved |
+ NSTrackingActiveAlways);
+ trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
+ options:opts
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:trackingArea];
+}
+
+- (void)mouseEntered:(NSEvent*)theEvent
+{
+ [self updateTrackingAreas];
+}
+
+- (void)mouseExited:(NSEvent*)theEvent
+{
+}
+
+- (void) mouseMoved:(NSEvent*)event
+{
+ if (puglview->motionFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) mouseDragged:(NSEvent*)event
+{
+ if (puglview->motionFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) mouseDown:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) mouseUp:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y);
+ }
+ [self updateTrackingAreas];
+}
+
+- (void) rightMouseDown:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) rightMouseUp:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) scrollWheel:(NSEvent*)event
+{
+ if (puglview->scrollFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->scrollFunc(puglview,
+ loc.x, puglview->height - loc.y,
+ [event deltaX], [event deltaY]);
+ }
+ [self updateTrackingAreas];
+}
+
+- (void) keyDown:(NSEvent*)event
+{
+ if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) {
+ NSString* chars = [event characters];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]);
+ }
+}
+
+- (void) keyUp:(NSEvent*)event
+{
+ if (puglview->keyboardFunc) {
+ NSString* chars = [event characters];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]);
+ }
+}
+
+- (void) flagsChanged:(NSEvent*)event
+{
+ if (puglview->specialFunc) {
+ const unsigned mods = getModifiers(puglview, [event modifierFlags]);
+ if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT);
+ } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL);
+ } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT);
+ } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER);
+ }
+ puglview->mods = mods;
+ }
+}
+
+@end
+
+struct PuglInternalsImpl {
+ PuglOpenGLView* glview;
+ id window;
+};
+
+PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = width;
+ view->height = height;
+
+ [NSAutoreleasePool new];
+ [NSApplication sharedApplication];
+
+ NSString* titleString = [[NSString alloc]
+ initWithBytes:title
+ length:strlen(title)
+ encoding:NSUTF8StringEncoding];
+
+ id window = [[PuglWindow new]retain];
+
+ [window setPuglview:view];
+ [window setTitle:titleString];
+
+ impl->glview = [PuglOpenGLView new];
+ impl->window = window;
+ impl->glview->puglview = view;
+
+ [window setContentView:impl->glview];
+ [NSApp activateIgnoringOtherApps:YES];
+ [window makeFirstResponder:impl->glview];
+
+ [window makeKeyAndOrderFront:window];
+
+ return view;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ view->impl->glview->puglview = NULL;
+ [view->impl->window close];
+ [view->impl->glview release];
+ [view->impl->window release];
+ free(view->impl);
+ free(view);
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ [view->impl->glview setNeedsDisplay: YES];
+
+ return PUGL_SUCCESS;
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return (PuglNativeWindow)view->impl->glview;
+}
diff --git a/pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base b/pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base
new file mode 100644
index 0000000..2a6a59f
--- /dev/null
+++ b/pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base
@@ -0,0 +1,357 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl.h API for Pugl, a minimal portable API for OpenGL.
+*/
+
+#ifndef PUGL_H_INCLUDED
+#define PUGL_H_INCLUDED
+
+#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"
+#else
+# ifdef _WIN32
+# include <windows.h> /* Broken Windows GL headers require this */
+# endif
+# include "GL/gl.h"
+#endif
+
+#ifdef PUGL_SHARED
+# ifdef _WIN32
+# define PUGL_LIB_IMPORT __declspec(dllimport)
+# define PUGL_LIB_EXPORT __declspec(dllexport)
+# else
+# define PUGL_LIB_IMPORT __attribute__((visibility("default")))
+# define PUGL_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef PUGL_INTERNAL
+# define PUGL_API PUGL_LIB_EXPORT
+# else
+# define PUGL_API PUGL_LIB_IMPORT
+# endif
+#else
+# define PUGL_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+/**
+ @defgroup pugl Pugl
+ A minimal portable API for OpenGL.
+ @{
+*/
+
+/**
+ An OpenGL view.
+*/
+typedef struct PuglViewImpl PuglView;
+
+/**
+ A native window handle.
+
+ On X11, this is a Window.
+ On OSX, this is an NSView*.
+ On Windows, this is a HWND.
+*/
+typedef intptr_t PuglNativeWindow;
+
+/**
+ Return status code.
+*/
+typedef enum {
+ PUGL_SUCCESS = 0
+} PuglStatus;
+
+/**
+ Convenience symbols for ASCII control characters.
+*/
+typedef enum {
+ PUGL_CHAR_BACKSPACE = 0x08,
+ PUGL_CHAR_ESCAPE = 0x1B,
+ PUGL_CHAR_DELETE = 0x7F
+} PuglChar;
+
+/**
+ Special (non-Unicode) keyboard keys.
+*/
+typedef enum {
+ PUGL_KEY_F1 = 1,
+ PUGL_KEY_F2,
+ PUGL_KEY_F3,
+ PUGL_KEY_F4,
+ PUGL_KEY_F5,
+ PUGL_KEY_F6,
+ PUGL_KEY_F7,
+ PUGL_KEY_F8,
+ PUGL_KEY_F9,
+ PUGL_KEY_F10,
+ PUGL_KEY_F11,
+ PUGL_KEY_F12,
+ PUGL_KEY_LEFT,
+ PUGL_KEY_UP,
+ PUGL_KEY_RIGHT,
+ PUGL_KEY_DOWN,
+ PUGL_KEY_PAGE_UP,
+ PUGL_KEY_PAGE_DOWN,
+ PUGL_KEY_HOME,
+ PUGL_KEY_END,
+ PUGL_KEY_INSERT,
+ PUGL_KEY_SHIFT,
+ PUGL_KEY_CTRL,
+ PUGL_KEY_ALT,
+ PUGL_KEY_SUPER
+} PuglKey;
+
+/**
+ Keyboard modifier flags.
+*/
+typedef enum {
+ PUGL_MOD_SHIFT = 1, /**< Shift key */
+ PUGL_MOD_CTRL = 1 << 1, /**< Control key */
+ PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
+ PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
+} PuglMod;
+
+/**
+ Handle for opaque user data.
+*/
+typedef void* PuglHandle;
+
+/**
+ A function called when the window is closed.
+*/
+typedef void (*PuglCloseFunc)(PuglView* view);
+
+/**
+ A function called to draw the view contents with OpenGL.
+*/
+typedef void (*PuglDisplayFunc)(PuglView* view);
+
+/**
+ A function called when a key is pressed or released.
+ @param view The view the event occured in.
+ @param press True if the key was pressed, false if released.
+ @param key Unicode point of the key pressed.
+*/
+typedef void (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key);
+
+/**
+ A function called when the pointer moves.
+ @param view The view the event occured in.
+ @param x The window-relative x coordinate of the pointer.
+ @param y The window-relative y coordinate of the pointer.
+*/
+typedef void (*PuglMotionFunc)(PuglView* view, int x, int y);
+
+/**
+ A function called when a mouse button is pressed or released.
+ @param view The view the event occured in.
+ @param button The button number (1 = left, 2 = middle, 3 = right).
+ @param press True if the key was pressed, false if released.
+ @param x The window-relative x coordinate of the pointer.
+ @param y The window-relative y coordinate of the pointer.
+*/
+typedef void (*PuglMouseFunc)(
+ PuglView* view, int button, bool press, int x, int y);
+
+/**
+ A function called when the view is resized.
+ @param view The view being resized.
+ @param width The new view width.
+ @param height The new view height.
+*/
+typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height);
+
+/**
+ A function called on scrolling (e.g. mouse wheel or track pad).
+
+ The distances used here are in "lines", a single tick of a clicking mouse
+ wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and
+ devices support finer resolution and/or higher values for fast scrolls,
+ so programs should handle any value gracefully.
+
+ @param view The view being scrolled.
+ @param dx The scroll x distance.
+ @param dx The scroll y distance.
+*/
+typedef void (*PuglScrollFunc)(PuglView* view,
+ int x,
+ int y,
+ float dx,
+ float dy);
+
+/**
+ A function called when a special key is pressed or released.
+
+ This callback allows the use of keys that do not have unicode points.
+
+ @param view The view the event occured in.
+ @param press True if the key was pressed, false if released.
+ @param key The key pressed.
+*/
+typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key);
+
+/**
+ Create a new GL window.
+ @param parent Parent window, or 0 for top level.
+ @param title Window title, or NULL.
+ @param width Window width in pixels.
+ @param height Window height in pixels.
+ @param resizable Whether window should be user resizable.
+ @param visible Whether window should be initially visible.
+*/
+PUGL_API PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible);
+
+/**
+ Set the handle to be passed to all callbacks.
+
+ This is generally a pointer to a struct which contains all necessary state.
+ Everything needed in callbacks should be here, not in static variables.
+
+ Note the lack of this facility makes GLUT unsuitable for plugins or
+ non-trivial programs; this mistake is largely why Pugl exists.
+*/
+PUGL_API void
+puglSetHandle(PuglView* view, PuglHandle handle);
+
+/**
+ Get the handle to be passed to all callbacks.
+*/
+PUGL_API PuglHandle
+puglGetHandle(PuglView* view);
+
+/**
+ Return the timestamp (if any) of the currently-processing event.
+*/
+PUGL_API uint32_t
+puglGetEventTimestamp(PuglView* view);
+
+/**
+ Get the currently active modifiers (PuglMod flags).
+
+ This should only be called from an event handler.
+*/
+PUGL_API int
+puglGetModifiers(PuglView* view);
+
+/**
+ Ignore synthetic repeated key events.
+*/
+PUGL_API void
+puglIgnoreKeyRepeat(PuglView* view, bool ignore);
+
+/**
+ Set the function to call when the window is closed.
+*/
+PUGL_API void
+puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc);
+
+/**
+ Set the display function which should draw the UI using GL.
+*/
+PUGL_API void
+puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc);
+
+/**
+ Set the function to call on keyboard events.
+*/
+PUGL_API void
+puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc);
+
+/**
+ Set the function to call on mouse motion.
+*/
+PUGL_API void
+puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc);
+
+/**
+ Set the function to call on mouse button events.
+*/
+PUGL_API void
+puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc);
+
+/**
+ Set the function to call on scroll events.
+*/
+PUGL_API void
+puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc);
+
+/**
+ Set the function to call on special events.
+*/
+PUGL_API void
+puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc);
+
+/**
+ Set the function to call when the window size changes.
+*/
+PUGL_API void
+puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc);
+
+/**
+ Return the native window handle.
+*/
+PUGL_API PuglNativeWindow
+puglGetNativeWindow(PuglView* view);
+
+/**
+ Process all pending window events.
+
+ This handles input events as well as rendering, so it should be called
+ regularly and rapidly enough to keep the UI responsive.
+*/
+PUGL_API PuglStatus
+puglProcessEvents(PuglView* view);
+
+/**
+ Request a redisplay on the next call to puglProcessEvents().
+*/
+PUGL_API void
+puglPostRedisplay(PuglView* view);
+
+/**
+ Destroy a GL window.
+*/
+PUGL_API void
+puglDestroy(PuglView* view);
+
+/**
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PUGL_H_INCLUDED */
diff --git a/pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base b/pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base
new file mode 100644
index 0000000..202ec66
--- /dev/null
+++ b/pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base
@@ -0,0 +1,5 @@
+Author:
+ David Robillard <d@drobilla.net>
+
+Original GLX inspiration, portability fixes:
+ Ben Loftis
diff --git a/pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base b/pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base
new file mode 100644
index 0000000..8cdada8
--- /dev/null
+++ b/pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base
@@ -0,0 +1,143 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_internal.h Private platform-independent definitions.
+
+ Note this file contains function definitions, so it must be compiled into
+ the final binary exactly once. Each platform specific implementation file
+ including it once should achieve this.
+*/
+
+#include "pugl.h"
+
+typedef struct PuglInternalsImpl PuglInternals;
+
+struct PuglViewImpl {
+ PuglHandle handle;
+ PuglCloseFunc closeFunc;
+ PuglDisplayFunc displayFunc;
+ PuglKeyboardFunc keyboardFunc;
+ PuglMotionFunc motionFunc;
+ PuglMouseFunc mouseFunc;
+ PuglReshapeFunc reshapeFunc;
+ PuglScrollFunc scrollFunc;
+ PuglSpecialFunc specialFunc;
+
+ PuglInternals* impl;
+
+ int width;
+ int height;
+ int mods;
+ bool mouse_in_view;
+ bool ignoreKeyRepeat;
+ bool redisplay;
+ uint32_t event_timestamp_ms;
+};
+
+void
+puglSetHandle(PuglView* view, PuglHandle handle)
+{
+ view->handle = handle;
+}
+
+PuglHandle
+puglGetHandle(PuglView* view)
+{
+ return view->handle;
+}
+
+uint32_t
+puglGetEventTimestamp(PuglView* view)
+{
+ return view->event_timestamp_ms;
+}
+
+int
+puglGetModifiers(PuglView* view)
+{
+ return view->mods;
+}
+
+void
+puglDefaultReshape(PuglView* view, int width, int height)
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, height, 0, 0, 1);
+ glViewport(0, 0, width, height);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ return;
+
+ // unused
+ (void)view;
+}
+
+void
+puglIgnoreKeyRepeat(PuglView* view, bool ignore)
+{
+ view->ignoreKeyRepeat = ignore;
+}
+
+void
+puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc)
+{
+ view->closeFunc = closeFunc;
+}
+
+void
+puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc)
+{
+ view->displayFunc = displayFunc;
+}
+
+void
+puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc)
+{
+ view->keyboardFunc = keyboardFunc;
+}
+
+void
+puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc)
+{
+ view->motionFunc = motionFunc;
+}
+
+void
+puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc)
+{
+ view->mouseFunc = mouseFunc;
+}
+
+void
+puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc)
+{
+ view->reshapeFunc = reshapeFunc;
+}
+
+void
+puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc)
+{
+ view->scrollFunc = scrollFunc;
+}
+
+void
+puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc)
+{
+ view->specialFunc = specialFunc;
+}
diff --git a/pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base b/pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base
new file mode 100644
index 0000000..c25b273
--- /dev/null
+++ b/pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base
@@ -0,0 +1,386 @@
+/*
+ Copyright 2012-2014 David Robillard <http://drobilla.net>
+ Copyright 2011-2012 Ben Loftis, Harrison Consoles
+
+ 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.
+*/
+
+/**
+ @file pugl_x11.c X11 Pugl Implementation.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+#include "pugl_internal.h"
+
+struct PuglInternalsImpl {
+ Display* display;
+ int screen;
+ Window win;
+ GLXContext ctx;
+ Bool doubleBuffered;
+};
+
+/**
+ Attributes for single-buffered RGBA with at least
+ 4 bits per color and a 16 bit depth buffer.
+*/
+static int attrListSgl[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ GLX_DEPTH_SIZE, 16,
+ None
+};
+
+/**
+ Attributes for double-buffered RGBA with at least
+ 4 bits per color and a 16 bit depth buffer.
+*/
+static int attrListDbl[] = {
+ GLX_RGBA, GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ GLX_DEPTH_SIZE, 16,
+ None
+};
+
+PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = width;
+ view->height = height;
+
+ impl->display = XOpenDisplay(0);
+ impl->screen = DefaultScreen(impl->display);
+
+ XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl);
+ if (!vi) {
+ vi = glXChooseVisual(impl->display, impl->screen, attrListSgl);
+ impl->doubleBuffered = False;
+ printf("singlebuffered rendering will be used, no doublebuffering available\n");
+ } else {
+ impl->doubleBuffered = True;
+ printf("doublebuffered rendering available\n");
+ }
+
+ int glxMajor, glxMinor;
+ glXQueryVersion(impl->display, &glxMajor, &glxMinor);
+ printf("GLX-Version %d.%d\n", glxMajor, glxMinor);
+
+ impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
+
+ Window xParent = parent
+ ? (Window)parent
+ : RootWindow(impl->display, impl->screen);
+
+ Colormap cmap = XCreateColormap(
+ impl->display, xParent, vi->visual, AllocNone);
+
+ XSetWindowAttributes attr;
+ memset(&attr, 0, sizeof(XSetWindowAttributes));
+ attr.colormap = cmap;
+ attr.border_pixel = 0;
+
+ attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | ButtonReleaseMask
+ | PointerMotionMask | StructureNotifyMask;
+
+ impl->win = XCreateWindow(
+ impl->display, xParent,
+ 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual,
+ CWBorderPixel | CWColormap | CWEventMask, &attr);
+
+ XSizeHints sizeHints;
+ memset(&sizeHints, 0, sizeof(sizeHints));
+ if (!resizable) {
+ sizeHints.flags = PMinSize|PMaxSize;
+ sizeHints.min_width = width;
+ sizeHints.min_height = height;
+ sizeHints.max_width = width;
+ sizeHints.max_height = height;
+ XSetNormalHints(impl->display, impl->win, &sizeHints);
+ }
+
+ if (title) {
+ XStoreName(impl->display, impl->win, title);
+ }
+
+ if (!parent) {
+ Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True);
+ XSetWMProtocols(impl->display, impl->win, &wmDelete, 1);
+ }
+
+ if (visible) {
+ XMapRaised(impl->display, impl->win);
+ }
+
+ if (glXIsDirect(impl->display, impl->ctx)) {
+ printf("DRI enabled\n");
+ } else {
+ printf("No DRI available\n");
+ }
+
+ XFree(vi);
+
+ return view;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ if (!view) {
+ return;
+ }
+
+ glXDestroyContext(view->impl->display, view->impl->ctx);
+ XDestroyWindow(view->impl->display, view->impl->win);
+ XCloseDisplay(view->impl->display);
+ free(view->impl);
+ free(view);
+}
+
+static void
+puglReshape(PuglView* view, int width, int height)
+{
+ glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
+
+ if (view->reshapeFunc) {
+ view->reshapeFunc(view, width, height);
+ } else {
+ puglDefaultReshape(view, width, height);
+ }
+
+ view->width = width;
+ view->height = height;
+}
+
+static void
+puglDisplay(PuglView* view)
+{
+ glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+
+ glFlush();
+ if (view->impl->doubleBuffered) {
+ glXSwapBuffers(view->impl->display, view->impl->win);
+ }
+
+ view->redisplay = false;
+}
+
+static PuglKey
+keySymToSpecial(KeySym sym)
+{
+ switch (sym) {
+ case XK_F1: return PUGL_KEY_F1;
+ case XK_F2: return PUGL_KEY_F2;
+ case XK_F3: return PUGL_KEY_F3;
+ case XK_F4: return PUGL_KEY_F4;
+ case XK_F5: return PUGL_KEY_F5;
+ case XK_F6: return PUGL_KEY_F6;
+ case XK_F7: return PUGL_KEY_F7;
+ case XK_F8: return PUGL_KEY_F8;
+ case XK_F9: return PUGL_KEY_F9;
+ case XK_F10: return PUGL_KEY_F10;
+ case XK_F11: return PUGL_KEY_F11;
+ case XK_F12: return PUGL_KEY_F12;
+ case XK_Left: return PUGL_KEY_LEFT;
+ case XK_Up: return PUGL_KEY_UP;
+ case XK_Right: return PUGL_KEY_RIGHT;
+ case XK_Down: return PUGL_KEY_DOWN;
+ case XK_Page_Up: return PUGL_KEY_PAGE_UP;
+ case XK_Page_Down: return PUGL_KEY_PAGE_DOWN;
+ case XK_Home: return PUGL_KEY_HOME;
+ case XK_End: return PUGL_KEY_END;
+ case XK_Insert: return PUGL_KEY_INSERT;
+ case XK_Shift_L: return PUGL_KEY_SHIFT;
+ case XK_Shift_R: return PUGL_KEY_SHIFT;
+ case XK_Control_L: return PUGL_KEY_CTRL;
+ case XK_Control_R: return PUGL_KEY_CTRL;
+ case XK_Alt_L: return PUGL_KEY_ALT;
+ case XK_Alt_R: return PUGL_KEY_ALT;
+ case XK_Super_L: return PUGL_KEY_SUPER;
+ case XK_Super_R: return PUGL_KEY_SUPER;
+ }
+ return (PuglKey)0;
+}
+
+static void
+setModifiers(PuglView* view, unsigned xstate, unsigned xtime)
+{
+ view->event_timestamp_ms = xtime;
+
+ view->mods = 0;
+ view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0;
+ view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0;
+ view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0;
+ view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0;
+}
+
+static void
+dispatchKey(PuglView* view, XEvent* event, bool press)
+{
+ KeySym sym;
+ char str[5];
+ const int n = XLookupString(&event->xkey, str, 4, &sym, NULL);
+ if (n == 0) {
+ return;
+ } else if (n > 1) {
+ fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym);
+ return;
+ }
+
+ const PuglKey special = keySymToSpecial(sym);
+ if (special && view->specialFunc) {
+ view->specialFunc(view, press, special);
+ } else if (!special && view->keyboardFunc) {
+ view->keyboardFunc(view, press, str[0]);
+ }
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ XEvent event;
+ while (XPending(view->impl->display) > 0) {
+ XNextEvent(view->impl->display, &event);
+ switch (event.type) {
+ case MapNotify:
+ puglReshape(view, view->width, view->height);
+ break;
+ case ConfigureNotify:
+ if ((event.xconfigure.width != view->width) ||
+ (event.xconfigure.height != view->height)) {
+ puglReshape(view,
+ event.xconfigure.width,
+ event.xconfigure.height);
+ }
+ break;
+ case Expose:
+ if (event.xexpose.count != 0) {
+ break;
+ }
+ puglDisplay(view);
+ break;
+ case MotionNotify:
+ setModifiers(view, event.xmotion.state, event.xmotion.time);
+ if (view->motionFunc) {
+ view->motionFunc(view, event.xmotion.x, event.xmotion.y);
+ }
+ break;
+ case ButtonPress:
+ setModifiers(view, event.xbutton.state, event.xbutton.time);
+ if (event.xbutton.button >= 4 && event.xbutton.button <= 7) {
+ if (view->scrollFunc) {
+ float dx = 0, dy = 0;
+ switch (event.xbutton.button) {
+ case 4: dy = 1.0f; break;
+ case 5: dy = -1.0f; break;
+ case 6: dx = -1.0f; break;
+ case 7: dx = 1.0f; break;
+ }
+ view->scrollFunc(view,
+ event.xbutton.x, event.xbutton.y,
+ dx, dy);
+ }
+ break;
+ }
+ // nobreak
+ case ButtonRelease:
+ setModifiers(view, event.xbutton.state, event.xbutton.time);
+ if (view->mouseFunc &&
+ (event.xbutton.button < 4 || event.xbutton.button > 7)) {
+ view->mouseFunc(view,
+ event.xbutton.button, event.type == ButtonPress,
+ event.xbutton.x, event.xbutton.y);
+ }
+ break;
+ case KeyPress:
+ setModifiers(view, event.xkey.state, event.xkey.time);
+ dispatchKey(view, &event, true);
+ break;
+ case KeyRelease:
+ setModifiers(view, event.xkey.state, event.xkey.time);
+ if (view->ignoreKeyRepeat &&
+ XEventsQueued(view->impl->display, QueuedAfterReading)) {
+ XEvent next;
+ XPeekEvent(view->impl->display, &next);
+ if (next.type == KeyPress &&
+ next.xkey.time == event.xkey.time &&
+ next.xkey.keycode == event.xkey.keycode) {
+ XNextEvent(view->impl->display, &event);
+ break;
+ }
+ }
+ dispatchKey(view, &event, false);
+ break;
+ case ClientMessage:
+ if (!strcmp(XGetAtomName(view->impl->display,
+ event.xclient.message_type),
+ "WM_PROTOCOLS")) {
+ if (view->closeFunc) {
+ view->closeFunc(view);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (view->redisplay) {
+ puglDisplay(view);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return view->impl->win;
+}
diff --git a/pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base b/pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base
new file mode 100644
index 0000000..c3c2af6
--- /dev/null
+++ b/pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base
@@ -0,0 +1,13 @@
+Copyright 2011-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. \ No newline at end of file
diff --git a/pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base b/pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base
new file mode 100644
index 0000000..b9a54f5
--- /dev/null
+++ b/pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base
@@ -0,0 +1,193 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_test.c A simple Pugl test that creates a top-level window.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pugl/pugl.h"
+
+// Argh!
+#ifdef __APPLE__
+# include <OpenGL/glu.h>
+#else
+# include <GL/glu.h>
+#endif
+
+static int quit = 0;
+static float xAngle = 0.0f;
+static float yAngle = 0.0f;
+static float dist = 10.0f;
+
+static void
+onReshape(PuglView* view, int width, int height)
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, width, height);
+ gluPerspective(45.0f, width/(float)height, 1.0f, 10.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+static void
+onDisplay(PuglView* view)
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ glTranslatef(0.0f, 0.0f, dist * -1);
+ glRotatef(xAngle, 0.0f, 1.0f, 0.0f);
+ glRotatef(yAngle, 1.0f, 0.0f, 0.0f);
+
+ glBegin(GL_QUADS);
+
+ glColor3f(0, 0, 0); glVertex3f(-1, -1, -1);
+ glColor3f(0, 0, 1); glVertex3f(-1, -1, 1);
+ glColor3f(0, 1, 1); glVertex3f(-1, 1, 1);
+ glColor3f(0, 1, 0); glVertex3f(-1, 1, -1);
+
+ glColor3f(1, 0, 0); glVertex3f( 1, -1, -1);
+ glColor3f(1, 0, 1); glVertex3f( 1, -1, 1);
+ glColor3f(1, 1, 1); glVertex3f( 1, 1, 1);
+ glColor3f(1, 1, 0); glVertex3f( 1, 1, -1);
+
+ glColor3f(0, 0, 0); glVertex3f(-1, -1, -1);
+ glColor3f(0, 0, 1); glVertex3f(-1, -1, 1);
+ glColor3f(1, 0, 1); glVertex3f( 1, -1, 1);
+ glColor3f(1, 0, 0); glVertex3f( 1, -1, -1);
+
+ glColor3f(0, 1, 0); glVertex3f(-1, 1, -1);
+ glColor3f(0, 1, 1); glVertex3f(-1, 1, 1);
+ glColor3f(1, 1, 1); glVertex3f( 1, 1, 1);
+ glColor3f(1, 1, 0); glVertex3f( 1, 1, -1);
+
+ glColor3f(0, 0, 0); glVertex3f(-1, -1, -1);
+ glColor3f(0, 1, 0); glVertex3f(-1, 1, -1);
+ glColor3f(1, 1, 0); glVertex3f( 1, 1, -1);
+ glColor3f(1, 0, 0); glVertex3f( 1, -1, -1);
+
+ glColor3f(0, 0, 1); glVertex3f(-1, -1, 1);
+ glColor3f(0, 1, 1); glVertex3f(-1, 1, 1);
+ glColor3f(1, 1, 1); glVertex3f( 1, 1, 1);
+ glColor3f(1, 0, 1); glVertex3f( 1, -1, 1);
+
+ glEnd();
+}
+
+static void
+printModifiers(PuglView* view)
+{
+ int mods = puglGetModifiers(view);
+ fprintf(stderr, "Modifiers:%s%s%s%s\n",
+ (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
+ (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
+ (mods & PUGL_MOD_ALT) ? " Alt" : "",
+ (mods & PUGL_MOD_SUPER) ? " Super" : "");
+}
+
+static void
+onKeyboard(PuglView* view, bool press, uint32_t key)
+{
+ fprintf(stderr, "Key %c %s ", (char)key, press ? "down" : "up");
+ printModifiers(view);
+ if (key == 'q' || key == 'Q' || key == PUGL_CHAR_ESCAPE ||
+ key == PUGL_CHAR_DELETE || key == PUGL_CHAR_BACKSPACE) {
+ quit = 1;
+ }
+}
+
+static void
+onSpecial(PuglView* view, bool press, PuglKey key)
+{
+ fprintf(stderr, "Special key %d %s ", key, press ? "down" : "up");
+ printModifiers(view);
+}
+
+static void
+onMotion(PuglView* view, int x, int y)
+{
+ xAngle = x % 360;
+ yAngle = y % 360;
+ puglPostRedisplay(view);
+}
+
+static void
+onMouse(PuglView* view, int button, bool press, int x, int y)
+{
+ fprintf(stderr, "Mouse %d %s at %d,%d ",
+ button, press ? "down" : "up", x, y);
+ printModifiers(view);
+}
+
+static void
+onScroll(PuglView* view, int x, int y, float dx, float dy)
+{
+ fprintf(stderr, "Scroll %d %d %f %f ", x, y, dx, dy);
+ printModifiers(view);
+ dist += dy / 4.0f;
+ puglPostRedisplay(view);
+}
+
+static void
+onClose(PuglView* view)
+{
+ quit = 1;
+}
+
+int
+main(int argc, char** argv)
+{
+ bool ignoreKeyRepeat = false;
+ bool resizable = false;
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "-h")) {
+ printf("USAGE: %s [OPTIONS]...\n\n"
+ " -h Display this help\n"
+ " -i Ignore key repeat\n"
+ " -r Resizable window\n", argv[0]);
+ return 0;
+ } else if (!strcmp(argv[i], "-i")) {
+ ignoreKeyRepeat = true;
+ } else if (!strcmp(argv[i], "-r")) {
+ resizable = true;
+ } else {
+ fprintf(stderr, "Unknown option: %s\n", argv[i]);
+ }
+ }
+
+ PuglView* view = puglCreate(0, "Pugl Test", 512, 512, resizable, true);
+ puglIgnoreKeyRepeat(view, ignoreKeyRepeat);
+ puglSetKeyboardFunc(view, onKeyboard);
+ puglSetMotionFunc(view, onMotion);
+ puglSetMouseFunc(view, onMouse);
+ puglSetScrollFunc(view, onScroll);
+ puglSetSpecialFunc(view, onSpecial);
+ puglSetDisplayFunc(view, onDisplay);
+ puglSetReshapeFunc(view, onReshape);
+ puglSetCloseFunc(view, onClose);
+
+ while (!quit) {
+ puglProcessEvents(view);
+ }
+
+ puglDestroy(view);
+ return 0;
+}
diff --git a/pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base b/pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base
new file mode 100644
index 0000000..87742f4
--- /dev/null
+++ b/pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base
@@ -0,0 +1,27 @@
+PUGL
+====
+
+Pugl is a minimal portable API for OpenGL GUIs which supports embedding and is
+suitable for use in plugins. It works on X11, Mac OS X, and Windows.
+
+Pugl is vaguely similar to GLUT, but with some significant distinctions:
+
+ * Minimal in scope, providing only what is necessary to draw and receive
+ keyboard and mouse input.
+
+ * No reliance on static data whatsoever, so the API can be used in plugins or
+ multiple independent parts of a program.
+
+ * Single implementation, which is small, liberally licensed Free / Open Source
+ Software, and suitable for direct inclusion in programs if avoiding a
+ library dependency is desired.
+
+ * Support for embedding in other windows, so Pugl code can draw to a widget
+ inside a larger GUI.
+
+ * More complete support for keyboard input, including additional "special"
+ keys, modifiers, and support for detecting individual modifier key presses.
+
+For more information, see <http://drobilla.net/software/pugl>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/pugl/.svn/wc.db b/pugl/.svn/wc.db
new file mode 100644
index 0000000..ca0c8da
--- /dev/null
+++ b/pugl/.svn/wc.db
Binary files differ
diff --git a/pugl/AUTHORS b/pugl/AUTHORS
new file mode 100644
index 0000000..202ec66
--- /dev/null
+++ b/pugl/AUTHORS
@@ -0,0 +1,5 @@
+Author:
+ David Robillard <d@drobilla.net>
+
+Original GLX inspiration, portability fixes:
+ Ben Loftis
diff --git a/pugl/COPYING b/pugl/COPYING
new file mode 100644
index 0000000..c3c2af6
--- /dev/null
+++ b/pugl/COPYING
@@ -0,0 +1,13 @@
+Copyright 2011-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. \ No newline at end of file
diff --git a/pugl/README b/pugl/README
new file mode 100644
index 0000000..87742f4
--- /dev/null
+++ b/pugl/README
@@ -0,0 +1,27 @@
+PUGL
+====
+
+Pugl is a minimal portable API for OpenGL GUIs which supports embedding and is
+suitable for use in plugins. It works on X11, Mac OS X, and Windows.
+
+Pugl is vaguely similar to GLUT, but with some significant distinctions:
+
+ * Minimal in scope, providing only what is necessary to draw and receive
+ keyboard and mouse input.
+
+ * No reliance on static data whatsoever, so the API can be used in plugins or
+ multiple independent parts of a program.
+
+ * Single implementation, which is small, liberally licensed Free / Open Source
+ Software, and suitable for direct inclusion in programs if avoiding a
+ library dependency is desired.
+
+ * Support for embedding in other windows, so Pugl code can draw to a widget
+ inside a larger GUI.
+
+ * More complete support for keyboard input, including additional "special"
+ keys, modifiers, and support for detecting individual modifier key presses.
+
+For more information, see <http://drobilla.net/software/pugl>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/pugl/pugl.pc.in b/pugl/pugl.pc.in
new file mode 100644
index 0000000..d3606cd
--- /dev/null
+++ b/pugl/pugl.pc.in
@@ -0,0 +1,10 @@
+prefix=@PREFIX@
+exec_prefix=@EXEC_PREFIX@
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: Pugl
+Version: @PUGL_VERSION@
+Description: Lightweight portable OpenGL API
+Libs: -L${libdir} -l@LIB_PUGL@
+Cflags: -I${includedir}/pugl-@PUGL_MAJOR_VERSION@
diff --git a/pugl/pugl/pugl.h b/pugl/pugl/pugl.h
new file mode 100644
index 0000000..2a6a59f
--- /dev/null
+++ b/pugl/pugl/pugl.h
@@ -0,0 +1,357 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl.h API for Pugl, a minimal portable API for OpenGL.
+*/
+
+#ifndef PUGL_H_INCLUDED
+#define PUGL_H_INCLUDED
+
+#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"
+#else
+# ifdef _WIN32
+# include <windows.h> /* Broken Windows GL headers require this */
+# endif
+# include "GL/gl.h"
+#endif
+
+#ifdef PUGL_SHARED
+# ifdef _WIN32
+# define PUGL_LIB_IMPORT __declspec(dllimport)
+# define PUGL_LIB_EXPORT __declspec(dllexport)
+# else
+# define PUGL_LIB_IMPORT __attribute__((visibility("default")))
+# define PUGL_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef PUGL_INTERNAL
+# define PUGL_API PUGL_LIB_EXPORT
+# else
+# define PUGL_API PUGL_LIB_IMPORT
+# endif
+#else
+# define PUGL_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+/**
+ @defgroup pugl Pugl
+ A minimal portable API for OpenGL.
+ @{
+*/
+
+/**
+ An OpenGL view.
+*/
+typedef struct PuglViewImpl PuglView;
+
+/**
+ A native window handle.
+
+ On X11, this is a Window.
+ On OSX, this is an NSView*.
+ On Windows, this is a HWND.
+*/
+typedef intptr_t PuglNativeWindow;
+
+/**
+ Return status code.
+*/
+typedef enum {
+ PUGL_SUCCESS = 0
+} PuglStatus;
+
+/**
+ Convenience symbols for ASCII control characters.
+*/
+typedef enum {
+ PUGL_CHAR_BACKSPACE = 0x08,
+ PUGL_CHAR_ESCAPE = 0x1B,
+ PUGL_CHAR_DELETE = 0x7F
+} PuglChar;
+
+/**
+ Special (non-Unicode) keyboard keys.
+*/
+typedef enum {
+ PUGL_KEY_F1 = 1,
+ PUGL_KEY_F2,
+ PUGL_KEY_F3,
+ PUGL_KEY_F4,
+ PUGL_KEY_F5,
+ PUGL_KEY_F6,
+ PUGL_KEY_F7,
+ PUGL_KEY_F8,
+ PUGL_KEY_F9,
+ PUGL_KEY_F10,
+ PUGL_KEY_F11,
+ PUGL_KEY_F12,
+ PUGL_KEY_LEFT,
+ PUGL_KEY_UP,
+ PUGL_KEY_RIGHT,
+ PUGL_KEY_DOWN,
+ PUGL_KEY_PAGE_UP,
+ PUGL_KEY_PAGE_DOWN,
+ PUGL_KEY_HOME,
+ PUGL_KEY_END,
+ PUGL_KEY_INSERT,
+ PUGL_KEY_SHIFT,
+ PUGL_KEY_CTRL,
+ PUGL_KEY_ALT,
+ PUGL_KEY_SUPER
+} PuglKey;
+
+/**
+ Keyboard modifier flags.
+*/
+typedef enum {
+ PUGL_MOD_SHIFT = 1, /**< Shift key */
+ PUGL_MOD_CTRL = 1 << 1, /**< Control key */
+ PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
+ PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
+} PuglMod;
+
+/**
+ Handle for opaque user data.
+*/
+typedef void* PuglHandle;
+
+/**
+ A function called when the window is closed.
+*/
+typedef void (*PuglCloseFunc)(PuglView* view);
+
+/**
+ A function called to draw the view contents with OpenGL.
+*/
+typedef void (*PuglDisplayFunc)(PuglView* view);
+
+/**
+ A function called when a key is pressed or released.
+ @param view The view the event occured in.
+ @param press True if the key was pressed, false if released.
+ @param key Unicode point of the key pressed.
+*/
+typedef void (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key);
+
+/**
+ A function called when the pointer moves.
+ @param view The view the event occured in.
+ @param x The window-relative x coordinate of the pointer.
+ @param y The window-relative y coordinate of the pointer.
+*/
+typedef void (*PuglMotionFunc)(PuglView* view, int x, int y);
+
+/**
+ A function called when a mouse button is pressed or released.
+ @param view The view the event occured in.
+ @param button The button number (1 = left, 2 = middle, 3 = right).
+ @param press True if the key was pressed, false if released.
+ @param x The window-relative x coordinate of the pointer.
+ @param y The window-relative y coordinate of the pointer.
+*/
+typedef void (*PuglMouseFunc)(
+ PuglView* view, int button, bool press, int x, int y);
+
+/**
+ A function called when the view is resized.
+ @param view The view being resized.
+ @param width The new view width.
+ @param height The new view height.
+*/
+typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height);
+
+/**
+ A function called on scrolling (e.g. mouse wheel or track pad).
+
+ The distances used here are in "lines", a single tick of a clicking mouse
+ wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and
+ devices support finer resolution and/or higher values for fast scrolls,
+ so programs should handle any value gracefully.
+
+ @param view The view being scrolled.
+ @param dx The scroll x distance.
+ @param dx The scroll y distance.
+*/
+typedef void (*PuglScrollFunc)(PuglView* view,
+ int x,
+ int y,
+ float dx,
+ float dy);
+
+/**
+ A function called when a special key is pressed or released.
+
+ This callback allows the use of keys that do not have unicode points.
+
+ @param view The view the event occured in.
+ @param press True if the key was pressed, false if released.
+ @param key The key pressed.
+*/
+typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key);
+
+/**
+ Create a new GL window.
+ @param parent Parent window, or 0 for top level.
+ @param title Window title, or NULL.
+ @param width Window width in pixels.
+ @param height Window height in pixels.
+ @param resizable Whether window should be user resizable.
+ @param visible Whether window should be initially visible.
+*/
+PUGL_API PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible);
+
+/**
+ Set the handle to be passed to all callbacks.
+
+ This is generally a pointer to a struct which contains all necessary state.
+ Everything needed in callbacks should be here, not in static variables.
+
+ Note the lack of this facility makes GLUT unsuitable for plugins or
+ non-trivial programs; this mistake is largely why Pugl exists.
+*/
+PUGL_API void
+puglSetHandle(PuglView* view, PuglHandle handle);
+
+/**
+ Get the handle to be passed to all callbacks.
+*/
+PUGL_API PuglHandle
+puglGetHandle(PuglView* view);
+
+/**
+ Return the timestamp (if any) of the currently-processing event.
+*/
+PUGL_API uint32_t
+puglGetEventTimestamp(PuglView* view);
+
+/**
+ Get the currently active modifiers (PuglMod flags).
+
+ This should only be called from an event handler.
+*/
+PUGL_API int
+puglGetModifiers(PuglView* view);
+
+/**
+ Ignore synthetic repeated key events.
+*/
+PUGL_API void
+puglIgnoreKeyRepeat(PuglView* view, bool ignore);
+
+/**
+ Set the function to call when the window is closed.
+*/
+PUGL_API void
+puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc);
+
+/**
+ Set the display function which should draw the UI using GL.
+*/
+PUGL_API void
+puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc);
+
+/**
+ Set the function to call on keyboard events.
+*/
+PUGL_API void
+puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc);
+
+/**
+ Set the function to call on mouse motion.
+*/
+PUGL_API void
+puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc);
+
+/**
+ Set the function to call on mouse button events.
+*/
+PUGL_API void
+puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc);
+
+/**
+ Set the function to call on scroll events.
+*/
+PUGL_API void
+puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc);
+
+/**
+ Set the function to call on special events.
+*/
+PUGL_API void
+puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc);
+
+/**
+ Set the function to call when the window size changes.
+*/
+PUGL_API void
+puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc);
+
+/**
+ Return the native window handle.
+*/
+PUGL_API PuglNativeWindow
+puglGetNativeWindow(PuglView* view);
+
+/**
+ Process all pending window events.
+
+ This handles input events as well as rendering, so it should be called
+ regularly and rapidly enough to keep the UI responsive.
+*/
+PUGL_API PuglStatus
+puglProcessEvents(PuglView* view);
+
+/**
+ Request a redisplay on the next call to puglProcessEvents().
+*/
+PUGL_API void
+puglPostRedisplay(PuglView* view);
+
+/**
+ Destroy a GL window.
+*/
+PUGL_API void
+puglDestroy(PuglView* view);
+
+/**
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PUGL_H_INCLUDED */
diff --git a/pugl/pugl/pugl_internal.h b/pugl/pugl/pugl_internal.h
new file mode 100644
index 0000000..8cdada8
--- /dev/null
+++ b/pugl/pugl/pugl_internal.h
@@ -0,0 +1,143 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_internal.h Private platform-independent definitions.
+
+ Note this file contains function definitions, so it must be compiled into
+ the final binary exactly once. Each platform specific implementation file
+ including it once should achieve this.
+*/
+
+#include "pugl.h"
+
+typedef struct PuglInternalsImpl PuglInternals;
+
+struct PuglViewImpl {
+ PuglHandle handle;
+ PuglCloseFunc closeFunc;
+ PuglDisplayFunc displayFunc;
+ PuglKeyboardFunc keyboardFunc;
+ PuglMotionFunc motionFunc;
+ PuglMouseFunc mouseFunc;
+ PuglReshapeFunc reshapeFunc;
+ PuglScrollFunc scrollFunc;
+ PuglSpecialFunc specialFunc;
+
+ PuglInternals* impl;
+
+ int width;
+ int height;
+ int mods;
+ bool mouse_in_view;
+ bool ignoreKeyRepeat;
+ bool redisplay;
+ uint32_t event_timestamp_ms;
+};
+
+void
+puglSetHandle(PuglView* view, PuglHandle handle)
+{
+ view->handle = handle;
+}
+
+PuglHandle
+puglGetHandle(PuglView* view)
+{
+ return view->handle;
+}
+
+uint32_t
+puglGetEventTimestamp(PuglView* view)
+{
+ return view->event_timestamp_ms;
+}
+
+int
+puglGetModifiers(PuglView* view)
+{
+ return view->mods;
+}
+
+void
+puglDefaultReshape(PuglView* view, int width, int height)
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, width, height, 0, 0, 1);
+ glViewport(0, 0, width, height);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ return;
+
+ // unused
+ (void)view;
+}
+
+void
+puglIgnoreKeyRepeat(PuglView* view, bool ignore)
+{
+ view->ignoreKeyRepeat = ignore;
+}
+
+void
+puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc)
+{
+ view->closeFunc = closeFunc;
+}
+
+void
+puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc)
+{
+ view->displayFunc = displayFunc;
+}
+
+void
+puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc)
+{
+ view->keyboardFunc = keyboardFunc;
+}
+
+void
+puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc)
+{
+ view->motionFunc = motionFunc;
+}
+
+void
+puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc)
+{
+ view->mouseFunc = mouseFunc;
+}
+
+void
+puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc)
+{
+ view->reshapeFunc = reshapeFunc;
+}
+
+void
+puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc)
+{
+ view->scrollFunc = scrollFunc;
+}
+
+void
+puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc)
+{
+ view->specialFunc = specialFunc;
+}
diff --git a/pugl/pugl/pugl_osx.m b/pugl/pugl/pugl_osx.m
new file mode 100644
index 0000000..c318d91
--- /dev/null
+++ b/pugl/pugl/pugl_osx.m
@@ -0,0 +1,400 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_osx.m OSX/Cocoa Pugl Implementation.
+*/
+
+#include <stdlib.h>
+
+#import <Cocoa/Cocoa.h>
+
+#include "pugl_internal.h"
+
+@interface PuglWindow : NSWindow
+{
+@public
+ PuglView* puglview;
+}
+
+- (id) initWithContentRect:(NSRect)contentRect
+ styleMask:(unsigned int)aStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)flag;
+- (void) setPuglview:(PuglView*)view;
+- (BOOL) windowShouldClose:(id)sender;
+@end
+
+@implementation PuglWindow
+
+- (id)initWithContentRect:(NSRect)contentRect
+ styleMask:(unsigned int)aStyle
+ backing:(NSBackingStoreType)bufferingType
+ defer:(BOOL)flag
+{
+ NSWindow* result = [super initWithContentRect:contentRect
+ styleMask:(NSClosableWindowMask |
+ NSTitledWindowMask |
+ NSResizableWindowMask)
+ backing:NSBackingStoreBuffered defer:NO];
+
+ [result setAcceptsMouseMovedEvents:YES];
+ [result setLevel: CGShieldingWindowLevel() + 1];
+
+ return result;
+}
+
+- (void)setPuglview:(PuglView*)view
+{
+ puglview = view;
+ [self setContentSize:NSMakeSize(view->width, view->height) ];
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ if (puglview->closeFunc)
+ puglview->closeFunc(puglview);
+ return YES;
+}
+
+@end
+
+void
+puglDisplay(PuglView* view)
+{
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+}
+
+@interface PuglOpenGLView : NSOpenGLView
+{
+ int colorBits;
+ int depthBits;
+@public
+ PuglView* puglview;
+
+ NSTrackingArea* trackingArea;
+}
+
+- (id) initWithFrame:(NSRect)frame
+ colorBits:(int)numColorBits
+ depthBits:(int)numDepthBits;
+- (void) reshape;
+- (void) drawRect:(NSRect)rect;
+- (void) mouseMoved:(NSEvent*)event;
+- (void) mouseDragged:(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;
+- (void) flagsChanged:(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 (puglview) {
+ /* NOTE: Apparently reshape gets called when the GC gets around to
+ deleting the view (?), so we must have reset puglview to NULL when
+ this comes around.
+ */
+ if (puglview->reshapeFunc) {
+ puglview->reshapeFunc(puglview, width, height);
+ } else {
+ puglDefaultReshape(puglview, width, height);
+ }
+
+ puglview->width = width;
+ puglview->height = height;
+ }
+}
+
+- (void) drawRect:(NSRect)rect
+{
+ puglDisplay(puglview);
+ glFlush();
+ glSwapAPPLE();
+}
+
+static unsigned
+getModifiers(PuglView* view, NSEvent* ev)
+{
+ const unsigned modifierFlags = [ev modifierFlags];
+
+ view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX);
+
+ unsigned mods = 0;
+ mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0;
+ mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0;
+ mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0;
+ mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0;
+ return mods;
+}
+
+-(void)updateTrackingAreas
+{
+ if (trackingArea != nil) {
+ [self removeTrackingArea:trackingArea];
+ [trackingArea release];
+ }
+
+ const int opts = (NSTrackingMouseEnteredAndExited |
+ NSTrackingMouseMoved |
+ NSTrackingActiveAlways);
+ trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
+ options:opts
+ owner:self
+ userInfo:nil];
+ [self addTrackingArea:trackingArea];
+}
+
+- (void)mouseEntered:(NSEvent*)theEvent
+{
+ [self updateTrackingAreas];
+}
+
+- (void)mouseExited:(NSEvent*)theEvent
+{
+}
+
+- (void) mouseMoved:(NSEvent*)event
+{
+ if (puglview->motionFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) mouseDragged:(NSEvent*)event
+{
+ if (puglview->motionFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->motionFunc(puglview, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) mouseDown:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) mouseUp:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y);
+ }
+ [self updateTrackingAreas];
+}
+
+- (void) rightMouseDown:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) rightMouseUp:(NSEvent*)event
+{
+ if (puglview->mouseFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y);
+ }
+}
+
+- (void) scrollWheel:(NSEvent*)event
+{
+ if (puglview->scrollFunc) {
+ NSPoint loc = [event locationInWindow];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->scrollFunc(puglview,
+ loc.x, puglview->height - loc.y,
+ [event deltaX], [event deltaY]);
+ }
+ [self updateTrackingAreas];
+}
+
+- (void) keyDown:(NSEvent*)event
+{
+ if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) {
+ NSString* chars = [event characters];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]);
+ }
+}
+
+- (void) keyUp:(NSEvent*)event
+{
+ if (puglview->keyboardFunc) {
+ NSString* chars = [event characters];
+ puglview->mods = getModifiers(puglview, event);
+ puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]);
+ }
+}
+
+- (void) flagsChanged:(NSEvent*)event
+{
+ if (puglview->specialFunc) {
+ const unsigned mods = getModifiers(puglview, [event modifierFlags]);
+ if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT);
+ } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL);
+ } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT);
+ } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) {
+ puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER);
+ }
+ puglview->mods = mods;
+ }
+}
+
+@end
+
+struct PuglInternalsImpl {
+ PuglOpenGLView* glview;
+ id window;
+};
+
+PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = width;
+ view->height = height;
+
+ [NSAutoreleasePool new];
+ [NSApplication sharedApplication];
+
+ NSString* titleString = [[NSString alloc]
+ initWithBytes:title
+ length:strlen(title)
+ encoding:NSUTF8StringEncoding];
+
+ id window = [[PuglWindow new]retain];
+
+ [window setPuglview:view];
+ [window setTitle:titleString];
+
+ impl->glview = [PuglOpenGLView new];
+ impl->window = window;
+ impl->glview->puglview = view;
+
+ [window setContentView:impl->glview];
+ [NSApp activateIgnoringOtherApps:YES];
+ [window makeFirstResponder:impl->glview];
+
+ [window makeKeyAndOrderFront:window];
+
+ return view;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ view->impl->glview->puglview = NULL;
+ [view->impl->window close];
+ [view->impl->glview release];
+ [view->impl->window release];
+ free(view->impl);
+ free(view);
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ [view->impl->glview setNeedsDisplay: YES];
+
+ return PUGL_SUCCESS;
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return (PuglNativeWindow)view->impl->glview;
+}
diff --git a/pugl/pugl/pugl_win.cpp b/pugl/pugl/pugl_win.cpp
new file mode 100644
index 0000000..5f85d12
--- /dev/null
+++ b/pugl/pugl/pugl_win.cpp
@@ -0,0 +1,376 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_win.cpp Windows/WGL Pugl Implementation.
+*/
+
+#include <windows.h>
+#include <windowsx.h>
+#include <GL/gl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pugl_internal.h"
+
+#ifndef WM_MOUSEWHEEL
+# define WM_MOUSEWHEEL 0x020A
+#endif
+#ifndef WM_MOUSEHWHEEL
+# define WM_MOUSEHWHEEL 0x020E
+#endif
+#ifndef WHEEL_DELTA
+# define WHEEL_DELTA 120
+#endif
+
+const int LOCAL_CLOSE_MSG = WM_USER + 50;
+
+struct PuglInternalsImpl {
+ HWND hwnd;
+ HDC hdc;
+ HGLRC hglrc;
+ WNDCLASS wc;
+};
+
+LRESULT CALLBACK
+wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = width;
+ view->height = height;
+
+ // FIXME: This is nasty, and pugl should not have static anything.
+ // Should class be a parameter? Does this make sense on other platforms?
+ static int wc_count = 0;
+ char classNameBuf[256];
+ _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++);
+
+ impl->wc.style = CS_OWNDC;
+ impl->wc.lpfnWndProc = wndProc;
+ impl->wc.cbClsExtra = 0;
+ impl->wc.cbWndExtra = 0;
+ impl->wc.hInstance = 0;
+ impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ impl->wc.lpszMenuName = NULL;
+ impl->wc.lpszClassName = classNameBuf;
+ RegisterClass(&impl->wc);
+
+ int winFlags = WS_POPUPWINDOW | WS_CAPTION;
+ if (resizable) {
+ winFlags |= WS_SIZEBOX;
+ }
+
+ // Adjust the overall window size to accomodate our requested client size
+ RECT wr = { 0, 0, width, height };
+ AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST);
+
+ impl->hwnd = CreateWindowEx(
+ WS_EX_TOPMOST,
+ classNameBuf, title,
+ (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags),
+ 0, 0, wr.right-wr.left, wr.bottom-wr.top,
+ (HWND)parent, NULL, NULL, NULL);
+
+ if (!impl->hwnd) {
+ free(impl);
+ free(view);
+ return NULL;
+ }
+
+ SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view);
+
+ impl->hdc = GetDC(impl->hwnd);
+
+ PIXELFORMATDESCRIPTOR pfd;
+ ZeroMemory(&pfd, sizeof(pfd));
+ pfd.nSize = sizeof(pfd);
+ pfd.nVersion = 1;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+ pfd.iPixelType = PFD_TYPE_RGBA;
+ pfd.cColorBits = 24;
+ pfd.cDepthBits = 16;
+ pfd.iLayerType = PFD_MAIN_PLANE;
+
+ int format = ChoosePixelFormat(impl->hdc, &pfd);
+ SetPixelFormat(impl->hdc, format, &pfd);
+
+ impl->hglrc = wglCreateContext(impl->hdc);
+ wglMakeCurrent(impl->hdc, impl->hglrc);
+
+ view->width = width;
+ view->height = height;
+
+ return view;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ wglMakeCurrent(NULL, NULL);
+ wglDeleteContext(view->impl->hglrc);
+ ReleaseDC(view->impl->hwnd, view->impl->hdc);
+ DestroyWindow(view->impl->hwnd);
+ UnregisterClass(view->impl->wc.lpszClassName, NULL);
+ free(view->impl);
+ free(view);
+}
+
+static void
+puglReshape(PuglView* view, int width, int height)
+{
+ wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
+
+ if (view->reshapeFunc) {
+ view->reshapeFunc(view, width, height);
+ } else {
+ puglDefaultReshape(view, width, height);
+ }
+
+ view->width = width;
+ view->height = height;
+}
+
+void
+puglDisplay(PuglView* view)
+{
+ wglMakeCurrent(view->impl->hdc, view->impl->hglrc);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+
+ glFlush();
+ SwapBuffers(view->impl->hdc);
+ view->redisplay = false;
+}
+
+static PuglKey
+keySymToSpecial(int sym)
+{
+ switch (sym) {
+ case VK_F1: return PUGL_KEY_F1;
+ case VK_F2: return PUGL_KEY_F2;
+ case VK_F3: return PUGL_KEY_F3;
+ case VK_F4: return PUGL_KEY_F4;
+ case VK_F5: return PUGL_KEY_F5;
+ case VK_F6: return PUGL_KEY_F6;
+ case VK_F7: return PUGL_KEY_F7;
+ case VK_F8: return PUGL_KEY_F8;
+ case VK_F9: return PUGL_KEY_F9;
+ case VK_F10: return PUGL_KEY_F10;
+ case VK_F11: return PUGL_KEY_F11;
+ case VK_F12: return PUGL_KEY_F12;
+ case VK_LEFT: return PUGL_KEY_LEFT;
+ case VK_UP: return PUGL_KEY_UP;
+ case VK_RIGHT: return PUGL_KEY_RIGHT;
+ case VK_DOWN: return PUGL_KEY_DOWN;
+ case VK_PRIOR: return PUGL_KEY_PAGE_UP;
+ case VK_NEXT: return PUGL_KEY_PAGE_DOWN;
+ case VK_HOME: return PUGL_KEY_HOME;
+ case VK_END: return PUGL_KEY_END;
+ case VK_INSERT: return PUGL_KEY_INSERT;
+ case VK_SHIFT: return PUGL_KEY_SHIFT;
+ case VK_CONTROL: return PUGL_KEY_CTRL;
+ case VK_MENU: return PUGL_KEY_ALT;
+ case VK_LWIN: return PUGL_KEY_SUPER;
+ case VK_RWIN: return PUGL_KEY_SUPER;
+ }
+ return (PuglKey)0;
+}
+
+static void
+processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam)
+{
+ view->event_timestamp_ms = GetMessageTime();
+ if (press) {
+ SetCapture(view->impl->hwnd);
+ } else {
+ ReleaseCapture();
+ }
+
+ if (view->mouseFunc) {
+ view->mouseFunc(view, button, press,
+ GET_X_LPARAM(lParam),
+ GET_Y_LPARAM(lParam));
+ }
+}
+
+static void
+setModifiers(PuglView* view)
+{
+ view->mods = 0;
+ view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0;
+ view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0;
+ view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0;
+ view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0;
+ view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0;
+}
+
+static LRESULT
+handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ PuglKey key;
+
+ setModifiers(view);
+ switch (message) {
+ case WM_CREATE:
+ case WM_SHOWWINDOW:
+ case WM_SIZE:
+ RECT rect;
+ GetClientRect(view->impl->hwnd, &rect);
+ puglReshape(view, rect.right, rect.bottom);
+ view->width = rect.right;
+ view->height = rect.bottom;
+ break;
+ case WM_PAINT:
+ BeginPaint(view->impl->hwnd, &ps);
+ puglDisplay(view);
+ EndPaint(view->impl->hwnd, &ps);
+ break;
+ case WM_MOUSEMOVE:
+ if (view->motionFunc) {
+ view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ processMouseEvent(view, 1, true, lParam);
+ break;
+ case WM_MBUTTONDOWN:
+ processMouseEvent(view, 2, true, lParam);
+ break;
+ case WM_RBUTTONDOWN:
+ processMouseEvent(view, 3, true, lParam);
+ break;
+ case WM_LBUTTONUP:
+ processMouseEvent(view, 1, false, lParam);
+ break;
+ case WM_MBUTTONUP:
+ processMouseEvent(view, 2, false, lParam);
+ break;
+ case WM_RBUTTONUP:
+ processMouseEvent(view, 3, false, lParam);
+ break;
+ case WM_MOUSEWHEEL:
+ if (view->scrollFunc) {
+ view->scrollFunc(
+ view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+ (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA);
+ }
+ break;
+ case WM_MOUSEHWHEEL:
+ if (view->scrollFunc) {
+ view->scrollFunc(
+ view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
+ (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0);
+ }
+ break;
+ case WM_KEYDOWN:
+ view->event_timestamp_ms = (GetMessageTime());
+ if (view->ignoreKeyRepeat && (lParam & (1 << 30))) {
+ break;
+ } // else nobreak
+ case WM_KEYUP:
+ if ((key = keySymToSpecial(wParam))) {
+ if (view->specialFunc) {
+ view->specialFunc(view, message == WM_KEYDOWN, key);
+ }
+ } else if (view->keyboardFunc) {
+ view->keyboardFunc(view, message == WM_KEYDOWN, wParam);
+ }
+ break;
+ case WM_QUIT:
+ case LOCAL_CLOSE_MSG:
+ if (view->closeFunc) {
+ view->closeFunc(view);
+ }
+ break;
+ default:
+ return DefWindowProc(
+ view->impl->hwnd, message, wParam, lParam);
+ }
+
+ return 0;
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ MSG msg;
+ while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) {
+ handleMessage(view, msg.message, msg.wParam, msg.lParam);
+ }
+
+
+ if (view->redisplay) {
+ InvalidateRect(view->impl->hwnd, NULL, FALSE);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+LRESULT CALLBACK
+wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA);
+ switch (message) {
+ case WM_CREATE:
+ PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0);
+ return 0;
+ case WM_CLOSE:
+ PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam);
+ return 0;
+ case WM_DESTROY:
+ return 0;
+ default:
+ if (view) {
+ return handleMessage(view, message, wParam, lParam);
+ } else {
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ }
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return (PuglNativeWindow)view->impl->hwnd;
+}
diff --git a/pugl/pugl/pugl_x11.c b/pugl/pugl/pugl_x11.c
new file mode 100644
index 0000000..c25b273
--- /dev/null
+++ b/pugl/pugl/pugl_x11.c
@@ -0,0 +1,386 @@
+/*
+ Copyright 2012-2014 David Robillard <http://drobilla.net>
+ Copyright 2011-2012 Ben Loftis, Harrison Consoles
+
+ 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.
+*/
+
+/**
+ @file pugl_x11.c X11 Pugl Implementation.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+#include "pugl_internal.h"
+
+struct PuglInternalsImpl {
+ Display* display;
+ int screen;
+ Window win;
+ GLXContext ctx;
+ Bool doubleBuffered;
+};
+
+/**
+ Attributes for single-buffered RGBA with at least
+ 4 bits per color and a 16 bit depth buffer.
+*/
+static int attrListSgl[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ GLX_DEPTH_SIZE, 16,
+ None
+};
+
+/**
+ Attributes for double-buffered RGBA with at least
+ 4 bits per color and a 16 bit depth buffer.
+*/
+static int attrListDbl[] = {
+ GLX_RGBA, GLX_DOUBLEBUFFER,
+ GLX_RED_SIZE, 4,
+ GLX_GREEN_SIZE, 4,
+ GLX_BLUE_SIZE, 4,
+ GLX_DEPTH_SIZE, 16,
+ None
+};
+
+PuglView*
+puglCreate(PuglNativeWindow parent,
+ const char* title,
+ int width,
+ int height,
+ bool resizable,
+ bool visible)
+{
+ PuglView* view = (PuglView*)calloc(1, sizeof(PuglView));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ if (!view || !impl) {
+ return NULL;
+ }
+
+ view->impl = impl;
+ view->width = width;
+ view->height = height;
+
+ impl->display = XOpenDisplay(0);
+ impl->screen = DefaultScreen(impl->display);
+
+ XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl);
+ if (!vi) {
+ vi = glXChooseVisual(impl->display, impl->screen, attrListSgl);
+ impl->doubleBuffered = False;
+ printf("singlebuffered rendering will be used, no doublebuffering available\n");
+ } else {
+ impl->doubleBuffered = True;
+ printf("doublebuffered rendering available\n");
+ }
+
+ int glxMajor, glxMinor;
+ glXQueryVersion(impl->display, &glxMajor, &glxMinor);
+ printf("GLX-Version %d.%d\n", glxMajor, glxMinor);
+
+ impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE);
+
+ Window xParent = parent
+ ? (Window)parent
+ : RootWindow(impl->display, impl->screen);
+
+ Colormap cmap = XCreateColormap(
+ impl->display, xParent, vi->visual, AllocNone);
+
+ XSetWindowAttributes attr;
+ memset(&attr, 0, sizeof(XSetWindowAttributes));
+ attr.colormap = cmap;
+ attr.border_pixel = 0;
+
+ attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask
+ | ButtonPressMask | ButtonReleaseMask
+ | PointerMotionMask | StructureNotifyMask;
+
+ impl->win = XCreateWindow(
+ impl->display, xParent,
+ 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual,
+ CWBorderPixel | CWColormap | CWEventMask, &attr);
+
+ XSizeHints sizeHints;
+ memset(&sizeHints, 0, sizeof(sizeHints));
+ if (!resizable) {
+ sizeHints.flags = PMinSize|PMaxSize;
+ sizeHints.min_width = width;
+ sizeHints.min_height = height;
+ sizeHints.max_width = width;
+ sizeHints.max_height = height;
+ XSetNormalHints(impl->display, impl->win, &sizeHints);
+ }
+
+ if (title) {
+ XStoreName(impl->display, impl->win, title);
+ }
+
+ if (!parent) {
+ Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True);
+ XSetWMProtocols(impl->display, impl->win, &wmDelete, 1);
+ }
+
+ if (visible) {
+ XMapRaised(impl->display, impl->win);
+ }
+
+ if (glXIsDirect(impl->display, impl->ctx)) {
+ printf("DRI enabled\n");
+ } else {
+ printf("No DRI available\n");
+ }
+
+ XFree(vi);
+
+ return view;
+}
+
+void
+puglDestroy(PuglView* view)
+{
+ if (!view) {
+ return;
+ }
+
+ glXDestroyContext(view->impl->display, view->impl->ctx);
+ XDestroyWindow(view->impl->display, view->impl->win);
+ XCloseDisplay(view->impl->display);
+ free(view->impl);
+ free(view);
+}
+
+static void
+puglReshape(PuglView* view, int width, int height)
+{
+ glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
+
+ if (view->reshapeFunc) {
+ view->reshapeFunc(view, width, height);
+ } else {
+ puglDefaultReshape(view, width, height);
+ }
+
+ view->width = width;
+ view->height = height;
+}
+
+static void
+puglDisplay(PuglView* view)
+{
+ glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ if (view->displayFunc) {
+ view->displayFunc(view);
+ }
+
+ glFlush();
+ if (view->impl->doubleBuffered) {
+ glXSwapBuffers(view->impl->display, view->impl->win);
+ }
+
+ view->redisplay = false;
+}
+
+static PuglKey
+keySymToSpecial(KeySym sym)
+{
+ switch (sym) {
+ case XK_F1: return PUGL_KEY_F1;
+ case XK_F2: return PUGL_KEY_F2;
+ case XK_F3: return PUGL_KEY_F3;
+ case XK_F4: return PUGL_KEY_F4;
+ case XK_F5: return PUGL_KEY_F5;
+ case XK_F6: return PUGL_KEY_F6;
+ case XK_F7: return PUGL_KEY_F7;
+ case XK_F8: return PUGL_KEY_F8;
+ case XK_F9: return PUGL_KEY_F9;
+ case XK_F10: return PUGL_KEY_F10;
+ case XK_F11: return PUGL_KEY_F11;
+ case XK_F12: return PUGL_KEY_F12;
+ case XK_Left: return PUGL_KEY_LEFT;
+ case XK_Up: return PUGL_KEY_UP;
+ case XK_Right: return PUGL_KEY_RIGHT;
+ case XK_Down: return PUGL_KEY_DOWN;
+ case XK_Page_Up: return PUGL_KEY_PAGE_UP;
+ case XK_Page_Down: return PUGL_KEY_PAGE_DOWN;
+ case XK_Home: return PUGL_KEY_HOME;
+ case XK_End: return PUGL_KEY_END;
+ case XK_Insert: return PUGL_KEY_INSERT;
+ case XK_Shift_L: return PUGL_KEY_SHIFT;
+ case XK_Shift_R: return PUGL_KEY_SHIFT;
+ case XK_Control_L: return PUGL_KEY_CTRL;
+ case XK_Control_R: return PUGL_KEY_CTRL;
+ case XK_Alt_L: return PUGL_KEY_ALT;
+ case XK_Alt_R: return PUGL_KEY_ALT;
+ case XK_Super_L: return PUGL_KEY_SUPER;
+ case XK_Super_R: return PUGL_KEY_SUPER;
+ }
+ return (PuglKey)0;
+}
+
+static void
+setModifiers(PuglView* view, unsigned xstate, unsigned xtime)
+{
+ view->event_timestamp_ms = xtime;
+
+ view->mods = 0;
+ view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0;
+ view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0;
+ view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0;
+ view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0;
+}
+
+static void
+dispatchKey(PuglView* view, XEvent* event, bool press)
+{
+ KeySym sym;
+ char str[5];
+ const int n = XLookupString(&event->xkey, str, 4, &sym, NULL);
+ if (n == 0) {
+ return;
+ } else if (n > 1) {
+ fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym);
+ return;
+ }
+
+ const PuglKey special = keySymToSpecial(sym);
+ if (special && view->specialFunc) {
+ view->specialFunc(view, press, special);
+ } else if (!special && view->keyboardFunc) {
+ view->keyboardFunc(view, press, str[0]);
+ }
+}
+
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ XEvent event;
+ while (XPending(view->impl->display) > 0) {
+ XNextEvent(view->impl->display, &event);
+ switch (event.type) {
+ case MapNotify:
+ puglReshape(view, view->width, view->height);
+ break;
+ case ConfigureNotify:
+ if ((event.xconfigure.width != view->width) ||
+ (event.xconfigure.height != view->height)) {
+ puglReshape(view,
+ event.xconfigure.width,
+ event.xconfigure.height);
+ }
+ break;
+ case Expose:
+ if (event.xexpose.count != 0) {
+ break;
+ }
+ puglDisplay(view);
+ break;
+ case MotionNotify:
+ setModifiers(view, event.xmotion.state, event.xmotion.time);
+ if (view->motionFunc) {
+ view->motionFunc(view, event.xmotion.x, event.xmotion.y);
+ }
+ break;
+ case ButtonPress:
+ setModifiers(view, event.xbutton.state, event.xbutton.time);
+ if (event.xbutton.button >= 4 && event.xbutton.button <= 7) {
+ if (view->scrollFunc) {
+ float dx = 0, dy = 0;
+ switch (event.xbutton.button) {
+ case 4: dy = 1.0f; break;
+ case 5: dy = -1.0f; break;
+ case 6: dx = -1.0f; break;
+ case 7: dx = 1.0f; break;
+ }
+ view->scrollFunc(view,
+ event.xbutton.x, event.xbutton.y,
+ dx, dy);
+ }
+ break;
+ }
+ // nobreak
+ case ButtonRelease:
+ setModifiers(view, event.xbutton.state, event.xbutton.time);
+ if (view->mouseFunc &&
+ (event.xbutton.button < 4 || event.xbutton.button > 7)) {
+ view->mouseFunc(view,
+ event.xbutton.button, event.type == ButtonPress,
+ event.xbutton.x, event.xbutton.y);
+ }
+ break;
+ case KeyPress:
+ setModifiers(view, event.xkey.state, event.xkey.time);
+ dispatchKey(view, &event, true);
+ break;
+ case KeyRelease:
+ setModifiers(view, event.xkey.state, event.xkey.time);
+ if (view->ignoreKeyRepeat &&
+ XEventsQueued(view->impl->display, QueuedAfterReading)) {
+ XEvent next;
+ XPeekEvent(view->impl->display, &next);
+ if (next.type == KeyPress &&
+ next.xkey.time == event.xkey.time &&
+ next.xkey.keycode == event.xkey.keycode) {
+ XNextEvent(view->impl->display, &event);
+ break;
+ }
+ }
+ dispatchKey(view, &event, false);
+ break;
+ case ClientMessage:
+ if (!strcmp(XGetAtomName(view->impl->display,
+ event.xclient.message_type),
+ "WM_PROTOCOLS")) {
+ if (view->closeFunc) {
+ view->closeFunc(view);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (view->redisplay) {
+ puglDisplay(view);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+void
+puglPostRedisplay(PuglView* view)
+{
+ view->redisplay = true;
+}
+
+PuglNativeWindow
+puglGetNativeWindow(PuglView* view)
+{
+ return view->impl->win;
+}
diff --git a/pugl/pugl_test.c b/pugl/pugl_test.c
new file mode 100644
index 0000000..b9a54f5
--- /dev/null
+++ b/pugl/pugl_test.c
@@ -0,0 +1,193 @@
+/*
+ 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.
+*/
+
+/**
+ @file pugl_test.c A simple Pugl test that creates a top-level window.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pugl/pugl.h"
+
+// Argh!
+#ifdef __APPLE__
+# include <OpenGL/glu.h>
+#else
+# include <GL/glu.h>
+#endif
+
+static int quit = 0;
+static float xAngle = 0.0f;
+static float yAngle = 0.0f;
+static float dist = 10.0f;
+
+static void
+onReshape(PuglView* view, int width, int height)
+{
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, width, height);
+ gluPerspective(45.0f, width/(float)height, 1.0f, 10.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+static void
+onDisplay(PuglView* view)
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLoadIdentity();
+
+ glTranslatef(0.0f, 0.0f, dist * -1);
+ glRotatef(xAngle, 0.0f, 1.0f, 0.0f);
+ glRotatef(yAngle, 1.0f, 0.0f, 0.0f);
+
+ glBegin(GL_QUADS);
+
+ glColor3f(0, 0, 0); glVertex3f(-1, -1, -1);
+ glColor3f(0, 0, 1); glVertex3f(-1, -1, 1);
+ glColor3f(0, 1, 1); glVertex3f(-1, 1, 1);
+ glColor3f(0, 1, 0); glVertex3f(-1, 1, -1);
+
+ glColor3f(1, 0, 0); glVertex3f( 1, -1, -1);
+ glColor3f(1, 0, 1); glVertex3f( 1, -1, 1);
+ glColor3f(1, 1, 1); glVertex3f( 1, 1, 1);
+ glColor3f(1, 1, 0); glVertex3f( 1, 1, -1);
+
+ glColor3f(0, 0, 0); glVertex3f(-1, -1, -1);
+ glColor3f(0, 0, 1); glVertex3f(-1, -1, 1);
+ glColor3f(1, 0, 1); glVertex3f( 1, -1, 1);
+ glColor3f(1, 0, 0); glVertex3f( 1, -1, -1);
+
+ glColor3f(0, 1, 0); glVertex3f(-1, 1, -1);
+ glColor3f(0, 1, 1); glVertex3f(-1, 1, 1);
+ glColor3f(1, 1, 1); glVertex3f( 1, 1, 1);
+ glColor3f(1, 1, 0); glVertex3f( 1, 1, -1);
+
+ glColor3f(0, 0, 0); glVertex3f(-1, -1, -1);
+ glColor3f(0, 1, 0); glVertex3f(-1, 1, -1);
+ glColor3f(1, 1, 0); glVertex3f( 1, 1, -1);
+ glColor3f(1, 0, 0); glVertex3f( 1, -1, -1);
+
+ glColor3f(0, 0, 1); glVertex3f(-1, -1, 1);
+ glColor3f(0, 1, 1); glVertex3f(-1, 1, 1);
+ glColor3f(1, 1, 1); glVertex3f( 1, 1, 1);
+ glColor3f(1, 0, 1); glVertex3f( 1, -1, 1);
+
+ glEnd();
+}
+
+static void
+printModifiers(PuglView* view)
+{
+ int mods = puglGetModifiers(view);
+ fprintf(stderr, "Modifiers:%s%s%s%s\n",
+ (mods & PUGL_MOD_SHIFT) ? " Shift" : "",
+ (mods & PUGL_MOD_CTRL) ? " Ctrl" : "",
+ (mods & PUGL_MOD_ALT) ? " Alt" : "",
+ (mods & PUGL_MOD_SUPER) ? " Super" : "");
+}
+
+static void
+onKeyboard(PuglView* view, bool press, uint32_t key)
+{
+ fprintf(stderr, "Key %c %s ", (char)key, press ? "down" : "up");
+ printModifiers(view);
+ if (key == 'q' || key == 'Q' || key == PUGL_CHAR_ESCAPE ||
+ key == PUGL_CHAR_DELETE || key == PUGL_CHAR_BACKSPACE) {
+ quit = 1;
+ }
+}
+
+static void
+onSpecial(PuglView* view, bool press, PuglKey key)
+{
+ fprintf(stderr, "Special key %d %s ", key, press ? "down" : "up");
+ printModifiers(view);
+}
+
+static void
+onMotion(PuglView* view, int x, int y)
+{
+ xAngle = x % 360;
+ yAngle = y % 360;
+ puglPostRedisplay(view);
+}
+
+static void
+onMouse(PuglView* view, int button, bool press, int x, int y)
+{
+ fprintf(stderr, "Mouse %d %s at %d,%d ",
+ button, press ? "down" : "up", x, y);
+ printModifiers(view);
+}
+
+static void
+onScroll(PuglView* view, int x, int y, float dx, float dy)
+{
+ fprintf(stderr, "Scroll %d %d %f %f ", x, y, dx, dy);
+ printModifiers(view);
+ dist += dy / 4.0f;
+ puglPostRedisplay(view);
+}
+
+static void
+onClose(PuglView* view)
+{
+ quit = 1;
+}
+
+int
+main(int argc, char** argv)
+{
+ bool ignoreKeyRepeat = false;
+ bool resizable = false;
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "-h")) {
+ printf("USAGE: %s [OPTIONS]...\n\n"
+ " -h Display this help\n"
+ " -i Ignore key repeat\n"
+ " -r Resizable window\n", argv[0]);
+ return 0;
+ } else if (!strcmp(argv[i], "-i")) {
+ ignoreKeyRepeat = true;
+ } else if (!strcmp(argv[i], "-r")) {
+ resizable = true;
+ } else {
+ fprintf(stderr, "Unknown option: %s\n", argv[i]);
+ }
+ }
+
+ PuglView* view = puglCreate(0, "Pugl Test", 512, 512, resizable, true);
+ puglIgnoreKeyRepeat(view, ignoreKeyRepeat);
+ puglSetKeyboardFunc(view, onKeyboard);
+ puglSetMotionFunc(view, onMotion);
+ puglSetMouseFunc(view, onMouse);
+ puglSetScrollFunc(view, onScroll);
+ puglSetSpecialFunc(view, onSpecial);
+ puglSetDisplayFunc(view, onDisplay);
+ puglSetReshapeFunc(view, onReshape);
+ puglSetCloseFunc(view, onClose);
+
+ while (!quit) {
+ puglProcessEvents(view);
+ }
+
+ puglDestroy(view);
+ return 0;
+}
diff --git a/pugl/waf b/pugl/waf
new file mode 100755
index 0000000..32ae229
--- /dev/null
+++ b/pugl/waf
Binary files differ
diff --git a/pugl/wscript b/pugl/wscript
new file mode 100644
index 0000000..3682d14
--- /dev/null
+++ b/pugl/wscript
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+import glob
+import os
+import sys
+
+from waflib.extras import autowaf as autowaf
+import waflib.Logs as Logs, waflib.Options as Options
+
+# Version of this package (even if built as a child)
+PUGL_VERSION = '0.2.0'
+PUGL_MAJOR_VERSION = '0'
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+# Pugl uses the same version number for both library and package
+PUGL_LIB_VERSION = PUGL_VERSION
+
+# Variables for 'waf dist'
+APPNAME = 'pugl'
+VERSION = PUGL_VERSION
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ opt.load('compiler_c')
+ if Options.platform == 'win32':
+ opt.load('compiler_cxx')
+ autowaf.set_options(opt)
+ opt.add_option('--test', action='store_true', default=False, dest='build_tests',
+ help="Build unit tests")
+ opt.add_option('--static', action='store_true', default=False, dest='static',
+ help="Build static library")
+ opt.add_option('--shared', action='store_true', default=False, dest='shared',
+ help="Build shared library")
+
+def configure(conf):
+ conf.load('compiler_c')
+ if Options.platform == 'win32':
+ conf.load('compiler_cxx')
+ autowaf.configure(conf)
+ autowaf.display_header('Pugl Configuration')
+
+ if conf.env['MSVC_COMPILER']:
+ conf.env.append_unique('CFLAGS', ['-TP', '-MD'])
+ else:
+ conf.env.append_unique('CFLAGS', '-std=c99')
+
+ # Shared library building is broken on win32 for some reason
+ conf.env['BUILD_TESTS'] = Options.options.build_tests
+ conf.env['BUILD_SHARED'] = (Options.platform != 'win32' or
+ Options.options.shared)
+ conf.env['BUILD_STATIC'] = (Options.options.build_tests or
+ Options.options.static)
+
+ autowaf.define(conf, 'PUGL_VERSION', PUGL_VERSION)
+ conf.write_config_header('pugl_config.h', remove=False)
+
+ conf.env['INCLUDES_PUGL'] = ['%s/pugl-%s' % (conf.env['INCLUDEDIR'],
+ PUGL_MAJOR_VERSION)]
+ conf.env['LIBPATH_PUGL'] = [conf.env['LIBDIR']]
+ conf.env['LIB_PUGL'] = ['pugl-%s' % PUGL_MAJOR_VERSION];
+
+ autowaf.display_msg(conf, "Static library", str(conf.env['BUILD_STATIC']))
+ autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS']))
+ print('')
+
+def build(bld):
+ # C Headers
+ includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION
+ bld.install_files(includedir, bld.path.ant_glob('pugl/*.h'))
+
+ # Pkgconfig file
+ autowaf.build_pc(bld, 'PUGL', PUGL_VERSION, PUGL_MAJOR_VERSION, [],
+ {'PUGL_MAJOR_VERSION' : PUGL_MAJOR_VERSION})
+
+ 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']
+ libs = ['X11', 'GL', 'GLU']
+ defines = []
+ if bld.env['MSVC_COMPILER']:
+ libflags = []
+
+ # Shared Library
+ if bld.env['BUILD_SHARED']:
+ obj = bld(features = '%s %sshlib' % (lang, lang),
+ export_includes = ['.'],
+ source = lib_source,
+ includes = ['.', './src'],
+ lib = libs,
+ framework = framework,
+ name = 'libpugl',
+ target = 'pugl-%s' % PUGL_MAJOR_VERSION,
+ vnum = PUGL_LIB_VERSION,
+ install_path = '${LIBDIR}',
+ defines = defines,
+ cflags = libflags + [ '-DPUGL_SHARED',
+ '-DPUGL_INTERNAL' ])
+
+ # Static library
+ if bld.env['BUILD_STATIC']:
+ obj = bld(features = '%s %sstlib' % (lang, lang),
+ export_includes = ['.'],
+ source = lib_source,
+ includes = ['.', './src'],
+ lib = libs,
+ framework = framework,
+ name = 'libpugl_static',
+ target = 'pugl-%s' % PUGL_MAJOR_VERSION,
+ vnum = PUGL_LIB_VERSION,
+ install_path = '${LIBDIR}',
+ defines = defines,
+ cflags = ['-DPUGL_INTERNAL'])
+
+ if bld.env['BUILD_TESTS']:
+ test_libs = libs
+ test_cflags = ['']
+
+ # Unit test program
+ obj = bld(features = 'c cprogram',
+ source = 'pugl_test.c',
+ includes = ['.', './src'],
+ use = 'libpugl_static',
+ lib = test_libs,
+ framework = framework,
+ target = 'pugl_test',
+ install_path = '',
+ defines = defines,
+ cflags = test_cflags)
+
+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)