diff options
Diffstat (limited to 'pugl')
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-baseBinary files differ new file mode 100644 index 0000000..32ae229 --- /dev/null +++ b/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base 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.dbBinary files differ new file mode 100644 index 0000000..ca0c8da --- /dev/null +++ b/pugl/.svn/wc.db 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/wafBinary files differ new file mode 100755 index 0000000..32ae229 --- /dev/null +++ b/pugl/waf 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) | 
