diff options
author | Jonas Suhr Christensen <jsc@umbraculum.org> | 2014-01-18 10:17:58 +0100 |
---|---|---|
committer | Jonas Suhr Christensen <jsc@umbraculum.org> | 2014-01-18 10:17:58 +0100 |
commit | fe9e38995f5a0abc196e9600c38d95385d6bf84f (patch) | |
tree | d106ff875aba7289296f5ed47626f50fc0930d2c /pugl/pugl | |
parent | 5f1e301d951c10533b101bcddc7a0262780743a3 (diff) |
Added PuGl window drawing. Enable with './configure -with-pugl'.
Diffstat (limited to 'pugl/pugl')
-rw-r--r-- | pugl/pugl/pugl.h | 357 | ||||
-rw-r--r-- | pugl/pugl/pugl_internal.h | 143 | ||||
-rw-r--r-- | pugl/pugl/pugl_osx.m | 400 | ||||
-rw-r--r-- | pugl/pugl/pugl_win.cpp | 376 | ||||
-rw-r--r-- | pugl/pugl/pugl_x11.c | 386 |
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; +} |