aboutsummaryrefslogtreecommitdiff
path: root/pugl/pugl
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2020-10-02 13:39:13 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2020-10-02 13:39:13 +0200
commitfc6c5a6882d9b0c76097d4720f06d21a226f1a77 (patch)
treefa4952b857e6108d6b338177986c8989650099f6 /pugl/pugl
parent86db492597a0006f546ce4b32342d03a7045ec08 (diff)
downloadvm.lv2-fc6c5a6882d9b0c76097d4720f06d21a226f1a77.tar.xz
Squashed 'subprojects/nk_pugl/' changes from abbf3be3..93f305cb
93f305cb Merge commit 'bfea30b198e3ab18cc645914ff61ce5a6964755c' into master bfea30b1 Squashed 'pugl/' changes from 8f28d8c9..1b1a1c3a 32ccfd5c meson: add missing dependency on glu. git-subtree-dir: subprojects/nk_pugl git-subtree-split: 93f305cbed22b904a0d5892413a41493293da742
Diffstat (limited to 'pugl/pugl')
-rw-r--r--pugl/pugl/detail/implementation.c18
-rw-r--r--pugl/pugl/detail/implementation.h29
-rw-r--r--pugl/pugl/detail/mac.h20
-rw-r--r--pugl/pugl/detail/mac.m359
-rw-r--r--pugl/pugl/detail/mac_cairo.m29
-rw-r--r--pugl/pugl/detail/mac_gl.m20
-rw-r--r--pugl/pugl/detail/mac_stub.m13
-rw-r--r--pugl/pugl/detail/stub.h75
-rw-r--r--pugl/pugl/detail/types.h43
-rw-r--r--pugl/pugl/detail/win.c100
-rw-r--r--pugl/pugl/detail/win.h36
-rw-r--r--pugl/pugl/detail/win_cairo.c8
-rw-r--r--pugl/pugl/detail/win_gl.c11
-rw-r--r--pugl/pugl/detail/x11.c237
-rw-r--r--pugl/pugl/detail/x11.h12
-rw-r--r--pugl/pugl/detail/x11_cairo.c5
-rw-r--r--pugl/pugl/detail/x11_gl.c53
-rw-r--r--pugl/pugl/gl.h5
-rw-r--r--pugl/pugl/glu.h5
-rw-r--r--pugl/pugl/pugl.h155
-rw-r--r--pugl/pugl/pugl.hpp674
-rw-r--r--pugl/pugl/pugl.ipp154
-rw-r--r--pugl/pugl/pugl_cairo.h7
-rw-r--r--pugl/pugl/pugl_cairo.hpp (renamed from pugl/pugl/pugl_cairo_backend.h)37
-rw-r--r--pugl/pugl/pugl_gl.h7
-rw-r--r--pugl/pugl/pugl_gl.hpp (renamed from pugl/pugl/pugl_gl_backend.h)47
-rw-r--r--pugl/pugl/pugl_stub.h51
-rw-r--r--pugl/pugl/pugl_stub.hpp (renamed from pugl/pugl/pugl_stub_backend.h)37
28 files changed, 1822 insertions, 425 deletions
diff --git a/pugl/pugl/detail/implementation.c b/pugl/pugl/detail/implementation.c
index ee9b242..055e917 100644
--- a/pugl/pugl/detail/implementation.c
+++ b/pugl/pugl/detail/implementation.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file implementation.c Platform-independent implementation.
+ @file implementation.c
+ @brief Platform-independent implementation.
*/
#include "pugl/detail/implementation.h"
@@ -60,6 +61,7 @@ puglStrerror(const PuglStatus status)
case PUGL_FAILURE: return "Non-fatal failure";
case PUGL_UNKNOWN_ERROR: return "Unknown system error";
case PUGL_BAD_BACKEND: return "Invalid or missing backend";
+ case PUGL_BAD_CONFIGURATION: return "Invalid view configuration";
case PUGL_BAD_PARAMETER: return "Invalid parameter";
case PUGL_BACKEND_FAILED: return "Backend initialisation failed";
case PUGL_REGISTRATION_FAILED: return "Class registration failed";
@@ -188,9 +190,9 @@ puglNewView(PuglWorld* const world)
return NULL;
}
- view->world = world;
- view->frame.width = 640;
- view->frame.height = 480;
+ view->world = world;
+ view->minWidth = 1;
+ view->minHeight = 1;
puglSetDefaultHints(view->hints);
@@ -309,7 +311,7 @@ PuglStatus
puglEnterContext(PuglView* view, bool drawing)
{
const PuglEventExpose expose = {
- PUGL_EXPOSE, 0, 0, 0, view->frame.width, view->frame.height, 0};
+ PUGL_EXPOSE, 0, 0.0, 0.0, view->frame.width, view->frame.height};
view->backend->enter(view, drawing ? &expose : NULL);
@@ -320,7 +322,7 @@ PuglStatus
puglLeaveContext(PuglView* view, bool drawing)
{
const PuglEventExpose expose = {
- PUGL_EXPOSE, 0, 0, 0, view->frame.width, view->frame.height, 0};
+ PUGL_EXPOSE, 0, 0.0, 0.0, view->frame.width, view->frame.height};
view->backend->leave(view, drawing ? &expose : NULL);
@@ -336,7 +338,7 @@ puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
return PUGL_SUCCESS;
}
-/** Return the code point for buf, or the replacement character on error. */
+/// Return the code point for buf, or the replacement character on error
uint32_t
puglDecodeUTF8(const uint8_t* buf)
{
diff --git a/pugl/pugl/detail/implementation.h b/pugl/pugl/detail/implementation.h
index bcecd85..ff97fef 100644
--- a/pugl/pugl/detail/implementation.h
+++ b/pugl/pugl/detail/implementation.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file implementation.h Shared declarations for implementation.
+ @file implementation.h
+ @brief Shared declarations for implementation.
*/
#ifndef PUGL_DETAIL_IMPLEMENTATION_H
@@ -29,42 +30,42 @@
PUGL_BEGIN_DECLS
-/** Set `blob` to `data` with length `len`, reallocating if necessary. */
+/// Set `blob` to `data` with length `len`, reallocating if necessary
void puglSetBlob(PuglBlob* dest, const void* data, size_t len);
-/** Reallocate and set `*dest` to `string`. */
+/// Reallocate and set `*dest` to `string`
void puglSetString(char** dest, const char* string);
-/** Allocate and initialise world internals (implemented once per platform) */
+/// Allocate and initialise world internals (implemented once per platform)
PuglWorldInternals*
puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags);
-/** Destroy and free world internals (implemented once per platform) */
+/// Destroy and free world internals (implemented once per platform)
void puglFreeWorldInternals(PuglWorld* world);
-/** Allocate and initialise view internals (implemented once per platform) */
+/// Allocate and initialise view internals (implemented once per platform)
PuglInternals* puglInitViewInternals(void);
-/** Destroy and free view internals (implemented once per platform) */
+/// Destroy and free view internals (implemented once per platform)
void puglFreeViewInternals(PuglView* view);
-/** Return the Unicode code point for `buf` or the replacement character. */
+/// Return the Unicode code point for `buf` or the replacement character
uint32_t puglDecodeUTF8(const uint8_t* buf);
-/** Dispatch an event with a simple `type` to `view`. */
+/// Dispatch an event with a simple `type` to `view`
void puglDispatchSimpleEvent(PuglView* view, PuglEventType type);
-/** Dispatch `event` to `view` while already in the graphics context. */
+/// Dispatch `event` to `view` while already in the graphics context
void puglDispatchEventInContext(PuglView* view, const PuglEvent* event);
-/** Dispatch `event` to `view`, entering graphics context if necessary. */
+/// Dispatch `event` to `view`, entering graphics context if necessary
void puglDispatchEvent(PuglView* view, const PuglEvent* event);
-/** Set internal (stored in view) clipboard contents. */
+/// Set internal (stored in view) clipboard contents
const void*
puglGetInternalClipboard(const PuglView* view, const char** type, size_t* len);
-/** Set internal (stored in view) clipboard contents. */
+/// Set internal (stored in view) clipboard contents
PuglStatus
puglSetInternalClipboard(PuglView* view,
const char* type,
diff --git a/pugl/pugl/detail/mac.h b/pugl/pugl/detail/mac.h
index 2243337..7b64cfe 100644
--- a/pugl/pugl/detail/mac.h
+++ b/pugl/pugl/detail/mac.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
Permission to use, copy, modify, and/or distribute this software for any
@@ -16,7 +16,8 @@
*/
/**
- @file mac.h Shared definitions for MacOS implementation.
+ @file mac.h
+ @brief Shared definitions for MacOS implementation.
*/
#include "pugl/pugl.h"
@@ -26,15 +27,6 @@
#include <stdint.h>
@interface PuglWrapperView : NSView<NSTextInputClient>
-{
-@public
- PuglView* puglview;
- NSTrackingArea* trackingArea;
- NSMutableAttributedString* markedText;
- NSTimer* timer;
- NSMutableDictionary* userTimers;
- bool reshaped;
-}
- (void) dispatchExpose:(NSRect)rect;
- (void) setReshaped;
@@ -42,10 +34,6 @@
@end
@interface PuglWindow : NSWindow
-{
-@public
- PuglView* puglview;
-}
- (void) setPuglview:(PuglView*)view;
@@ -60,6 +48,8 @@ struct PuglInternalsImpl {
NSApplication* app;
PuglWrapperView* wrapperView;
NSView* drawView;
+ NSCursor* cursor;
PuglWindow* window;
uint32_t mods;
+ bool mouseTracked;
};
diff --git a/pugl/pugl/detail/mac.m b/pugl/pugl/detail/mac.m
index 501be02..23671ae 100644
--- a/pugl/pugl/detail/mac.m
+++ b/pugl/pugl/detail/mac.m
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
Permission to use, copy, modify, and/or distribute this software for any
@@ -16,7 +16,8 @@
*/
/**
- @file mac.m MacOS implementation.
+ @file mac.m
+ @brief MacOS implementation.
*/
#define GL_SILENCE_DEPRECATION 1
@@ -40,31 +41,88 @@ typedef NSUInteger NSWindowStyleMask;
#endif
static NSRect
-rectToScreen(NSRect rect)
+rectToScreen(NSScreen* screen, NSRect rect)
{
- const double screenHeight = [[NSScreen mainScreen] frame].size.height;
+ const double screenHeight = [screen frame].size.height;
rect.origin.y = screenHeight - rect.origin.y - rect.size.height;
return rect;
}
+static NSScreen*
+viewScreen(PuglView* view)
+{
+ return view->impl->window ? [view->impl->window screen] : [NSScreen mainScreen];
+}
+
+static NSRect
+nsRectToPoints(PuglView* view, const NSRect rect)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakeRect(rect.origin.x / scaleFactor,
+ rect.origin.y / scaleFactor,
+ rect.size.width / scaleFactor,
+ rect.size.height / scaleFactor);
+}
+
+static NSRect
+nsRectFromPoints(PuglView* view, const NSRect rect)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakeRect(rect.origin.x * scaleFactor,
+ rect.origin.y * scaleFactor,
+ rect.size.width * scaleFactor,
+ rect.size.height * scaleFactor);
+}
+
+static NSPoint
+nsPointFromPoints(PuglView* view, const NSPoint point)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor);
+}
+
+static NSRect
+rectToNsRect(const PuglRect rect)
+{
+ return NSMakeRect(rect.x, rect.y, rect.width, rect.height);
+}
+
+static NSSize
+sizePoints(PuglView* view, const double width, const double height)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakeSize(width / scaleFactor, height / scaleFactor);
+}
+
static void
updateViewRect(PuglView* view)
{
NSWindow* const window = view->impl->window;
if (window) {
- const double screenHeight = [[NSScreen mainScreen] frame].size.height;
- const NSRect frame = [window frame];
- const NSRect content = [window contentRectForFrameRect:frame];
-
- view->frame.x = content.origin.x;
- view->frame.y = screenHeight - content.origin.y - content.size.height;
- view->frame.width = content.size.width;
- view->frame.height = content.size.height;
+ const NSRect screenFramePt = [[NSScreen mainScreen] frame];
+ const NSRect screenFramePx = nsRectFromPoints(view, screenFramePt);
+ const NSRect framePt = [window frame];
+ const NSRect contentPt = [window contentRectForFrameRect:framePt];
+ const NSRect contentPx = nsRectFromPoints(view, contentPt);
+ const double screenHeight = screenFramePx.size.height;
+
+ view->frame.x = contentPx.origin.x;
+ view->frame.y = screenHeight - contentPx.origin.y - contentPx.size.height;
+ view->frame.width = contentPx.size.width;
+ view->frame.height = contentPx.size.height;
}
}
@implementation PuglWindow
+{
+@public
+ PuglView* puglview;
+}
- (id) initWithContentRect:(NSRect)contentRect
styleMask:(NSWindowStyleMask)aStyle
@@ -74,9 +132,9 @@ updateViewRect(PuglView* view)
(void)flag;
NSWindow* result = [super initWithContentRect:contentRect
- styleMask:aStyle
- backing:bufferingType
- defer:NO];
+ styleMask:aStyle
+ backing:bufferingType
+ defer:NO];
[result setAcceptsMouseMovedEvents:YES];
return (PuglWindow*)result;
@@ -85,7 +143,9 @@ updateViewRect(PuglView* view)
- (void)setPuglview:(PuglView*)view
{
puglview = view;
- [self setContentSize:NSMakeSize(view->frame.width, view->frame.height)];
+
+ [self
+ setContentSize:sizePoints(view, view->frame.width, view->frame.height)];
}
- (BOOL) canBecomeKeyWindow
@@ -124,13 +184,24 @@ updateViewRect(PuglView* view)
@end
@implementation PuglWrapperView
+{
+@public
+ PuglView* puglview;
+ NSTrackingArea* trackingArea;
+ NSMutableAttributedString* markedText;
+ NSTimer* timer;
+ NSMutableDictionary* userTimers;
+ bool reshaped;
+}
- (void) dispatchExpose:(NSRect)rect
{
+ const double scaleFactor = [[NSScreen mainScreen] backingScaleFactor];
+
if (reshaped) {
updateViewRect(puglview);
- const PuglEventConfigure ev = {
+ const PuglEventConfigure ev = {
PUGL_CONFIGURE,
0,
puglview->frame.x,
@@ -150,16 +221,26 @@ updateViewRect(PuglView* view)
const PuglEventExpose ev = {
PUGL_EXPOSE,
0,
- rect.origin.x,
- rect.origin.y,
- rect.size.width,
- rect.size.height,
- 0
+ rect.origin.x * scaleFactor,
+ rect.origin.y * scaleFactor,
+ rect.size.width * scaleFactor,
+ rect.size.height * scaleFactor,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
+- (NSSize) intrinsicContentSize
+{
+ if (puglview->defaultWidth || puglview->defaultHeight) {
+ return sizePoints(puglview,
+ puglview->defaultWidth,
+ puglview->defaultHeight);
+ }
+
+ return NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric);
+}
+
- (BOOL) isFlipped
{
return YES;
@@ -246,7 +327,9 @@ keySymToSpecial(const NSEvent* const ev)
- (NSPoint) eventLocation:(NSEvent*)event
{
- return [self convertPoint:[event locationInWindow] fromView:nil];
+ return nsPointFromPoints(puglview,
+ [self convertPoint:[event locationInWindow]
+ fromView:nil]);
}
static void
@@ -272,11 +355,15 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
- (void) mouseEntered:(NSEvent*)event
{
handleCrossing(self, event, PUGL_POINTER_IN);
+ [puglview->impl->cursor set];
+ puglview->impl->mouseTracked = true;
}
- (void) mouseExited:(NSEvent*)event
{
+ [[NSCursor arrowCursor] set];
handleCrossing(self, event, PUGL_POINTER_OUT);
+ puglview->impl->mouseTracked = false;
}
- (void) mouseMoved:(NSEvent*)event
@@ -292,8 +379,6 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
- 0,
- 1,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
@@ -374,19 +459,32 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
- (void) scrollWheel:(NSEvent*)event
{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglEventScroll ev = {
- PUGL_SCROLL,
- 0,
- [event timestamp],
- wloc.x,
- wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(event),
- [event scrollingDeltaX],
- [event scrollingDeltaY],
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const double dx = [event scrollingDeltaX];
+ const double dy = [event scrollingDeltaY];
+ const PuglScrollDirection dir =
+ ((dx == 0.0 && dy > 0.0)
+ ? PUGL_SCROLL_UP
+ : ((dx == 0.0 && dy < 0.0)
+ ? PUGL_SCROLL_DOWN
+ : ((dy == 0.0 && dx > 0.0)
+ ? PUGL_SCROLL_RIGHT
+ : ((dy == 0.0 && dx < 0.0) ? PUGL_SCROLL_LEFT
+ : PUGL_SCROLL_SMOOTH))));
+
+ const PuglEventScroll ev = {
+ PUGL_SCROLL,
+ 0,
+ [event timestamp],
+ wloc.x,
+ wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(event),
+ [event hasPreciseScrollingDeltas] ? PUGL_SCROLL_SMOOTH : dir,
+ dx,
+ dy,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
@@ -653,15 +751,15 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
@end
@interface PuglWindowDelegate : NSObject<NSWindowDelegate>
-{
- PuglWindow* window;
-}
- (instancetype) initWithPuglWindow:(PuglWindow*)window;
@end
@implementation PuglWindowDelegate
+{
+ PuglWindow* window;
+}
- (instancetype) initWithPuglWindow:(PuglWindow*)puglWindow
{
@@ -692,7 +790,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
(void)notification;
PuglEvent ev = {{PUGL_FOCUS_IN, 0}};
- ev.focus.grab = false;
+ ev.focus.mode = PUGL_CROSSING_NORMAL;
puglDispatchEvent(window->puglview, &ev);
}
@@ -701,21 +799,23 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
(void)notification;
PuglEvent ev = {{PUGL_FOCUS_OUT, 0}};
- ev.focus.grab = false;
+ ev.focus.mode = PUGL_CROSSING_NORMAL;
puglDispatchEvent(window->puglview, &ev);
}
@end
PuglWorldInternals*
-puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
- PuglWorldFlags PUGL_UNUSED(flags))
+puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags))
{
PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
1, sizeof(PuglWorldInternals));
- impl->app = [NSApplication sharedApplication];
- impl->autoreleasePool = [NSAutoreleasePool new];
+ impl->app = [NSApplication sharedApplication];
+
+ if (type == PUGL_PROGRAM) {
+ impl->autoreleasePool = [NSAutoreleasePool new];
+ }
return impl;
}
@@ -723,7 +823,10 @@ puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
void
puglFreeWorldInternals(PuglWorld* world)
{
- [world->impl->autoreleasePool drain];
+ if (world->impl->autoreleasePool) {
+ [world->impl->autoreleasePool drain];
+ }
+
free(world->impl);
}
@@ -736,7 +839,11 @@ puglGetNativeWorld(PuglWorld* PUGL_UNUSED(world))
PuglInternals*
puglInitViewInternals(void)
{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+
+ impl->cursor = [NSCursor arrowCursor];
+
+ return impl;
}
static NSLayoutConstraint*
@@ -749,13 +856,35 @@ puglConstraint(id item, NSLayoutAttribute attribute, float constant)
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
- constant: constant];
+ constant: (CGFloat)constant];
}
PuglStatus
puglRealize(PuglView* view)
{
- PuglInternals* impl = view->impl;
+ PuglInternals* impl = view->impl;
+ const NSScreen* const screen = [NSScreen mainScreen];
+ const double scaleFactor = [screen backingScaleFactor];
+
+ if (view->frame.width == 0.0 && view->frame.height == 0.0) {
+ if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) {
+ return PUGL_BAD_CONFIGURATION;
+ }
+
+ const double screenWidthPx = [screen frame].size.width * scaleFactor;
+ const double screenHeightPx = [screen frame].size.height * scaleFactor;
+
+ view->frame.width = view->defaultWidth;
+ view->frame.height = view->defaultHeight;
+ view->frame.x = screenWidthPx / 2.0 - view->frame.width / 2.0;
+ view->frame.y = screenHeightPx / 2.0 - view->frame.height / 2.0;
+ }
+
+ const NSRect framePx = rectToNsRect(view->frame);
+ const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor,
+ framePx.origin.y / scaleFactor,
+ framePx.size.width / scaleFactor,
+ framePx.size.height / scaleFactor);
// Create wrapper view to handle input
impl->wrapperView = [PuglWrapperView alloc];
@@ -763,8 +892,7 @@ puglRealize(PuglView* view)
impl->wrapperView->userTimers = [[NSMutableDictionary alloc] init];
impl->wrapperView->markedText = [[NSMutableAttributedString alloc] init];
[impl->wrapperView setAutoresizesSubviews:YES];
- [impl->wrapperView initWithFrame:
- NSMakeRect(0, 0, view->frame.width, view->frame.height)];
+ [impl->wrapperView initWithFrame:framePt];
[impl->wrapperView addConstraint:
puglConstraint(impl->wrapperView, NSLayoutAttributeWidth, view->minWidth)];
[impl->wrapperView addConstraint:
@@ -788,10 +916,6 @@ puglRealize(PuglView* view)
[impl->drawView setHidden:NO];
[[impl->drawView window] makeFirstResponder:impl->wrapperView];
} else {
- const NSRect frame = rectToScreen(
- NSMakeRect(view->frame.x, view->frame.y,
- view->minWidth, view->minHeight));
-
unsigned style = (NSClosableWindowMask |
NSTitledWindowMask |
NSMiniaturizableWindowMask );
@@ -800,7 +924,7 @@ puglRealize(PuglView* view)
}
PuglWindow* window = [[[PuglWindow alloc]
- initWithContentRect:frame
+ initWithContentRect:rectToScreen([NSScreen mainScreen], framePt)
styleMask:style
backing:NSBackingStoreBuffered
defer:NO
@@ -817,7 +941,8 @@ puglRealize(PuglView* view)
}
if (view->minWidth || view->minHeight) {
- [window setContentMinSize:NSMakeSize(view->minWidth,
+ [window setContentMinSize:sizePoints(view,
+ view->minWidth,
view->minHeight)];
}
impl->window = window;
@@ -826,10 +951,13 @@ puglRealize(PuglView* view)
initWithPuglWindow:window];
if (view->minAspectX && view->minAspectY) {
- [window setContentAspectRatio:NSMakeSize(view->minAspectX,
+ [window setContentAspectRatio:sizePoints(view,
+ view->minAspectX,
view->minAspectY)];
}
+ puglSetFrame(view, view->frame);
+
[window setContentView:impl->wrapperView];
[view->world->impl->app activateIgnoringOtherApps:YES];
[window makeFirstResponder:impl->wrapperView];
@@ -1066,8 +1194,9 @@ puglPostRedisplay(PuglView* view)
PuglStatus
puglPostRedisplayRect(PuglView* view, const PuglRect rect)
{
- [view->impl->drawView setNeedsDisplayInRect:
- NSMakeRect(rect.x, rect.y, rect.width, rect.height)];
+ const NSRect rectPx = rectToNsRect(rect);
+
+ [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)];
return PUGL_SUCCESS;
}
@@ -1103,32 +1232,59 @@ puglSetFrame(PuglView* view, const PuglRect frame)
// Update view frame to exactly the requested frame in Pugl coordinates
view->frame = frame;
- const NSRect rect = NSMakeRect(frame.x, frame.y, frame.width, frame.height);
+ const NSRect framePx = rectToNsRect(frame);
+ const NSRect framePt = nsRectToPoints(view, framePx);
if (impl->window) {
// Resize window to fit new content rect
- const NSRect windowFrame = [
- impl->window frameRectForContentRect:rectToScreen(rect)];
+ const NSRect screenPt = rectToScreen(viewScreen(view), framePt);
+ const NSRect winFrame = [impl->window frameRectForContentRect:screenPt];
- [impl->window setFrame:windowFrame display:NO];
+ [impl->window setFrame:winFrame display:NO];
}
// Resize views
- const NSRect drawRect = NSMakeRect(0, 0, frame.width, frame.height);
- [impl->wrapperView setFrame:(impl->window ? drawRect : rect)];
- [impl->drawView setFrame:drawRect];
+ const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height);
+ const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx];
+
+ [impl->wrapperView setFrame:(impl->window ? sizePt : framePt)];
+ [impl->drawView setFrame:sizePt];
return PUGL_SUCCESS;
}
PuglStatus
+puglSetDefaultSize(PuglView* const view, const int width, const int height)
+{
+ view->defaultWidth = width;
+ view->defaultHeight = height;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
puglSetMinSize(PuglView* const view, const int width, const int height)
{
view->minWidth = width;
view->minHeight = height;
if (view->impl->window && (view->minWidth || view->minHeight)) {
- [view->impl->window
- setContentMinSize:NSMakeSize(view->minWidth, view->minHeight)];
+ [view->impl->window setContentMinSize:sizePoints(view,
+ view->minWidth,
+ view->minHeight)];
+ }
+
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglSetMaxSize(PuglView* const view, const int width, const int height)
+{
+ view->maxWidth = width;
+ view->maxHeight = height;
+
+ if (view->impl->window && (view->maxWidth || view->maxHeight)) {
+ [view->impl->window setContentMaxSize:sizePoints(view,
+ view->maxWidth,
+ view->maxHeight)];
}
return PUGL_SUCCESS;
@@ -1147,13 +1303,31 @@ puglSetAspectRatio(PuglView* const view,
view->maxAspectY = maxY;
if (view->impl->window && view->minAspectX && view->minAspectY) {
- [view->impl->window setContentAspectRatio:NSMakeSize(view->minAspectX,
+ [view->impl->window setContentAspectRatio:sizePoints(view,
+ view->minAspectX,
view->minAspectY)];
}
return PUGL_SUCCESS;
}
+PuglStatus
+puglSetTransientFor(PuglView* view, PuglNativeView parent)
+{
+ view->transientParent = parent;
+
+ if (view->impl->window) {
+ NSWindow* parentWindow = [(NSView*)parent window];
+ if (parentWindow) {
+ [parentWindow addChildWindow:view->impl->window
+ ordered:NSWindowAbove];
+ return PUGL_SUCCESS;
+ }
+ }
+
+ return PUGL_FAILURE;
+}
+
const void*
puglGetClipboard(PuglView* const view,
const char** const type,
@@ -1171,6 +1345,47 @@ puglGetClipboard(PuglView* const view,
return puglGetInternalClipboard(view, type, len);
}
+static NSCursor*
+puglGetNsCursor(const PuglCursor cursor)
+{
+ switch (cursor) {
+ case PUGL_CURSOR_ARROW:
+ return [NSCursor arrowCursor];
+ case PUGL_CURSOR_CARET:
+ return [NSCursor IBeamCursor];
+ case PUGL_CURSOR_CROSSHAIR:
+ return [NSCursor crosshairCursor];
+ case PUGL_CURSOR_HAND:
+ return [NSCursor pointingHandCursor];
+ case PUGL_CURSOR_NO:
+ return [NSCursor operationNotAllowedCursor];
+ case PUGL_CURSOR_LEFT_RIGHT:
+ return [NSCursor resizeLeftRightCursor];
+ case PUGL_CURSOR_UP_DOWN:
+ return [NSCursor resizeUpDownCursor];
+ }
+
+ return NULL;
+}
+
+PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor)
+{
+ PuglInternals* const impl = view->impl;
+ NSCursor* const cur = puglGetNsCursor(cursor);
+ if (!cur) {
+ return PUGL_FAILURE;
+ }
+
+ impl->cursor = cur;
+
+ if (impl->mouseTracked) {
+ [cur set];
+ }
+
+ return PUGL_SUCCESS;
+}
+
PuglStatus
puglSetClipboard(PuglView* const view,
const char* const type,
diff --git a/pugl/pugl/detail/mac_cairo.m b/pugl/pugl/detail/mac_cairo.m
index 51c1c13..18209d9 100644
--- a/pugl/pugl/detail/mac_cairo.m
+++ b/pugl/pugl/detail/mac_cairo.m
@@ -1,5 +1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@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
@@ -15,13 +15,14 @@
*/
/**
- @file mac_cairo.m Cairo graphics backend for MacOS.
+ @file mac_cairo.m
+ @brief Cairo graphics backend for MacOS.
*/
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl_cairo.h"
-#include "pugl/pugl_stub.h"
#include <cairo-quartz.h>
@@ -30,6 +31,9 @@
#include <assert.h>
@interface PuglCairoView : NSView
+@end
+
+@implementation PuglCairoView
{
@public
PuglView* puglview;
@@ -37,13 +41,11 @@
cairo_t* cr;
}
-@end
-
-@implementation PuglCairoView
-
- (id) initWithFrame:(NSRect)frame
{
- return (self = [super initWithFrame:frame]);
+ self = [super initWithFrame:frame];
+
+ return self;
}
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize
@@ -69,7 +71,7 @@ puglMacCairoCreate(PuglView* view)
PuglCairoView* drawView = [PuglCairoView alloc];
drawView->puglview = view;
- [drawView initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)];
+ [drawView initWithFrame:[impl->wrapperView bounds]];
if (view->hints[PUGL_RESIZABLE]) {
[drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
} else {
@@ -103,10 +105,17 @@ puglMacCairoEnter(PuglView* view, const PuglEventExpose* expose)
assert(!drawView->surface);
assert(!drawView->cr);
+ const double scale = 1.0 / [[NSScreen mainScreen] backingScaleFactor];
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
+ const CGSize sizePx = {view->frame.width, view->frame.height};
+ const CGSize sizePt = CGContextConvertSizeToUserSpace(context, sizePx);
+
+ // Convert coordinates to standard Cairo space
+ CGContextTranslateCTM(context, 0.0, -sizePt.height);
+ CGContextScaleCTM(context, scale, -scale);
drawView->surface = cairo_quartz_surface_create_for_cg_context(
- context, view->frame.width, view->frame.height);
+ context, (unsigned)sizePx.width, (unsigned)sizePx.height);
drawView->cr = cairo_create(drawView->surface);
diff --git a/pugl/pugl/detail/mac_gl.m b/pugl/pugl/detail/mac_gl.m
index eda4371..4bf6fc1 100644
--- a/pugl/pugl/detail/mac_gl.m
+++ b/pugl/pugl/detail/mac_gl.m
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@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
@@ -15,28 +15,28 @@
*/
/**
- @file mac_gl.m OpenGL graphics backend for MacOS.
+ @file mac_gl.m
+ @brief OpenGL graphics backend for MacOS.
*/
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl_gl.h"
-#include "pugl/pugl_stub.h"
#ifndef __MAC_10_10
# define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core
#endif
@interface PuglOpenGLView : NSOpenGLView
+@end
+
+@implementation PuglOpenGLView
{
@public
PuglView* puglview;
}
-@end
-
-@implementation PuglOpenGLView
-
- (id) initWithFrame:(NSRect)frame
{
const bool compat = puglview->hints[PUGL_USE_COMPAT_PROFILE];
@@ -69,6 +69,8 @@
self = [super initWithFrame:frame];
}
+ [self setWantsBestResolutionOpenGLSurface:YES];
+
if (self) {
[[self openGLContext] makeCurrentContext];
[self reshape];
@@ -97,11 +99,9 @@ puglMacGlCreate(PuglView* view)
{
PuglInternals* impl = view->impl;
PuglOpenGLView* drawView = [PuglOpenGLView alloc];
- const NSRect rect = NSMakeRect(
- 0, 0, view->frame.width, view->frame.height);
drawView->puglview = view;
- [drawView initWithFrame:rect];
+ [drawView initWithFrame:[impl->wrapperView bounds]];
if (view->hints[PUGL_RESIZABLE]) {
[drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
} else {
diff --git a/pugl/pugl/detail/mac_stub.m b/pugl/pugl/detail/mac_stub.m
index 71a54b8..8271735 100644
--- a/pugl/pugl/detail/mac_stub.m
+++ b/pugl/pugl/detail/mac_stub.m
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@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
@@ -15,25 +15,26 @@
*/
/**
- @file mac_stub.m Stub graphics backend for MacOS.
+ @file mac_stub.m
+ @brief Stub graphics backend for MacOS.
*/
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl_stub.h"
#import <Cocoa/Cocoa.h>
@interface PuglStubView : NSView
+@end
+
+@implementation PuglStubView
{
@public
PuglView* puglview;
}
-@end
-
-@implementation PuglStubView
-
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize
{
PuglWrapperView* wrapper = (PuglWrapperView*)[self superview];
diff --git a/pugl/pugl/detail/stub.h b/pugl/pugl/detail/stub.h
new file mode 100644
index 0000000..acd3181
--- /dev/null
+++ b/pugl/pugl/detail/stub.h
@@ -0,0 +1,75 @@
+/*
+ Copyright 2012-2020 David Robillard <d@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 stub.h
+ @brief Definition of generic stub backend functions.
+*/
+
+#ifndef PUGL_DETAIL_STUB_H
+#define PUGL_DETAIL_STUB_H
+
+#include "pugl/pugl.h"
+
+PUGL_BEGIN_DECLS
+
+static inline PuglStatus
+puglStubConfigure(PuglView* view)
+{
+ (void)view;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubCreate(PuglView* view)
+{
+ (void)view;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubDestroy(PuglView* view)
+{
+ (void)view;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubEnter(PuglView* view, const PuglEventExpose* expose)
+{
+ (void)view;
+ (void)expose;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubLeave(PuglView* view, const PuglEventExpose* expose)
+{
+ (void)view;
+ (void)expose;
+ return PUGL_SUCCESS;
+}
+
+static inline void*
+puglStubGetContext(PuglView* view)
+{
+ (void)view;
+ return NULL;
+}
+
+PUGL_END_DECLS
+
+#endif // PUGL_DETAIL_STUB_H
diff --git a/pugl/pugl/detail/types.h b/pugl/pugl/detail/types.h
index eb450e1..edd2bd0 100644
--- a/pugl/pugl/detail/types.h
+++ b/pugl/pugl/detail/types.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file types.h Shared internal type definitions.
+ @file types.h
+ @brief Shared internal type definitions.
*/
#ifndef PUGL_DETAIL_TYPES_H
@@ -36,22 +37,22 @@
# define PUGL_UNUSED(name) name
#endif
-/** Platform-specific world internals. */
+/// Platform-specific world internals
typedef struct PuglWorldInternalsImpl PuglWorldInternals;
-/** Platform-specific view internals. */
+/// Platform-specific view internals
typedef struct PuglInternalsImpl PuglInternals;
-/** View hints. */
+/// View hints
typedef int PuglHints[PUGL_NUM_VIEW_HINTS];
-/** Blob of arbitrary data. */
+/// Blob of arbitrary data
typedef struct {
- void* data; //!< Dynamically allocated data
- size_t len; //!< Length of data in bytes
+ void* data; ///< Dynamically allocated data
+ size_t len; ///< Length of data in bytes
} PuglBlob;
-/** Cross-platform view definition. */
+/// Cross-platform view definition
struct PuglViewImpl {
PuglWorld* world;
const PuglBackend* backend;
@@ -62,11 +63,15 @@ struct PuglViewImpl {
PuglBlob clipboard;
PuglNativeView parent;
uintptr_t transientParent;
- PuglHints hints;
PuglRect frame;
PuglEventConfigure lastConfigure;
+ PuglHints hints;
+ int defaultWidth;
+ int defaultHeight;
int minWidth;
int minHeight;
+ int maxWidth;
+ int maxHeight;
int minAspectX;
int minAspectY;
int maxAspectX;
@@ -74,7 +79,7 @@ struct PuglViewImpl {
bool visible;
};
-/** Cross-platform world definition. */
+/// Cross-platform world definition
struct PuglWorldImpl {
PuglWorldInternals* impl;
PuglWorldHandle handle;
@@ -86,27 +91,27 @@ struct PuglWorldImpl {
PuglLogLevel logLevel;
};
-/** Opaque surface used by graphics backend. */
+/// Opaque surface used by graphics backend
typedef void PuglSurface;
-/** Graphics backend interface. */
+/// Graphics backend interface
struct PuglBackendImpl {
- /** Get visual information from display and setup view as necessary. */
+ /// Get visual information from display and setup view as necessary
PuglStatus (*configure)(PuglView*);
- /** Create surface and drawing context. */
+ /// Create surface and drawing context
PuglStatus (*create)(PuglView*);
- /** Destroy surface and drawing context. */
+ /// Destroy surface and drawing context
PuglStatus (*destroy)(PuglView*);
- /** Enter drawing context, for drawing if expose is non-null. */
+ /// Enter drawing context, for drawing if expose is non-null
PuglStatus (*enter)(PuglView*, const PuglEventExpose*);
- /** Leave drawing context, after drawing if expose is non-null. */
+ /// Leave drawing context, after drawing if expose is non-null
PuglStatus (*leave)(PuglView*, const PuglEventExpose*);
- /** Return the puglGetContext() handle for the application, if any. */
+ /// Return the puglGetContext() handle for the application, if any
void* (*getContext)(PuglView*);
};
diff --git a/pugl/pugl/detail/win.c b/pugl/pugl/detail/win.c
index 44ba6cd..4f7afee 100644
--- a/pugl/pugl/detail/win.c
+++ b/pugl/pugl/detail/win.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,12 +15,14 @@
*/
/**
- @file win.c Windows implementation.
+ @file win.c
+ @brief Windows implementation.
*/
#include "pugl/detail/win.h"
#include "pugl/detail/implementation.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl.h"
#include "pugl/pugl_stub.h"
@@ -189,6 +191,8 @@ puglRealize(PuglView* view)
puglSetWindowTitle(view, view->title);
}
+ view->impl->cursor = LoadCursor(NULL, IDC_ARROW);
+
puglSetFrame(view, view->frame);
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
@@ -342,7 +346,7 @@ initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam)
event->scroll.dy = 0;
}
-/** Return the code point for buf, or the replacement character on error. */
+/// Return the code point for buf, or the replacement character on error
static uint32_t
puglDecodeUTF16(const wchar_t* buf, const int len)
{
@@ -539,6 +543,11 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
}
switch (message) {
+ case WM_SETCURSOR:
+ if (LOWORD(lParam) == HTCLIENT) {
+ SetCursor(view->impl->cursor);
+ }
+ break;
case WM_SHOWWINDOW:
if (wParam) {
handleConfigure(view, &event);
@@ -577,8 +586,9 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
RedrawWindow(view->impl->hwnd, NULL, NULL,
RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT);
} else if (wParam >= PUGL_USER_TIMER_MIN) {
- const PuglEventTimer ev = {PUGL_TIMER, 0, wParam - PUGL_USER_TIMER_MIN};
- puglDispatchEvent(view, (const PuglEvent*)&ev);
+ PuglEvent ev = {{PUGL_TIMER, 0}};
+ ev.timer.id = wParam - PUGL_USER_TIMER_MIN;
+ puglDispatchEvent(view, &ev);
}
break;
case WM_EXITSIZEMOVE:
@@ -591,6 +601,10 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize.x = view->minWidth;
mmi->ptMinTrackSize.y = view->minHeight;
+ if (view->maxWidth > 0 && view->maxHeight > 0) {
+ mmi->ptMaxTrackSize.x = view->maxWidth;
+ mmi->ptMaxTrackSize.y = view->maxHeight;
+ }
break;
case WM_PAINT:
GetUpdateRect(view->impl->hwnd, &rect, false);
@@ -599,7 +613,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
event.expose.y = rect.top;
event.expose.width = rect.right - rect.left;
event.expose.height = rect.bottom - rect.top;
- event.expose.count = 0;
break;
case WM_ERASEBKGND:
return true;
@@ -627,7 +640,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
event.motion.xRoot = pt.x;
event.motion.yRoot = pt.y;
event.motion.state = getModifiers();
- event.motion.isHint = false;
break;
case WM_MOUSELEAVE:
GetCursorPos(&pt);
@@ -656,10 +668,16 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
case WM_MOUSEWHEEL:
initScrollEvent(&event, view, lParam);
event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ event.scroll.direction = (event.scroll.dy > 0
+ ? PUGL_SCROLL_UP
+ : PUGL_SCROLL_DOWN);
break;
case WM_MOUSEHWHEEL:
initScrollEvent(&event, view, lParam);
event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ event.scroll.direction = (event.scroll.dx > 0
+ ? PUGL_SCROLL_RIGHT
+ : PUGL_SCROLL_LEFT);
break;
case WM_KEYDOWN:
if (!ignoreKeyEvent(view, lParam)) {
@@ -958,6 +976,14 @@ puglSetFrame(PuglView* view, const PuglRect frame)
}
PuglStatus
+puglSetDefaultSize(PuglView* const view, const int width, const int height)
+{
+ view->defaultWidth = width;
+ view->defaultHeight = height;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
puglSetMinSize(PuglView* const view, const int width, const int height)
{
view->minWidth = width;
@@ -966,6 +992,14 @@ puglSetMinSize(PuglView* const view, const int width, const int height)
}
PuglStatus
+puglSetMaxSize(PuglView* const view, const int width, const int height)
+{
+ view->maxWidth = width;
+ view->maxHeight = height;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
puglSetAspectRatio(PuglView* const view,
const int minX,
const int minY,
@@ -979,6 +1013,23 @@ puglSetAspectRatio(PuglView* const view,
return PUGL_SUCCESS;
}
+PuglStatus
+puglSetTransientFor(PuglView* view, PuglNativeView parent)
+{
+ if (view->parent) {
+ return PUGL_FAILURE;
+ }
+
+ view->transientParent = parent;
+
+ if (view->impl->hwnd) {
+ SetWindowLongPtr(view->impl->hwnd, GWLP_HWNDPARENT, (LONG_PTR)parent);
+ return GetLastError() == NO_ERROR ? PUGL_SUCCESS : PUGL_FAILURE;
+ }
+
+ return PUGL_SUCCESS;
+}
+
const void*
puglGetClipboard(PuglView* const view,
const char** const type,
@@ -1064,7 +1115,40 @@ puglWinStubLeave(PuglView* view, const PuglEventExpose* expose)
if (expose) {
PAINTSTRUCT ps;
EndPaint(view->impl->hwnd, &ps);
- SwapBuffers(view->impl->hdc);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+static const char* const cursor_ids[] = {
+ IDC_ARROW, // ARROW
+ IDC_IBEAM, // CARET
+ IDC_CROSS, // CROSSHAIR
+ IDC_HAND, // HAND
+ IDC_NO, // NO
+ IDC_SIZEWE, // LEFT_RIGHT
+ IDC_SIZENS, // UP_DOWN
+};
+
+PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor)
+{
+ PuglInternals* const impl = view->impl;
+ const unsigned index = (unsigned)cursor;
+ const unsigned count = sizeof(cursor_ids) / sizeof(cursor_ids[0]);
+
+ if (index >= count) {
+ return PUGL_BAD_PARAMETER;
+ }
+
+ const HCURSOR cur = LoadCursor(NULL, cursor_ids[index]);
+ if (!cur) {
+ return PUGL_FAILURE;
+ }
+
+ impl->cursor = cur;
+ if (impl->mouseTracked) {
+ SetCursor(cur);
}
return PUGL_SUCCESS;
diff --git a/pugl/pugl/detail/win.h b/pugl/pugl/detail/win.h
index 949fa90..1b9b0c4 100644
--- a/pugl/pugl/detail/win.h
+++ b/pugl/pugl/detail/win.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file win.h Shared definitions for Windows implementation.
+ @file win.h
+ @brief Shared definitions for Windows implementation.
*/
#include "pugl/detail/implementation.h"
@@ -34,6 +35,7 @@ struct PuglInternalsImpl {
PuglWinPFD pfd;
int pfId;
HWND hwnd;
+ HCURSOR cursor;
HDC hdc;
PuglSurface* surface;
DWORD refreshRate;
@@ -87,15 +89,35 @@ puglWinGetWindowExFlags(const PuglView* const view)
}
static inline PuglStatus
-puglWinCreateWindow(const PuglView* const view,
- const char* const title,
- HWND* const hwnd,
- HDC* const hdc)
+puglWinCreateWindow(PuglView* const view,
+ const char* const title,
+ HWND* const hwnd,
+ HDC* const hdc)
{
const char* className = (const char*)view->world->className;
const unsigned winFlags = puglWinGetWindowFlags(view);
const unsigned winExFlags = puglWinGetWindowExFlags(view);
+ if (view->frame.width == 0.0 && view->frame.height == 0.0) {
+ if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) {
+ return PUGL_BAD_CONFIGURATION;
+ }
+
+ RECT desktopRect;
+ GetClientRect(GetDesktopWindow(), &desktopRect);
+
+ const int screenWidth = desktopRect.right - desktopRect.left;
+ const int screenHeight = desktopRect.bottom - desktopRect.top;
+
+ view->frame.width = view->defaultWidth;
+ view->frame.height = view->defaultHeight;
+ view->frame.x = screenWidth / 2.0 - view->frame.width / 2.0;
+ view->frame.y = screenHeight / 2.0 - view->frame.height / 2.0;
+ }
+
+ // The meaning of "parent" depends on the window type (WS_CHILD)
+ PuglNativeView parent = view->parent ? view->parent : view->transientParent;
+
// Calculate total window size to accommodate requested view size
RECT wr = { (long)view->frame.x, (long)view->frame.y,
(long)view->frame.width, (long)view->frame.height };
@@ -105,7 +127,7 @@ puglWinCreateWindow(const PuglView* const view,
if (!(*hwnd = CreateWindowEx(winExFlags, className, title, winFlags,
CW_USEDEFAULT, CW_USEDEFAULT,
wr.right-wr.left, wr.bottom-wr.top,
- (HWND)view->parent, NULL, NULL, NULL))) {
+ (HWND)parent, NULL, NULL, NULL))) {
return PUGL_REALIZE_FAILED;
} else if (!(*hdc = GetDC(*hwnd))) {
DestroyWindow(*hwnd);
diff --git a/pugl/pugl/detail/win_cairo.c b/pugl/pugl/detail/win_cairo.c
index a8b371f..1b9afb9 100644
--- a/pugl/pugl/detail/win_cairo.c
+++ b/pugl/pugl/detail/win_cairo.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,13 +15,14 @@
*/
/**
- @file win_cairo.c Cairo graphics backend for Windows.
+ @file win_cairo.c
+ @brief Cairo graphics backend for Windows.
*/
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/detail/win.h"
#include "pugl/pugl_cairo.h"
-#include "pugl/pugl_stub.h"
#include <cairo-win32.h>
#include <cairo.h>
@@ -152,7 +153,6 @@ puglWinCairoLeave(PuglView* view, const PuglEventExpose* expose)
PAINTSTRUCT ps;
EndPaint(view->impl->hwnd, &ps);
- SwapBuffers(view->impl->hdc);
}
return PUGL_SUCCESS;
diff --git a/pugl/pugl/detail/win_gl.c b/pugl/pugl/detail/win_gl.c
index f5acfd6..8cdad76 100644
--- a/pugl/pugl/detail/win_gl.c
+++ b/pugl/pugl/detail/win_gl.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,13 +15,14 @@
*/
/**
- @file win_gl.c OpenGL graphics backend for Windows.
+ @file win_gl.c
+ @brief OpenGL graphics backend for Windows.
*/
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/detail/win.h"
#include "pugl/pugl_gl.h"
-#include "pugl/pugl_stub.h"
#include <windows.h>
@@ -35,7 +36,6 @@
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
-#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_BLUE_BITS_ARB 0x2019
@@ -49,7 +49,6 @@
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
-#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
@@ -295,7 +294,7 @@ puglGetProcAddress(const char* name)
}
const PuglBackend*
-puglGlBackend()
+puglGlBackend(void)
{
static const PuglBackend backend = {puglWinGlConfigure,
puglWinGlCreate,
diff --git a/pugl/pugl/detail/x11.c b/pugl/pugl/detail/x11.c
index e3fb264..7b8daf2 100644
--- a/pugl/pugl/detail/x11.c
+++ b/pugl/pugl/detail/x11.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Copyright 2013 Robin Gareus <robin@gareus.org>
Copyright 2011-2012 Ben Loftis, Harrison Consoles
@@ -17,7 +17,8 @@
*/
/**
- @file x11.c X11 implementation.
+ @file x11.c
+ @brief X11 implementation.
*/
#define _POSIX_C_SOURCE 199309L
@@ -25,6 +26,7 @@
#include "pugl/detail/x11.h"
#include "pugl/detail/implementation.h"
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/pugl.h"
#include "pugl/pugl_stub.h"
@@ -40,6 +42,11 @@
# include <X11/extensions/syncconst.h>
#endif
+#ifdef HAVE_XCURSOR
+# include <X11/Xcursor/Xcursor.h>
+# include <X11/cursorfont.h>
+#endif
+
#include <sys/select.h>
#include <sys/time.h>
@@ -74,11 +81,11 @@ static bool
puglInitXSync(PuglWorldInternals* impl)
{
#ifdef HAVE_XSYNC
- int syncMajor;
- int syncMinor;
- int errorBase;
- XSyncSystemCounter* counters;
- int numCounters;
+ int syncMajor = 0;
+ int syncMinor = 0;
+ int errorBase = 0;
+ XSyncSystemCounter* counters = NULL;
+ int numCounters = 0;
if (XSyncQueryExtension(impl->display, &impl->syncEventBase, &errorBase) &&
XSyncInitialize(impl->display, &syncMajor, &syncMinor) &&
@@ -151,7 +158,13 @@ puglGetNativeWorld(PuglWorld* world)
PuglInternals*
puglInitViewInternals(void)
{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+
+#ifdef HAVE_XCURSOR
+ impl->cursorShape = XC_arrow;
+#endif
+
+ return impl;
}
static PuglStatus
@@ -192,23 +205,40 @@ puglFindView(PuglWorld* world, const Window window)
return NULL;
}
-static XSizeHints
-getSizeHints(const PuglView* view)
+static PuglStatus
+updateSizeHints(const PuglView* view)
{
+ if (!view->impl->win) {
+ return PUGL_SUCCESS;
+ }
+
+ Display* display = view->world->impl->display;
XSizeHints sizeHints = {0};
if (!view->hints[PUGL_RESIZABLE]) {
- sizeHints.flags = PMinSize|PMaxSize;
- sizeHints.min_width = (int)view->frame.width;
- sizeHints.min_height = (int)view->frame.height;
- sizeHints.max_width = (int)view->frame.width;
- sizeHints.max_height = (int)view->frame.height;
+ sizeHints.flags = PBaseSize | PMinSize | PMaxSize;
+ sizeHints.base_width = (int)view->frame.width;
+ sizeHints.base_height = (int)view->frame.height;
+ sizeHints.min_width = (int)view->frame.width;
+ sizeHints.min_height = (int)view->frame.height;
+ sizeHints.max_width = (int)view->frame.width;
+ sizeHints.max_height = (int)view->frame.height;
} else {
+ if (view->defaultWidth || view->defaultHeight) {
+ sizeHints.flags = PBaseSize;
+ sizeHints.base_width = view->defaultWidth;
+ sizeHints.base_height = view->defaultHeight;
+ }
if (view->minWidth || view->minHeight) {
sizeHints.flags = PMinSize;
sizeHints.min_width = view->minWidth;
sizeHints.min_height = view->minHeight;
}
+ if (view->maxWidth || view->maxHeight) {
+ sizeHints.flags = PMaxSize;
+ sizeHints.max_width = view->maxWidth;
+ sizeHints.max_height = view->maxHeight;
+ }
if (view->minAspectX) {
sizeHints.flags |= PAspect;
sizeHints.min_aspect.x = view->minAspectX;
@@ -218,8 +248,28 @@ getSizeHints(const PuglView* view)
}
}
- return sizeHints;
+ XSetNormalHints(display, view->impl->win, &sizeHints);
+ return PUGL_SUCCESS;
+}
+
+#ifdef HAVE_XCURSOR
+static PuglStatus
+puglDefineCursorShape(PuglView* view, unsigned shape)
+{
+ PuglInternals* const impl = view->impl;
+ PuglWorld* const world = view->world;
+ Display* const display = world->impl->display;
+ const Cursor cur = XcursorShapeLoadCursor(display, shape);
+
+ if (cur) {
+ XDefineCursor(display, impl->win, cur);
+ XFreeCursor(display, cur);
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_FAILURE;
}
+#endif
PuglStatus
puglRealize(PuglView* view)
@@ -234,6 +284,18 @@ puglRealize(PuglView* view)
if (!view->backend || !view->backend->configure) {
return PUGL_BAD_BACKEND;
+ } else if (view->frame.width == 0.0 && view->frame.height == 0.0) {
+ if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) {
+ return PUGL_BAD_CONFIGURATION;
+ }
+
+ const int screenWidth = DisplayWidth(display, impl->screen);
+ const int screenHeight = DisplayHeight(display, impl->screen);
+
+ view->frame.width = view->defaultWidth;
+ view->frame.height = view->defaultHeight;
+ view->frame.x = screenWidth / 2.0 - view->frame.width / 2.0;
+ view->frame.y = screenHeight / 2.0 - view->frame.height / 2.0;
}
PuglStatus st = view->backend->configure(view);
@@ -263,8 +325,7 @@ puglRealize(PuglView* view)
return st;
}
- XSizeHints sizeHints = getSizeHints(view);
- XSetNormalHints(display, win, &sizeHints);
+ updateSizeHints(view);
XClassHint classHint = { world->className, world->className };
XSetClassHint(display, win, &classHint);
@@ -293,6 +354,10 @@ puglRealize(PuglView* view)
"XCreateID failed\n");
}
+#ifdef HAVE_XCURSOR
+ puglDefineCursorShape(view, impl->cursorShape);
+#endif
+
puglDispatchSimpleEvent(view, PUGL_CREATE);
return PUGL_SUCCESS;
@@ -450,10 +515,10 @@ translateKey(PuglView* view, XEvent* xevent, PuglEvent* event)
static uint32_t
translateModifiers(const unsigned xstate)
{
- return (((xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0) |
- ((xstate & ControlMask) ? PUGL_MOD_CTRL : 0) |
- ((xstate & Mod1Mask) ? PUGL_MOD_ALT : 0) |
- ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0));
+ return (((xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0u) |
+ ((xstate & ControlMask) ? PUGL_MOD_CTRL : 0u) |
+ ((xstate & Mod1Mask) ? PUGL_MOD_ALT : 0u) |
+ ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0u));
}
static PuglEvent
@@ -500,22 +565,23 @@ translateEvent(PuglView* view, XEvent xevent)
event.expose.y = xevent.xexpose.y;
event.expose.width = xevent.xexpose.width;
event.expose.height = xevent.xexpose.height;
- event.expose.count = xevent.xexpose.count;
break;
case MotionNotify:
event.type = PUGL_MOTION;
- event.motion.time = xevent.xmotion.time / 1e3;
+ event.motion.time = (double)xevent.xmotion.time / 1e3;
event.motion.x = xevent.xmotion.x;
event.motion.y = xevent.xmotion.y;
event.motion.xRoot = xevent.xmotion.x_root;
event.motion.yRoot = xevent.xmotion.y_root;
event.motion.state = translateModifiers(xevent.xmotion.state);
- event.motion.isHint = (xevent.xmotion.is_hint == NotifyHint);
+ if (xevent.xmotion.is_hint == NotifyHint) {
+ event.motion.flags |= PUGL_IS_HINT;
+ }
break;
case ButtonPress:
if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) {
event.type = PUGL_SCROLL;
- event.scroll.time = xevent.xbutton.time / 1e3;
+ event.scroll.time = (double)xevent.xbutton.time / 1e3;
event.scroll.x = xevent.xbutton.x;
event.scroll.y = xevent.xbutton.y;
event.scroll.xRoot = xevent.xbutton.x_root;
@@ -524,10 +590,22 @@ translateEvent(PuglView* view, XEvent xevent)
event.scroll.dx = 0.0;
event.scroll.dy = 0.0;
switch (xevent.xbutton.button) {
- case 4: event.scroll.dy = 1.0; break;
- case 5: event.scroll.dy = -1.0; break;
- case 6: event.scroll.dx = -1.0; break;
- case 7: event.scroll.dx = 1.0; break;
+ case 4:
+ event.scroll.dy = 1.0;
+ event.scroll.direction = PUGL_SCROLL_UP;
+ break;
+ case 5:
+ event.scroll.dy = -1.0;
+ event.scroll.direction = PUGL_SCROLL_DOWN;
+ break;
+ case 6:
+ event.scroll.dx = -1.0;
+ event.scroll.direction = PUGL_SCROLL_LEFT;
+ break;
+ case 7:
+ event.scroll.dx = 1.0;
+ event.scroll.direction = PUGL_SCROLL_RIGHT;
+ break;
}
// fallthru
}
@@ -537,7 +615,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.button.type = ((xevent.type == ButtonPress)
? PUGL_BUTTON_PRESS
: PUGL_BUTTON_RELEASE);
- event.button.time = xevent.xbutton.time / 1e3;
+ event.button.time = (double)xevent.xbutton.time / 1e3;
event.button.x = xevent.xbutton.x;
event.button.y = xevent.xbutton.y;
event.button.xRoot = xevent.xbutton.x_root;
@@ -551,7 +629,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.type = ((xevent.type == KeyPress)
? PUGL_KEY_PRESS
: PUGL_KEY_RELEASE);
- event.key.time = xevent.xkey.time / 1e3;
+ event.key.time = (double)xevent.xkey.time / 1e3;
event.key.x = xevent.xkey.x;
event.key.y = xevent.xkey.y;
event.key.xRoot = xevent.xkey.x_root;
@@ -564,7 +642,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.type = ((xevent.type == EnterNotify)
? PUGL_POINTER_IN
: PUGL_POINTER_OUT);
- event.crossing.time = xevent.xcrossing.time / 1e3;
+ event.crossing.time = (double)xevent.xcrossing.time / 1e3;
event.crossing.x = xevent.xcrossing.x;
event.crossing.y = xevent.xcrossing.y;
event.crossing.xRoot = xevent.xcrossing.x_root;
@@ -581,7 +659,12 @@ translateEvent(PuglView* view, XEvent xevent)
case FocusIn:
case FocusOut:
event.type = (xevent.type == FocusIn) ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT;
- event.focus.grab = (xevent.xfocus.mode != NotifyNormal);
+ event.focus.mode = PUGL_CROSSING_NORMAL;
+ if (xevent.xfocus.mode == NotifyGrab) {
+ event.focus.mode = PUGL_CROSSING_GRAB;
+ } else if (xevent.xfocus.mode == NotifyUngrab) {
+ event.focus.mode = PUGL_CROSSING_UNGRAB;
+ }
break;
default:
@@ -646,7 +729,8 @@ puglStartTimer(PuglView* view, uintptr_t id, double timeout)
PuglWorldInternals* w = view->world->impl;
Display* const display = w->display;
const XSyncCounter counter = w->serverTimeCounter;
- const XSyncTrigger trigger = {counter, XSyncRelative, value, 0};
+ const XSyncTestType type = XSyncPositiveTransition;
+ const XSyncTrigger trigger = {counter, XSyncRelative, value, type};
XSyncAlarmAttributes attr = {trigger, value, True, XSyncAlarmActive};
const XSyncAlarm alarm = XSyncCreateAlarm(display, 0x17, &attr);
const PuglTimer timer = {alarm, view, id};
@@ -791,7 +875,6 @@ mergeExposeEvents(PuglEvent* dst, const PuglEvent* src)
dst->expose.y = MIN(dst->expose.y, src->expose.y);
dst->expose.width = max_x - dst->expose.x;
dst->expose.height = max_y - dst->expose.y;
- dst->expose.count = MIN(dst->expose.count, src->expose.count);
}
}
@@ -896,8 +979,10 @@ handleTimerEvent(PuglWorld* world, XEvent xevent)
for (size_t i = 0; i < world->impl->numTimers; ++i) {
if (world->impl->timers[i].alarm == notify->alarm) {
- const PuglEventTimer ev = {PUGL_TIMER, 0, world->impl->timers[i].id};
- puglDispatchEvent(world->impl->timers[i].view, (const PuglEvent*)&ev);
+ PuglEvent event = {{PUGL_TIMER, 0}};
+ event.timer.id = world->impl->timers[i].id;
+ puglDispatchEvent(world->impl->timers[i].view,
+ (const PuglEvent*)&event);
}
}
@@ -1033,7 +1118,8 @@ puglGetTime(const PuglWorld* world)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return ((double)ts.tv_sec + ts.tv_nsec / 1000000000.0) - world->startTime;
+ return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) -
+ world->startTime;
}
PuglStatus
@@ -1048,7 +1134,7 @@ PuglStatus
puglPostRedisplayRect(PuglView* view, PuglRect rect)
{
const PuglEventExpose event = {
- PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height, 0
+ PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height
};
if (view->world->impl->dispatchingEvents) {
@@ -1102,19 +1188,27 @@ puglSetFrame(PuglView* view, const PuglRect frame)
}
PuglStatus
-puglSetMinSize(PuglView* const view, const int width, const int height)
+puglSetDefaultSize(PuglView* const view, const int width, const int height)
{
- Display* display = view->world->impl->display;
+ view->defaultWidth = width;
+ view->defaultHeight = height;
+ return updateSizeHints(view);
+}
+PuglStatus
+puglSetMinSize(PuglView* const view, const int width, const int height)
+{
view->minWidth = width;
view->minHeight = height;
+ return updateSizeHints(view);
+}
- if (view->impl->win) {
- XSizeHints sizeHints = getSizeHints(view);
- XSetNormalHints(display, view->impl->win, &sizeHints);
- }
-
- return PUGL_SUCCESS;
+PuglStatus
+puglSetMaxSize(PuglView* const view, const int width, const int height)
+{
+ view->minWidth = width;
+ view->minHeight = height;
+ return updateSizeHints(view);
}
PuglStatus
@@ -1124,19 +1218,12 @@ puglSetAspectRatio(PuglView* const view,
const int maxX,
const int maxY)
{
- Display* display = view->world->impl->display;
-
view->minAspectX = minX;
view->minAspectY = minY;
view->maxAspectX = maxX;
view->maxAspectY = maxY;
- if (view->impl->win) {
- XSizeHints sizeHints = getSizeHints(view);
- XSetNormalHints(display, view->impl->win, &sizeHints);
- }
-
- return PUGL_SUCCESS;
+ return updateSizeHints(view);
}
PuglStatus
@@ -1202,6 +1289,44 @@ puglSetClipboard(PuglView* const view,
return PUGL_SUCCESS;
}
+#ifdef HAVE_XCURSOR
+static const unsigned cursor_nums[] = {
+ XC_arrow, // ARROW
+ XC_xterm, // CARET
+ XC_crosshair, // CROSSHAIR
+ XC_hand2, // HAND
+ XC_pirate, // NO
+ XC_sb_h_double_arrow, // LEFT_RIGHT
+ XC_sb_v_double_arrow, // UP_DOWN
+};
+#endif
+
+PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor)
+{
+#ifdef HAVE_XCURSOR
+ PuglInternals* const impl = view->impl;
+ const unsigned index = (unsigned)cursor;
+ const unsigned count = sizeof(cursor_nums) / sizeof(cursor_nums[0]);
+ if (index >= count) {
+ return PUGL_BAD_PARAMETER;
+ }
+
+ const unsigned shape = cursor_nums[index];
+ if (!impl->win || impl->cursorShape == shape) {
+ return PUGL_SUCCESS;
+ }
+
+ impl->cursorShape = cursor_nums[index];
+
+ return puglDefineCursorShape(view, impl->cursorShape);
+#else
+ (void)view;
+ (void)cursor;
+ return PUGL_FAILURE;
+#endif
+}
+
const PuglBackend*
puglStubBackend(void)
{
diff --git a/pugl/pugl/detail/x11.h b/pugl/pugl/detail/x11.h
index 6b7a150..cf647ed 100644
--- a/pugl/pugl/detail/x11.h
+++ b/pugl/pugl/detail/x11.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file x11.h Shared definitions for X11 implementation.
+ @file x11.h
+ @brief Shared definitions for X11 implementation.
*/
#include "pugl/detail/types.h"
@@ -43,7 +44,7 @@ typedef struct {
typedef struct {
XID alarm;
PuglView* view;
- uint64_t id;
+ uintptr_t id;
} PuglTimer;
struct PuglWorldInternalsImpl {
@@ -60,13 +61,16 @@ struct PuglWorldInternalsImpl {
struct PuglInternalsImpl {
Display* display;
- int screen;
XVisualInfo* vi;
Window win;
XIC xic;
PuglSurface* surface;
PuglEvent pendingConfigure;
PuglEvent pendingExpose;
+ int screen;
+#ifdef HAVE_XCURSOR
+ unsigned cursorShape;
+#endif
};
static inline PuglStatus
diff --git a/pugl/pugl/detail/x11_cairo.c b/pugl/pugl/detail/x11_cairo.c
index 0229d97..0112c4e 100644
--- a/pugl/pugl/detail/x11_cairo.c
+++ b/pugl/pugl/detail/x11_cairo.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file x11_cairo.c Cairo graphics backend for X11.
+ @file x11_cairo.c
+ @brief Cairo graphics backend for X11.
*/
#include "pugl/detail/types.h"
diff --git a/pugl/pugl/detail/x11_gl.c b/pugl/pugl/detail/x11_gl.c
index 33a05df..f5e6b8d 100644
--- a/pugl/pugl/detail/x11_gl.c
+++ b/pugl/pugl/detail/x11_gl.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,14 +15,15 @@
*/
/**
- @file x11_gl.c OpenGL graphics backend for X11.
+ @file x11_gl.c
+ @brief OpenGL graphics backend for X11.
*/
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/detail/x11.h"
#include "pugl/pugl.h"
#include "pugl/pugl_gl.h"
-#include "pugl/pugl_stub.h"
#include <GL/glx.h>
#include <X11/X.h>
@@ -114,6 +115,28 @@ puglX11GlConfigure(PuglView* view)
}
static PuglStatus
+puglX11GlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+ glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
+puglX11GlLeave(PuglView* view, const PuglEventExpose* expose)
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+
+ if (expose && surface->double_buffered) {
+ glXSwapBuffers(view->impl->display, view->impl->win);
+ }
+
+ glXMakeCurrent(view->impl->display, None, NULL);
+
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
puglX11GlCreate(PuglView* view)
{
PuglInternals* const impl = view->impl;
@@ -152,7 +175,9 @@ puglX11GlCreate(PuglView* view)
const int swapInterval = view->hints[PUGL_SWAP_INTERVAL];
if (glXSwapIntervalEXT && swapInterval != PUGL_DONT_CARE) {
+ puglX11GlEnter(view, NULL);
glXSwapIntervalEXT(display, impl->win, swapInterval);
+ puglX11GlLeave(view, NULL);
}
glXGetConfig(impl->display,
@@ -175,28 +200,6 @@ puglX11GlDestroy(PuglView* view)
return PUGL_SUCCESS;
}
-static PuglStatus
-puglX11GlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
-{
- PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
- glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
- return PUGL_SUCCESS;
-}
-
-static PuglStatus
-puglX11GlLeave(PuglView* view, const PuglEventExpose* expose)
-{
- PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
-
- if (expose && surface->double_buffered) {
- glXSwapBuffers(view->impl->display, view->impl->win);
- }
-
- glXMakeCurrent(view->impl->display, None, NULL);
-
- return PUGL_SUCCESS;
-}
-
PuglGlFunc
puglGetProcAddress(const char* name)
{
diff --git a/pugl/pugl/gl.h b/pugl/pugl/gl.h
index 55a55c4..dbb2e60 100644
--- a/pugl/pugl/gl.h
+++ b/pugl/pugl/gl.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2014 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file gl.h Portable header wrapper for gl.h.
+ @file gl.h
+ @brief Portable header wrapper for gl.h.
Unfortunately, GL includes vary across platforms so this header allows for
pure portable programs.
diff --git a/pugl/pugl/glu.h b/pugl/pugl/glu.h
index 0ade70c..94da8fc 100644
--- a/pugl/pugl/glu.h
+++ b/pugl/pugl/glu.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2015 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file glu.h Portable header wrapper for glu.h.
+ @file glu.h
+ @brief Portable header wrapper for glu.h.
Unfortunately, GL includes vary across platforms so this header allows for
pure portable programs.
diff --git a/pugl/pugl/pugl.h b/pugl/pugl/pugl.h
index 57e23fa..c32a17d 100644
--- a/pugl/pugl/pugl.h
+++ b/pugl/pugl/pugl.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file pugl.h Pugl API.
+ @file pugl.h
+ @brief Pugl API.
*/
#ifndef PUGL_PUGL_H
@@ -52,6 +53,12 @@
# endif
#endif
+#if defined(__GNUC__)
+# define PUGL_CONST_FUNC __attribute__((const))
+#else
+# define PUGL_CONST_FUNC
+#endif
+
#ifdef __cplusplus
# define PUGL_BEGIN_DECLS extern "C" {
# define PUGL_END_DECLS }
@@ -63,9 +70,13 @@
PUGL_BEGIN_DECLS
/**
- @defgroup pugl_api Pugl
+ @defgroup pugl Pugl
A minimal portable API for embeddable GUIs.
@{
+
+ @defgroup pugl_c C API
+ Public C API.
+ @{
*/
/**
@@ -87,7 +98,7 @@ typedef struct {
Event definitions.
All updates to the view happen via events, which are dispatched to the
- view's #PuglEventFunc by Pugl. Most events map directly to one from the
+ view's event function by Pugl. Most events map directly to one from the
underlying window system, but some are constructed by Pugl itself so there
is not necessarily a direct correspondence.
@@ -210,7 +221,8 @@ typedef enum {
Common flags for all event types.
*/
typedef enum {
- PUGL_IS_SEND_EVENT = 1 ///< Event is synthetic
+ PUGL_IS_SEND_EVENT = 1, ///< Event is synthetic
+ PUGL_IS_HINT = 2 ///< Event is a hint (not direct user input)
} PuglEventFlag;
/**
@@ -228,6 +240,22 @@ typedef enum {
} PuglCrossingMode;
/**
+ Scroll direction.
+
+ Describes the direction of a #PuglEventScroll along with whether the scroll
+ is a "smooth" scroll. The discrete directions are for devices like mouse
+ wheels with constrained axes, while a smooth scroll is for those with
+ arbitrary scroll direction freedom, like some touchpads.
+*/
+typedef enum {
+ PUGL_SCROLL_UP, ///< Scroll up
+ PUGL_SCROLL_DOWN, ///< Scroll down
+ PUGL_SCROLL_LEFT, ///< Scroll left
+ PUGL_SCROLL_RIGHT, ///< Scroll right
+ PUGL_SCROLL_SMOOTH ///< Smooth scroll in any direction
+} PuglScrollDirection;
+
+/**
Common header for all event structs.
*/
typedef struct {
@@ -322,7 +350,6 @@ typedef struct {
double y; ///< View-relative Y coordinate
double width; ///< Width of exposed region
double height; ///< Height of exposed region
- int count; ///< Number of expose events to follow
} PuglEventExpose;
/**
@@ -342,9 +369,9 @@ typedef PuglEventAny PuglEventClose;
view with the keyboard focus will receive any key press or release events.
*/
typedef struct {
- PuglEventType type; ///< #PUGL_FOCUS_IN or #PUGL_FOCUS_OUT
- PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
- bool grab; ///< True iff this is a grab/ungrab event
+ PuglEventType type; ///< #PUGL_FOCUS_IN or #PUGL_FOCUS_OUT
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ PuglCrossingMode mode; ///< Reason for focus change
} PuglEventFocus;
/**
@@ -355,12 +382,12 @@ typedef struct {
as text input.
Keys are represented portably as Unicode code points, using the "natural"
- code point for the key where possible (see #PuglKey for details). The #key
+ code point for the key where possible (see #PuglKey for details). The `key`
field is the code for the pressed key, without any modifiers applied. For
- example, a press or release of the 'A' key will have #key 97 ('a')
+ example, a press or release of the 'A' key will have `key` 97 ('a')
regardless of whether shift or control are being held.
- Alternatively, the raw #keycode can be used to work directly with physical
+ Alternatively, the raw `keycode` can be used to work directly with physical
keys, but note that this value is not portable and differs between platforms
and hardware.
*/
@@ -407,7 +434,7 @@ typedef struct {
This event is sent when the pointer enters or leaves the view. This can
happen for several reasons (not just the user dragging the pointer over the
- window edge), as described by the #mode field.
+ window edge), as described by the `mode` field.
*/
typedef struct {
PuglEventType type; ///< #PUGL_POINTER_IN or #PUGL_POINTER_OUT
@@ -448,30 +475,29 @@ typedef struct {
double xRoot; ///< Root-relative X coordinate
double yRoot; ///< Root-relative Y coordinate
PuglMods state; ///< Bitwise OR of #PuglMod flags
- bool isHint; ///< True iff this event is a motion hint
- bool focus; ///< True iff this is the focused view
} PuglEventMotion;
/**
Scroll event.
The scroll distance is expressed in "lines", an arbitrary unit that
- corresponds to a single tick of a detented mouse wheel. For example, #dy =
+ corresponds to a single tick of a detented mouse wheel. For example, `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.
*/
typedef struct {
- PuglEventType type; ///< #PUGL_SCROLL
- PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
- double time; ///< Time in seconds
- double x; ///< View-relative X coordinate
- double y; ///< View-relative Y coordinate
- double xRoot; ///< Root-relative X coordinate
- double yRoot; ///< Root-relative Y coordinate
- PuglMods state; ///< Bitwise OR of #PuglMod flags
- double dx; ///< Scroll X distance in lines
- double dy; ///< Scroll Y distance in lines
+ PuglEventType type; ///< #PUGL_SCROLL
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ PuglScrollDirection direction; ///< Scroll direction
+ double dx; ///< Scroll X distance in lines
+ double dy; ///< Scroll Y distance in lines
} PuglEventScroll;
/**
@@ -494,7 +520,7 @@ typedef struct {
This event is sent at the regular interval specified in the call to
puglStartTimer() that activated it.
- The #id is the application-specific ID given to puglStartTimer() which
+ The `id` is the application-specific ID given to puglStartTimer() which
distinguishes this timer from others. It should always be checked in the
event handler, even in applications that register only one timer.
*/
@@ -507,7 +533,7 @@ typedef struct {
/**
View event.
- This is a union of all event types. The #type must be checked to determine
+ This is a union of all event types. The type must be checked to determine
which fields are safe to access. A pointer to PuglEvent can either be cast
to the appropriate type, or the union members used.
@@ -548,6 +574,7 @@ typedef enum {
PUGL_FAILURE, ///< Non-fatal failure
PUGL_UNKNOWN_ERROR, ///< Unknown system error
PUGL_BAD_BACKEND, ///< Invalid or missing backend
+ PUGL_BAD_CONFIGURATION, ///< Invalid view configuration
PUGL_BAD_PARAMETER, ///< Invalid parameter
PUGL_BACKEND_FAILED, ///< Backend initialisation failed
PUGL_REGISTRATION_FAILED, ///< Class registration failed
@@ -594,7 +621,7 @@ typedef struct PuglWorldImpl PuglWorld;
typedef void* PuglWorldHandle;
/**
- The type of a PuglWorld.
+ The type of a World.
*/
typedef enum {
PUGL_PROGRAM, ///< Top-level application
@@ -728,19 +755,18 @@ puglGetTime(const PuglWorld* world);
This function is a single iteration of the main loop, and should be called
repeatedly to update all views.
- If a positive timeout is given, then events will be processed for that
- amount of time, starting from when this function was called. For purely
- event-driven programs, a timeout of -1.0 can be used to block indefinitely
- until something happens. For continuously animating programs, a timeout
- that is a reasonable fraction of the ideal frame period should be used, to
- minimise input latency by ensuring that as many input events are consumed as
- possible before drawing. Plugins should always use a timeout of 0.0 to
- avoid blocking the host.
+ If `timeout` is zero, then this function will not block. Plugins should
+ always use a timeout of zero to avoid blocking the host.
- @param world The world to update.
+ If a positive `timeout` is given, then events will be processed for that
+ amount of time, starting from when this function was called.
- @param timeout Maximum time to wait, in seconds. If zero, the call returns
- immediately, if negative, the call blocks indefinitely.
+ If a negative `timeout` is given, this function will block indefinitely
+ until an event occurs.
+
+ For continuously animating programs, a timeout that is a reasonable fraction
+ of the ideal frame period should be used, to minimise input latency by
+ ensuring that as many input events are consumed as possible before drawing.
@return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error.
*/
@@ -937,6 +963,16 @@ PUGL_API PuglStatus
puglSetFrame(PuglView* view, PuglRect frame);
/**
+ Set the default size of the view.
+
+ This should be called before puglResize() to set the default size of the
+ view, which will be the initial size of the window if this is a top level
+ view.
+*/
+PUGL_API PuglStatus
+puglSetDefaultSize(PuglView* view, int width, int height);
+
+/**
Set the minimum size of the view.
If an initial minimum size is known, this should be called before
@@ -946,6 +982,15 @@ PUGL_API PuglStatus
puglSetMinSize(PuglView* view, int width, int height);
/**
+ Set the maximum size of the view.
+
+ If an initial maximum size is known, this should be called before
+ puglRealize() to avoid stutter, though it can be called afterwards as well.
+*/
+PUGL_API PuglStatus
+puglSetMaxSize(PuglView* view, int width, int height);
+
+/**
Set the view aspect ratio range.
The x and y values here represent a ratio of width to height. To set a
@@ -992,6 +1037,9 @@ puglSetParentWindow(PuglView* view, PuglNativeView parent);
Set this for transient children like dialogs, to have them properly
associated with their parent window. This should be called before
puglRealize().
+
+ A view can either have a parent (for embedding) or a transient parent (for
+ top-level windows like dialogs), but not both.
*/
PUGL_API PuglStatus
puglSetTransientFor(PuglView* view, PuglNativeView parent);
@@ -1091,6 +1139,22 @@ puglPostRedisplayRect(PuglView* view, PuglRect rect);
*/
/**
+ A mouse cursor type.
+
+ This is a portable subset of mouse cursors that exist on X11, MacOS, and
+ Windows.
+*/
+typedef enum {
+ PUGL_CURSOR_ARROW, ///< Default pointing arrow
+ PUGL_CURSOR_CARET, ///< Caret (I-Beam) for text entry
+ PUGL_CURSOR_CROSSHAIR, ///< Cross-hair
+ PUGL_CURSOR_HAND, ///< Hand with a pointing finger
+ PUGL_CURSOR_NO, ///< Operation not allowed
+ PUGL_CURSOR_LEFT_RIGHT, ///< Left/right arrow for horizontal resize
+ PUGL_CURSOR_UP_DOWN, ///< Up/down arrow for vertical resize
+} PuglCursor;
+
+/**
Grab the keyboard input focus.
*/
PUGL_API PuglStatus
@@ -1134,6 +1198,16 @@ PUGL_API const void*
puglGetClipboard(PuglView* view, const char** type, size_t* len);
/**
+ Set the mouse cursor.
+
+ This changes the system cursor that is displayed when the pointer is inside
+ the view. May fail if setting the cursor is not supported on this system,
+ for example if compiled on X11 without Xcursor support.
+ */
+PUGL_API PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor);
+
+/**
Request user attention.
This hints to the system that the window or application requires attention
@@ -1496,6 +1570,7 @@ puglLeaveContext(PuglView* view, bool drawing);
/**
@}
@}
+ @}
*/
PUGL_END_DECLS
diff --git a/pugl/pugl/pugl.hpp b/pugl/pugl/pugl.hpp
index 73cfe2a..3072560 100644
--- a/pugl/pugl/pugl.hpp
+++ b/pugl/pugl/pugl.hpp
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file pugl.hpp Pugl C++ API wrapper.
+ @file pugl.hpp
+ @brief Pugl C++ API wrapper.
*/
#ifndef PUGL_PUGL_HPP
@@ -23,12 +24,18 @@
#include "pugl/pugl.h"
-/**
- @defgroup puglxx C++
+#include <cassert>
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+/**
+ @defgroup pugl_cxx C++ API
C++ API wrapper.
- @ingroup pugl_api
+ @ingroup pugl
@{
*/
@@ -37,84 +44,657 @@
*/
namespace pugl {
+namespace detail {
+
+/// Free function for a C object
+template<typename T>
+using FreeFunc = void (*)(T*);
+
+/// Simple overhead-free deleter for a C object
+template<typename T, FreeFunc<T> Free>
+struct Deleter {
+ void operator()(T* ptr) { Free(ptr); }
+};
+
+/// Generic C++ wrapper for a C object
+template<class T, FreeFunc<T> Free>
+class Wrapper
+{
+public:
+ T* cobj() { return _ptr.get(); }
+ const T* cobj() const { return _ptr.get(); }
+
+protected:
+ explicit Wrapper(T* ptr)
+ : _ptr(ptr, Deleter<T, Free>{})
+ {}
+
+private:
+ std::unique_ptr<T, Deleter<T, Free>> _ptr;
+};
+
+} // namespace detail
+
+using Rect = PuglRect; ///< @copydoc PuglRect
+
/**
- A drawable region that receives events.
+ @defgroup eventsxx Events
+ @ingroup pugl_cxx
+ @copydoc events
+ @{
+*/
- This is a thin wrapper for a PuglView that contains only a pointer.
+/**
+ A strongly-typed analogue of PuglEvent.
- @ingroup puglxx
+ This is bit-for-bit identical to the corresponding PuglEvent, so events are
+ simply cast to this type to avoid any copying overhead.
+
+ @tparam t The `type` field of the corresponding PuglEvent.
+
+ @tparam Base The specific struct type of the corresponding PuglEvent.
*/
-class View {
+template<PuglEventType t, class Base>
+struct Event final : Base {
+ using BaseEvent = Base;
+
+ static constexpr const PuglEventType type = t;
+};
+
+using Mod = PuglMod; ///< @copydoc PuglMod
+using Mods = PuglMods; ///< @copydoc PuglMods
+using Key = PuglKey; ///< @copydoc PuglKey
+using EventType = PuglEventType; ///< @copydoc PuglEventType
+using EventFlag = PuglEventFlag; ///< @copydoc PuglEventFlag
+using EventFlags = PuglEventFlags; ///< @copydoc PuglEventFlags
+using CrossingMode = PuglCrossingMode; ///< @copydoc PuglCrossingMode
+
+/// @copydoc PuglEventCreate
+using CreateEvent = Event<PUGL_CREATE, PuglEventCreate>;
+
+/// @copydoc PuglEventDestroy
+using DestroyEvent = Event<PUGL_DESTROY, PuglEventDestroy>;
+
+/// @copydoc PuglEventConfigure
+using ConfigureEvent = Event<PUGL_CONFIGURE, PuglEventConfigure>;
+
+/// @copydoc PuglEventMap
+using MapEvent = Event<PUGL_MAP, PuglEventMap>;
+
+/// @copydoc PuglEventUnmap
+using UnmapEvent = Event<PUGL_UNMAP, PuglEventUnmap>;
+
+/// @copydoc PuglEventUpdate
+using UpdateEvent = Event<PUGL_UPDATE, PuglEventUpdate>;
+
+/// @copydoc PuglEventExpose
+using ExposeEvent = Event<PUGL_EXPOSE, PuglEventExpose>;
+
+/// @copydoc PuglEventClose
+using CloseEvent = Event<PUGL_CLOSE, PuglEventClose>;
+
+/// @copydoc PuglEventFocus
+using FocusInEvent = Event<PUGL_FOCUS_IN, PuglEventFocus>;
+
+/// @copydoc PuglEventFocus
+using FocusOutEvent = Event<PUGL_FOCUS_OUT, PuglEventFocus>;
+
+/// @copydoc PuglEventKey
+using KeyPressEvent = Event<PUGL_KEY_PRESS, PuglEventKey>;
+
+/// @copydoc PuglEventKey
+using KeyReleaseEvent = Event<PUGL_KEY_RELEASE, PuglEventKey>;
+
+/// @copydoc PuglEventText
+using TextEvent = Event<PUGL_TEXT, PuglEventText>;
+
+/// @copydoc PuglEventCrossing
+using PointerInEvent = Event<PUGL_POINTER_IN, PuglEventCrossing>;
+
+/// @copydoc PuglEventCrossing
+using PointerOutEvent = Event<PUGL_POINTER_OUT, PuglEventCrossing>;
+
+/// @copydoc PuglEventButton
+using ButtonPressEvent = Event<PUGL_BUTTON_PRESS, PuglEventButton>;
+
+/// @copydoc PuglEventButton
+using ButtonReleaseEvent = Event<PUGL_BUTTON_RELEASE, PuglEventButton>;
+
+/// @copydoc PuglEventMotion
+using MotionEvent = Event<PUGL_MOTION, PuglEventMotion>;
+
+/// @copydoc PuglEventScroll
+using ScrollEvent = Event<PUGL_SCROLL, PuglEventScroll>;
+
+/// @copydoc PuglEventClient
+using ClientEvent = Event<PUGL_CLIENT, PuglEventClient>;
+
+/// @copydoc PuglEventTimer
+using TimerEvent = Event<PUGL_TIMER, PuglEventTimer>;
+
+/**
+ @}
+ @defgroup statusxx Status
+ @ingroup pugl_cxx
+ @copydoc status
+ @{
+*/
+
+/// @copydoc PuglStatus
+enum class Status {
+ success, ///< @copydoc PUGL_SUCCESS
+ failure, ///< @copydoc PUGL_FAILURE
+ unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR
+ badBackend, ///< @copydoc PUGL_BAD_BACKEND
+ badConfiguration, ///< @copydoc PUGL_BAD_CONFIGURATION
+ badParameter, ///< @copydoc PUGL_BAD_PARAMETER
+ backendFailed, ///< @copydoc PUGL_BACKEND_FAILED
+ registrationFailed, ///< @copydoc PUGL_REGISTRATION_FAILED
+ realizeFailed, ///< @copydoc PUGL_REALIZE_FAILED
+ setFormatFailed, ///< @copydoc PUGL_SET_FORMAT_FAILED
+ createContextFailed, ///< @copydoc PUGL_CREATE_CONTEXT_FAILED
+ unsupportedType, ///< @copydoc PUGL_UNSUPPORTED_TYPE
+};
+
+static_assert(Status(PUGL_UNSUPPORTED_TYPE) == Status::unsupportedType, "");
+
+/// @copydoc puglStrerror
+static inline const char*
+strerror(const pugl::Status status)
+{
+ return puglStrerror(static_cast<PuglStatus>(status));
+}
+
+/**
+ @}
+ @defgroup worldxx World
+ @ingroup pugl_cxx
+ @copydoc world
+ @{
+*/
+
+class World;
+
+/// @copydoc PuglWorldType
+enum class WorldType {
+ program, ///< @copydoc PUGL_PROGRAM
+ module, ///< @copydoc PUGL_MODULE
+};
+
+static_assert(WorldType(PUGL_MODULE) == WorldType::module, "");
+
+/// @copydoc PuglWorldFlag
+enum class WorldFlag {
+ threads = PUGL_WORLD_THREADS, ///< @copydoc PUGL_WORLD_THREADS
+};
+
+static_assert(WorldFlag(PUGL_WORLD_THREADS) == WorldFlag::threads, "");
+
+using WorldFlags = PuglWorldFlags; ///< @copydoc PuglWorldFlags
+
+/// @copydoc PuglLogLevel
+enum class LogLevel {
+ err = PUGL_LOG_LEVEL_ERR, ///< @copydoc PUGL_LOG_LEVEL_ERR
+ warning = PUGL_LOG_LEVEL_WARNING, ///< @copydoc PUGL_LOG_LEVEL_WARNING
+ info = PUGL_LOG_LEVEL_INFO, ///< @copydoc PUGL_LOG_LEVEL_INFO
+ debug = PUGL_LOG_LEVEL_DEBUG, ///< @copydoc PUGL_LOG_LEVEL_DEBUG
+};
+
+static_assert(LogLevel(PUGL_LOG_LEVEL_DEBUG) == LogLevel::debug, "");
+
+/// @copydoc PuglLogFunc
+using LogFunc =
+ std::function<void(World& world, LogLevel level, const char* msg)>;
+
+/**
+ A `std::chrono` compatible clock that uses Pugl time.
+*/
+class Clock
+{
public:
- View(int* pargc, char** argv)
- : _view(puglInit(pargc, argv))
+ using rep = double; ///< Time representation
+ using duration = std::chrono::duration<double>; ///< Duration in seconds
+ using time_point = std::chrono::time_point<Clock>; ///< A Pugl time point
+
+ static constexpr bool is_steady = true; ///< Steady clock flag, always true
+
+ /// Construct a clock that uses time from puglGetTime()
+ explicit Clock(World& world)
+ : _world{world}
+ {}
+
+ /// Return the current time
+ time_point now() const;
+
+private:
+ const pugl::World& _world;
+};
+
+/// @copydoc PuglWorld
+class World : public detail::Wrapper<PuglWorld, puglFreeWorld>
+{
+public:
+ explicit World(WorldType type, WorldFlags flags)
+ : Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), flags)}
+ , _clock(*this)
+ {
+ if (!cobj()) {
+ throw std::runtime_error("Failed to create pugl::World");
+ }
+ }
+
+ explicit World(WorldType type)
+ : World{type, {}}
{
- puglSetHandle(_view, this);
- puglSetEventFunc(_view, _onEvent);
+ if (!cobj()) {
+ throw std::runtime_error("Failed to create pugl::World");
+ }
}
- virtual ~View() { puglDestroy(_view); }
+ /// @copydoc puglGetNativeWorld
+ void* nativeWorld() { return puglGetNativeWorld(cobj()); }
+
+ // TODO: setLogFunc
- virtual void initWindowParent(PuglNativeWindow parent) {
- puglInitWindowParent(_view, parent);
+ Status setLogLevel(const LogLevel level)
+ {
+ return static_cast<Status>(
+ puglSetLogLevel(cobj(), static_cast<PuglLogLevel>(level)));
}
- virtual void initWindowSize(int width, int height) {
- puglInitWindowSize(_view, width, height);
+ /// @copydoc puglSetClassName
+ Status setClassName(const char* const name)
+ {
+ return static_cast<Status>(puglSetClassName(cobj(), name));
}
- virtual void initWindowMinSize(int width, int height) {
- puglInitWindowMinSize(_view, width, height);
+ /// @copydoc puglGetTime
+ double time() const { return puglGetTime(cobj()); }
+
+ /// @copydoc puglUpdate
+ Status update(const double timeout)
+ {
+ return static_cast<Status>(puglUpdate(cobj(), timeout));
+ }
+
+ /// Return a clock that uses Pugl time
+ const Clock& clock() { return _clock; }
+
+private:
+ Clock _clock;
+};
+
+inline Clock::time_point
+Clock::now() const
+{
+ return time_point{duration{_world.time()}};
+}
+
+/**
+ @}
+ @defgroup viewxx View
+ @ingroup pugl_cxx
+ @copydoc view
+ @{
+*/
+
+using Backend = PuglBackend; ///< @copydoc PuglBackend
+using NativeView = PuglNativeView; ///< @copydoc PuglNativeView
+
+/// @copydoc PuglViewHint
+enum class ViewHint {
+ useCompatProfile, ///< @copydoc PUGL_USE_COMPAT_PROFILE
+ useDebugContext, ///< @copydoc PUGL_USE_DEBUG_CONTEXT
+ contextVersionMajor, ///< @copydoc PUGL_CONTEXT_VERSION_MAJOR
+ contextVersionMinor, ///< @copydoc PUGL_CONTEXT_VERSION_MINOR
+ redBits, ///< @copydoc PUGL_RED_BITS
+ greenBits, ///< @copydoc PUGL_GREEN_BITS
+ blueBits, ///< @copydoc PUGL_BLUE_BITS
+ alphaBits, ///< @copydoc PUGL_ALPHA_BITS
+ depthBits, ///< @copydoc PUGL_DEPTH_BITS
+ stencilBits, ///< @copydoc PUGL_STENCIL_BITS
+ samples, ///< @copydoc PUGL_SAMPLES
+ doubleBuffer, ///< @copydoc PUGL_DOUBLE_BUFFER
+ swapInterval, ///< @copydoc PUGL_SWAP_INTERVAL
+ resizable, ///< @copydoc PUGL_RESIZABLE
+ ignoreKeyRepeat, ///< @copydoc PUGL_IGNORE_KEY_REPEAT
+};
+
+static_assert(ViewHint(PUGL_IGNORE_KEY_REPEAT) == ViewHint::ignoreKeyRepeat,
+ "");
+
+using ViewHintValue = PuglViewHintValue; ///< @copydoc PuglViewHintValue
+
+/// @copydoc PuglCursor
+enum class Cursor {
+ arrow, ///< @copydoc PUGL_CURSOR_ARROW
+ caret, ///< @copydoc PUGL_CURSOR_CARET
+ crosshair, ///< @copydoc PUGL_CURSOR_CROSSHAIR
+ hand, ///< @copydoc PUGL_CURSOR_HAND
+ no, ///< @copydoc PUGL_CURSOR_NO
+ leftRight, ///< @copydoc PUGL_CURSOR_LEFT_RIGHT
+ upDown, ///< @copydoc PUGL_CURSOR_UP_DOWN
+};
+
+static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, "");
+
+/// @copydoc PuglView
+class View : protected detail::Wrapper<PuglView, puglFreeView>
+{
+public:
+ /**
+ @name Setup
+ Methods for creating and destroying a view.
+ @{
+ */
+
+ explicit View(World& world)
+ : Wrapper{puglNewView(world.cobj())}
+ , _world(world)
+ {
+ if (!cobj()) {
+ throw std::runtime_error("Failed to create pugl::View");
+ }
+
+ puglSetHandle(cobj(), this);
+ puglSetEventFunc(cobj(), dispatchEvent);
+ }
+
+ virtual ~View() = default;
+
+ View(const View&) = delete;
+ View& operator=(const View&) = delete;
+
+ View(View&&) = delete;
+ View&& operator=(View&&) = delete;
+
+ const pugl::World& world() const { return _world; }
+ pugl::World& world() { return _world; }
+
+ /// @copydoc puglSetViewHint
+ Status setHint(ViewHint hint, int value)
+ {
+ return static_cast<Status>(
+ puglSetViewHint(cobj(), static_cast<PuglViewHint>(hint), value));
+ }
+
+ /**
+ @}
+ @name Frame
+ Methods for working with the position and size of a view.
+ @{
+ */
+
+ /// @copydoc puglGetFrame
+ Rect frame() const { return puglGetFrame(cobj()); }
+
+ /// @copydoc puglSetFrame
+ Status setFrame(Rect frame)
+ {
+ return static_cast<Status>(puglSetFrame(cobj(), frame));
+ }
+
+ /// @copydoc puglSetDefaultSize
+ Status setDefaultSize(int width, int height)
+ {
+ return static_cast<Status>(puglSetDefaultSize(cobj(), width, height));
+ }
+
+ /// @copydoc puglSetMinSize
+ Status setMinSize(int width, int height)
+ {
+ return static_cast<Status>(puglSetMinSize(cobj(), width, height));
}
- virtual void initWindowAspectRatio(int min_x, int min_y, int max_x, int max_y) {
- puglInitWindowAspectRatio(_view, min_x, min_y, max_x, max_y);
+ /// @copydoc puglSetMaxSize
+ Status setMaxSize(int width, int height)
+ {
+ return static_cast<Status>(puglSetMaxSize(cobj(), width, height));
}
- virtual void initResizable(bool resizable) {
- puglInitResizable(_view, resizable);
+ /// @copydoc puglSetAspectRatio
+ Status setAspectRatio(int minX, int minY, int maxX, int maxY)
+ {
+ return static_cast<Status>(
+ puglSetAspectRatio(cobj(), minX, minY, maxX, maxY));
}
- virtual void initTransientFor(uintptr_t parent) {
- puglInitTransientFor(_view, parent);
+ /**
+ @}
+ @name Windows
+ Methods for working with top-level windows.
+ @{
+ */
+
+ /// @copydoc puglSetWindowTitle
+ Status setWindowTitle(const char* title)
+ {
+ return static_cast<Status>(puglSetWindowTitle(cobj(), title));
}
- virtual void initBackend(const PuglBackend* backend) {
- puglInitBackend(_view, backend);
+ /// @copydoc puglSetParentWindow
+ Status setParentWindow(NativeView parent)
+ {
+ return static_cast<Status>(puglSetParentWindow(cobj(), parent));
+ }
+
+ /// @copydoc puglSetTransientFor
+ Status setTransientFor(NativeView parent)
+ {
+ return static_cast<Status>(puglSetTransientFor(cobj(), parent));
+ }
+
+ /// @copydoc puglRealize
+ Status realize() { return static_cast<Status>(puglRealize(cobj())); }
+
+ /// @copydoc puglShowWindow
+ Status showWindow() { return static_cast<Status>(puglShowWindow(cobj())); }
+
+ /// @copydoc puglHideWindow
+ Status hideWindow() { return static_cast<Status>(puglHideWindow(cobj())); }
+
+ /// @copydoc puglGetVisible
+ bool visible() const { return puglGetVisible(cobj()); }
+
+ /// @copydoc puglGetNativeWindow
+ NativeView nativeWindow() { return puglGetNativeWindow(cobj()); }
+
+ /**
+ @}
+ @name Graphics
+ Methods for working with the graphics context and scheduling
+ redisplays.
+ @{
+ */
+
+ /// @copydoc puglGetContext
+ void* context() { return puglGetContext(cobj()); }
+
+ /// @copydoc puglPostRedisplay
+ Status postRedisplay()
+ {
+ return static_cast<Status>(puglPostRedisplay(cobj()));
}
- virtual void createWindow(const char* title) {
- puglCreateWindow(_view, title);
+ /// @copydoc puglPostRedisplayRect
+ Status postRedisplayRect(const Rect rect)
+ {
+ return static_cast<Status>(puglPostRedisplayRect(cobj(), rect));
}
- virtual void showWindow() { puglShowWindow(_view); }
- virtual void hideWindow() { puglHideWindow(_view); }
- virtual PuglNativeWindow getNativeWindow() { return puglGetNativeWindow(_view); }
+ /**
+ @}
+ @name Interaction
+ Methods for interacting with the user and window system.
+ @{
+ */
- virtual void onEvent(const PuglEvent* event) = 0;
+ /// @copydoc puglGrabFocus
+ Status grabFocus() { return static_cast<Status>(puglGrabFocus(cobj())); }
- virtual void* getContext() { return puglGetContext(_view); }
- virtual void ignoreKeyRepeat(bool ignore) { puglIgnoreKeyRepeat(_view, ignore); }
- virtual void grabFocus() { puglGrabFocus(_view); }
- virtual void requestAttention() { puglRequestAttention(_view); }
- virtual PuglStatus waitForEvent() { return puglWaitForEvent(_view); }
- virtual PuglStatus processEvents() { return puglProcessEvents(_view); }
- virtual void postRedisplay() { puglPostRedisplay(_view); }
+ /// @copydoc puglHasFocus
+ bool hasFocus() const { return puglHasFocus(cobj()); }
- PuglView* cobj() { return _view; }
+ /// @copydoc puglSetBackend
+ Status setBackend(const PuglBackend* backend)
+ {
+ return static_cast<Status>(puglSetBackend(cobj(), backend));
+ }
+
+ /// @copydoc puglSetCursor
+ Status setCursor(const Cursor cursor)
+ {
+ return static_cast<Status>(
+ puglSetCursor(cobj(), static_cast<PuglCursor>(cursor)));
+ }
+
+ /// @copydoc puglRequestAttention
+ Status requestAttention()
+ {
+ return static_cast<Status>(puglRequestAttention(cobj()));
+ }
+
+ /**
+ @}
+ @name Event Handlers
+ Methods called when events are dispatched to the view.
+ @{
+ */
+
+ virtual Status onCreate(const CreateEvent&) PUGL_CONST_FUNC;
+ virtual Status onDestroy(const DestroyEvent&) PUGL_CONST_FUNC;
+ virtual Status onConfigure(const ConfigureEvent&) PUGL_CONST_FUNC;
+ virtual Status onMap(const MapEvent&) PUGL_CONST_FUNC;
+ virtual Status onUnmap(const UnmapEvent&) PUGL_CONST_FUNC;
+ virtual Status onUpdate(const UpdateEvent&) PUGL_CONST_FUNC;
+ virtual Status onExpose(const ExposeEvent&) PUGL_CONST_FUNC;
+ virtual Status onClose(const CloseEvent&) PUGL_CONST_FUNC;
+ virtual Status onFocusIn(const FocusInEvent&) PUGL_CONST_FUNC;
+ virtual Status onFocusOut(const FocusOutEvent&) PUGL_CONST_FUNC;
+ virtual Status onKeyPress(const KeyPressEvent&) PUGL_CONST_FUNC;
+ virtual Status onKeyRelease(const KeyReleaseEvent&) PUGL_CONST_FUNC;
+ virtual Status onText(const TextEvent&) PUGL_CONST_FUNC;
+ virtual Status onPointerIn(const PointerInEvent&) PUGL_CONST_FUNC;
+ virtual Status onPointerOut(const PointerOutEvent&) PUGL_CONST_FUNC;
+ virtual Status onButtonPress(const ButtonPressEvent&) PUGL_CONST_FUNC;
+ virtual Status onButtonRelease(const ButtonReleaseEvent&) PUGL_CONST_FUNC;
+ virtual Status onMotion(const MotionEvent&) PUGL_CONST_FUNC;
+ virtual Status onScroll(const ScrollEvent&) PUGL_CONST_FUNC;
+ virtual Status onClient(const ClientEvent&) PUGL_CONST_FUNC;
+ virtual Status onTimer(const TimerEvent&) PUGL_CONST_FUNC;
+
+ /**
+ @}
+ */
+
+ PuglView* cobj() { return Wrapper::cobj(); }
+ const PuglView* cobj() const { return Wrapper::cobj(); }
private:
- static void _onEvent(PuglView* view, const PuglEvent* event) {
- ((View*)puglGetHandle(view))->onEvent(event);
+ template<class Typed, class Base>
+ static const Typed& typedEventRef(const Base& base)
+ {
+ const auto& event = static_cast<const Typed&>(base);
+ static_assert(sizeof(event) == sizeof(typename Typed::BaseEvent), "");
+ static_assert(std::is_standard_layout<Typed>::value, "");
+ assert(event.type == Typed::type);
+ return event;
}
- PuglView* _view;
+ static PuglStatus dispatchEvent(PuglView* view, const PuglEvent* event) noexcept {
+ try {
+ View* self = static_cast<View*>(puglGetHandle(view));
+
+ return self->dispatch(event);
+ } catch (...) {
+ return PUGL_UNKNOWN_ERROR;
+ }
+ }
+
+ PuglStatus dispatch(const PuglEvent* event)
+ {
+ switch (event->type) {
+ case PUGL_NOTHING:
+ return PUGL_SUCCESS;
+ case PUGL_CREATE:
+ return static_cast<PuglStatus>(
+ onCreate(typedEventRef<CreateEvent>(event->any)));
+ case PUGL_DESTROY:
+ return static_cast<PuglStatus>(
+ onDestroy(typedEventRef<DestroyEvent>(event->any)));
+ case PUGL_CONFIGURE:
+ return static_cast<PuglStatus>(onConfigure(
+ typedEventRef<ConfigureEvent>(event->configure)));
+ case PUGL_MAP:
+ return static_cast<PuglStatus>(
+ onMap(typedEventRef<MapEvent>(event->any)));
+ case PUGL_UNMAP:
+ return static_cast<PuglStatus>(
+ onUnmap(typedEventRef<UnmapEvent>(event->any)));
+ case PUGL_UPDATE:
+ return static_cast<PuglStatus>(
+ onUpdate(typedEventRef<UpdateEvent>(event->any)));
+ case PUGL_EXPOSE:
+ return static_cast<PuglStatus>(
+ onExpose(typedEventRef<ExposeEvent>(event->expose)));
+ case PUGL_CLOSE:
+ return static_cast<PuglStatus>(
+ onClose(typedEventRef<CloseEvent>(event->any)));
+ case PUGL_FOCUS_IN:
+ return static_cast<PuglStatus>(
+ onFocusIn(typedEventRef<FocusInEvent>(event->focus)));
+ case PUGL_FOCUS_OUT:
+ return static_cast<PuglStatus>(
+ onFocusOut(typedEventRef<FocusOutEvent>(event->focus)));
+ case PUGL_KEY_PRESS:
+ return static_cast<PuglStatus>(
+ onKeyPress(typedEventRef<KeyPressEvent>(event->key)));
+ case PUGL_KEY_RELEASE:
+ return static_cast<PuglStatus>(
+ onKeyRelease(typedEventRef<KeyReleaseEvent>(event->key)));
+ case PUGL_TEXT:
+ return static_cast<PuglStatus>(
+ onText(typedEventRef<TextEvent>(event->text)));
+ case PUGL_POINTER_IN:
+ return static_cast<PuglStatus>(onPointerIn(
+ typedEventRef<PointerInEvent>(event->crossing)));
+ case PUGL_POINTER_OUT:
+ return static_cast<PuglStatus>(onPointerOut(
+ typedEventRef<PointerOutEvent>(event->crossing)));
+ case PUGL_BUTTON_PRESS:
+ return static_cast<PuglStatus>(onButtonPress(
+ typedEventRef<ButtonPressEvent>(event->button)));
+ case PUGL_BUTTON_RELEASE:
+ return static_cast<PuglStatus>(onButtonRelease(
+ typedEventRef<ButtonReleaseEvent>(event->button)));
+ case PUGL_MOTION:
+ return static_cast<PuglStatus>(
+ onMotion(typedEventRef<MotionEvent>(event->motion)));
+ case PUGL_SCROLL:
+ return static_cast<PuglStatus>(
+ onScroll(typedEventRef<ScrollEvent>(event->scroll)));
+ case PUGL_CLIENT:
+ return static_cast<PuglStatus>(
+ onClient(typedEventRef<ClientEvent>(event->client)));
+ case PUGL_TIMER:
+ return static_cast<PuglStatus>(
+ onTimer(typedEventRef<TimerEvent>(event->timer)));
+ }
+
+ return PUGL_FAILURE;
+ }
+
+ World& _world;
};
-} // namespace pugl
+/**
+ @}
+*/
+
+} // namespace pugl
/**
@}
*/
-#endif /* PUGL_PUGL_HPP */
+#endif /* PUGL_PUGL_HPP */
diff --git a/pugl/pugl/pugl.ipp b/pugl/pugl/pugl.ipp
new file mode 100644
index 0000000..7c39a63
--- /dev/null
+++ b/pugl/pugl/pugl.ipp
@@ -0,0 +1,154 @@
+/*
+ Copyright 2012-2020 David Robillard <d@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.ipp
+ @brief Pugl C++ API wrapper implementation.
+
+ This file must be included exactly once in the application.
+*/
+
+#include "pugl/pugl.hpp"
+
+namespace pugl {
+
+Status
+View::onCreate(const CreateEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onDestroy(const DestroyEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onConfigure(const ConfigureEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onMap(const MapEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onUnmap(const UnmapEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onUpdate(const UpdateEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onExpose(const ExposeEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onClose(const CloseEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onFocusIn(const FocusInEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onFocusOut(const FocusOutEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onKeyPress(const KeyPressEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onKeyRelease(const KeyReleaseEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onText(const TextEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onPointerIn(const PointerInEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onPointerOut(const PointerOutEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onButtonPress(const ButtonPressEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onButtonRelease(const ButtonReleaseEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onMotion(const MotionEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onScroll(const ScrollEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onClient(const ClientEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onTimer(const TimerEvent&)
+{
+ return pugl::Status::success;
+}
+
+}
diff --git a/pugl/pugl/pugl_cairo.h b/pugl/pugl/pugl_cairo.h
index e71072e..c68f6bb 100644
--- a/pugl/pugl/pugl_cairo.h
+++ b/pugl/pugl/pugl_cairo.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file pugl_cairo.h Declaration of Cairo backend accessor.
+ @file pugl_cairo.h
+ @brief Declaration of Cairo backend accessor.
*/
#ifndef PUGL_PUGL_CAIRO_H
@@ -28,7 +29,7 @@ PUGL_BEGIN_DECLS
/**
@defgroup cairo Cairo
Cairo graphics support.
- @ingroup pugl_api
+ @ingroup pugl_c
@{
*/
diff --git a/pugl/pugl/pugl_cairo_backend.h b/pugl/pugl/pugl_cairo.hpp
index 3f8cec3..5b17ab7 100644
--- a/pugl/pugl/pugl_cairo_backend.h
+++ b/pugl/pugl/pugl_cairo.hpp
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -14,10 +14,37 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef PUGL_PUGL_CAIRO_BACKEND_H
-#define PUGL_PUGL_CAIRO_BACKEND_H
+/**
+ @file pugl_cairo.hpp
+ @brief Declaration of Cairo backend accessor for C++.
+*/
+
+#ifndef PUGL_PUGL_CAIRO_HPP
+#define PUGL_PUGL_CAIRO_HPP
-#warning "This header is deprecated, use pugl/pugl_cairo.h instead."
+#include "pugl/pugl.h"
#include "pugl/pugl_cairo.h"
-#endif // PUGL_PUGL_CAIRO_BACKEND_H
+namespace pugl {
+
+/**
+ @defgroup cairoxx Cairo
+ Cairo graphics support.
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc puglCairoBackend
+static inline const PuglBackend*
+cairoBackend()
+{
+ return puglCairoBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_CAIRO_HPP
diff --git a/pugl/pugl/pugl_gl.h b/pugl/pugl/pugl_gl.h
index 9c5fa94..d501b3c 100644
--- a/pugl/pugl/pugl_gl.h
+++ b/pugl/pugl/pugl_gl.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file pugl_gl.h OpenGL-specific API.
+ @file pugl_gl.h
+ @brief OpenGL-specific API.
*/
#ifndef PUGL_PUGL_GL_H
@@ -28,7 +29,7 @@ PUGL_BEGIN_DECLS
/**
@defgroup gl OpenGL
OpenGL graphics support.
- @ingroup pugl_api
+ @ingroup pugl_c
@{
*/
diff --git a/pugl/pugl/pugl_gl_backend.h b/pugl/pugl/pugl_gl.hpp
index e1b9a15..4bc5bbd 100644
--- a/pugl/pugl/pugl_gl_backend.h
+++ b/pugl/pugl/pugl_gl.hpp
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -14,10 +14,47 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef PUGL_PUGL_GL_BACKEND_H
-#define PUGL_PUGL_GL_BACKEND_H
+/**
+ @file pugl_gl.hpp
+ @brief OpenGL-specific C++ API.
+*/
+
+#ifndef PUGL_PUGL_GL_HPP
+#define PUGL_PUGL_GL_HPP
-#warning "This header is deprecated, use pugl/pugl_gl.h instead."
+#include "pugl/pugl.h"
#include "pugl/pugl_gl.h"
-#endif // PUGL_PUGL_GL_BACKEND_H
+namespace pugl {
+
+/**
+ @defgroup glxx OpenGL
+ OpenGL graphics support.
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc PuglGlFunc
+using GlFunc = PuglGlFunc;
+
+/// @copydoc puglGetProcAddress
+static inline GlFunc
+getProcAddress(const char* name)
+{
+ return puglGetProcAddress(name);
+}
+
+/// @copydoc puglGlBackend
+static inline const PuglBackend*
+glBackend()
+{
+ return puglGlBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_GL_HPP
diff --git a/pugl/pugl/pugl_stub.h b/pugl/pugl/pugl_stub.h
index da918aa..ef48000 100644
--- a/pugl/pugl/pugl_stub.h
+++ b/pugl/pugl/pugl_stub.h
@@ -1,5 +1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@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
@@ -15,7 +15,8 @@
*/
/**
- @file pugl_stub.h Stub backend functions and accessor declaration.
+ @file pugl_stub.h
+ @brief Stub backend functions and accessor declaration.
*/
#ifndef PUGL_PUGL_STUB_H
@@ -36,7 +37,7 @@ PUGL_BEGIN_DECLS
for other backends to reuse since not all need non-trivial implementations
of every backend function.
- @ingroup pugl_api
+ @ingroup pugl_c
@{
*/
@@ -50,50 +51,6 @@ PUGL_API
const PuglBackend*
puglStubBackend(void);
-static inline PuglStatus
-puglStubConfigure(PuglView* view)
-{
- (void)view;
- return PUGL_SUCCESS;
-}
-
-static inline PuglStatus
-puglStubCreate(PuglView* view)
-{
- (void)view;
- return PUGL_SUCCESS;
-}
-
-static inline PuglStatus
-puglStubDestroy(PuglView* view)
-{
- (void)view;
- return PUGL_SUCCESS;
-}
-
-static inline PuglStatus
-puglStubEnter(PuglView* view, const PuglEventExpose* expose)
-{
- (void)view;
- (void)expose;
- return PUGL_SUCCESS;
-}
-
-static inline PuglStatus
-puglStubLeave(PuglView* view, const PuglEventExpose* expose)
-{
- (void)view;
- (void)expose;
- return PUGL_SUCCESS;
-}
-
-static inline void*
-puglStubGetContext(PuglView* view)
-{
- (void)view;
- return NULL;
-}
-
/**
@}
*/
diff --git a/pugl/pugl/pugl_stub_backend.h b/pugl/pugl/pugl_stub.hpp
index e5aa513..c5f3901 100644
--- a/pugl/pugl/pugl_stub_backend.h
+++ b/pugl/pugl/pugl_stub.hpp
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@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
@@ -14,10 +14,37 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef PUGL_PUGL_STUB_BACKEND_H
-#define PUGL_PUGL_STUB_BACKEND_H
+/**
+ @file pugl_stub.hpp
+ @brief Declaration of Stub backend accessor for C++.
+*/
+
+#ifndef PUGL_PUGL_STUB_HPP
+#define PUGL_PUGL_STUB_HPP
-#warning "This header is deprecated, use pugl/pugl_stub.h instead."
+#include "pugl/pugl.h"
#include "pugl/pugl_stub.h"
-#endif // PUGL_PUGL_STUB_BACKEND_H
+namespace pugl {
+
+/**
+ @defgroup stubxx Stub
+ Stub graphics support.
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc puglStubBackend
+static inline const PuglBackend*
+stubBackend()
+{
+ return puglStubBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_STUB_HPP