summaryrefslogtreecommitdiff
path: root/pugl/pugl
diff options
context:
space:
mode:
authorJonas Suhr Christensen <jsc@umbraculum.org>2014-01-18 10:17:58 +0100
committerJonas Suhr Christensen <jsc@umbraculum.org>2014-01-18 10:17:58 +0100
commitfe9e38995f5a0abc196e9600c38d95385d6bf84f (patch)
treed106ff875aba7289296f5ed47626f50fc0930d2c /pugl/pugl
parent5f1e301d951c10533b101bcddc7a0262780743a3 (diff)
Added PuGl window drawing. Enable with './configure -with-pugl'.
Diffstat (limited to 'pugl/pugl')
-rw-r--r--pugl/pugl/pugl.h357
-rw-r--r--pugl/pugl/pugl_internal.h143
-rw-r--r--pugl/pugl/pugl_osx.m400
-rw-r--r--pugl/pugl/pugl_win.cpp376
-rw-r--r--pugl/pugl/pugl_x11.c386
5 files changed, 1662 insertions, 0 deletions
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;
+}