diff options
Diffstat (limited to 'pugl')
-rw-r--r-- | pugl/detail/implementation.c | 493 | ||||
-rw-r--r-- | pugl/detail/implementation.h | 77 | ||||
-rw-r--r-- | pugl/detail/mac.h | 55 | ||||
-rw-r--r-- | pugl/detail/mac.m | 1459 | ||||
-rw-r--r-- | pugl/detail/mac_cairo.m | 164 | ||||
-rw-r--r-- | pugl/detail/mac_gl.m | 201 | ||||
-rw-r--r-- | pugl/detail/mac_stub.m | 96 | ||||
-rw-r--r-- | pugl/detail/stub.h | 75 | ||||
-rw-r--r-- | pugl/detail/types.h | 118 | ||||
-rw-r--r-- | pugl/detail/win.c | 1152 | ||||
-rw-r--r-- | pugl/detail/win.h | 147 | ||||
-rw-r--r-- | pugl/detail/win_cairo.c | 178 | ||||
-rw-r--r-- | pugl/detail/win_gl.c | 325 | ||||
-rw-r--r-- | pugl/detail/win_stub.c | 80 | ||||
-rw-r--r-- | pugl/detail/x11.c | 1348 | ||||
-rw-r--r-- | pugl/detail/x11.h | 76 | ||||
-rw-r--r-- | pugl/detail/x11_cairo.c | 170 | ||||
-rw-r--r-- | pugl/detail/x11_gl.c | 238 | ||||
-rw-r--r-- | pugl/detail/x11_stub.c | 54 | ||||
-rw-r--r-- | pugl/gl.h | 36 | ||||
-rw-r--r-- | pugl/glu.h | 36 | ||||
-rw-r--r-- | pugl/pugl.h | 1624 | ||||
-rw-r--r-- | pugl/pugl.hpp | 726 | ||||
-rw-r--r-- | pugl/pugl.ipp | 154 | ||||
-rw-r--r-- | pugl/pugl_cairo.h | 50 | ||||
-rw-r--r-- | pugl/pugl_cairo.hpp | 50 | ||||
-rw-r--r-- | pugl/pugl_gl.h | 61 | ||||
-rw-r--r-- | pugl/pugl_gl.hpp | 60 | ||||
-rw-r--r-- | pugl/pugl_stub.h | 60 | ||||
-rw-r--r-- | pugl/pugl_stub.hpp | 50 |
30 files changed, 0 insertions, 9413 deletions
diff --git a/pugl/detail/implementation.c b/pugl/detail/implementation.c deleted file mode 100644 index 6a5f932..0000000 --- a/pugl/detail/implementation.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - 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 implementation.c - @brief Platform-independent implementation. -*/ - -#include "pugl/detail/implementation.h" -#include "pugl/pugl.h" - -#include <assert.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -static const char* -puglLogLevelPrefix(const PuglLogLevel level) -{ - switch (level) { - case PUGL_LOG_LEVEL_ERR: - return "error: "; - case PUGL_LOG_LEVEL_WARNING: - return "warning: "; - case PUGL_LOG_LEVEL_INFO: - case PUGL_LOG_LEVEL_DEBUG: - return ""; - } - - return ""; -} - -static void -puglDefaultLogFunc(PuglWorld* PUGL_UNUSED(world), - PuglLogLevel level, - const char* msg) -{ - fprintf(stderr, "%s%s", puglLogLevelPrefix(level), msg); -} - -const char* -puglStrerror(const PuglStatus status) -{ - // clang-format off - switch (status) { - case PUGL_SUCCESS: return "Success"; - 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"; - case PUGL_REALIZE_FAILED: return "View creation failed"; - case PUGL_SET_FORMAT_FAILED: return "Failed to set pixel format"; - case PUGL_CREATE_CONTEXT_FAILED: return "Failed to create drawing context"; - case PUGL_UNSUPPORTED_TYPE: return "Unsupported data type"; - } - // clang-format on - - return "Unknown error"; -} - -void -puglSetString(char** dest, const char* string) -{ - if (*dest != string) { - const size_t len = strlen(string); - - *dest = (char*)realloc(*dest, len + 1); - strncpy(*dest, string, len + 1); - } -} - -void -puglSetBlob(PuglBlob* const dest, const void* const data, const size_t len) -{ - if (data) { - dest->len = len; - dest->data = realloc(dest->data, len + 1); - memcpy(dest->data, data, len); - ((char*)dest->data)[len] = 0; - } else { - dest->len = 0; - dest->data = NULL; - } -} - -static void -puglSetDefaultHints(PuglHints hints) -{ - hints[PUGL_USE_COMPAT_PROFILE] = PUGL_TRUE; - hints[PUGL_CONTEXT_VERSION_MAJOR] = 2; - hints[PUGL_CONTEXT_VERSION_MINOR] = 0; - hints[PUGL_RED_BITS] = 8; - hints[PUGL_GREEN_BITS] = 8; - hints[PUGL_BLUE_BITS] = 8; - hints[PUGL_ALPHA_BITS] = 8; - hints[PUGL_DEPTH_BITS] = 0; - hints[PUGL_STENCIL_BITS] = 0; - hints[PUGL_SAMPLES] = 0; - hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE; - hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE; - hints[PUGL_RESIZABLE] = PUGL_FALSE; - hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE; - hints[PUGL_REFRESH_RATE] = PUGL_DONT_CARE; -} - -PuglWorld* -puglNewWorld(PuglWorldType type, PuglWorldFlags flags) -{ - PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld)); - if (!world || !(world->impl = puglInitWorldInternals(type, flags))) { - free(world); - return NULL; - } - - world->startTime = puglGetTime(world); - world->logFunc = puglDefaultLogFunc; - world->logLevel = PUGL_LOG_LEVEL_INFO; - - puglSetString(&world->className, "Pugl"); - - return world; -} - -void -puglFreeWorld(PuglWorld* const world) -{ - puglFreeWorldInternals(world); - free(world->className); - free(world->views); - free(world); -} - -void -puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle) -{ - world->handle = handle; -} - -PuglWorldHandle -puglGetWorldHandle(PuglWorld* world) -{ - return world->handle; -} - -PuglStatus -puglSetLogFunc(PuglWorld* world, PuglLogFunc logFunc) -{ - world->logFunc = logFunc; - - return PUGL_SUCCESS; -} - -PuglStatus -puglSetLogLevel(PuglWorld* world, PuglLogLevel level) -{ - world->logLevel = level; - - return PUGL_SUCCESS; -} - -PuglStatus -puglSetClassName(PuglWorld* const world, const char* const name) -{ - puglSetString(&world->className, name); - return PUGL_SUCCESS; -} - -PuglView* -puglNewView(PuglWorld* const world) -{ - PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); - if (!view || !(view->impl = puglInitViewInternals())) { - free(view); - return NULL; - } - - view->world = world; - view->minWidth = 1; - view->minHeight = 1; - - puglSetDefaultHints(view->hints); - - // Add to world view list - ++world->numViews; - world->views = (PuglView**)realloc(world->views, - world->numViews * sizeof(PuglView*)); - - world->views[world->numViews - 1] = view; - - return view; -} - -void -puglFreeView(PuglView* view) -{ - puglDispatchSimpleEvent(view, PUGL_DESTROY); - - // Remove from world view list - PuglWorld* world = view->world; - for (size_t i = 0; i < world->numViews; ++i) { - if (world->views[i] == view) { - if (i == world->numViews - 1) { - world->views[i] = NULL; - } else { - memmove(world->views + i, world->views + i + 1, - sizeof(PuglView*) * (world->numViews - i - 1)); - world->views[world->numViews - 1] = NULL; - } - --world->numViews; - } - } - - free(view->title); - free(view->clipboard.data); - puglFreeViewInternals(view); - free(view); -} - -PuglWorld* -puglGetWorld(PuglView* view) -{ - return view->world; -} - -PuglStatus -puglSetViewHint(PuglView* view, PuglViewHint hint, int value) -{ - if (value == PUGL_DONT_CARE) { - switch (hint) { - case PUGL_USE_COMPAT_PROFILE: - case PUGL_USE_DEBUG_CONTEXT: - case PUGL_CONTEXT_VERSION_MAJOR: - case PUGL_CONTEXT_VERSION_MINOR: - case PUGL_SWAP_INTERVAL: - return PUGL_BAD_PARAMETER; - default: - break; - } - } - - if (hint < PUGL_NUM_VIEW_HINTS) { - view->hints[hint] = value; - return PUGL_SUCCESS; - } - - return PUGL_BAD_PARAMETER; -} - -int -puglGetViewHint(const PuglView* view, PuglViewHint hint) -{ - if (hint < PUGL_NUM_VIEW_HINTS) { - return view->hints[hint]; - } - - return PUGL_DONT_CARE; -} - -PuglStatus -puglSetParentWindow(PuglView* view, PuglNativeView parent) -{ - view->parent = parent; - return PUGL_SUCCESS; -} - -PuglStatus -puglSetBackend(PuglView* view, const PuglBackend* backend) -{ - view->backend = backend; - return PUGL_SUCCESS; -} - -void -puglSetHandle(PuglView* view, PuglHandle handle) -{ - view->handle = handle; -} - -PuglHandle -puglGetHandle(PuglView* view) -{ - return view->handle; -} - -bool -puglGetVisible(const PuglView* view) -{ - return view->visible; -} - -PuglRect -puglGetFrame(const PuglView* view) -{ - return view->frame; -} - -void* -puglGetContext(PuglView* view) -{ - return view->backend->getContext(view); -} - -#ifndef PUGL_DISABLE_DEPRECATED - -PuglStatus -puglPollEvents(PuglWorld* world, double timeout) -{ - return puglUpdate(world, timeout); -} - -PuglStatus -puglDispatchEvents(PuglWorld* world) -{ - return puglUpdate(world, 0.0); -} - -PuglStatus -puglEnterContext(PuglView* view, bool drawing) -{ - const PuglEventExpose expose = { - PUGL_EXPOSE, 0, 0.0, 0.0, view->frame.width, view->frame.height}; - - view->backend->enter(view, drawing ? &expose : NULL); - - return PUGL_SUCCESS; -} - -PuglStatus -puglLeaveContext(PuglView* view, bool drawing) -{ - const PuglEventExpose expose = { - PUGL_EXPOSE, 0, 0.0, 0.0, view->frame.width, view->frame.height}; - - view->backend->leave(view, drawing ? &expose : NULL); - - return PUGL_SUCCESS; -} - -#endif - -PuglStatus -puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc) -{ - view->eventFunc = eventFunc; - return PUGL_SUCCESS; -} - -/// Return the code point for buf, or the replacement character on error -uint32_t -puglDecodeUTF8(const uint8_t* buf) -{ -#define FAIL_IF(cond) do { if (cond) return 0xFFFD; } while (0) - - // http://en.wikipedia.org/wiki/UTF-8 - - if (buf[0] < 0x80) { - return buf[0]; - } else if (buf[0] < 0xC2) { - return 0xFFFD; - } else if (buf[0] < 0xE0) { - FAIL_IF((buf[1] & 0xC0u) != 0x80); - return ((uint32_t)buf[0] << 6u) + buf[1] - 0x3080u; - } else if (buf[0] < 0xF0) { - FAIL_IF((buf[1] & 0xC0u) != 0x80); - FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0); - FAIL_IF((buf[2] & 0xC0u) != 0x80); - return ((uint32_t)buf[0] << 12u) + // - ((uint32_t)buf[1] << 6u) + // - ((uint32_t)buf[2] - 0xE2080u); - } else if (buf[0] < 0xF5) { - FAIL_IF((buf[1] & 0xC0u) != 0x80); - FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90); - FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90); - FAIL_IF((buf[2] & 0xC0u) != 0x80u); - FAIL_IF((buf[3] & 0xC0u) != 0x80u); - return (((uint32_t)buf[0] << 18u) + // - ((uint32_t)buf[1] << 12u) + // - ((uint32_t)buf[2] << 6u) + // - ((uint32_t)buf[3] - 0x3C82080u)); - } - return 0xFFFD; -} - -static inline bool -puglMustConfigure(PuglView* view, const PuglEventConfigure* configure) -{ - return memcmp(configure, &view->lastConfigure, sizeof(PuglEventConfigure)); -} - -void -puglDispatchSimpleEvent(PuglView* view, const PuglEventType type) -{ - assert(type == PUGL_CREATE || type == PUGL_DESTROY || type == PUGL_MAP || - type == PUGL_UNMAP || type == PUGL_UPDATE || type == PUGL_CLOSE); - - const PuglEvent event = {{type, 0}}; - puglDispatchEvent(view, &event); -} - -void -puglDispatchEventInContext(PuglView* view, const PuglEvent* event) -{ - if (event->type == PUGL_CONFIGURE) { - view->frame.x = event->configure.x; - view->frame.y = event->configure.y; - view->frame.width = event->configure.width; - view->frame.height = event->configure.height; - - if (puglMustConfigure(view, &event->configure)) { - view->eventFunc(view, event); - view->lastConfigure = event->configure; - } - } else { - view->eventFunc(view, event); - } -} - -void -puglDispatchEvent(PuglView* view, const PuglEvent* event) -{ - switch (event->type) { - case PUGL_NOTHING: - break; - case PUGL_CREATE: - case PUGL_DESTROY: - view->backend->enter(view, NULL); - view->eventFunc(view, event); - view->backend->leave(view, NULL); - break; - case PUGL_CONFIGURE: - if (puglMustConfigure(view, &event->configure)) { - view->backend->enter(view, NULL); - puglDispatchEventInContext(view, event); - view->backend->leave(view, NULL); - } - break; - case PUGL_EXPOSE: - view->backend->enter(view, &event->expose); - view->eventFunc(view, event); - view->backend->leave(view, &event->expose); - break; - default: - view->eventFunc(view, event); - } -} - -const void* -puglGetInternalClipboard(const PuglView* const view, - const char** const type, - size_t* const len) -{ - if (len) { - *len = view->clipboard.len; - } - - if (type) { - *type = "text/plain"; - } - - return view->clipboard.data; -} - -PuglStatus -puglSetInternalClipboard(PuglView* const view, - const char* const type, - const void* const data, - const size_t len) -{ - if (type && strcmp(type, "text/plain")) { - return PUGL_UNSUPPORTED_TYPE; - } - - puglSetBlob(&view->clipboard, data, len); - return PUGL_SUCCESS; -} - diff --git a/pugl/detail/implementation.h b/pugl/detail/implementation.h deleted file mode 100644 index ff97fef..0000000 --- a/pugl/detail/implementation.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - 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 implementation.h - @brief Shared declarations for implementation. -*/ - -#ifndef PUGL_DETAIL_IMPLEMENTATION_H -#define PUGL_DETAIL_IMPLEMENTATION_H - -#include "pugl/detail/types.h" -#include "pugl/pugl.h" - -#include <stddef.h> -#include <stdint.h> - -PUGL_BEGIN_DECLS - -/// 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` -void puglSetString(char** dest, const char* string); - -/// Allocate and initialise world internals (implemented once per platform) -PuglWorldInternals* -puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); - -/// Destroy and free world internals (implemented once per platform) -void puglFreeWorldInternals(PuglWorld* world); - -/// Allocate and initialise view internals (implemented once per platform) -PuglInternals* puglInitViewInternals(void); - -/// Destroy and free view internals (implemented once per platform) -void puglFreeViewInternals(PuglView* view); - -/// 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` -void puglDispatchSimpleEvent(PuglView* view, PuglEventType type); - -/// 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 -void puglDispatchEvent(PuglView* view, const PuglEvent* event); - -/// 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 -PuglStatus -puglSetInternalClipboard(PuglView* view, - const char* type, - const void* data, - size_t len); - -PUGL_END_DECLS - -#endif // PUGL_DETAIL_IMPLEMENTATION_H diff --git a/pugl/detail/mac.h b/pugl/detail/mac.h deleted file mode 100644 index 7b64cfe..0000000 --- a/pugl/detail/mac.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - 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 mac.h - @brief Shared definitions for MacOS implementation. -*/ - -#include "pugl/pugl.h" - -#import <Cocoa/Cocoa.h> - -#include <stdint.h> - -@interface PuglWrapperView : NSView<NSTextInputClient> - -- (void) dispatchExpose:(NSRect)rect; -- (void) setReshaped; - -@end - -@interface PuglWindow : NSWindow - -- (void) setPuglview:(PuglView*)view; - -@end - -struct PuglWorldInternalsImpl { - NSApplication* app; - NSAutoreleasePool* autoreleasePool; -}; - -struct PuglInternalsImpl { - NSApplication* app; - PuglWrapperView* wrapperView; - NSView* drawView; - NSCursor* cursor; - PuglWindow* window; - uint32_t mods; - bool mouseTracked; -}; diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m deleted file mode 100644 index 354546a..0000000 --- a/pugl/detail/mac.m +++ /dev/null @@ -1,1459 +0,0 @@ -/* - 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 - 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 mac.m - @brief MacOS implementation. -*/ - -#define GL_SILENCE_DEPRECATION 1 - -#include "pugl/detail/implementation.h" -#include "pugl/detail/mac.h" -#include "pugl/pugl.h" - -#import <Cocoa/Cocoa.h> - -#include <mach/mach_time.h> - -#include <stdlib.h> - -#ifndef __MAC_10_10 -typedef NSUInteger NSEventModifierFlags; -#endif - -#ifndef __MAC_10_12 -typedef NSUInteger NSWindowStyleMask; -#endif - -static NSRect -rectToScreen(NSScreen* screen, NSRect rect) -{ - 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 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 - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)flag -{ - (void)flag; - - NSWindow* result = [super initWithContentRect:contentRect - styleMask:aStyle - backing:bufferingType - defer:NO]; - - [result setAcceptsMouseMovedEvents:YES]; - return (PuglWindow*)result; -} - -- (void)setPuglview:(PuglView*)view -{ - puglview = view; - - [self - setContentSize:sizePoints(view, view->frame.width, view->frame.height)]; -} - -- (BOOL) canBecomeKeyWindow -{ - return YES; -} - -- (BOOL) canBecomeMainWindow -{ - return YES; -} - -- (void) setIsVisible:(BOOL)flag -{ - if (flag && !puglview->visible) { - const PuglEventConfigure ev = { - PUGL_CONFIGURE, - 0, - puglview->frame.x, - puglview->frame.y, - puglview->frame.width, - puglview->frame.height, - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); - puglDispatchSimpleEvent(puglview, PUGL_MAP); - } else if (!flag && puglview->visible) { - puglDispatchSimpleEvent(puglview, PUGL_UNMAP); - } - - puglview->visible = flag; - - [super setIsVisible:flag]; -} - -@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 = { - PUGL_CONFIGURE, - 0, - puglview->frame.x, - puglview->frame.y, - puglview->frame.width, - puglview->frame.height, - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); - reshaped = false; - } - - if (![[puglview->impl->drawView window] isVisible]) { - return; - } - - const PuglEventExpose ev = { - PUGL_EXPOSE, - 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; -} - -- (BOOL) acceptsFirstResponder -{ - return YES; -} - -- (void) setReshaped -{ - reshaped = true; -} - -static uint32_t -getModifiers(const NSEvent* const ev) -{ - const NSEventModifierFlags modifierFlags = [ev modifierFlags]; - - return (((modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0) | - ((modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0) | - ((modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0) | - ((modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0)); -} - -static PuglKey -keySymToSpecial(const NSEvent* const ev) -{ - NSString* chars = [ev charactersIgnoringModifiers]; - if ([chars length] == 1) { - switch ([chars characterAtIndex:0]) { - case NSF1FunctionKey: return PUGL_KEY_F1; - case NSF2FunctionKey: return PUGL_KEY_F2; - case NSF3FunctionKey: return PUGL_KEY_F3; - case NSF4FunctionKey: return PUGL_KEY_F4; - case NSF5FunctionKey: return PUGL_KEY_F5; - case NSF6FunctionKey: return PUGL_KEY_F6; - case NSF7FunctionKey: return PUGL_KEY_F7; - case NSF8FunctionKey: return PUGL_KEY_F8; - case NSF9FunctionKey: return PUGL_KEY_F9; - case NSF10FunctionKey: return PUGL_KEY_F10; - case NSF11FunctionKey: return PUGL_KEY_F11; - case NSF12FunctionKey: return PUGL_KEY_F12; - case NSDeleteCharacter: return PUGL_KEY_BACKSPACE; - case NSDeleteFunctionKey: return PUGL_KEY_DELETE; - case NSLeftArrowFunctionKey: return PUGL_KEY_LEFT; - case NSUpArrowFunctionKey: return PUGL_KEY_UP; - case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT; - case NSDownArrowFunctionKey: return PUGL_KEY_DOWN; - case NSPageUpFunctionKey: return PUGL_KEY_PAGE_UP; - case NSPageDownFunctionKey: return PUGL_KEY_PAGE_DOWN; - case NSHomeFunctionKey: return PUGL_KEY_HOME; - case NSEndFunctionKey: return PUGL_KEY_END; - case NSInsertFunctionKey: return PUGL_KEY_INSERT; - case NSMenuFunctionKey: return PUGL_KEY_MENU; - case NSScrollLockFunctionKey: return PUGL_KEY_SCROLL_LOCK; - case NSClearLineFunctionKey: return PUGL_KEY_NUM_LOCK; - case NSPrintScreenFunctionKey: return PUGL_KEY_PRINT_SCREEN; - case NSPauseFunctionKey: return PUGL_KEY_PAUSE; - } - // SHIFT, CTRL, ALT, and SUPER are handled in [flagsChanged] - } - return (PuglKey)0; -} - -- (void) updateTrackingAreas -{ - if (trackingArea != nil) { - [self removeTrackingArea:trackingArea]; - [trackingArea release]; - } - - const int opts = (NSTrackingMouseEnteredAndExited | - NSTrackingMouseMoved | - NSTrackingActiveAlways); - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] - options:opts - owner:self - userInfo:nil]; - [self addTrackingArea:trackingArea]; - [super updateTrackingAreas]; -} - -- (NSPoint) eventLocation:(NSEvent*)event -{ - return nsPointFromPoints(puglview, - [self convertPoint:[event locationInWindow] - fromView:nil]); -} - -static void -handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) -{ - const NSPoint wloc = [view eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - const PuglEventCrossing ev = { - type, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - PUGL_CROSSING_NORMAL, - }; - - puglDispatchEvent(view->puglview, (const PuglEvent*)&ev); -} - -- (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) cursorUpdate:(NSEvent*)event -{ - (void)event; - [puglview->impl->cursor set]; -} - -- (void) mouseMoved:(NSEvent*)event -{ - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - const PuglEventMotion ev = { - PUGL_MOTION, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); -} - -- (void) mouseDragged:(NSEvent*)event -{ - [self mouseMoved: event]; -} - -- (void) rightMouseDragged:(NSEvent*)event -{ - [self mouseMoved: event]; -} - -- (void) otherMouseDragged:(NSEvent*)event -{ - [self mouseMoved: event]; -} - -- (void) mouseDown:(NSEvent*)event -{ - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - const PuglEventButton ev = { - PUGL_BUTTON_PRESS, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - (uint32_t)[event buttonNumber] + 1, - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); -} - -- (void) mouseUp:(NSEvent*)event -{ - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - const PuglEventButton ev = { - PUGL_BUTTON_RELEASE, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - (uint32_t)[event buttonNumber] + 1, - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); -} - -- (void) rightMouseDown:(NSEvent*)event -{ - [self mouseDown: event]; -} - -- (void) rightMouseUp:(NSEvent*)event -{ - [self mouseUp: event]; -} - -- (void) otherMouseDown:(NSEvent*)event -{ - [self mouseDown: event]; -} - -- (void) otherMouseUp:(NSEvent*)event -{ - [self mouseUp: event]; -} - -- (void) scrollWheel:(NSEvent*)event -{ - 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); -} - -- (void) keyDown:(NSEvent*)event -{ - if (puglview->hints[PUGL_IGNORE_KEY_REPEAT] && [event isARepeat]) { - return; - } - - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - const PuglKey spec = keySymToSpecial(event); - const NSString* chars = [event charactersIgnoringModifiers]; - const char* str = [[chars lowercaseString] UTF8String]; - const uint32_t code = (spec ? spec : puglDecodeUTF8((const uint8_t*)str)); - - const PuglEventKey ev = { - PUGL_KEY_PRESS, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - [event keyCode], - (code != 0xFFFD) ? code : 0, - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); - - if (!spec) { - [self interpretKeyEvents:@[event]]; - } -} - -- (void)keyUp:(NSEvent*)event -{ - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - const PuglKey spec = keySymToSpecial(event); - const NSString* chars = [event charactersIgnoringModifiers]; - const char* str = [[chars lowercaseString] UTF8String]; - const uint32_t code = (spec ? spec : puglDecodeUTF8((const uint8_t*)str)); - - const PuglEventKey ev = { - PUGL_KEY_RELEASE, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - [event keyCode], - (code != 0xFFFD) ? code : 0, - }; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); -} - -- (BOOL) hasMarkedText -{ - return [markedText length] > 0; -} - -- (NSRange) markedRange -{ - return (([markedText length] > 0) - ? NSMakeRange(0, [markedText length] - 1) - : NSMakeRange(NSNotFound, 0)); -} - -- (NSRange) selectedRange -{ - return NSMakeRange(NSNotFound, 0); -} - -- (void)setMarkedText:(id)string - selectedRange:(NSRange)selected - replacementRange:(NSRange)replacement -{ - (void)selected; - (void)replacement; - [markedText release]; - markedText = ( - [(NSObject*)string isKindOfClass:[NSAttributedString class]] - ? [[NSMutableAttributedString alloc] initWithAttributedString:string] - : [[NSMutableAttributedString alloc] initWithString:string]); -} - -- (void) unmarkText -{ - [[markedText mutableString] setString:@""]; -} - -- (NSArray*) validAttributesForMarkedText -{ - return @[]; -} - -- (NSAttributedString*) - attributedSubstringForProposedRange:(NSRange)range - actualRange:(NSRangePointer)actual -{ - (void)range; - (void)actual; - return nil; -} - -- (NSUInteger) characterIndexForPoint:(NSPoint)point -{ - (void)point; - return 0; -} - -- (NSRect) firstRectForCharacterRange:(NSRange)range - actualRange:(NSRangePointer)actual -{ - (void)range; - (void)actual; - - const NSRect frame = [self bounds]; - return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0); -} - -- (void) doCommandBySelector:(SEL)selector -{ - (void)selector; -} - -- (void) insertText:(id)string - replacementRange:(NSRange)replacement -{ - (void)replacement; - - NSEvent* const event = [NSApp currentEvent]; - NSString* const characters = - ([(NSObject*)string isKindOfClass:[NSAttributedString class]] - ? [(NSAttributedString*)string string] - : (NSString*)string); - - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - for (size_t i = 0; i < [characters length]; ++i) { - const uint32_t code = [characters characterAtIndex:i]; - char utf8[8] = {0}; - NSUInteger len = 0; - - [characters getBytes:utf8 - maxLength:sizeof(utf8) - usedLength:&len - encoding:NSUTF8StringEncoding - options:0 - range:NSMakeRange(i, i + 1) - remainingRange:nil]; - - PuglEventText ev = { - PUGL_TEXT, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - getModifiers(event), - [event keyCode], - code, - { 0, 0, 0, 0, 0, 0, 0, 0 }, - }; - - memcpy(ev.string, utf8, len); - puglDispatchEvent(puglview, (const PuglEvent*)&ev); - } -} - -- (void) flagsChanged:(NSEvent*)event -{ - const uint32_t mods = getModifiers(event); - PuglEventType type = PUGL_NOTHING; - PuglKey special = (PuglKey)0; - - if ((mods & PUGL_MOD_SHIFT) != (puglview->impl->mods & PUGL_MOD_SHIFT)) { - type = mods & PUGL_MOD_SHIFT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; - special = PUGL_KEY_SHIFT; - } else if ((mods & PUGL_MOD_CTRL) != (puglview->impl->mods & PUGL_MOD_CTRL)) { - type = mods & PUGL_MOD_CTRL ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; - special = PUGL_KEY_CTRL; - } else if ((mods & PUGL_MOD_ALT) != (puglview->impl->mods & PUGL_MOD_ALT)) { - type = mods & PUGL_MOD_ALT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; - special = PUGL_KEY_ALT; - } else if ((mods & PUGL_MOD_SUPER) != (puglview->impl->mods & PUGL_MOD_SUPER)) { - type = mods & PUGL_MOD_SUPER ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; - special = PUGL_KEY_SUPER; - } - - if (special != 0) { - const NSPoint wloc = [self eventLocation:event]; - const NSPoint rloc = [NSEvent mouseLocation]; - PuglEventKey ev = { - type, - 0, - [event timestamp], - wloc.x, - wloc.y, - rloc.x, - [[NSScreen mainScreen] frame].size.height - rloc.y, - mods, - [event keyCode], - special - }; - puglDispatchEvent(puglview, (const PuglEvent*)&ev); - } - - puglview->impl->mods = mods; -} - -- (BOOL) preservesContentInLiveResize -{ - return NO; -} - -- (void) viewWillStartLiveResize -{ - timer = [NSTimer timerWithTimeInterval:(1 / 60.0) - target:self - selector:@selector(resizeTick) - userInfo:nil - repeats:YES]; - [[NSRunLoop currentRunLoop] addTimer:timer - forMode:NSRunLoopCommonModes]; - - [super viewWillStartLiveResize]; -} - -- (void) viewWillDraw -{ - puglDispatchSimpleEvent(puglview, PUGL_UPDATE); - [super viewWillDraw]; -} - -- (void) resizeTick -{ - puglPostRedisplay(puglview); -} - -- (void) timerTick:(NSTimer*)userTimer -{ - const NSNumber* userInfo = userTimer.userInfo; - const PuglEventTimer ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue}; - - puglDispatchEvent(puglview, (const PuglEvent*)&ev); -} - -- (void) viewDidEndLiveResize -{ - [super viewDidEndLiveResize]; - [timer invalidate]; - timer = NULL; -} - -@end - -@interface PuglWindowDelegate : NSObject<NSWindowDelegate> - -- (instancetype) initWithPuglWindow:(PuglWindow*)window; - -@end - -@implementation PuglWindowDelegate -{ - PuglWindow* window; -} - -- (instancetype) initWithPuglWindow:(PuglWindow*)puglWindow -{ - if ((self = [super init])) { - window = puglWindow; - } - - return self; -} - -- (BOOL) windowShouldClose:(id)sender -{ - (void)sender; - - puglDispatchSimpleEvent(window->puglview, PUGL_CLOSE); - return YES; -} - -- (void) windowDidMove:(NSNotification*)notification -{ - (void)notification; - - updateViewRect(window->puglview); -} - -- (void) windowDidBecomeKey:(NSNotification*)notification -{ - (void)notification; - - PuglEvent ev = {{PUGL_FOCUS_IN, 0}}; - ev.focus.mode = PUGL_CROSSING_NORMAL; - puglDispatchEvent(window->puglview, &ev); -} - -- (void) windowDidResignKey:(NSNotification*)notification -{ - (void)notification; - - PuglEvent ev = {{PUGL_FOCUS_OUT, 0}}; - ev.focus.mode = PUGL_CROSSING_NORMAL; - puglDispatchEvent(window->puglview, &ev); -} - -@end - -PuglWorldInternals* -puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags)) -{ - PuglWorldInternals* impl = (PuglWorldInternals*)calloc( - 1, sizeof(PuglWorldInternals)); - - impl->app = [NSApplication sharedApplication]; - - if (type == PUGL_PROGRAM) { - impl->autoreleasePool = [NSAutoreleasePool new]; - } - - return impl; -} - -void -puglFreeWorldInternals(PuglWorld* world) -{ - if (world->impl->autoreleasePool) { - [world->impl->autoreleasePool drain]; - } - - free(world->impl); -} - -void* -puglGetNativeWorld(PuglWorld* PUGL_UNUSED(world)) -{ - return NULL; -} - -PuglInternals* -puglInitViewInternals(void) -{ - PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); - - impl->cursor = [NSCursor arrowCursor]; - - return impl; -} - -static NSLayoutConstraint* -puglConstraint(id item, NSLayoutAttribute attribute, float constant) -{ - return [NSLayoutConstraint - constraintWithItem: item - attribute: attribute - relatedBy: NSLayoutRelationGreaterThanOrEqual - toItem: nil - attribute: NSLayoutAttributeNotAnAttribute - multiplier: 1.0 - constant: (CGFloat)constant]; -} - -PuglStatus -puglRealize(PuglView* view) -{ - PuglInternals* impl = view->impl; - if (impl->wrapperView) { - return PUGL_FAILURE; - } - - const NSScreen* const screen = [NSScreen mainScreen]; - const double scaleFactor = [screen backingScaleFactor]; - - // Getting depth from the display mode seems tedious, just set usual values - if (view->hints[PUGL_RED_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_RED_BITS] = 8; - } - if (view->hints[PUGL_BLUE_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_BLUE_BITS] = 8; - } - if (view->hints[PUGL_GREEN_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_GREEN_BITS] = 8; - } - if (view->hints[PUGL_ALPHA_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_ALPHA_BITS] = 8; - } - - CGDirectDisplayID displayId = CGMainDisplayID(); - CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId); - - // Try to get refresh rate from mode (usually fails) - view->hints[PUGL_REFRESH_RATE] = (int)CGDisplayModeGetRefreshRate(mode); - - CGDisplayModeRelease(mode); - if (view->hints[PUGL_REFRESH_RATE] == 0) { - // Get refresh rate from a display link - // TODO: Keep and actually use the display link for something? - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(displayId, &link); - - const CVTime p = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); - const double r = p.timeScale / (double)p.timeValue; - view->hints[PUGL_REFRESH_RATE] = (int)lrint(r); - - CVDisplayLinkRelease(link); - } - - 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]; - impl->wrapperView->puglview = view; - impl->wrapperView->userTimers = [[NSMutableDictionary alloc] init]; - impl->wrapperView->markedText = [[NSMutableAttributedString alloc] init]; - [impl->wrapperView setAutoresizesSubviews:YES]; - [impl->wrapperView initWithFrame:framePt]; - [impl->wrapperView addConstraint: - puglConstraint(impl->wrapperView, NSLayoutAttributeWidth, view->minWidth)]; - [impl->wrapperView addConstraint: - puglConstraint(impl->wrapperView, NSLayoutAttributeHeight, view->minHeight)]; - - // Create draw view to be rendered to - PuglStatus st = PUGL_SUCCESS; - if ((st = view->backend->configure(view)) || - (st = view->backend->create(view))) { - return st; - } - - // Add draw view to wrapper view - [impl->wrapperView addSubview:impl->drawView]; - [impl->wrapperView setHidden:NO]; - [impl->drawView setHidden:NO]; - - if (view->parent) { - NSView* pview = (NSView*)view->parent; - [pview addSubview:impl->wrapperView]; - [impl->drawView setHidden:NO]; - [[impl->drawView window] makeFirstResponder:impl->wrapperView]; - } else { - unsigned style = (NSClosableWindowMask | - NSTitledWindowMask | - NSMiniaturizableWindowMask ); - if (view->hints[PUGL_RESIZABLE]) { - style |= NSResizableWindowMask; - } - - PuglWindow* window = [[[PuglWindow alloc] - initWithContentRect:rectToScreen([NSScreen mainScreen], framePt) - styleMask:style - backing:NSBackingStoreBuffered - defer:NO - ] retain]; - [window setPuglview:view]; - - if (view->title) { - NSString* titleString = [[NSString alloc] - initWithBytes:view->title - length:strlen(view->title) - encoding:NSUTF8StringEncoding]; - - [window setTitle:titleString]; - } - - if (view->minWidth || view->minHeight) { - [window setContentMinSize:sizePoints(view, - view->minWidth, - view->minHeight)]; - } - impl->window = window; - - ((NSWindow*)window).delegate = [[PuglWindowDelegate alloc] - initWithPuglWindow:window]; - - if (view->minAspectX && view->minAspectY) { - [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]; - [window makeKeyAndOrderFront:window]; - [impl->window setIsVisible:NO]; - } - - [impl->wrapperView updateTrackingAreas]; - - puglDispatchSimpleEvent(view, PUGL_CREATE); - - return PUGL_SUCCESS; -} - -PuglStatus -puglShowWindow(PuglView* view) -{ - if (![view->impl->window isVisible]) { - [view->impl->window setIsVisible:YES]; - [view->impl->drawView setNeedsDisplay: YES]; - updateViewRect(view); - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglHideWindow(PuglView* view) -{ - [view->impl->window setIsVisible:NO]; - return PUGL_SUCCESS; -} - -void -puglFreeViewInternals(PuglView* view) -{ - if (view) { - if (view->backend) { - view->backend->destroy(view); - } - - if (view->impl) { - [view->impl->wrapperView removeFromSuperview]; - view->impl->wrapperView->puglview = NULL; - if (view->impl->window) { - [view->impl->window close]; - } - [view->impl->wrapperView release]; - if (view->impl->window) { - [view->impl->window release]; - } - free(view->impl); - } - } -} - -PuglStatus -puglGrabFocus(PuglView* view) -{ - NSWindow* window = [view->impl->wrapperView window]; - - [window makeKeyWindow]; - [window makeFirstResponder:view->impl->wrapperView]; - return PUGL_SUCCESS; -} - -bool -puglHasFocus(const PuglView* view) -{ - PuglInternals* const impl = view->impl; - - return ([[impl->wrapperView window] isKeyWindow] && - [[impl->wrapperView window] firstResponder] == impl->wrapperView); -} - -PuglStatus -puglRequestAttention(PuglView* view) -{ - if (![view->impl->window isKeyWindow]) { - [view->world->impl->app requestUserAttention:NSInformationalRequest]; - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglStartTimer(PuglView* view, uintptr_t id, double timeout) -{ - puglStopTimer(view, id); - - NSNumber* idNumber = [NSNumber numberWithUnsignedLong:id]; - - NSTimer* timer = [NSTimer timerWithTimeInterval:timeout - target:view->impl->wrapperView - selector:@selector(timerTick:) - userInfo:idNumber - repeats:YES]; - - [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; - - view->impl->wrapperView->userTimers[idNumber] = timer; - - return PUGL_SUCCESS; -} - -PuglStatus -puglStopTimer(PuglView* view, uintptr_t id) -{ - NSNumber* idNumber = [NSNumber numberWithUnsignedLong:id]; - NSTimer* timer = view->impl->wrapperView->userTimers[idNumber]; - - if (timer) { - [view->impl->wrapperView->userTimers removeObjectForKey:timer]; - [timer invalidate]; - return PUGL_SUCCESS; - } - - return PUGL_UNKNOWN_ERROR; -} - -PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event) -{ - if (event->type == PUGL_CLIENT) { - PuglWrapperView* wrapper = view->impl->wrapperView; - const NSWindow* window = [wrapper window]; - const NSRect rect = [wrapper frame]; - const NSPoint center = {NSMidX(rect), NSMidY(rect)}; - - NSEvent* nsevent = [NSEvent - otherEventWithType:NSApplicationDefined - location:center - modifierFlags:0 - timestamp:[[NSProcessInfo processInfo] systemUptime] - windowNumber:window.windowNumber - context:nil - subtype:PUGL_CLIENT - data1:(NSInteger)event->client.data1 - data2:(NSInteger)event->client.data2]; - - [view->world->impl->app postEvent:nsevent atStart:false]; - return PUGL_SUCCESS; - } - - return PUGL_UNSUPPORTED_TYPE; -} - -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglWaitForEvent(PuglView* view) -{ - return puglPollEvents(view->world, -1.0); -} -#endif - -static void -dispatchClientEvent(PuglWorld* world, NSEvent* ev) -{ - NSWindow* win = [ev window]; - NSPoint loc = [ev locationInWindow]; - for (size_t i = 0; i < world->numViews; ++i) { - PuglView* view = world->views[i]; - PuglWrapperView* wrapper = view->impl->wrapperView; - if ([wrapper window] == win && NSPointInRect(loc, [wrapper frame])) { - const PuglEventClient event = {PUGL_CLIENT, - 0, - (uintptr_t)[ev data1], - (uintptr_t)[ev data2]}; - - puglDispatchEvent(view, (const PuglEvent*)&event); - } - } -} - -PuglStatus -puglUpdate(PuglWorld* world, const double timeout) -{ - NSDate* date = ((timeout < 0) - ? [NSDate distantFuture] - : [NSDate dateWithTimeIntervalSinceNow:timeout]); - - for (NSEvent* ev = NULL; - (ev = [world->impl->app nextEventMatchingMask:NSAnyEventMask - untilDate:date - inMode:NSDefaultRunLoopMode - dequeue:YES]);) { - - if ([ev type] == NSApplicationDefined && [ev subtype] == PUGL_CLIENT) { - dispatchClientEvent(world, ev); - } - - [world->impl->app sendEvent: ev]; - - if (timeout < 0) { - // Now that we've waited and got an event, set the date to now to - // avoid looping forever - date = [NSDate date]; - } - } - - for (size_t i = 0; i < world->numViews; ++i) { - PuglView* const view = world->views[i]; - - if ([[view->impl->drawView window] isVisible]) { - puglDispatchSimpleEvent(view, PUGL_UPDATE); - } - - [view->impl->drawView displayIfNeeded]; - } - - return PUGL_SUCCESS; -} - -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglProcessEvents(PuglView* view) -{ - return puglDispatchEvents(view->world); -} -#endif - -double -puglGetTime(const PuglWorld* world) -{ - return (mach_absolute_time() / 1e9) - world->startTime; -} - -PuglStatus -puglPostRedisplay(PuglView* view) -{ - [view->impl->drawView setNeedsDisplay: YES]; - return PUGL_SUCCESS; -} - -PuglStatus -puglPostRedisplayRect(PuglView* view, const PuglRect rect) -{ - const NSRect rectPx = rectToNsRect(rect); - - [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; - - return PUGL_SUCCESS; -} - -PuglNativeView -puglGetNativeWindow(PuglView* view) -{ - return (PuglNativeView)view->impl->wrapperView; -} - -PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) -{ - puglSetString(&view->title, title); - - if (view->impl->window) { - NSString* titleString = [[NSString alloc] - initWithBytes:title - length:strlen(title) - encoding:NSUTF8StringEncoding]; - - [view->impl->window setTitle:titleString]; - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglSetFrame(PuglView* view, const PuglRect frame) -{ - PuglInternals* const impl = view->impl; - - // Update view frame to exactly the requested frame in Pugl coordinates - view->frame = frame; - - const NSRect framePx = rectToNsRect(frame); - const NSRect framePt = nsRectToPoints(view, framePx); - if (impl->window) { - // Resize window to fit new content rect - const NSRect screenPt = rectToScreen(viewScreen(view), framePt); - const NSRect winFrame = [impl->window frameRectForContentRect:screenPt]; - - [impl->window setFrame:winFrame display:NO]; - } - - // Resize views - 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: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; -} - -PuglStatus -puglSetAspectRatio(PuglView* const view, - const int minX, - const int minY, - const int maxX, - const int maxY) -{ - view->minAspectX = minX; - view->minAspectY = minY; - view->maxAspectX = maxX; - view->maxAspectY = maxY; - - if (view->impl->window && view->minAspectX && view->minAspectY) { - [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, - size_t* const len) -{ - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; - - if ([[pasteboard types] containsObject:NSStringPboardType]) { - const NSString* str = [pasteboard stringForType:NSStringPboardType]; - const char* utf8 = [str UTF8String]; - - puglSetBlob(&view->clipboard, utf8, strlen(utf8) + 1); - } - - 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, - const void* const data, - const size_t len) -{ - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; - const char* const str = (const char*)data; - - PuglStatus st = puglSetInternalClipboard(view, type, data, len); - if (st) { - return st; - } - - NSString* nsString = [NSString stringWithUTF8String:str]; - if (nsString) { - [pasteboard - declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] - owner:nil]; - - [pasteboard setString:nsString forType:NSStringPboardType]; - - return PUGL_SUCCESS; - } - - return PUGL_UNKNOWN_ERROR; -} diff --git a/pugl/detail/mac_cairo.m b/pugl/detail/mac_cairo.m deleted file mode 100644 index 18209d9..0000000 --- a/pugl/detail/mac_cairo.m +++ /dev/null @@ -1,164 +0,0 @@ -/* - 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 - 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 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 <cairo-quartz.h> - -#import <Cocoa/Cocoa.h> - -#include <assert.h> - -@interface PuglCairoView : NSView -@end - -@implementation PuglCairoView -{ -@public - PuglView* puglview; - cairo_surface_t* surface; - cairo_t* cr; -} - -- (id) initWithFrame:(NSRect)frame -{ - self = [super initWithFrame:frame]; - - return self; -} - -- (void) resizeWithOldSuperviewSize:(NSSize)oldSize -{ - PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; - - [super resizeWithOldSuperviewSize:oldSize]; - [wrapper setReshaped]; -} - -- (void) drawRect:(NSRect)rect -{ - PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; - [wrapper dispatchExpose:rect]; -} - -@end - -static PuglStatus -puglMacCairoCreate(PuglView* view) -{ - PuglInternals* impl = view->impl; - PuglCairoView* drawView = [PuglCairoView alloc]; - - drawView->puglview = view; - [drawView initWithFrame:[impl->wrapperView bounds]]; - if (view->hints[PUGL_RESIZABLE]) { - [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - } else { - [drawView setAutoresizingMask:NSViewNotSizable]; - } - - impl->drawView = drawView; - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacCairoDestroy(PuglView* view) -{ - PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView; - - [drawView removeFromSuperview]; - [drawView release]; - - view->impl->drawView = nil; - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacCairoEnter(PuglView* view, const PuglEventExpose* expose) -{ - PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView; - if (!expose) { - return PUGL_SUCCESS; - } - - 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, (unsigned)sizePx.width, (unsigned)sizePx.height); - - drawView->cr = cairo_create(drawView->surface); - - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacCairoLeave(PuglView* view, const PuglEventExpose* expose) -{ - PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView; - if (!expose) { - return PUGL_SUCCESS; - } - - assert(drawView->surface); - assert(drawView->cr); - - CGContextRef context = cairo_quartz_surface_get_cg_context(drawView->surface); - - cairo_destroy(drawView->cr); - cairo_surface_destroy(drawView->surface); - drawView->cr = NULL; - drawView->surface = NULL; - - CGContextFlush(context); - - return PUGL_SUCCESS; -} - -static void* -puglMacCairoGetContext(PuglView* view) -{ - return ((PuglCairoView*)view->impl->drawView)->cr; -} - -const PuglBackend* puglCairoBackend(void) -{ - static const PuglBackend backend = {puglStubConfigure, - puglMacCairoCreate, - puglMacCairoDestroy, - puglMacCairoEnter, - puglMacCairoLeave, - puglMacCairoGetContext}; - - return &backend; -} diff --git a/pugl/detail/mac_gl.m b/pugl/detail/mac_gl.m deleted file mode 100644 index 4d4f324..0000000 --- a/pugl/detail/mac_gl.m +++ /dev/null @@ -1,201 +0,0 @@ -/* - 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 - 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 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" - -#ifndef __MAC_10_10 -# define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core -#endif - -@interface PuglOpenGLView : NSOpenGLView -@end - -@implementation PuglOpenGLView -{ -@public - PuglView* puglview; -} - -- (id) initWithFrame:(NSRect)frame -{ - const bool compat = puglview->hints[PUGL_USE_COMPAT_PROFILE]; - const unsigned samples = (unsigned)puglview->hints[PUGL_SAMPLES]; - const int major = puglview->hints[PUGL_CONTEXT_VERSION_MAJOR]; - const unsigned profile = ((compat || major < 3) - ? NSOpenGLProfileVersionLegacy - : (major >= 4 - ? NSOpenGLProfileVersion4_1Core - : NSOpenGLProfileVersion3_2Core)); - - // Set attributes to default if they are unset - // (There is no GLX_DONT_CARE equivalent on MacOS) - if (puglview->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { - puglview->hints[PUGL_DEPTH_BITS] = 0; - } - if (puglview->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { - puglview->hints[PUGL_STENCIL_BITS] = 0; - } - if (puglview->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { - puglview->hints[PUGL_SAMPLES] = 1; - } - if (puglview->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { - puglview->hints[PUGL_DOUBLE_BUFFER] = 1; - } - if (puglview->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { - puglview->hints[PUGL_SWAP_INTERVAL] = 1; - } - - const unsigned colorSize = (unsigned)(puglview->hints[PUGL_RED_BITS] + - puglview->hints[PUGL_BLUE_BITS] + - puglview->hints[PUGL_GREEN_BITS] + - puglview->hints[PUGL_ALPHA_BITS]); - - NSOpenGLPixelFormatAttribute pixelAttribs[17] = { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAAccelerated, - NSOpenGLPFAOpenGLProfile, profile, - NSOpenGLPFAColorSize, colorSize, - NSOpenGLPFADepthSize, (unsigned)puglview->hints[PUGL_DEPTH_BITS], - NSOpenGLPFAStencilSize, (unsigned)puglview->hints[PUGL_STENCIL_BITS], - NSOpenGLPFAMultisample, samples ? 1 : 0, - NSOpenGLPFASampleBuffers, samples ? 1 : 0, - NSOpenGLPFASamples, samples, - 0}; - - NSOpenGLPixelFormat* pixelFormat = [ - [NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs]; - - if (pixelFormat) { - self = [super initWithFrame:frame pixelFormat:pixelFormat]; - [pixelFormat release]; - } else { - self = [super initWithFrame:frame]; - } - - [self setWantsBestResolutionOpenGLSurface:YES]; - - if (self) { - [[self openGLContext] makeCurrentContext]; - [self reshape]; - } - return self; -} - -- (void) reshape -{ - PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; - - [super reshape]; - [wrapper setReshaped]; -} - -- (void) drawRect:(NSRect)rect -{ - PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; - [wrapper dispatchExpose:rect]; -} - -@end - -static PuglStatus -puglMacGlCreate(PuglView* view) -{ - PuglInternals* impl = view->impl; - PuglOpenGLView* drawView = [PuglOpenGLView alloc]; - - drawView->puglview = view; - [drawView initWithFrame:[impl->wrapperView bounds]]; - if (view->hints[PUGL_RESIZABLE]) { - [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - } else { - [drawView setAutoresizingMask:NSViewNotSizable]; - } - - impl->drawView = drawView; - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacGlDestroy(PuglView* view) -{ - PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; - - [drawView removeFromSuperview]; - [drawView release]; - - view->impl->drawView = nil; - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacGlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose)) -{ - PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; - - [[drawView openGLContext] makeCurrentContext]; - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacGlLeave(PuglView* view, const PuglEventExpose* expose) -{ - PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; - - if (expose) { - [[drawView openGLContext] flushBuffer]; - } - - [NSOpenGLContext clearCurrentContext]; - - return PUGL_SUCCESS; -} - -PuglGlFunc -puglGetProcAddress(const char *name) -{ - CFBundleRef framework = - CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); - - CFStringRef symbol = CFStringCreateWithCString( - kCFAllocatorDefault, name, kCFStringEncodingASCII); - - PuglGlFunc func = (PuglGlFunc)CFBundleGetFunctionPointerForName( - framework, symbol); - - CFRelease(symbol); - - return func; -} - -const PuglBackend* puglGlBackend(void) -{ - static const PuglBackend backend = {puglStubConfigure, - puglMacGlCreate, - puglMacGlDestroy, - puglMacGlEnter, - puglMacGlLeave, - puglStubGetContext}; - - return &backend; -} diff --git a/pugl/detail/mac_stub.m b/pugl/detail/mac_stub.m deleted file mode 100644 index 8271735..0000000 --- a/pugl/detail/mac_stub.m +++ /dev/null @@ -1,96 +0,0 @@ -/* - 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 - 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 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; -} - -- (void) resizeWithOldSuperviewSize:(NSSize)oldSize -{ - PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; - - [super resizeWithOldSuperviewSize:oldSize]; - [wrapper setReshaped]; -} - -- (void) drawRect:(NSRect)rect -{ - PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; - - [wrapper dispatchExpose:rect]; -} - -@end - -static PuglStatus -puglMacStubCreate(PuglView* view) -{ - PuglInternals* impl = view->impl; - PuglStubView* drawView = [PuglStubView alloc]; - - drawView->puglview = view; - [drawView initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)]; - if (view->hints[PUGL_RESIZABLE]) { - [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - } else { - [drawView setAutoresizingMask:NSViewNotSizable]; - } - - impl->drawView = drawView; - return PUGL_SUCCESS; -} - -static PuglStatus -puglMacStubDestroy(PuglView* view) -{ - PuglStubView* const drawView = (PuglStubView*)view->impl->drawView; - - [drawView removeFromSuperview]; - [drawView release]; - - view->impl->drawView = nil; - return PUGL_SUCCESS; -} - -const PuglBackend* -puglStubBackend(void) -{ - static const PuglBackend backend = {puglStubConfigure, - puglMacStubCreate, - puglMacStubDestroy, - puglStubEnter, - puglStubLeave, - puglStubGetContext}; - - return &backend; -} diff --git a/pugl/detail/stub.h b/pugl/detail/stub.h deleted file mode 100644 index acd3181..0000000 --- a/pugl/detail/stub.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - 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/detail/types.h b/pugl/detail/types.h deleted file mode 100644 index edd2bd0..0000000 --- a/pugl/detail/types.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - 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 types.h - @brief Shared internal type definitions. -*/ - -#ifndef PUGL_DETAIL_TYPES_H -#define PUGL_DETAIL_TYPES_H - -#include "pugl/pugl.h" - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -// Unused parameter macro to suppresses warnings and make it impossible to use -#if defined(__cplusplus) -# define PUGL_UNUSED(name) -#elif defined(__GNUC__) -# define PUGL_UNUSED(name) name##_unused __attribute__((__unused__)) -#else -# define PUGL_UNUSED(name) name -#endif - -/// Platform-specific world internals -typedef struct PuglWorldInternalsImpl PuglWorldInternals; - -/// Platform-specific view internals -typedef struct PuglInternalsImpl PuglInternals; - -/// View hints -typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; - -/// Blob of arbitrary data -typedef struct { - void* data; ///< Dynamically allocated data - size_t len; ///< Length of data in bytes -} PuglBlob; - -/// Cross-platform view definition -struct PuglViewImpl { - PuglWorld* world; - const PuglBackend* backend; - PuglInternals* impl; - PuglHandle handle; - PuglEventFunc eventFunc; - char* title; - PuglBlob clipboard; - PuglNativeView parent; - uintptr_t transientParent; - PuglRect frame; - PuglEventConfigure lastConfigure; - PuglHints hints; - int defaultWidth; - int defaultHeight; - int minWidth; - int minHeight; - int maxWidth; - int maxHeight; - int minAspectX; - int minAspectY; - int maxAspectX; - int maxAspectY; - bool visible; -}; - -/// Cross-platform world definition -struct PuglWorldImpl { - PuglWorldInternals* impl; - PuglWorldHandle handle; - PuglLogFunc logFunc; - char* className; - double startTime; - size_t numViews; - PuglView** views; - PuglLogLevel logLevel; -}; - -/// Opaque surface used by graphics backend -typedef void PuglSurface; - -/// Graphics backend interface -struct PuglBackendImpl { - /// Get visual information from display and setup view as necessary - PuglStatus (*configure)(PuglView*); - - /// Create surface and drawing context - PuglStatus (*create)(PuglView*); - - /// Destroy surface and drawing context - PuglStatus (*destroy)(PuglView*); - - /// 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 - PuglStatus (*leave)(PuglView*, const PuglEventExpose*); - - /// Return the puglGetContext() handle for the application, if any - void* (*getContext)(PuglView*); -}; - -#endif // PUGL_DETAIL_TYPES_H diff --git a/pugl/detail/win.c b/pugl/detail/win.c deleted file mode 100644 index 078b0a0..0000000 --- a/pugl/detail/win.c +++ /dev/null @@ -1,1152 +0,0 @@ -/* - 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 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" - -#include <windows.h> -#include <windowsx.h> - -#include <math.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <wctype.h> - -#ifndef WM_MOUSEWHEEL -# define WM_MOUSEWHEEL 0x020A -#endif -#ifndef WM_MOUSEHWHEEL -# define WM_MOUSEHWHEEL 0x020E -#endif -#ifndef WHEEL_DELTA -# define WHEEL_DELTA 120 -#endif -#ifndef GWLP_USERDATA -# define GWLP_USERDATA (-21) -#endif - -#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) -#define PUGL_LOCAL_MARK_MSG (WM_USER + 51) -#define PUGL_LOCAL_CLIENT_MSG (WM_USER + 52) -#define PUGL_RESIZE_TIMER_ID 9461 -#define PUGL_USER_TIMER_MIN 9470 - -typedef BOOL (WINAPI *PFN_SetProcessDPIAware)(void); - -LRESULT CALLBACK -wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); - -static wchar_t* -puglUtf8ToWideChar(const char* const utf8) -{ - const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); - if (len > 0) { - wchar_t* result = (wchar_t*)calloc((size_t)len, sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, utf8, -1, result, len); - return result; - } - - return NULL; -} - -static char* -puglWideCharToUtf8(const wchar_t* const wstr, size_t* len) -{ - int n = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); - if (n > 0) { - char* result = (char*)calloc((size_t)n, sizeof(char)); - WideCharToMultiByte(CP_UTF8, 0, wstr, -1, result, n, NULL, NULL); - *len = (size_t)n; - return result; - } - - return NULL; -} - -static bool -puglRegisterWindowClass(const char* name) -{ - WNDCLASSEX wc = { 0 }; - if (GetClassInfoEx(GetModuleHandle(NULL), name, &wc)) { - return true; // Already registered - } - - wc.cbSize = sizeof(wc); - wc.style = CS_OWNDC; - wc.lpfnWndProc = wndProc; - wc.hInstance = GetModuleHandle(NULL); - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wc.lpszClassName = name; - - return RegisterClassEx(&wc); -} - -PuglWorldInternals* -puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type), - PuglWorldFlags PUGL_UNUSED(flags)) -{ - PuglWorldInternals* impl = (PuglWorldInternals*)calloc( - 1, sizeof(PuglWorldInternals)); - if (!impl) { - return NULL; - } - - HMODULE user32 = LoadLibrary("user32.dll"); - if (user32) { - PFN_SetProcessDPIAware SetProcessDPIAware = - (PFN_SetProcessDPIAware)GetProcAddress( - user32, "SetProcessDPIAware"); - if (SetProcessDPIAware) { - SetProcessDPIAware(); - } - - FreeLibrary(user32); - } - - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - impl->timerFrequency = (double)frequency.QuadPart; - - return impl; -} - -void* -puglGetNativeWorld(PuglWorld* PUGL_UNUSED(world)) -{ - return GetModuleHandle(NULL); -} - -PuglInternals* -puglInitViewInternals(void) -{ - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); -} - -static PuglStatus -puglPollWinEvents(PuglWorld* world, const double timeout) -{ - (void)world; - - if (timeout < 0) { - WaitMessage(); - } else { - MsgWaitForMultipleObjects( - 0, NULL, FALSE, (DWORD)(timeout * 1e3), QS_ALLEVENTS); - } - return PUGL_SUCCESS; -} - -PuglStatus -puglRealize(PuglView* view) -{ - PuglInternals* impl = view->impl; - if (impl->hwnd) { - return PUGL_FAILURE; - } - - // Getting depth from the display mode seems tedious, just set usual values - if (view->hints[PUGL_RED_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_RED_BITS] = 8; - } - if (view->hints[PUGL_BLUE_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_BLUE_BITS] = 8; - } - if (view->hints[PUGL_GREEN_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_GREEN_BITS] = 8; - } - if (view->hints[PUGL_ALPHA_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_ALPHA_BITS] = 8; - } - - // Get refresh rate for resize draw timer - DEVMODEA devMode = {0}; - EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devMode); - view->hints[PUGL_REFRESH_RATE] = (int)devMode.dmDisplayFrequency; - - // Register window class if necessary - if (!puglRegisterWindowClass(view->world->className)) { - return PUGL_REGISTRATION_FAILED; - } - - if (!view->backend || !view->backend->configure) { - return PUGL_BAD_BACKEND; - } - - PuglStatus st; - if ((st = view->backend->configure(view))) { - return st; - } else if ((st = view->backend->create(view))) { - return st; - } - - if (view->title) { - puglSetWindowTitle(view, view->title); - } - - view->impl->cursor = LoadCursor(NULL, IDC_ARROW); - - puglSetFrame(view, view->frame); - SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); - - puglDispatchSimpleEvent(view, PUGL_CREATE); - - return PUGL_SUCCESS; -} - -PuglStatus -puglShowWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - ShowWindow(impl->hwnd, SW_SHOWNORMAL); - SetFocus(impl->hwnd); - return PUGL_SUCCESS; -} - -PuglStatus -puglHideWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - ShowWindow(impl->hwnd, SW_HIDE); - return PUGL_SUCCESS; -} - -void -puglFreeViewInternals(PuglView* view) -{ - if (view) { - if (view->backend) { - view->backend->destroy(view); - } - - ReleaseDC(view->impl->hwnd, view->impl->hdc); - DestroyWindow(view->impl->hwnd); - free(view->impl); - } -} - -void -puglFreeWorldInternals(PuglWorld* world) -{ - UnregisterClass(world->className, NULL); - free(world->impl); -} - -static PuglKey -keySymToSpecial(WPARAM sym) -{ - // clang-format off - switch (sym) { - case VK_F1: return PUGL_KEY_F1; - case VK_F2: return PUGL_KEY_F2; - case VK_F3: return PUGL_KEY_F3; - case VK_F4: return PUGL_KEY_F4; - case VK_F5: return PUGL_KEY_F5; - case VK_F6: return PUGL_KEY_F6; - case VK_F7: return PUGL_KEY_F7; - case VK_F8: return PUGL_KEY_F8; - case VK_F9: return PUGL_KEY_F9; - case VK_F10: return PUGL_KEY_F10; - case VK_F11: return PUGL_KEY_F11; - case VK_F12: return PUGL_KEY_F12; - case VK_BACK: return PUGL_KEY_BACKSPACE; - case VK_DELETE: return PUGL_KEY_DELETE; - case VK_LEFT: return PUGL_KEY_LEFT; - case VK_UP: return PUGL_KEY_UP; - case VK_RIGHT: return PUGL_KEY_RIGHT; - case VK_DOWN: return PUGL_KEY_DOWN; - case VK_PRIOR: return PUGL_KEY_PAGE_UP; - case VK_NEXT: return PUGL_KEY_PAGE_DOWN; - case VK_HOME: return PUGL_KEY_HOME; - case VK_END: return PUGL_KEY_END; - case VK_INSERT: return PUGL_KEY_INSERT; - case VK_SHIFT: - case VK_LSHIFT: return PUGL_KEY_SHIFT_L; - case VK_RSHIFT: return PUGL_KEY_SHIFT_R; - case VK_CONTROL: - case VK_LCONTROL: return PUGL_KEY_CTRL_L; - case VK_RCONTROL: return PUGL_KEY_CTRL_R; - case VK_MENU: - case VK_LMENU: return PUGL_KEY_ALT_L; - case VK_RMENU: return PUGL_KEY_ALT_R; - case VK_LWIN: return PUGL_KEY_SUPER_L; - case VK_RWIN: return PUGL_KEY_SUPER_R; - case VK_CAPITAL: return PUGL_KEY_CAPS_LOCK; - case VK_SCROLL: return PUGL_KEY_SCROLL_LOCK; - case VK_NUMLOCK: return PUGL_KEY_NUM_LOCK; - case VK_SNAPSHOT: return PUGL_KEY_PRINT_SCREEN; - case VK_PAUSE: return PUGL_KEY_PAUSE; - } - // clang-format on - - return (PuglKey)0; -} - -static uint32_t -getModifiers(void) -{ - // clang-format off - return (((GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0u) | - ((GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0u) | - ((GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0u) | - ((GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0u) | - ((GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0u)); - // clang-format on -} - -static void -initMouseEvent(PuglEvent* event, - PuglView* view, - int button, - bool press, - LPARAM lParam) -{ - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ClientToScreen(view->impl->hwnd, &pt); - - if (press) { - SetCapture(view->impl->hwnd); - } else { - ReleaseCapture(); - } - - event->button.time = GetMessageTime() / 1e3; - event->button.type = press ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE; - event->button.x = GET_X_LPARAM(lParam); - event->button.y = GET_Y_LPARAM(lParam); - event->button.xRoot = pt.x; - event->button.yRoot = pt.y; - event->button.state = getModifiers(); - event->button.button = (uint32_t)button; -} - -static void -initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam) -{ - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(view->impl->hwnd, &pt); - - event->scroll.time = GetMessageTime() / 1e3; - event->scroll.type = PUGL_SCROLL; - event->scroll.x = pt.x; - event->scroll.y = pt.y; - event->scroll.xRoot = GET_X_LPARAM(lParam); - event->scroll.yRoot = GET_Y_LPARAM(lParam); - event->scroll.state = getModifiers(); - event->scroll.dx = 0; - event->scroll.dy = 0; -} - -/// Return the code point for buf, or the replacement character on error -static uint32_t -puglDecodeUTF16(const wchar_t* buf, const int len) -{ - const uint32_t c0 = buf[0]; - const uint32_t c1 = buf[0]; - if (c0 >= 0xD800 && c0 < 0xDC00) { - if (len < 2) { - return 0xFFFD; // Surrogate, but length is only 1 - } else if (c1 >= 0xDC00 && c1 <= 0xDFFF) { - return ((c0 & 0x03FF) << 10) + (c1 & 0x03FF) + 0x10000; - } - - return 0xFFFD; // Unpaired surrogates - } - - return c0; -} - -static void -initKeyEvent(PuglEventKey* event, - PuglView* view, - bool press, - WPARAM wParam, - LPARAM lParam) -{ - POINT rpos = { 0, 0 }; - GetCursorPos(&rpos); - - POINT cpos = { rpos.x, rpos.y }; - ScreenToClient(view->impl->hwnd, &rpos); - - const unsigned scode = (uint32_t)((lParam & 0xFF0000) >> 16); - const unsigned vkey = ((wParam == VK_SHIFT) - ? MapVirtualKey(scode, MAPVK_VSC_TO_VK_EX) - : (unsigned)wParam); - - const unsigned vcode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC); - const unsigned kchar = MapVirtualKey(vkey, MAPVK_VK_TO_CHAR); - const bool dead = kchar >> (sizeof(UINT) * 8 - 1) & 1; - const bool ext = lParam & 0x01000000; - - event->type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; - event->time = GetMessageTime() / 1e3; - event->state = getModifiers(); - event->xRoot = rpos.x; - event->yRoot = rpos.y; - event->x = cpos.x; - event->y = cpos.y; - event->keycode = (uint32_t)((lParam & 0xFF0000) >> 16); - event->key = 0; - - const PuglKey special = keySymToSpecial(vkey); - if (special) { - if (ext && (special == PUGL_KEY_CTRL || special == PUGL_KEY_ALT)) { - event->key = special + 1u; // Right hand key - } else { - event->key = special; - } - } else if (!dead) { - // Translate unshifted key - BYTE keyboardState[256] = {0}; - wchar_t buf[5] = {0}; - - event->key = puglDecodeUTF16( - buf, ToUnicode(vkey, vcode, keyboardState, buf, 4, 1 << 2)); - } -} - -static void -initCharEvent(PuglEvent* event, PuglView* view, WPARAM wParam, LPARAM lParam) -{ - const wchar_t utf16[2] = { wParam & 0xFFFF, (wParam >> 16) & 0xFFFF }; - - initKeyEvent(&event->key, view, true, wParam, lParam); - event->type = PUGL_TEXT; - event->text.character = puglDecodeUTF16(utf16, 2); - - if (!WideCharToMultiByte( - CP_UTF8, 0, utf16, 2, event->text.string, 8, NULL, NULL)) { - memset(event->text.string, 0, 8); - } -} - -static bool -ignoreKeyEvent(PuglView* view, LPARAM lParam) -{ - return view->hints[PUGL_IGNORE_KEY_REPEAT] && (lParam & (1 << 30)); -} - -static RECT -handleConfigure(PuglView* view, PuglEvent* event) -{ - RECT rect; - GetClientRect(view->impl->hwnd, &rect); - MapWindowPoints(view->impl->hwnd, - view->parent ? (HWND)view->parent : HWND_DESKTOP, - (LPPOINT)&rect, - 2); - - const LONG width = rect.right - rect.left; - const LONG height = rect.bottom - rect.top; - - view->frame.x = rect.left; - view->frame.y = rect.top; - - event->configure.type = PUGL_CONFIGURE; - event->configure.x = view->frame.x; - event->configure.y = view->frame.y; - event->configure.width = width; - event->configure.height = height; - - if (view->frame.width != width || view->frame.height != height) { - view->frame.width = width; - view->frame.height = height; - } - - return rect; -} - -static void -handleCrossing(PuglView* view, const PuglEventType type, POINT pos) -{ - POINT root_pos = pos; - ClientToScreen(view->impl->hwnd, &root_pos); - - const PuglEventCrossing ev = { - type, - 0, - GetMessageTime() / 1e3, - (double)pos.x, - (double)pos.y, - (double)root_pos.x, - (double)root_pos.y, - getModifiers(), - PUGL_CROSSING_NORMAL, - }; - - puglDispatchEvent(view, (const PuglEvent*)&ev); -} - -static void -constrainAspect(const PuglView* const view, - RECT* const size, - const WPARAM wParam) -{ - const float minA = (float)view->minAspectX / (float)view->minAspectY; - const float maxA = (float)view->maxAspectX / (float)view->maxAspectY; - const int w = size->right - size->left; - const int h = size->bottom - size->top; - const float a = (float)w / (float)h; - - switch (wParam) { - case WMSZ_TOP: - size->top = (a < minA ? (LONG)(size->bottom - w * minA) : - a > maxA ? (LONG)(size->bottom - w * maxA) : - size->top); - break; - case WMSZ_TOPRIGHT: - case WMSZ_RIGHT: - case WMSZ_BOTTOMRIGHT: - size->right = (a < minA ? (LONG)(size->left + h * minA) : - a > maxA ? (LONG)(size->left + h * maxA) : - size->right); - break; - case WMSZ_BOTTOM: - size->bottom = (a < minA ? (LONG)(size->top + w * minA) : - a > maxA ? (LONG)(size->top + w * maxA) : - size->bottom); - break; - case WMSZ_BOTTOMLEFT: - case WMSZ_LEFT: - case WMSZ_TOPLEFT: - size->left = (a < minA ? (LONG)(size->right - h * minA) : - a > maxA ? (LONG)(size->right - h * maxA) : - size->left); - break; - } -} - -static LRESULT -handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) -{ - PuglEvent event; - void* dummy_ptr = NULL; - RECT rect; - MINMAXINFO* mmi; - POINT pt; - - memset(&event, 0, sizeof(event)); - - event.any.type = PUGL_NOTHING; - if (InSendMessageEx(dummy_ptr)) { - event.any.flags |= PUGL_IS_SEND_EVENT; - } - - switch (message) { - case WM_SETCURSOR: - if (LOWORD(lParam) == HTCLIENT) { - SetCursor(view->impl->cursor); - } else { - return DefWindowProc(view->impl->hwnd, message, wParam, lParam); - } - break; - case WM_SHOWWINDOW: - if (wParam) { - handleConfigure(view, &event); - puglDispatchEvent(view, &event); - event.type = PUGL_NOTHING; - - RedrawWindow(view->impl->hwnd, NULL, NULL, - RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT); - } - - if ((bool)wParam != view->visible) { - view->visible = wParam; - event.any.type = wParam ? PUGL_MAP : PUGL_UNMAP; - } - break; - case WM_SIZE: - handleConfigure(view, &event); - InvalidateRect(view->impl->hwnd, NULL, false); - break; - case WM_SIZING: - if (view->minAspectX) { - constrainAspect(view, (RECT*)lParam, wParam); - return TRUE; - } - break; - case WM_ENTERSIZEMOVE: - case WM_ENTERMENULOOP: - view->impl->resizing = true; - SetTimer(view->impl->hwnd, - PUGL_RESIZE_TIMER_ID, - 1000 / (UINT)view->hints[PUGL_REFRESH_RATE], - NULL); - break; - case WM_TIMER: - if (wParam == PUGL_RESIZE_TIMER_ID) { - RedrawWindow(view->impl->hwnd, NULL, NULL, - RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT); - } else if (wParam >= PUGL_USER_TIMER_MIN) { - PuglEvent ev = {{PUGL_TIMER, 0}}; - ev.timer.id = wParam - PUGL_USER_TIMER_MIN; - puglDispatchEvent(view, &ev); - } - break; - case WM_EXITSIZEMOVE: - case WM_EXITMENULOOP: - KillTimer(view->impl->hwnd, PUGL_RESIZE_TIMER_ID); - view->impl->resizing = false; - puglPostRedisplay(view); - break; - case WM_GETMINMAXINFO: - 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); - event.expose.type = PUGL_EXPOSE; - event.expose.x = rect.left; - event.expose.y = rect.top; - event.expose.width = rect.right - rect.left; - event.expose.height = rect.bottom - rect.top; - break; - case WM_ERASEBKGND: - return true; - case WM_MOUSEMOVE: - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - - if (!view->impl->mouseTracked) { - TRACKMOUSEEVENT tme = {0}; - - tme.cbSize = sizeof(tme); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = view->impl->hwnd; - TrackMouseEvent(&tme); - - handleCrossing(view, PUGL_POINTER_IN, pt); - view->impl->mouseTracked = true; - } - - ClientToScreen(view->impl->hwnd, &pt); - event.motion.type = PUGL_MOTION; - event.motion.time = GetMessageTime() / 1e3; - event.motion.x = GET_X_LPARAM(lParam); - event.motion.y = GET_Y_LPARAM(lParam); - event.motion.xRoot = pt.x; - event.motion.yRoot = pt.y; - event.motion.state = getModifiers(); - break; - case WM_MOUSELEAVE: - GetCursorPos(&pt); - ScreenToClient(view->impl->hwnd, &pt); - handleCrossing(view, PUGL_POINTER_OUT, pt); - view->impl->mouseTracked = false; - break; - case WM_LBUTTONDOWN: - initMouseEvent(&event, view, 1, true, lParam); - break; - case WM_MBUTTONDOWN: - initMouseEvent(&event, view, 2, true, lParam); - break; - case WM_RBUTTONDOWN: - initMouseEvent(&event, view, 3, true, lParam); - break; - case WM_LBUTTONUP: - initMouseEvent(&event, view, 1, false, lParam); - break; - case WM_MBUTTONUP: - initMouseEvent(&event, view, 2, false, lParam); - break; - case WM_RBUTTONUP: - initMouseEvent(&event, view, 3, false, lParam); - break; - 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)) { - initKeyEvent(&event.key, view, true, wParam, lParam); - } - break; - case WM_KEYUP: - initKeyEvent(&event.key, view, false, wParam, lParam); - break; - case WM_CHAR: - initCharEvent(&event, view, wParam, lParam); - break; - case WM_SETFOCUS: - event.type = PUGL_FOCUS_IN; - break; - case WM_KILLFOCUS: - event.type = PUGL_FOCUS_OUT; - break; - case WM_SYSKEYDOWN: - initKeyEvent(&event.key, view, true, wParam, lParam); - break; - case WM_SYSKEYUP: - initKeyEvent(&event.key, view, false, wParam, lParam); - break; - case WM_SYSCHAR: - return TRUE; - case PUGL_LOCAL_CLIENT_MSG: - event.client.type = PUGL_CLIENT; - event.client.data1 = (uintptr_t)wParam; - event.client.data2 = (uintptr_t)lParam; - break; - case WM_QUIT: - case PUGL_LOCAL_CLOSE_MSG: - event.any.type = PUGL_CLOSE; - break; - default: - return DefWindowProc(view->impl->hwnd, message, wParam, lParam); - } - - puglDispatchEvent(view, &event); - - return 0; -} - -PuglStatus -puglGrabFocus(PuglView* view) -{ - SetFocus(view->impl->hwnd); - return PUGL_SUCCESS; -} - -bool -puglHasFocus(const PuglView* view) -{ - return GetFocus() == view->impl->hwnd; -} - -PuglStatus -puglRequestAttention(PuglView* view) -{ - FLASHWINFO info = {sizeof(FLASHWINFO), - view->impl->hwnd, - FLASHW_ALL|FLASHW_TIMERNOFG, - 1, - 0}; - - FlashWindowEx(&info); - - return PUGL_SUCCESS; -} - -PuglStatus -puglStartTimer(PuglView* view, uintptr_t id, double timeout) -{ - const UINT msec = (UINT)floor(timeout * 1000.0); - - return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL) - ? PUGL_SUCCESS - : PUGL_UNKNOWN_ERROR); -} - -PuglStatus -puglStopTimer(PuglView* view, uintptr_t id) -{ - return (KillTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id) - ? PUGL_SUCCESS - : PUGL_UNKNOWN_ERROR); -} - -PuglStatus -puglSendEvent(PuglView* view, const PuglEvent* event) -{ - if (event->type == PUGL_CLIENT) { - PostMessage(view->impl->hwnd, - PUGL_LOCAL_CLIENT_MSG, - (WPARAM)event->client.data1, - (LPARAM)event->client.data2); - - return PUGL_SUCCESS; - } - - return PUGL_UNSUPPORTED_TYPE; -} - -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglWaitForEvent(PuglView* PUGL_UNUSED(view)) -{ - WaitMessage(); - return PUGL_SUCCESS; -} -#endif - -static PuglStatus -puglDispatchViewEvents(PuglView* view) -{ - /* Windows has no facility to process only currently queued messages, which - causes the event loop to run forever in cases like mouse movement where - the queue is constantly being filled with new messages. To work around - this, we post a message to ourselves before starting, record its time - when it is received, then break the loop on the first message that was - created afterwards. */ - - long markTime = 0; - MSG msg; - while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { - if (msg.message == PUGL_LOCAL_MARK_MSG) { - markTime = GetMessageTime(); - } else { - TranslateMessage(&msg); - DispatchMessage(&msg); - if (markTime != 0 && GetMessageTime() > markTime) { - break; - } - } - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglDispatchWinEvents(PuglWorld* world) -{ - for (size_t i = 0; i < world->numViews; ++i) { - PostMessage(world->views[i]->impl->hwnd, PUGL_LOCAL_MARK_MSG, 0, 0); - } - - for (size_t i = 0; i < world->numViews; ++i) { - puglDispatchViewEvents(world->views[i]); - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglUpdate(PuglWorld* world, double timeout) -{ - const double startTime = puglGetTime(world); - PuglStatus st = PUGL_SUCCESS; - - if (timeout < 0.0) { - st = puglPollWinEvents(world, timeout); - st = st ? st : puglDispatchWinEvents(world); - } else if (timeout == 0.0) { - st = puglDispatchWinEvents(world); - } else { - const double endTime = startTime + timeout - 0.001; - for (double t = startTime; t < endTime; t = puglGetTime(world)) { - if ((st = puglPollWinEvents(world, endTime - t)) || - (st = puglDispatchWinEvents(world))) { - break; - } - } - } - - for (size_t i = 0; i < world->numViews; ++i) { - if (world->views[i]->visible) { - puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE); - } - - UpdateWindow(world->views[i]->impl->hwnd); - } - - return st; -} - -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglProcessEvents(PuglView* view) -{ - return puglUpdate(view->world, 0.0); -} -#endif - -LRESULT CALLBACK -wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch (message) { - case WM_CREATE: - PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); - return 0; - case WM_CLOSE: - PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); - return 0; - case WM_DESTROY: - return 0; - default: - if (view && hwnd == view->impl->hwnd) { - return handleMessage(view, message, wParam, lParam); - } else { - return DefWindowProc(hwnd, message, wParam, lParam); - } - } -} - -double -puglGetTime(const PuglWorld* world) -{ - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - return ((double)count.QuadPart / world->impl->timerFrequency - - world->startTime); -} - -PuglStatus -puglPostRedisplay(PuglView* view) -{ - InvalidateRect(view->impl->hwnd, NULL, false); - return PUGL_SUCCESS; -} - -PuglStatus -puglPostRedisplayRect(PuglView* view, const PuglRect rect) -{ - const RECT r = {(long)floor(rect.x), - (long)floor(rect.y), - (long)ceil(rect.x + rect.width), - (long)ceil(rect.y + rect.height)}; - - InvalidateRect(view->impl->hwnd, &r, false); - - return PUGL_SUCCESS; -} - -PuglNativeView -puglGetNativeWindow(PuglView* view) -{ - return (PuglNativeView)view->impl->hwnd; -} - -PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) -{ - puglSetString(&view->title, title); - - if (view->impl->hwnd) { - wchar_t* wtitle = puglUtf8ToWideChar(title); - if (wtitle) { - SetWindowTextW(view->impl->hwnd, wtitle); - free(wtitle); - } - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglSetFrame(PuglView* view, const PuglRect frame) -{ - view->frame = frame; - - if (view->impl->hwnd) { - RECT rect = { (long)frame.x, - (long)frame.y, - (long)frame.x + (long)frame.width, - (long)frame.y + (long)frame.height }; - - AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), - FALSE, - puglWinGetWindowExFlags(view)); - - if (!SetWindowPos(view->impl->hwnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER)) { - return PUGL_UNKNOWN_ERROR; - } - } - - 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; - return PUGL_SUCCESS; -} - -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, - const int maxX, - const int maxY) -{ - view->minAspectX = minX; - view->minAspectY = minY; - view->maxAspectX = maxX; - view->maxAspectY = maxY; - 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, - size_t* const len) -{ - PuglInternals* const impl = view->impl; - - if (!IsClipboardFormatAvailable(CF_UNICODETEXT) || - !OpenClipboard(impl->hwnd)) { - return NULL; - } - - HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); - wchar_t* wstr = mem ? (wchar_t*)GlobalLock(mem) : NULL; - if (!wstr) { - CloseClipboard(); - return NULL; - } - - free(view->clipboard.data); - view->clipboard.data = puglWideCharToUtf8(wstr, &view->clipboard.len); - GlobalUnlock(mem); - CloseClipboard(); - - return puglGetInternalClipboard(view, type, len); -} - -PuglStatus -puglSetClipboard(PuglView* const view, - const char* const type, - const void* const data, - const size_t len) -{ - PuglInternals* const impl = view->impl; - - PuglStatus st = puglSetInternalClipboard(view, type, data, len); - if (st) { - return st; - } else if (!OpenClipboard(impl->hwnd)) { - return PUGL_UNKNOWN_ERROR; - } - - // Measure string and allocate global memory for clipboard - const char* str = (const char*)data; - const int wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); - HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wlen + 1) * sizeof(wchar_t)); - if (!mem) { - CloseClipboard(); - return PUGL_UNKNOWN_ERROR; - } - - // Lock global memory - wchar_t* wstr = (wchar_t*)GlobalLock(mem); - if (!wstr) { - GlobalFree(mem); - CloseClipboard(); - return PUGL_UNKNOWN_ERROR; - } - - // Convert string into global memory and set it as clipboard data - MultiByteToWideChar(CP_UTF8, 0, str, (int)len, wstr, wlen); - wstr[wlen] = 0; - GlobalUnlock(mem); - SetClipboardData(CF_UNICODETEXT, mem); - CloseClipboard(); - 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/detail/win.h b/pugl/detail/win.h deleted file mode 100644 index b0d92e0..0000000 --- a/pugl/detail/win.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 win.h - @brief Shared definitions for Windows implementation. -*/ - -#include "pugl/detail/implementation.h" - -#include <windows.h> - -#include <stdbool.h> - -typedef PIXELFORMATDESCRIPTOR PuglWinPFD; - -struct PuglWorldInternalsImpl { - double timerFrequency; -}; - -struct PuglInternalsImpl { - PuglWinPFD pfd; - int pfId; - HWND hwnd; - HCURSOR cursor; - HDC hdc; - PuglSurface* surface; - bool flashing; - bool resizing; - bool mouseTracked; -}; - -static inline PuglWinPFD -puglWinGetPixelFormatDescriptor(const PuglHints hints) -{ - const int rgbBits = (hints[PUGL_RED_BITS] + // - hints[PUGL_GREEN_BITS] + // - hints[PUGL_BLUE_BITS]); - - const DWORD dwFlags = hints[PUGL_DOUBLE_BUFFER] ? PFD_DOUBLEBUFFER : 0u; - - PuglWinPFD pfd; - ZeroMemory(&pfd, sizeof(pfd)); - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | dwFlags; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = (BYTE)rgbBits; - pfd.cRedBits = (BYTE)hints[PUGL_RED_BITS]; - pfd.cGreenBits = (BYTE)hints[PUGL_GREEN_BITS]; - pfd.cBlueBits = (BYTE)hints[PUGL_BLUE_BITS]; - pfd.cAlphaBits = (BYTE)hints[PUGL_ALPHA_BITS]; - pfd.cDepthBits = (BYTE)hints[PUGL_DEPTH_BITS]; - pfd.cStencilBits = (BYTE)hints[PUGL_STENCIL_BITS]; - pfd.iLayerType = PFD_MAIN_PLANE; - return pfd; -} - -static inline unsigned -puglWinGetWindowFlags(const PuglView* const view) -{ - const bool resizable = view->hints[PUGL_RESIZABLE]; - const unsigned sizeFlags = resizable ? (WS_SIZEBOX | WS_MAXIMIZEBOX) : 0u; - - return (WS_CLIPCHILDREN | WS_CLIPSIBLINGS | - (view->parent - ? WS_CHILD - : (WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | sizeFlags))); -} - -static inline unsigned -puglWinGetWindowExFlags(const PuglView* const view) -{ - return WS_EX_NOINHERITLAYOUT | (view->parent ? 0u : WS_EX_APPWINDOW); -} - -static inline PuglStatus -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 }; - AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); - - // Create window and get drawing context - if (!(*hwnd = CreateWindowEx(winExFlags, className, title, winFlags, - CW_USEDEFAULT, CW_USEDEFAULT, - wr.right-wr.left, wr.bottom-wr.top, - (HWND)parent, NULL, NULL, NULL))) { - return PUGL_REALIZE_FAILED; - } else if (!(*hdc = GetDC(*hwnd))) { - DestroyWindow(*hwnd); - *hwnd = NULL; - return PUGL_REALIZE_FAILED; - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglWinStubConfigure(PuglView* view); - -PuglStatus -puglWinStubEnter(PuglView* view, const PuglEventExpose* expose); - -PuglStatus -puglWinStubLeave(PuglView* view, const PuglEventExpose* expose); diff --git a/pugl/detail/win_cairo.c b/pugl/detail/win_cairo.c deleted file mode 100644 index 1b9afb9..0000000 --- a/pugl/detail/win_cairo.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - 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 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 <cairo-win32.h> -#include <cairo.h> - -#include <stdlib.h> - -typedef struct { - cairo_surface_t* surface; - cairo_t* cr; - HDC drawDc; - HBITMAP drawBitmap; -} PuglWinCairoSurface; - -static PuglStatus -puglWinCairoCreateDrawContext(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; - - surface->drawDc = CreateCompatibleDC(impl->hdc); - surface->drawBitmap = CreateCompatibleBitmap( - impl->hdc, (int)view->frame.width, (int)view->frame.height); - - DeleteObject(SelectObject(surface->drawDc, surface->drawBitmap)); - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinCairoDestroyDrawContext(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; - - DeleteDC(surface->drawDc); - DeleteObject(surface->drawBitmap); - - surface->drawDc = NULL; - surface->drawBitmap = NULL; - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinCairoConfigure(PuglView* view) -{ - const PuglStatus st = puglWinStubConfigure(view); - - if (!st) { - view->impl->surface = (PuglWinCairoSurface*)calloc( - 1, sizeof(PuglWinCairoSurface)); - } - - return st; -} - -static void -puglWinCairoClose(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; - - cairo_surface_destroy(surface->surface); - - surface->surface = NULL; -} - -static PuglStatus -puglWinCairoOpen(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; - - cairo_status_t st = CAIRO_STATUS_SUCCESS; - if (!(surface->surface = cairo_win32_surface_create(surface->drawDc)) || - (st = cairo_surface_status(surface->surface)) || - !(surface->cr = cairo_create(surface->surface)) || - (st = cairo_status(surface->cr))) { - return PUGL_CREATE_CONTEXT_FAILED; - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinCairoDestroy(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; - - puglWinCairoClose(view); - puglWinCairoDestroyDrawContext(view); - free(surface); - impl->surface = NULL; - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinCairoEnter(PuglView* view, const PuglEventExpose* expose) -{ - PuglStatus st = PUGL_SUCCESS; - - if (expose && - !(st = puglWinCairoCreateDrawContext(view)) && - !(st = puglWinCairoOpen(view))) { - PAINTSTRUCT ps; - BeginPaint(view->impl->hwnd, &ps); - } - - return st; -} - -static PuglStatus -puglWinCairoLeave(PuglView* view, const PuglEventExpose* expose) -{ - PuglInternals* const impl = view->impl; - PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; - - if (expose) { - cairo_surface_flush(surface->surface); - BitBlt(impl->hdc, - 0, 0, (int)view->frame.width, (int)view->frame.height, - surface->drawDc, 0, 0, SRCCOPY); - - puglWinCairoClose(view); - puglWinCairoDestroyDrawContext(view); - - PAINTSTRUCT ps; - EndPaint(view->impl->hwnd, &ps); - } - - return PUGL_SUCCESS; -} - -static void* -puglWinCairoGetContext(PuglView* view) -{ - return ((PuglWinCairoSurface*)view->impl->surface)->cr; -} - -const PuglBackend* -puglCairoBackend() -{ - static const PuglBackend backend = {puglWinCairoConfigure, - puglStubCreate, - puglWinCairoDestroy, - puglWinCairoEnter, - puglWinCairoLeave, - puglWinCairoGetContext}; - - return &backend; -} diff --git a/pugl/detail/win_gl.c b/pugl/detail/win_gl.c deleted file mode 100644 index 096c715..0000000 --- a/pugl/detail/win_gl.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - 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 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 <windows.h> - -#include <GL/gl.h> - -#include <stdbool.h> -#include <stdlib.h> - -#define WGL_DRAW_TO_WINDOW_ARB 0x2001 -#define WGL_ACCELERATION_ARB 0x2003 -#define WGL_SUPPORT_OPENGL_ARB 0x2010 -#define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_PIXEL_TYPE_ARB 0x2013 -#define WGL_RED_BITS_ARB 0x2015 -#define WGL_GREEN_BITS_ARB 0x2017 -#define WGL_BLUE_BITS_ARB 0x2019 -#define WGL_ALPHA_BITS_ARB 0x201b -#define WGL_DEPTH_BITS_ARB 0x2022 -#define WGL_STENCIL_BITS_ARB 0x2023 -#define WGL_FULL_ACCELERATION_ARB 0x2027 -#define WGL_TYPE_RGBA_ARB 0x202b -#define WGL_SAMPLE_BUFFERS_ARB 0x2041 -#define WGL_SAMPLES_ARB 0x2042 - -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 - -#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 - -typedef HGLRC (*WglCreateContextAttribs)(HDC, HGLRC, const int*); -typedef BOOL (*WglSwapInterval)(int); -typedef BOOL (*WglChoosePixelFormat)( - HDC, const int*, const FLOAT*, UINT, int*, UINT*); - -typedef struct { - WglChoosePixelFormat wglChoosePixelFormat; - WglCreateContextAttribs wglCreateContextAttribs; - WglSwapInterval wglSwapInterval; -} PuglWinGlProcs; - -typedef struct { - PuglWinGlProcs procs; - HGLRC hglrc; -} PuglWinGlSurface; - -// Struct to manage the fake window used during configuration -typedef struct { - HWND hwnd; - HDC hdc; -} PuglFakeWindow; - -static PuglStatus -puglWinError(PuglFakeWindow* fakeWin, const PuglStatus status) -{ - if (fakeWin->hwnd) { - ReleaseDC(fakeWin->hwnd, fakeWin->hdc); - DestroyWindow(fakeWin->hwnd); - } - - return status; -} - -static PuglWinGlProcs puglWinGlGetProcs(void) -{ - const PuglWinGlProcs procs = { - (WglChoosePixelFormat)( - wglGetProcAddress("wglChoosePixelFormatARB")), - (WglCreateContextAttribs)( - wglGetProcAddress("wglCreateContextAttribsARB")), - (WglSwapInterval)( - wglGetProcAddress("wglSwapIntervalEXT")) - }; - - return procs; -} - -static PuglStatus -puglWinGlConfigure(PuglView* view) -{ - PuglInternals* impl = view->impl; - - // Set attributes to default if they are unset - // (There is no GLX_DONT_CARE equivalent on Windows) - if (view->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_DEPTH_BITS] = 0; - } - if (view->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { - view->hints[PUGL_STENCIL_BITS] = 0; - } - if (view->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { - view->hints[PUGL_SAMPLES] = 1; - } - if (view->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { - view->hints[PUGL_DOUBLE_BUFFER] = 1; - } - if (view->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { - view->hints[PUGL_SWAP_INTERVAL] = 1; - } - - // clang-format off - const int pixelAttrs[] = { - WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_SUPPORT_OPENGL_ARB, GL_TRUE, - WGL_DOUBLE_BUFFER_ARB, view->hints[PUGL_DOUBLE_BUFFER], - WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, - WGL_SAMPLE_BUFFERS_ARB, view->hints[PUGL_SAMPLES] ? 1 : 0, - WGL_SAMPLES_ARB, view->hints[PUGL_SAMPLES], - WGL_RED_BITS_ARB, view->hints[PUGL_RED_BITS], - WGL_GREEN_BITS_ARB, view->hints[PUGL_GREEN_BITS], - WGL_BLUE_BITS_ARB, view->hints[PUGL_BLUE_BITS], - WGL_ALPHA_BITS_ARB, view->hints[PUGL_ALPHA_BITS], - WGL_DEPTH_BITS_ARB, view->hints[PUGL_DEPTH_BITS], - WGL_STENCIL_BITS_ARB, view->hints[PUGL_STENCIL_BITS], - 0, - }; - // clang-format on - - PuglWinGlSurface* const surface = - (PuglWinGlSurface*)calloc(1, sizeof(PuglWinGlSurface)); - impl->surface = surface; - - // Create fake window for getting at GL context - PuglStatus st = PUGL_SUCCESS; - PuglFakeWindow fakeWin = {0, 0}; - static const char* title = "Pugl Configuration"; - if ((st = puglWinCreateWindow(view, title, &fakeWin.hwnd, &fakeWin.hdc))) { - return puglWinError(&fakeWin, st); - } - - // Set pixel format for fake window - const PuglWinPFD fakePfd = puglWinGetPixelFormatDescriptor(view->hints); - const int fakePfId = ChoosePixelFormat(fakeWin.hdc, &fakePfd); - if (!fakePfId) { - return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED); - } else if (!SetPixelFormat(fakeWin.hdc, fakePfId, &fakePfd)) { - return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED); - } - - // Create fake GL context to get at the functions we need - HGLRC fakeRc = wglCreateContext(fakeWin.hdc); - if (!fakeRc) { - return puglWinError(&fakeWin, PUGL_CREATE_CONTEXT_FAILED); - } - - // Enter fake context and get extension functions - wglMakeCurrent(fakeWin.hdc, fakeRc); - surface->procs = puglWinGlGetProcs(); - - if (surface->procs.wglChoosePixelFormat) { - // Choose pixel format based on attributes - UINT numFormats = 0; - if (!surface->procs.wglChoosePixelFormat( - fakeWin.hdc, pixelAttrs, NULL, 1u, &impl->pfId, &numFormats)) { - return puglWinError(&fakeWin, PUGL_SET_FORMAT_FAILED); - } - - DescribePixelFormat( - impl->hdc, impl->pfId, sizeof(impl->pfd), &impl->pfd); - } else { - // Modern extensions not available, use basic pixel format - impl->pfd = fakePfd; - impl->pfId = fakePfId; - } - - // Dispose of fake window and context - wglMakeCurrent(NULL, NULL); - wglDeleteContext(fakeRc); - ReleaseDC(fakeWin.hwnd, fakeWin.hdc); - DestroyWindow(fakeWin.hwnd); - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinGlCreate(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglWinGlSurface* const surface = (PuglWinGlSurface*)impl->surface; - PuglStatus st = PUGL_SUCCESS; - - const int contextAttribs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, - view->hints[PUGL_CONTEXT_VERSION_MAJOR], - - WGL_CONTEXT_MINOR_VERSION_ARB, - view->hints[PUGL_CONTEXT_VERSION_MINOR], - - WGL_CONTEXT_FLAGS_ARB, - (view->hints[PUGL_USE_DEBUG_CONTEXT] ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), - - WGL_CONTEXT_PROFILE_MASK_ARB, - (view->hints[PUGL_USE_COMPAT_PROFILE] - ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB - : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB), - - 0}; - - // Create real window with desired pixel format - if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { - return st; - } else if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) { - ReleaseDC(impl->hwnd, impl->hdc); - DestroyWindow(impl->hwnd); - impl->hwnd = NULL; - impl->hdc = NULL; - return PUGL_SET_FORMAT_FAILED; - } - - // Create GL context - if (surface->procs.wglCreateContextAttribs && - !(surface->hglrc = surface->procs.wglCreateContextAttribs( - impl->hdc, 0, contextAttribs))) { - return PUGL_CREATE_CONTEXT_FAILED; - } else if (!(surface->hglrc = wglCreateContext(impl->hdc))) { - return PUGL_CREATE_CONTEXT_FAILED; - } - - // Enter context and set swap interval - wglMakeCurrent(impl->hdc, surface->hglrc); - const int swapInterval = view->hints[PUGL_SWAP_INTERVAL]; - if (surface->procs.wglSwapInterval && swapInterval != PUGL_DONT_CARE) { - surface->procs.wglSwapInterval(swapInterval); - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinGlDestroy(PuglView* view) -{ - PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface; - if (surface) { - wglMakeCurrent(NULL, NULL); - wglDeleteContext(surface->hglrc); - free(surface); - view->impl->surface = NULL; - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinGlEnter(PuglView* view, const PuglEventExpose* expose) -{ - PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface; - - wglMakeCurrent(view->impl->hdc, surface->hglrc); - - if (expose) { - PAINTSTRUCT ps; - BeginPaint(view->impl->hwnd, &ps); - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglWinGlLeave(PuglView* view, const PuglEventExpose* expose) -{ - if (expose) { - PAINTSTRUCT ps; - EndPaint(view->impl->hwnd, &ps); - SwapBuffers(view->impl->hdc); - } - - wglMakeCurrent(NULL, NULL); - return PUGL_SUCCESS; -} - -PuglGlFunc -puglGetProcAddress(const char* name) -{ - const PuglGlFunc func = (PuglGlFunc)wglGetProcAddress(name); - - /* Windows has the annoying property that wglGetProcAddress returns NULL - for functions from OpenGL 1.1, so we fall back to pulling them directly - from opengl32.dll */ - - return func - ? func - : (PuglGlFunc)GetProcAddress(GetModuleHandle("opengl32.dll"), name); -} - -const PuglBackend* -puglGlBackend(void) -{ - static const PuglBackend backend = {puglWinGlConfigure, - puglWinGlCreate, - puglWinGlDestroy, - puglWinGlEnter, - puglWinGlLeave, - puglStubGetContext}; - - return &backend; -} diff --git a/pugl/detail/win_stub.c b/pugl/detail/win_stub.c deleted file mode 100644 index ab9e6aa..0000000 --- a/pugl/detail/win_stub.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - 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. -*/ - -#include "pugl/pugl_stub.h" - -#include "pugl/detail/stub.h" -#include "pugl/detail/types.h" -#include "pugl/detail/win.h" - -PuglStatus -puglWinStubConfigure(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglStatus st = PUGL_SUCCESS; - - if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { - return st; - } - - impl->pfd = puglWinGetPixelFormatDescriptor(view->hints); - impl->pfId = ChoosePixelFormat(impl->hdc, &impl->pfd); - - if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) { - ReleaseDC(impl->hwnd, impl->hdc); - DestroyWindow(impl->hwnd); - impl->hwnd = NULL; - impl->hdc = NULL; - return PUGL_SET_FORMAT_FAILED; - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglWinStubEnter(PuglView* view, const PuglEventExpose* expose) -{ - if (expose) { - PAINTSTRUCT ps; - BeginPaint(view->impl->hwnd, &ps); - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglWinStubLeave(PuglView* view, const PuglEventExpose* expose) -{ - if (expose) { - PAINTSTRUCT ps; - EndPaint(view->impl->hwnd, &ps); - } - - return PUGL_SUCCESS; -} - -const PuglBackend* -puglStubBackend(void) -{ - static const PuglBackend backend = {puglWinStubConfigure, - puglStubCreate, - puglStubDestroy, - puglWinStubEnter, - puglWinStubLeave, - puglStubGetContext}; - - return &backend; -} diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c deleted file mode 100644 index fd76606..0000000 --- a/pugl/detail/x11.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* - Copyright 2012-2020 David Robillard <d@drobilla.net> - Copyright 2013 Robin Gareus <robin@gareus.org> - Copyright 2011-2012 Ben Loftis, Harrison Consoles - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file x11.c - @brief X11 implementation. -*/ - -#define _POSIX_C_SOURCE 199309L - -#include "pugl/detail/x11.h" - -#include "pugl/detail/implementation.h" -#include "pugl/detail/types.h" -#include "pugl/pugl.h" - -#include <X11/X.h> -#include <X11/Xatom.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <X11/keysym.h> - -#ifdef HAVE_XRANDR -# include <X11/extensions/Xrandr.h> -#endif - -#ifdef HAVE_XSYNC -# include <X11/extensions/sync.h> -# 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> - -#include <math.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef MIN -# define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -# define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -enum WmClientStateMessageAction { - WM_STATE_REMOVE, - WM_STATE_ADD, - WM_STATE_TOGGLE -}; - -static const long eventMask = - (ExposureMask | StructureNotifyMask | - VisibilityChangeMask | FocusChangeMask | - EnterWindowMask | LeaveWindowMask | PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask); - -static bool -puglInitXSync(PuglWorldInternals* impl) -{ -#ifdef HAVE_XSYNC - 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) && - (counters = XSyncListSystemCounters(impl->display, &numCounters))) { - - for (int n = 0; n < numCounters; ++n) { - if (!strcmp(counters[n].name, "SERVERTIME")) { - impl->serverTimeCounter = counters[n].counter; - impl->syncSupported = true; - break; - } - } - - XSyncFreeSystemCounterList(counters); - } -#else - (void)impl; -#endif - - return false; -} - -PuglWorldInternals* -puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags) -{ - if (type == PUGL_PROGRAM && (flags & PUGL_WORLD_THREADS)) { - XInitThreads(); - } - - Display* display = XOpenDisplay(NULL); - if (!display) { - return NULL; - } - - PuglWorldInternals* impl = (PuglWorldInternals*)calloc( - 1, sizeof(PuglWorldInternals)); - - impl->display = display; - - // Intern the various atoms we will need - impl->atoms.CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0); - impl->atoms.UTF8_STRING = XInternAtom(display, "UTF8_STRING", 0); - impl->atoms.WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0); - impl->atoms.WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", 0); - impl->atoms.PUGL_CLIENT_MSG = XInternAtom(display, "_PUGL_CLIENT_MSG", 0); - impl->atoms.NET_WM_NAME = XInternAtom(display, "_NET_WM_NAME", 0); - impl->atoms.NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", 0); - impl->atoms.NET_WM_STATE_DEMANDS_ATTENTION = - XInternAtom(display, "_NET_WM_STATE_DEMANDS_ATTENTION", 0); - - // Open input method - XSetLocaleModifiers(""); - if (!(impl->xim = XOpenIM(display, NULL, NULL, NULL))) { - XSetLocaleModifiers("@im="); - impl->xim = XOpenIM(display, NULL, NULL, NULL); - } - - puglInitXSync(impl); - XFlush(display); - - return impl; -} - -void* -puglGetNativeWorld(PuglWorld* world) -{ - return world->impl->display; -} - -PuglInternals* -puglInitViewInternals(void) -{ - PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); - -#ifdef HAVE_XCURSOR - impl->cursorShape = XC_arrow; -#endif - - return impl; -} - -static PuglStatus -puglPollX11Socket(PuglWorld* world, const double timeout) -{ - if (XPending(world->impl->display) > 0) { - return PUGL_SUCCESS; - } - - const int fd = ConnectionNumber(world->impl->display); - const int nfds = fd + 1; - int ret = 0; - fd_set fds; - FD_ZERO(&fds); // NOLINT - FD_SET(fd, &fds); - - if (timeout < 0.0) { - ret = select(nfds, &fds, NULL, NULL, NULL); - } else { - const long sec = (long)timeout; - const long usec = (long)((timeout - (double)sec) * 1e6); - struct timeval tv = {sec, usec}; - ret = select(nfds, &fds, NULL, NULL, &tv); - } - - return ret < 0 ? PUGL_UNKNOWN_ERROR : PUGL_SUCCESS; -} - -static PuglView* -puglFindView(PuglWorld* world, const Window window) -{ - for (size_t i = 0; i < world->numViews; ++i) { - if (world->views[i]->impl->win == window) { - return world->views[i]; - } - } - - return NULL; -} - -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 = 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; - sizeHints.min_aspect.y = view->minAspectY; - sizeHints.max_aspect.x = view->maxAspectX; - sizeHints.max_aspect.y = view->maxAspectY; - } - } - - 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) -{ - PuglInternals* const impl = view->impl; - if (impl->win) { - return PUGL_FAILURE; - } - - PuglWorld* const world = view->world; - PuglX11Atoms* const atoms = &view->world->impl->atoms; - Display* const display = world->impl->display; - - impl->display = display; - impl->screen = DefaultScreen(display); - - 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); - if (st || !impl->vi) { - view->backend->destroy(view); - return st ? st : PUGL_BACKEND_FAILED; - } - - Window xParent = view->parent ? (Window)view->parent - : RootWindow(display, impl->screen); - - Colormap cmap = XCreateColormap( - display, xParent, impl->vi->visual, AllocNone); - - XSetWindowAttributes attr = {0}; - attr.colormap = cmap; - attr.event_mask = eventMask; - - const Window win = impl->win = XCreateWindow( - display, xParent, - (int)view->frame.x, (int)view->frame.y, - (unsigned)view->frame.width, (unsigned)view->frame.height, - 0, impl->vi->depth, InputOutput, - impl->vi->visual, CWColormap | CWEventMask, &attr); - - if ((st = view->backend->create(view))) { - return st; - } - -#ifdef HAVE_XRANDR - // Set refresh rate hint to the real refresh rate - XRRScreenConfiguration* conf = XRRGetScreenInfo(display, xParent); - short current_rate = XRRConfigCurrentRate(conf); - - view->hints[PUGL_REFRESH_RATE] = current_rate; - XRRFreeScreenConfigInfo(conf); -#endif - - updateSizeHints(view); - - XClassHint classHint = { world->className, world->className }; - XSetClassHint(display, win, &classHint); - - if (view->title) { - puglSetWindowTitle(view, view->title); - } - - if (!view->parent) { - XSetWMProtocols(display, win, &atoms->WM_DELETE_WINDOW, 1); - } - - if (view->transientParent) { - XSetTransientForHint(display, win, (Window)(view->transientParent)); - } - - // Create input context - const XIMStyle im_style = XIMPreeditNothing | XIMStatusNothing; - if (!(impl->xic = XCreateIC(world->impl->xim, - XNInputStyle, im_style, - XNClientWindow, win, - XNFocusWindow, win, - NULL))) { - view->world->logFunc(view->world, - PUGL_LOG_LEVEL_WARNING, - "XCreateID failed\n"); - } - -#ifdef HAVE_XCURSOR - puglDefineCursorShape(view, impl->cursorShape); -#endif - - puglDispatchSimpleEvent(view, PUGL_CREATE); - - return PUGL_SUCCESS; -} - -PuglStatus -puglShowWindow(PuglView* view) -{ - PuglStatus st = PUGL_SUCCESS; - - if (!view->impl->win) { - if ((st = puglRealize(view))) { - return st; - } - } - - XMapRaised(view->impl->display, view->impl->win); - puglPostRedisplay(view); - - return st; -} - -PuglStatus -puglHideWindow(PuglView* view) -{ - XUnmapWindow(view->impl->display, view->impl->win); - return PUGL_SUCCESS; -} - -void -puglFreeViewInternals(PuglView* view) -{ - if (view && view->impl) { - if (view->impl->xic) { - XDestroyIC(view->impl->xic); - } - if (view->backend) { - view->backend->destroy(view); - } - if (view->impl->display) { - XDestroyWindow(view->impl->display, view->impl->win); - } - XFree(view->impl->vi); - free(view->impl); - } -} - -void -puglFreeWorldInternals(PuglWorld* world) -{ - if (world->impl->xim) { - XCloseIM(world->impl->xim); - } - XCloseDisplay(world->impl->display); - free(world->impl->timers); - free(world->impl); -} - -static PuglKey -keySymToSpecial(KeySym sym) -{ - switch (sym) { - case XK_F1: return PUGL_KEY_F1; - case XK_F2: return PUGL_KEY_F2; - case XK_F3: return PUGL_KEY_F3; - case XK_F4: return PUGL_KEY_F4; - case XK_F5: return PUGL_KEY_F5; - case XK_F6: return PUGL_KEY_F6; - case XK_F7: return PUGL_KEY_F7; - case XK_F8: return PUGL_KEY_F8; - case XK_F9: return PUGL_KEY_F9; - case XK_F10: return PUGL_KEY_F10; - case XK_F11: return PUGL_KEY_F11; - case XK_F12: return PUGL_KEY_F12; - case XK_Left: return PUGL_KEY_LEFT; - case XK_Up: return PUGL_KEY_UP; - case XK_Right: return PUGL_KEY_RIGHT; - case XK_Down: return PUGL_KEY_DOWN; - case XK_Page_Up: return PUGL_KEY_PAGE_UP; - case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; - case XK_Home: return PUGL_KEY_HOME; - case XK_End: return PUGL_KEY_END; - case XK_Insert: return PUGL_KEY_INSERT; - case XK_Shift_L: return PUGL_KEY_SHIFT_L; - case XK_Shift_R: return PUGL_KEY_SHIFT_R; - case XK_Control_L: return PUGL_KEY_CTRL_L; - case XK_Control_R: return PUGL_KEY_CTRL_R; - case XK_Alt_L: return PUGL_KEY_ALT_L; - case XK_ISO_Level3_Shift: - case XK_Alt_R: return PUGL_KEY_ALT_R; - case XK_Super_L: return PUGL_KEY_SUPER_L; - case XK_Super_R: return PUGL_KEY_SUPER_R; - case XK_Menu: return PUGL_KEY_MENU; - case XK_Caps_Lock: return PUGL_KEY_CAPS_LOCK; - case XK_Scroll_Lock: return PUGL_KEY_SCROLL_LOCK; - case XK_Num_Lock: return PUGL_KEY_NUM_LOCK; - case XK_Print: return PUGL_KEY_PRINT_SCREEN; - case XK_Pause: return PUGL_KEY_PAUSE; - default: break; - } - return (PuglKey)0; -} - -static int -lookupString(XIC xic, XEvent* xevent, char* str, KeySym* sym) -{ - Status status = 0; - -#ifdef X_HAVE_UTF8_STRING - const int n = Xutf8LookupString(xic, &xevent->xkey, str, 7, sym, &status); -#else - const int n = XmbLookupString(xic, &xevent->xkey, str, 7, sym, &status); -#endif - - return status == XBufferOverflow ? 0 : n; -} - -static void -translateKey(PuglView* view, XEvent* xevent, PuglEvent* event) -{ - const unsigned state = xevent->xkey.state; - const bool filter = XFilterEvent(xevent, None); - - event->key.keycode = xevent->xkey.keycode; - xevent->xkey.state = 0; - - // Lookup unshifted key - char ustr[8] = {0}; - KeySym sym = 0; - const int ufound = XLookupString(&xevent->xkey, ustr, 8, &sym, NULL); - const PuglKey special = keySymToSpecial(sym); - - event->key.key = ((special || ufound <= 0) - ? special - : puglDecodeUTF8((const uint8_t*)ustr)); - - if (xevent->type == KeyPress && !filter && !special) { - // Lookup shifted key for possible text event - xevent->xkey.state = state; - - char sstr[8] = {0}; - const int sfound = lookupString(view->impl->xic, xevent, sstr, &sym); - if (sfound > 0) { - // Dispatch key event now - puglDispatchEvent(view, event); - - // "Return" a text event in its place - event->text.type = PUGL_TEXT; - event->text.character = puglDecodeUTF8((const uint8_t*)sstr); - memcpy(event->text.string, sstr, sizeof(sstr)); - } - } -} - -static uint32_t -translateModifiers(const unsigned xstate) -{ - 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 -translateEvent(PuglView* view, XEvent xevent) -{ - const PuglX11Atoms* atoms = &view->world->impl->atoms; - - PuglEvent event = {{PUGL_NOTHING, 0}}; - event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0; - - switch (xevent.type) { - case ClientMessage: - if (xevent.xclient.message_type == atoms->WM_PROTOCOLS) { - const Atom protocol = (Atom)xevent.xclient.data.l[0]; - if (protocol == atoms->WM_DELETE_WINDOW) { - event.type = PUGL_CLOSE; - } - } else if (xevent.xclient.message_type == atoms->PUGL_CLIENT_MSG) { - event.type = PUGL_CLIENT; - event.client.data1 = (uintptr_t)xevent.xclient.data.l[0]; - event.client.data2 = (uintptr_t)xevent.xclient.data.l[1]; - } - break; - case VisibilityNotify: - view->visible = xevent.xvisibility.state != VisibilityFullyObscured; - break; - case MapNotify: - event.type = PUGL_MAP; - break; - case UnmapNotify: - event.type = PUGL_UNMAP; - view->visible = false; - break; - case ConfigureNotify: - event.type = PUGL_CONFIGURE; - event.configure.x = xevent.xconfigure.x; - event.configure.y = xevent.xconfigure.y; - event.configure.width = xevent.xconfigure.width; - event.configure.height = xevent.xconfigure.height; - break; - case Expose: - event.type = PUGL_EXPOSE; - event.expose.x = xevent.xexpose.x; - event.expose.y = xevent.xexpose.y; - event.expose.width = xevent.xexpose.width; - event.expose.height = xevent.xexpose.height; - break; - case MotionNotify: - event.type = PUGL_MOTION; - 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); - 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 = (double)xevent.xbutton.time / 1e3; - event.scroll.x = xevent.xbutton.x; - event.scroll.y = xevent.xbutton.y; - event.scroll.xRoot = xevent.xbutton.x_root; - event.scroll.yRoot = xevent.xbutton.y_root; - event.scroll.state = translateModifiers(xevent.xbutton.state); - event.scroll.dx = 0.0; - event.scroll.dy = 0.0; - switch (xevent.xbutton.button) { - 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 - } - // fallthru - case ButtonRelease: - if (xevent.xbutton.button < 4 || xevent.xbutton.button > 7) { - event.button.type = ((xevent.type == ButtonPress) - ? PUGL_BUTTON_PRESS - : PUGL_BUTTON_RELEASE); - 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; - event.button.yRoot = xevent.xbutton.y_root; - event.button.state = translateModifiers(xevent.xbutton.state); - event.button.button = xevent.xbutton.button; - } - break; - case KeyPress: - case KeyRelease: - event.type = ((xevent.type == KeyPress) - ? PUGL_KEY_PRESS - : PUGL_KEY_RELEASE); - 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; - event.key.yRoot = xevent.xkey.y_root; - event.key.state = translateModifiers(xevent.xkey.state); - translateKey(view, &xevent, &event); - break; - case EnterNotify: - case LeaveNotify: - event.type = ((xevent.type == EnterNotify) - ? PUGL_POINTER_IN - : PUGL_POINTER_OUT); - 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; - event.crossing.yRoot = xevent.xcrossing.y_root; - event.crossing.state = translateModifiers(xevent.xcrossing.state); - event.crossing.mode = PUGL_CROSSING_NORMAL; - if (xevent.xcrossing.mode == NotifyGrab) { - event.crossing.mode = PUGL_CROSSING_GRAB; - } else if (xevent.xcrossing.mode == NotifyUngrab) { - event.crossing.mode = PUGL_CROSSING_UNGRAB; - } - break; - - case FocusIn: - case FocusOut: - event.type = (xevent.type == FocusIn) ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT; - 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: - break; - } - - return event; -} - -PuglStatus -puglGrabFocus(PuglView* view) -{ - XSetInputFocus( - view->impl->display, view->impl->win, RevertToNone, CurrentTime); - return PUGL_SUCCESS; -} - -bool -puglHasFocus(const PuglView* view) -{ - int revertTo = 0; - Window focusedWindow = 0; - XGetInputFocus(view->impl->display, &focusedWindow, &revertTo); - return focusedWindow == view->impl->win; -} - -PuglStatus -puglRequestAttention(PuglView* view) -{ - PuglInternals* const impl = view->impl; - const PuglX11Atoms* const atoms = &view->world->impl->atoms; - XEvent event = {0}; - - event.type = ClientMessage; - event.xclient.window = impl->win; - event.xclient.format = 32; - event.xclient.message_type = atoms->NET_WM_STATE; - event.xclient.data.l[0] = WM_STATE_ADD; - event.xclient.data.l[1] = (long)atoms->NET_WM_STATE_DEMANDS_ATTENTION; - event.xclient.data.l[2] = 0; - event.xclient.data.l[3] = 1; - event.xclient.data.l[4] = 0; - - const Window root = RootWindow(impl->display, impl->screen); - XSendEvent(impl->display, - root, - False, - SubstructureNotifyMask | SubstructureRedirectMask, - &event); - - return PUGL_SUCCESS; -} - -PuglStatus -puglStartTimer(PuglView* view, uintptr_t id, double timeout) -{ -#ifdef HAVE_XSYNC - if (view->world->impl->syncSupported) { - XSyncValue value; - XSyncIntToValue(&value, (int)floor(timeout * 1000.0)); - - PuglWorldInternals* w = view->world->impl; - Display* const display = w->display; - const XSyncCounter counter = w->serverTimeCounter; - 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}; - - if (alarm != None) { - for (size_t i = 0; i < w->numTimers; ++i) { - if (w->timers[i].view == view && w->timers[i].id == id) { - // Replace existing timer - XSyncDestroyAlarm(w->display, w->timers[i].alarm); - w->timers[i] = timer; - return PUGL_SUCCESS; - } - } - - // Add new timer - const size_t size = ++w->numTimers * sizeof(timer); - w->timers = (PuglTimer*)realloc(w->timers, size); - w->timers[w->numTimers - 1] = timer; - return PUGL_SUCCESS; - } - } -#else - (void)view; - (void)id; - (void)timeout; -#endif - - return PUGL_FAILURE; -} - -PuglStatus -puglStopTimer(PuglView* view, uintptr_t id) -{ -#ifdef HAVE_XSYNC - PuglWorldInternals* w = view->world->impl; - - for (size_t i = 0; i < w->numTimers; ++i) { - if (w->timers[i].view == view && w->timers[i].id == id) { - XSyncDestroyAlarm(w->display, w->timers[i].alarm); - - if (i == w->numTimers - 1) { - memset(&w->timers[i], 0, sizeof(PuglTimer)); - } else { - memmove(w->timers + i, - w->timers + i + 1, - sizeof(PuglTimer) * (w->numTimers - i - 1)); - - memset(&w->timers[i], 0, sizeof(PuglTimer)); - } - - --w->numTimers; - return PUGL_SUCCESS; - } - } -#else - (void)view; - (void)id; -#endif - - return PUGL_FAILURE; -} - -static XEvent -puglEventToX(PuglView* view, const PuglEvent* event) -{ - XEvent xev = {0}; - xev.xany.send_event = True; - - switch (event->type) { - case PUGL_EXPOSE: { - const double x = floor(event->expose.x); - const double y = floor(event->expose.y); - const double w = ceil(event->expose.x + event->expose.width) - x; - const double h = ceil(event->expose.y + event->expose.height) - y; - - xev.xexpose.type = Expose; - xev.xexpose.serial = 0; - xev.xexpose.display = view->impl->display; - xev.xexpose.window = view->impl->win; - xev.xexpose.x = (int)x; - xev.xexpose.y = (int)y; - xev.xexpose.width = (int)w; - xev.xexpose.height = (int)h; - break; - } - - case PUGL_CLIENT: - xev.xclient.type = ClientMessage; - xev.xclient.serial = 0; - xev.xclient.send_event = True; - xev.xclient.display = view->impl->display; - xev.xclient.window = view->impl->win; - xev.xclient.message_type = view->world->impl->atoms.PUGL_CLIENT_MSG; - xev.xclient.format = 32; - xev.xclient.data.l[0] = (long)event->client.data1; - xev.xclient.data.l[1] = (long)event->client.data2; - break; - - default: - break; - } - - return xev; -} - -PuglStatus -puglSendEvent(PuglView* view, const PuglEvent* event) -{ - XEvent xev = puglEventToX(view, event); - - if (xev.type) { - if (XSendEvent(view->impl->display, view->impl->win, False, 0, &xev)) { - return PUGL_SUCCESS; - } else { - return PUGL_UNKNOWN_ERROR; - } - } - - return PUGL_UNSUPPORTED_TYPE; -} - -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglWaitForEvent(PuglView* view) -{ - XEvent xevent; - XPeekEvent(view->impl->display, &xevent); - return PUGL_SUCCESS; -} -#endif - -static void -mergeExposeEvents(PuglEvent* dst, const PuglEvent* src) -{ - if (!dst->type) { - *dst = *src; - } else { - const double max_x = MAX(dst->expose.x + dst->expose.width, - src->expose.x + src->expose.width); - const double max_y = MAX(dst->expose.y + dst->expose.height, - src->expose.y + src->expose.height); - - dst->expose.x = MIN(dst->expose.x, src->expose.x); - 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; - } -} - -static void -handleSelectionNotify(const PuglWorld* world, PuglView* view) -{ - uint8_t* str = NULL; - Atom type = 0; - int fmt = 0; - unsigned long len = 0; - unsigned long left = 0; - - XGetWindowProperty(world->impl->display, - view->impl->win, - XA_PRIMARY, - 0, - 0x1FFFFFFF, - False, - AnyPropertyType, - &type, - &fmt, - &len, - &left, - &str); - - if (str && fmt == 8 && type == world->impl->atoms.UTF8_STRING && - left == 0) { - puglSetBlob(&view->clipboard, str, len); - } - - XFree(str); -} - -static void -handleSelectionRequest(const PuglWorld* world, - PuglView* view, - const XSelectionRequestEvent* request) -{ - XSelectionEvent note = {SelectionNotify, - request->serial, - False, - world->impl->display, - request->requestor, - request->selection, - request->target, - None, - request->time}; - - const char* type = NULL; - size_t len = 0; - const void* data = puglGetInternalClipboard(view, &type, &len); - if (data && request->selection == world->impl->atoms.CLIPBOARD && - request->target == world->impl->atoms.UTF8_STRING) { - note.property = request->property; - XChangeProperty(world->impl->display, - note.requestor, - note.property, - note.target, - 8, - PropModeReplace, - (const uint8_t*)data, - (int)len); - } else { - note.property = None; - } - - XSendEvent(world->impl->display, note.requestor, True, 0, (XEvent*)¬e); -} - -/// Flush pending configure and expose events for all views -static void -flushExposures(PuglWorld* world) -{ - for (size_t i = 0; i < world->numViews; ++i) { - PuglView* const view = world->views[i]; - - if (view->visible) { - puglDispatchSimpleEvent(view, PUGL_UPDATE); - } - - const PuglEvent configure = view->impl->pendingConfigure; - const PuglEvent expose = view->impl->pendingExpose; - - view->impl->pendingConfigure.type = PUGL_NOTHING; - view->impl->pendingExpose.type = PUGL_NOTHING; - - if (configure.type || expose.type) { - view->backend->enter(view, expose.type ? &expose.expose : NULL); - puglDispatchEventInContext(view, &configure); - puglDispatchEventInContext(view, &expose); - view->backend->leave(view, expose.type ? &expose.expose : NULL); - } - } -} - -static bool -handleTimerEvent(PuglWorld* world, XEvent xevent) -{ -#ifdef HAVE_XSYNC - if (xevent.type == world->impl->syncEventBase + XSyncAlarmNotify) { - XSyncAlarmNotifyEvent* notify = ((XSyncAlarmNotifyEvent*)&xevent); - - for (size_t i = 0; i < world->impl->numTimers; ++i) { - if (world->impl->timers[i].alarm == notify->alarm) { - PuglEvent event = {{PUGL_TIMER, 0}}; - event.timer.id = world->impl->timers[i].id; - puglDispatchEvent(world->impl->timers[i].view, - (const PuglEvent*)&event); - } - } - - return true; - } -#else - (void)world; - (void)xevent; -#endif - - return false; -} - -static PuglStatus -puglDispatchX11Events(PuglWorld* world) -{ - const PuglX11Atoms* const atoms = &world->impl->atoms; - - // Flush output to the server once at the start - Display* display = world->impl->display; - XFlush(display); - - // Process all queued events (without further flushing) - while (XEventsQueued(display, QueuedAfterReading) > 0) { - XEvent xevent; - XNextEvent(display, &xevent); - - if (handleTimerEvent(world, xevent)) { - continue; - } - - PuglView* view = puglFindView(world, xevent.xany.window); - if (!view) { - continue; - } - - // Handle special events - PuglInternals* const impl = view->impl; - if (xevent.type == KeyRelease && view->hints[PUGL_IGNORE_KEY_REPEAT]) { - XEvent next; - if (XCheckTypedWindowEvent(display, impl->win, KeyPress, &next) && - next.type == KeyPress && - next.xkey.time == xevent.xkey.time && - next.xkey.keycode == xevent.xkey.keycode) { - continue; - } - } else if (xevent.type == FocusIn) { - XSetICFocus(impl->xic); - } else if (xevent.type == FocusOut) { - XUnsetICFocus(impl->xic); - } else if (xevent.type == SelectionClear) { - puglSetBlob(&view->clipboard, NULL, 0); - } else if (xevent.type == SelectionNotify && - xevent.xselection.selection == atoms->CLIPBOARD && - xevent.xselection.target == atoms->UTF8_STRING && - xevent.xselection.property == XA_PRIMARY) { - handleSelectionNotify(world, view); - } else if (xevent.type == SelectionRequest) { - handleSelectionRequest(world, view, &xevent.xselectionrequest); - } - - // Translate X11 event to Pugl event - const PuglEvent event = translateEvent(view, xevent); - - if (event.type == PUGL_EXPOSE) { - // Expand expose event to be dispatched after loop - mergeExposeEvents(&view->impl->pendingExpose, &event); - } else if (event.type == PUGL_CONFIGURE) { - // Expand configure event to be dispatched after loop - view->impl->pendingConfigure = event; - view->frame.x = event.configure.x; - view->frame.y = event.configure.y; - view->frame.width = event.configure.width; - view->frame.height = event.configure.height; - } else if (event.type == PUGL_MAP && view->parent) { - XWindowAttributes attrs; - XGetWindowAttributes(view->impl->display, view->impl->win, &attrs); - - const PuglEventConfigure configure = { - PUGL_CONFIGURE, 0, attrs.x, attrs.y, attrs.width, attrs.height}; - - puglDispatchEvent(view, (const PuglEvent*)&configure); - puglDispatchEvent(view, &event); - } else { - // Dispatch event to application immediately - puglDispatchEvent(view, &event); - } - } - - return PUGL_SUCCESS; -} - -#ifndef PUGL_DISABLE_DEPRECATED -PuglStatus -puglProcessEvents(PuglView* view) -{ - return puglUpdate(view->world, 0.0); -} -#endif - -PuglStatus -puglUpdate(PuglWorld* world, double timeout) -{ - const double startTime = puglGetTime(world); - PuglStatus st = PUGL_SUCCESS; - - world->impl->dispatchingEvents = true; - - if (timeout < 0.0) { - st = puglPollX11Socket(world, timeout); - st = st ? st : puglDispatchX11Events(world); - } else if (timeout <= 0.001) { - st = puglDispatchX11Events(world); - } else { - const double endTime = startTime + timeout - 0.001; - for (double t = startTime; t < endTime; t = puglGetTime(world)) { - if ((st = puglPollX11Socket(world, endTime - t)) || - (st = puglDispatchX11Events(world))) { - break; - } - } - } - - flushExposures(world); - - world->impl->dispatchingEvents = false; - - return st; -} - -double -puglGetTime(const PuglWorld* world) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - - world->startTime; -} - -PuglStatus -puglPostRedisplay(PuglView* view) -{ - const PuglRect rect = { 0, 0, view->frame.width, view->frame.height }; - - return puglPostRedisplayRect(view, rect); -} - -PuglStatus -puglPostRedisplayRect(PuglView* view, PuglRect rect) -{ - const PuglEventExpose event = { - PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height - }; - - if (view->world->impl->dispatchingEvents) { - // Currently dispatching events, add/expand expose for the loop end - mergeExposeEvents(&view->impl->pendingExpose, (const PuglEvent*)&event); - } else if (view->visible) { - // Not dispatching events, send an X expose so we wake up next time - return puglSendEvent(view, (const PuglEvent*)&event); - } - - return PUGL_SUCCESS; -} - -PuglNativeView -puglGetNativeWindow(PuglView* view) -{ - return (PuglNativeView)view->impl->win; -} - -PuglStatus -puglSetWindowTitle(PuglView* view, const char* title) -{ - Display* display = view->world->impl->display; - const PuglX11Atoms* const atoms = &view->world->impl->atoms; - - puglSetString(&view->title, title); - - if (view->impl->win) { - XStoreName(display, view->impl->win, title); - XChangeProperty(display, view->impl->win, atoms->NET_WM_NAME, - atoms->UTF8_STRING, 8, PropModeReplace, - (const uint8_t*)title, (int)strlen(title)); - } - - return PUGL_SUCCESS; -} - -PuglStatus -puglSetFrame(PuglView* view, const PuglRect frame) -{ - if (view->impl->win) { - if (!XMoveResizeWindow(view->world->impl->display, - view->impl->win, - (int)frame.x, - (int)frame.y, - (unsigned)frame.width, - (unsigned)frame.height)) { - return PUGL_UNKNOWN_ERROR; - } - } - - view->frame = frame; - return PUGL_SUCCESS; -} - -PuglStatus -puglSetDefaultSize(PuglView* const view, const int width, const int height) -{ - 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); -} - -PuglStatus -puglSetMaxSize(PuglView* const view, const int width, const int height) -{ - view->minWidth = width; - view->minHeight = height; - return updateSizeHints(view); -} - -PuglStatus -puglSetAspectRatio(PuglView* const view, - const int minX, - const int minY, - const int maxX, - const int maxY) -{ - view->minAspectX = minX; - view->minAspectY = minY; - view->maxAspectX = maxX; - view->maxAspectY = maxY; - - return updateSizeHints(view); -} - -PuglStatus -puglSetTransientFor(PuglView* view, PuglNativeView parent) -{ - Display* display = view->world->impl->display; - - view->transientParent = parent; - - if (view->impl->win) { - XSetTransientForHint(display, view->impl->win, - (Window)view->transientParent); - } - - return PUGL_SUCCESS; -} - -const void* -puglGetClipboard(PuglView* const view, - const char** const type, - size_t* const len) -{ - PuglInternals* const impl = view->impl; - const PuglX11Atoms* const atoms = &view->world->impl->atoms; - - const Window owner = XGetSelectionOwner(impl->display, atoms->CLIPBOARD); - if (owner != None && owner != impl->win) { - // Clear internal selection - puglSetBlob(&view->clipboard, NULL, 0); - - // Request selection from the owner - XConvertSelection(impl->display, - atoms->CLIPBOARD, - atoms->UTF8_STRING, - XA_PRIMARY, - impl->win, - CurrentTime); - - // Run event loop until data is received - while (!view->clipboard.data) { - puglUpdate(view->world, -1.0); - } - } - - return puglGetInternalClipboard(view, type, len); -} - -PuglStatus -puglSetClipboard(PuglView* const view, - const char* const type, - const void* const data, - const size_t len) -{ - PuglInternals* const impl = view->impl; - const PuglX11Atoms* const atoms = &view->world->impl->atoms; - - PuglStatus st = puglSetInternalClipboard(view, type, data, len); - if (st) { - return st; - } - - XSetSelectionOwner(impl->display, atoms->CLIPBOARD, impl->win, CurrentTime); - 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 -} diff --git a/pugl/detail/x11.h b/pugl/detail/x11.h deleted file mode 100644 index b5ca75c..0000000 --- a/pugl/detail/x11.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - 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 x11.h - @brief Shared definitions for X11 implementation. -*/ - -#include "pugl/detail/types.h" -#include "pugl/pugl.h" - -#include <X11/X.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -typedef struct { - Atom CLIPBOARD; - Atom UTF8_STRING; - Atom WM_PROTOCOLS; - Atom WM_DELETE_WINDOW; - Atom PUGL_CLIENT_MSG; - Atom NET_WM_NAME; - Atom NET_WM_STATE; - Atom NET_WM_STATE_DEMANDS_ATTENTION; -} PuglX11Atoms; - -typedef struct { - XID alarm; - PuglView* view; - uintptr_t id; -} PuglTimer; - -struct PuglWorldInternalsImpl { - Display* display; - PuglX11Atoms atoms; - XIM xim; - PuglTimer* timers; - size_t numTimers; - XID serverTimeCounter; - int syncEventBase; - bool syncSupported; - bool dispatchingEvents; -}; - -struct PuglInternalsImpl { - Display* display; - XVisualInfo* vi; - Window win; - XIC xic; - PuglSurface* surface; - PuglEvent pendingConfigure; - PuglEvent pendingExpose; - int screen; -#ifdef HAVE_XCURSOR - unsigned cursorShape; -#endif -}; - -PuglStatus puglX11StubConfigure(PuglView* view); diff --git a/pugl/detail/x11_cairo.c b/pugl/detail/x11_cairo.c deleted file mode 100644 index 0112c4e..0000000 --- a/pugl/detail/x11_cairo.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - 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 x11_cairo.c - @brief Cairo graphics backend for X11. -*/ - -#include "pugl/detail/types.h" -#include "pugl/detail/x11.h" -#include "pugl/pugl.h" -#include "pugl/pugl_cairo.h" - -#include <X11/Xutil.h> -#include <cairo-xlib.h> -#include <cairo.h> - -#include <stdlib.h> - -typedef struct { - cairo_surface_t* back; - cairo_surface_t* front; - cairo_t* cr; -} PuglX11CairoSurface; - -static void -puglX11CairoClose(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - - cairo_surface_destroy(surface->front); - cairo_surface_destroy(surface->back); - surface->front = surface->back = NULL; -} - -static PuglStatus -puglX11CairoOpen(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - - surface->back = cairo_xlib_surface_create(impl->display, - impl->win, - impl->vi->visual, - (int)view->frame.width, - (int)view->frame.height); - - surface->front = cairo_surface_create_similar( - surface->back, - cairo_surface_get_content(surface->back), - (int)view->frame.width, - (int)view->frame.height); - - if (cairo_surface_status(surface->back) || - cairo_surface_status(surface->front)) { - puglX11CairoClose(view); - return PUGL_CREATE_CONTEXT_FAILED; - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglX11CairoCreate(PuglView* view) -{ - PuglInternals* const impl = view->impl; - - impl->surface = (cairo_surface_t*)calloc(1, sizeof(PuglX11CairoSurface)); - - return PUGL_SUCCESS; -} - -static PuglStatus -puglX11CairoDestroy(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - - puglX11CairoClose(view); - free(surface); - - return PUGL_SUCCESS; -} - -static PuglStatus -puglX11CairoEnter(PuglView* view, const PuglEventExpose* expose) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - PuglStatus st = PUGL_SUCCESS; - - if (expose && !(st = puglX11CairoOpen(view))) { - surface->cr = cairo_create(surface->front); - - if (cairo_status(surface->cr)) { - st = PUGL_CREATE_CONTEXT_FAILED; - } - } - - return st; -} - -static PuglStatus -puglX11CairoLeave(PuglView* view, const PuglEventExpose* expose) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - - if (expose) { - // Destroy front context and create a new one for drawing to the back - cairo_destroy(surface->cr); - surface->cr = cairo_create(surface->back); - - // Clip to expose region - cairo_rectangle(surface->cr, - expose->x, - expose->y, - expose->width, - expose->height); - cairo_clip(surface->cr); - - // Paint front onto back - cairo_set_source_surface(surface->cr, surface->front, 0, 0); - cairo_paint(surface->cr); - - // Flush to X and close everything - cairo_destroy(surface->cr); - cairo_surface_flush(surface->back); - puglX11CairoClose(view); - surface->cr = NULL; - } - - return PUGL_SUCCESS; -} - -static void* -puglX11CairoGetContext(PuglView* view) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - - return surface->cr; -} - -const PuglBackend* -puglCairoBackend(void) -{ - static const PuglBackend backend = {puglX11StubConfigure, - puglX11CairoCreate, - puglX11CairoDestroy, - puglX11CairoEnter, - puglX11CairoLeave, - puglX11CairoGetContext}; - - return &backend; -} diff --git a/pugl/detail/x11_gl.c b/pugl/detail/x11_gl.c deleted file mode 100644 index b756355..0000000 --- a/pugl/detail/x11_gl.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - 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 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 <GL/glx.h> -#include <X11/X.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -typedef struct { - GLXFBConfig fb_config; - GLXContext ctx; -} PuglX11GlSurface; - -static int -puglX11GlHintValue(const int value) -{ - return value == PUGL_DONT_CARE ? (int)GLX_DONT_CARE : value; -} - -static int -puglX11GlGetAttrib(Display* const display, - GLXFBConfig fb_config, - const int attrib) -{ - int value = 0; - glXGetFBConfigAttrib(display, fb_config, attrib, &value); - return value; -} - -static PuglStatus -puglX11GlConfigure(PuglView* view) -{ - PuglInternals* const impl = view->impl; - const int screen = impl->screen; - Display* const display = impl->display; - - PuglX11GlSurface* const surface = - (PuglX11GlSurface*)calloc(1, sizeof(PuglX11GlSurface)); - impl->surface = surface; - - const int attrs[] = { - GLX_X_RENDERABLE, True, - GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_SAMPLES, puglX11GlHintValue(view->hints[PUGL_SAMPLES]), - GLX_RED_SIZE, puglX11GlHintValue(view->hints[PUGL_RED_BITS]), - GLX_GREEN_SIZE, puglX11GlHintValue(view->hints[PUGL_GREEN_BITS]), - GLX_BLUE_SIZE, puglX11GlHintValue(view->hints[PUGL_BLUE_BITS]), - GLX_ALPHA_SIZE, puglX11GlHintValue(view->hints[PUGL_ALPHA_BITS]), - GLX_DEPTH_SIZE, puglX11GlHintValue(view->hints[PUGL_DEPTH_BITS]), - GLX_STENCIL_SIZE, puglX11GlHintValue(view->hints[PUGL_STENCIL_BITS]), - GLX_DOUBLEBUFFER, puglX11GlHintValue(view->hints[PUGL_DOUBLE_BUFFER]), - None - }; - - int n_fbc = 0; - GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc); - if (n_fbc <= 0) { - return PUGL_CREATE_CONTEXT_FAILED; - } - - surface->fb_config = fbc[0]; - impl->vi = glXGetVisualFromFBConfig(impl->display, fbc[0]); - - view->hints[PUGL_RED_BITS] = puglX11GlGetAttrib( - display, fbc[0], GLX_RED_SIZE); - view->hints[PUGL_GREEN_BITS] = puglX11GlGetAttrib( - display, fbc[0], GLX_GREEN_SIZE); - view->hints[PUGL_BLUE_BITS] = puglX11GlGetAttrib( - display, fbc[0], GLX_BLUE_SIZE); - view->hints[PUGL_ALPHA_BITS] = puglX11GlGetAttrib( - display, fbc[0], GLX_ALPHA_SIZE); - view->hints[PUGL_DEPTH_BITS] = puglX11GlGetAttrib( - display, fbc[0], GLX_DEPTH_SIZE); - view->hints[PUGL_STENCIL_BITS] = puglX11GlGetAttrib( - display, fbc[0], GLX_STENCIL_SIZE); - view->hints[PUGL_SAMPLES] = puglX11GlGetAttrib( - display, fbc[0], GLX_SAMPLES); - view->hints[PUGL_DOUBLE_BUFFER] = puglX11GlGetAttrib( - display, fbc[0], GLX_DOUBLEBUFFER); - - char msg[256]; - - snprintf( - msg, - sizeof(msg), - "Using visual 0x%lX: R=%d G=%d B=%d A=%d D=%d DOUBLE=%d SAMPLES=%d\n", - impl->vi->visualid, - puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE), - puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE), - puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE), - puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE), - puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE), - puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER), - puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES)); - - view->world->logFunc(view->world, PUGL_LOG_LEVEL_INFO, msg); - - XFree(fbc); - - 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) -{ - if (expose && view->hints[PUGL_DOUBLE_BUFFER]) { - 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; - PuglX11GlSurface* const surface = (PuglX11GlSurface*)impl->surface; - Display* const display = impl->display; - GLXFBConfig fb_config = surface->fb_config; - - const int ctx_attrs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MAJOR], - GLX_CONTEXT_MINOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MINOR], - GLX_CONTEXT_FLAGS_ARB, (view->hints[PUGL_USE_DEBUG_CONTEXT] - ? GLX_CONTEXT_DEBUG_BIT_ARB - : 0), - GLX_CONTEXT_PROFILE_MASK_ARB, (view->hints[PUGL_USE_COMPAT_PROFILE] - ? GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB - : GLX_CONTEXT_CORE_PROFILE_BIT_ARB), - 0}; - - PFNGLXCREATECONTEXTATTRIBSARBPROC create_context = - (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress( - (const uint8_t*)"glXCreateContextAttribsARB"); - - PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = - (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress( - (const uint8_t*)"glXSwapIntervalEXT"); - - surface->ctx = create_context(display, fb_config, 0, True, ctx_attrs); - if (!surface->ctx) { - surface->ctx = - glXCreateNewContext(display, fb_config, GLX_RGBA_TYPE, 0, True); - } - - if (!surface->ctx) { - return PUGL_CREATE_CONTEXT_FAILED; - } - - 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, - impl->vi, - GLX_DOUBLEBUFFER, - &view->hints[PUGL_DOUBLE_BUFFER]); - - glXQueryDrawable(display, - impl->win, - GLX_SWAP_INTERVAL_EXT, - (unsigned int*)&view->hints[PUGL_SWAP_INTERVAL]); - - return PUGL_SUCCESS; -} - -static PuglStatus -puglX11GlDestroy(PuglView* view) -{ - PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; - if (surface) { - glXDestroyContext(view->impl->display, surface->ctx); - free(surface); - view->impl->surface = NULL; - } - return PUGL_SUCCESS; -} - -PuglGlFunc -puglGetProcAddress(const char* name) -{ - return glXGetProcAddress((const uint8_t*)name); -} - -const PuglBackend* puglGlBackend(void) -{ - static const PuglBackend backend = {puglX11GlConfigure, - puglX11GlCreate, - puglX11GlDestroy, - puglX11GlEnter, - puglX11GlLeave, - puglStubGetContext}; - - return &backend; -} diff --git a/pugl/detail/x11_stub.c b/pugl/detail/x11_stub.c deleted file mode 100644 index 8efd68f..0000000 --- a/pugl/detail/x11_stub.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - 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. -*/ - -#include "pugl/pugl_stub.h" - -#include "pugl/detail/stub.h" -#include "pugl/detail/types.h" -#include "pugl/detail/x11.h" - -PuglStatus -puglX11StubConfigure(PuglView* view) -{ - PuglInternals* const impl = view->impl; - XVisualInfo pat = {0}; - int n = 0; - - pat.screen = impl->screen; - impl->vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); - - view->hints[PUGL_RED_BITS] = impl->vi->bits_per_rgb; - view->hints[PUGL_GREEN_BITS] = impl->vi->bits_per_rgb; - view->hints[PUGL_BLUE_BITS] = impl->vi->bits_per_rgb; - view->hints[PUGL_ALPHA_BITS] = 0; - - return PUGL_SUCCESS; -} - -const PuglBackend* -puglStubBackend(void) -{ - static const PuglBackend backend = { - puglX11StubConfigure, - puglStubCreate, - puglStubDestroy, - puglStubEnter, - puglStubLeave, - puglStubGetContext, - }; - - return &backend; -} diff --git a/pugl/gl.h b/pugl/gl.h deleted file mode 100644 index 9f7a741..0000000 --- a/pugl/gl.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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 gl.h - @brief Portable header wrapper for gl.h. - - Unfortunately, GL includes vary across platforms so this header allows for - pure portable programs. -*/ - -// IWYU pragma: begin_exports - -#ifdef __APPLE__ -# include "OpenGL/gl.h" -#else -# ifdef _WIN32 -# include <windows.h> /* Broken Windows GL headers require this */ -# endif -# include "GL/gl.h" -#endif - -// IWYU pragma: end_exports diff --git a/pugl/glu.h b/pugl/glu.h deleted file mode 100644 index 423a917..0000000 --- a/pugl/glu.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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 glu.h - @brief Portable header wrapper for glu.h. - - Unfortunately, GL includes vary across platforms so this header allows for - pure portable programs. -*/ - -// IWYU pragma: begin_exports - -#ifdef __APPLE__ -# include "OpenGL/glu.h" -#else -# ifdef _WIN32 -# include <windows.h> /* Broken Windows GL headers require this */ -# endif -# include "GL/glu.h" -#endif - -// IWYU pragma: end_exports diff --git a/pugl/pugl.h b/pugl/pugl.h deleted file mode 100644 index ad24c0d..0000000 --- a/pugl/pugl.h +++ /dev/null @@ -1,1624 +0,0 @@ -/* - 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.h - @brief Pugl API. -*/ - -#ifndef PUGL_PUGL_H -#define PUGL_PUGL_H - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#ifdef PUGL_SHARED -# ifdef _WIN32 -# define PUGL_LIB_IMPORT __declspec(dllimport) -# define PUGL_LIB_EXPORT __declspec(dllexport) -# else -# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) -# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) -# endif -# ifdef PUGL_INTERNAL -# define PUGL_API PUGL_LIB_EXPORT -# else -# define PUGL_API PUGL_LIB_IMPORT -# endif -#else -# define PUGL_API -#endif - -#ifndef PUGL_DISABLE_DEPRECATED -# if defined(__clang__) -# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("", rep))) -# elif defined(__GNUC__) -# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("Use " rep))) -# else -# define PUGL_DEPRECATED_BY(rep) -# 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 } -#else -# define PUGL_BEGIN_DECLS -# define PUGL_END_DECLS -#endif - -PUGL_BEGIN_DECLS - -/** - @defgroup pugl Pugl - A minimal portable API for embeddable GUIs. - @{ - - @defgroup pugl_c C API - Public C API. - @{ -*/ - -/** - A rectangle. - - This is used to describe things like view position and size. Pugl generally - uses coordinates where the top left corner is 0,0. -*/ -typedef struct { - double x; - double y; - double width; - double height; -} PuglRect; - -/** - @defgroup events Events - - Event definitions. - - All updates to the view happen via events, which are dispatched to 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. - - @{ -*/ - -/** - Keyboard modifier flags. -*/ -typedef enum { - PUGL_MOD_SHIFT = 1u << 0u, ///< Shift key - PUGL_MOD_CTRL = 1u << 1u, ///< Control key - PUGL_MOD_ALT = 1u << 2u, ///< Alt/Option key - PUGL_MOD_SUPER = 1u << 3u ///< Mod4/Command/Windows key -} PuglMod; - -/** - Bitwise OR of #PuglMod values. -*/ -typedef uint32_t PuglMods; - -/** - Keyboard key codepoints. - - All keys are identified by a Unicode code point in PuglEventKey::key. This - enumeration defines constants for special keys that do not have a standard - code point, and some convenience constants for control characters. Note - that all keys are handled in the same way, this enumeration is just for - convenience when writing hard-coded key bindings. - - Keys that do not have a standard code point use values in the Private Use - Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications - must take care to not interpret these values beyond key detection, the - mapping used here is arbitrary and specific to Pugl. -*/ -typedef enum { - // ASCII control codes - PUGL_KEY_BACKSPACE = 0x08, - PUGL_KEY_ESCAPE = 0x1B, - PUGL_KEY_DELETE = 0x7F, - - // Unicode Private Use Area - PUGL_KEY_F1 = 0xE000, - PUGL_KEY_F2, - PUGL_KEY_F3, - PUGL_KEY_F4, - PUGL_KEY_F5, - PUGL_KEY_F6, - PUGL_KEY_F7, - PUGL_KEY_F8, - PUGL_KEY_F9, - PUGL_KEY_F10, - PUGL_KEY_F11, - PUGL_KEY_F12, - PUGL_KEY_LEFT, - PUGL_KEY_UP, - PUGL_KEY_RIGHT, - PUGL_KEY_DOWN, - PUGL_KEY_PAGE_UP, - PUGL_KEY_PAGE_DOWN, - PUGL_KEY_HOME, - PUGL_KEY_END, - PUGL_KEY_INSERT, - PUGL_KEY_SHIFT, - PUGL_KEY_SHIFT_L = PUGL_KEY_SHIFT, - PUGL_KEY_SHIFT_R, - PUGL_KEY_CTRL, - PUGL_KEY_CTRL_L = PUGL_KEY_CTRL, - PUGL_KEY_CTRL_R, - PUGL_KEY_ALT, - PUGL_KEY_ALT_L = PUGL_KEY_ALT, - PUGL_KEY_ALT_R, - PUGL_KEY_SUPER, - PUGL_KEY_SUPER_L = PUGL_KEY_SUPER, - PUGL_KEY_SUPER_R, - PUGL_KEY_MENU, - PUGL_KEY_CAPS_LOCK, - PUGL_KEY_SCROLL_LOCK, - PUGL_KEY_NUM_LOCK, - PUGL_KEY_PRINT_SCREEN, - PUGL_KEY_PAUSE -} PuglKey; - -/** - The type of a PuglEvent. -*/ -typedef enum { - PUGL_NOTHING, ///< No event - PUGL_CREATE, ///< View created, a #PuglEventCreate - PUGL_DESTROY, ///< View destroyed, a #PuglEventDestroy - PUGL_CONFIGURE, ///< View moved/resized, a #PuglEventConfigure - PUGL_MAP, ///< View made visible, a #PuglEventMap - PUGL_UNMAP, ///< View made invisible, a #PuglEventUnmap - PUGL_UPDATE, ///< View ready to draw, a #PuglEventUpdate - PUGL_EXPOSE, ///< View must be drawn, a #PuglEventExpose - PUGL_CLOSE, ///< View will be closed, a #PuglEventClose - PUGL_FOCUS_IN, ///< Keyboard focus entered view, a #PuglEventFocus - PUGL_FOCUS_OUT, ///< Keyboard focus left view, a #PuglEventFocus - PUGL_KEY_PRESS, ///< Key pressed, a #PuglEventKey - PUGL_KEY_RELEASE, ///< Key released, a #PuglEventKey - PUGL_TEXT, ///< Character entered, a #PuglEventText - PUGL_POINTER_IN, ///< Pointer entered view, a #PuglEventCrossing - PUGL_POINTER_OUT, ///< Pointer left view, a #PuglEventCrossing - PUGL_BUTTON_PRESS, ///< Mouse button pressed, a #PuglEventButton - PUGL_BUTTON_RELEASE, ///< Mouse button released, a #PuglEventButton - PUGL_MOTION, ///< Pointer moved, a #PuglEventMotion - PUGL_SCROLL, ///< Scrolled, a #PuglEventScroll - PUGL_CLIENT, ///< Custom client message, a #PuglEventClient - PUGL_TIMER, ///< Timer triggered, a #PuglEventTimer - -#ifndef PUGL_DISABLE_DEPRECATED - PUGL_ENTER_NOTIFY PUGL_DEPRECATED_BY("PUGL_POINTER_IN") = PUGL_POINTER_IN, - PUGL_LEAVE_NOTIFY PUGL_DEPRECATED_BY("PUGL_POINTER_OUT") = PUGL_POINTER_OUT, - PUGL_MOTION_NOTIFY PUGL_DEPRECATED_BY("PUGL_MOTION") = PUGL_MOTION, -#endif - -} PuglEventType; - -/** - Common flags for all event types. -*/ -typedef enum { - PUGL_IS_SEND_EVENT = 1, ///< Event is synthetic - PUGL_IS_HINT = 2 ///< Event is a hint (not direct user input) -} PuglEventFlag; - -/** - Bitwise OR of #PuglEventFlag values. -*/ -typedef uint32_t PuglEventFlags; - -/** - Reason for a PuglEventCrossing. -*/ -typedef enum { - PUGL_CROSSING_NORMAL, ///< Crossing due to pointer motion - PUGL_CROSSING_GRAB, ///< Crossing due to a grab - PUGL_CROSSING_UNGRAB ///< Crossing due to a grab release -} 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 { - PuglEventType type; ///< Event type - PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values -} PuglEventAny; - -/** - View create event. - - This event is sent when a view is realized before it is first displayed, - with the graphics context entered. This is typically used for setting up - the graphics system, for example by loading OpenGL extensions. - - This event type has no extra fields. -*/ -typedef PuglEventAny PuglEventCreate; - -/** - View destroy event. - - This event is the counterpart to #PuglEventCreate, and it is sent when the - view is being destroyed. This is typically used for tearing down the - graphics system, or otherwise freeing any resources allocated when the - create event was handled. - - This is the last event sent to any view, and immediately after it is - processed, the view is destroyed and may no longer be used. - - This event type has no extra fields. -*/ -typedef PuglEventAny PuglEventDestroy; - -/** - View resize or move event. - - A configure event is sent whenever the view is resized or moved. When a - configure event is received, the graphics context is active but not set up - for drawing. For example, it is valid to adjust the OpenGL viewport or - otherwise configure the context, but not to draw anything. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_CONFIGURE - PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values - double x; ///< New parent-relative X coordinate - double y; ///< New parent-relative Y coordinate - double width; ///< New width - double height; ///< New height -} PuglEventConfigure; - -/** - View show event. - - This event is sent when a view is mapped to the screen and made visible. - - This event type has no extra fields. -*/ -typedef PuglEventAny PuglEventMap; - -/** - View hide event. - - This event is sent when a view is unmapped from the screen and made - invisible. - - This event type has no extra fields. -*/ -typedef PuglEventAny PuglEventUnmap; - -/** - View update event. - - This event is sent to every view near the end of a main loop iteration when - any pending exposures are about to be redrawn. It is typically used to mark - regions to expose with puglPostRedisplay() or puglPostRedisplayRect(). For - example, to continuously animate, a view calls puglPostRedisplay() when an - update event is received, and it will then shortly receive an expose event. -*/ -typedef PuglEventAny PuglEventUpdate; - -/** - Expose event for when a region must be redrawn. - - When an expose event is received, the graphics context is active, and the - view must draw the entire specified region. The contents of the region are - undefined, there is no preservation of anything drawn previously. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_EXPOSE - PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values - double x; ///< View-relative X coordinate - double y; ///< View-relative Y coordinate - double width; ///< Width of exposed region - double height; ///< Height of exposed region -} PuglEventExpose; - -/** - View close event. - - This event is sent when the view is to be closed, for example when the user - clicks the close button. - - This event type has no extra fields. -*/ -typedef PuglEventAny PuglEventClose; - -/** - Keyboard focus event. - - This event is sent whenever the view gains or loses the keyboard focus. The - 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 - PuglCrossingMode mode; ///< Reason for focus change -} PuglEventFocus; - -/** - Key press or release event. - - This event represents low-level key presses and releases. This can be used - for "direct" keyboard handing like key bindings, but must not be interpreted - 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` - 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') - regardless of whether shift or control are being held. - - 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. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_KEY_PRESS or #PUGL_KEY_RELEASE - 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 - uint32_t keycode; ///< Raw key code - uint32_t key; ///< Unshifted Unicode character code, or 0 -} PuglEventKey; - -/** - Character input event. - - This event represents text input, usually as the result of a key press. The - text is given both as a Unicode character code and a UTF-8 string. - - Note that this event is generated by the platform's input system, so there - is not necessarily a direct correspondence between text events and physical - key presses. For example, with some input methods a sequence of several key - presses will generate a single character. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_TEXT - 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 - uint32_t keycode; ///< Raw key code - uint32_t character; ///< Unicode character code - char string[8]; ///< UTF-8 string -} PuglEventText; - -/** - Pointer enter or leave event. - - 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. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_POINTER_IN or #PUGL_POINTER_OUT - 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 - PuglCrossingMode mode; ///< Reason for crossing -} PuglEventCrossing; - -/** - Button press or release event. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_BUTTON_PRESS or #PUGL_BUTTON_RELEASE - 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 - uint32_t button; ///< Button number starting from 1 -} PuglEventButton; - -/** - Pointer motion event. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_MOTION - 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 -} 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` = - 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 - PuglScrollDirection direction; ///< Scroll direction - double dx; ///< Scroll X distance in lines - double dy; ///< Scroll Y distance in lines -} PuglEventScroll; - -/** - Custom client message event. - - This can be used to send a custom message to a view, which is delivered via - the window system and processed in the event loop as usual. Among other - things, this makes it possible to wake up the event loop for any reason. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_CLIENT - PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values - uintptr_t data1; ///< Client-specific data - uintptr_t data2; ///< Client-specific data -} PuglEventClient; - -/** - Timer event. - - 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 - distinguishes this timer from others. It should always be checked in the - event handler, even in applications that register only one timer. -*/ -typedef struct { - PuglEventType type; ///< #PUGL_TIMER - PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values - uintptr_t id; ///< Timer ID -} PuglEventTimer; - -/** - View event. - - 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. - - The graphics system may only be accessed when handling certain events. The - graphics context is active for #PUGL_CREATE, #PUGL_DESTROY, #PUGL_CONFIGURE, - and #PUGL_EXPOSE, but only enabled for drawing for #PUGL_EXPOSE. -*/ -typedef union { - PuglEventAny any; ///< Valid for all event types - PuglEventType type; ///< Event type - PuglEventButton button; ///< #PUGL_BUTTON_PRESS, #PUGL_BUTTON_RELEASE - PuglEventConfigure configure; ///< #PUGL_CONFIGURE - PuglEventExpose expose; ///< #PUGL_EXPOSE - PuglEventKey key; ///< #PUGL_KEY_PRESS, #PUGL_KEY_RELEASE - PuglEventText text; ///< #PUGL_TEXT - PuglEventCrossing crossing; ///< #PUGL_POINTER_IN, #PUGL_POINTER_OUT - PuglEventMotion motion; ///< #PUGL_MOTION - PuglEventScroll scroll; ///< #PUGL_SCROLL - PuglEventFocus focus; ///< #PUGL_FOCUS_IN, #PUGL_FOCUS_OUT - PuglEventClient client; ///< #PUGL_CLIENT - PuglEventTimer timer; ///< #PUGL_TIMER -} PuglEvent; - -/** - @} - @defgroup status Status - - Status codes and error handling. - - @{ -*/ - -/** - Return status code. -*/ -typedef enum { - PUGL_SUCCESS, ///< Success - 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 - PUGL_REALIZE_FAILED, ///< System view realization failed - PUGL_SET_FORMAT_FAILED, ///< Failed to set pixel format - PUGL_CREATE_CONTEXT_FAILED, ///< Failed to create drawing context - PUGL_UNSUPPORTED_TYPE, ///< Unsupported data type -} PuglStatus; - -/** - Return a string describing a status code. -*/ -PUGL_API PUGL_CONST_FUNC -const char* -puglStrerror(PuglStatus status); - -/** - @} - @defgroup world World - - The top-level context of a Pugl application or plugin. - - The world contains all library-wide state. There is no static data in Pugl, - so it is safe to use multiple worlds in a single process. This is to - facilitate plugins or other situations where it is not possible to share a - world, but a single world should be shared for all views where possible. - - @{ -*/ - -/** - The "world" of application state. - - The world represents everything that is not associated with a particular - view. Several worlds can be created in a single process, but code using - different worlds must be isolated so they are never mixed. Views are - strongly associated with the world they were created in. -*/ -typedef struct PuglWorldImpl PuglWorld; - -/** - Handle for the world's opaque user data. -*/ -typedef void* PuglWorldHandle; - -/** - The type of a World. -*/ -typedef enum { - PUGL_PROGRAM, ///< Top-level application - PUGL_MODULE ///< Plugin or module within a larger application -} PuglWorldType; - -/** - World flags. -*/ -typedef enum { - /** - Set up support for threads if necessary. - - - X11: Calls XInitThreads() which is required for some drivers. - */ - PUGL_WORLD_THREADS = 1u << 0u -} PuglWorldFlag; - -/** - Bitwise OR of #PuglWorldFlag values. -*/ -typedef uint32_t PuglWorldFlags; - -/** - A log message level, compatible with syslog. -*/ -typedef enum { - PUGL_LOG_LEVEL_ERR = 3, ///< Error - PUGL_LOG_LEVEL_WARNING = 4, ///< Warning - PUGL_LOG_LEVEL_INFO = 6, ///< Informational message - PUGL_LOG_LEVEL_DEBUG = 7 ///< Debug message -} PuglLogLevel; - -/** - A function called to report log messages. - - @param world The world that produced this log message. - @param level Log level. - @param msg Message string. -*/ -typedef void (*PuglLogFunc)(PuglWorld* world, - PuglLogLevel level, - const char* msg); - -/** - Create a new world. - - @param type The type, which dictates what this world is responsible for. - @param flags Flags to control world features. - @return A new world, which must be later freed with puglFreeWorld(). -*/ -PUGL_API PuglWorld* -puglNewWorld(PuglWorldType type, PuglWorldFlags flags); - -/** - Free a world allocated with puglNewWorld(). -*/ -PUGL_API void -puglFreeWorld(PuglWorld* world); - -/** - Set the user data for the world. - - This is usually a pointer to a struct that contains all the state which must - be accessed by several views. - - The handle is opaque to Pugl and is not interpreted in any way. -*/ -PUGL_API void -puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle); - -/** - Get the user data for the world. -*/ -PUGL_API PuglWorldHandle -puglGetWorldHandle(PuglWorld* world); - -/** - Return a pointer to the native handle of the world. - - @return - - X11: A pointer to the `Display`. - - MacOS: `NULL`. - - Windows: The `HMODULE` of the calling process. -*/ -PUGL_API void* -puglGetNativeWorld(PuglWorld* world); - -/** - Set the function to call to log a message. - - This will be called to report any log messages generated internally by Pugl - which are enabled according to the log level. -*/ -PUGL_API PuglStatus -puglSetLogFunc(PuglWorld* world, PuglLogFunc logFunc); - -/** - Set the level of log messages to emit. - - Any log messages with a level less than or equal to `level` will be emitted. -*/ -PUGL_API PuglStatus -puglSetLogLevel(PuglWorld* world, PuglLogLevel level); - -/** - Set the class name of the application. - - This is a stable identifier for the application, used as the window - class/instance name on X11 and Windows. It is not displayed to the user, - but can be used in scripts and by window managers, so it should be the same - for every instance of the application, but different from other - applications. -*/ -PUGL_API PuglStatus -puglSetClassName(PuglWorld* world, const char* name); - -/** - Return the time in seconds. - - This is a monotonically increasing clock with high resolution. The returned - time is only useful to compare against other times returned by this - function, its absolute value has no meaning. -*/ -PUGL_API double -puglGetTime(const PuglWorld* world); - -/** - Update by processing events from the window system. - - This function is a single iteration of the main loop, and should be called - repeatedly to update all views. - - If `timeout` is zero, then this function will not block. Plugins should - always use a timeout of zero to avoid blocking the host. - - If a positive `timeout` is given, then events will be processed for that - amount of time, starting from when this function was called. - - 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. -*/ -PUGL_API PuglStatus -puglUpdate(PuglWorld* world, double timeout); - -/** - @} - - @defgroup view View - - A drawable region that receives events. - - A view can be thought of as a window, but does not necessarily correspond to - a top-level window in a desktop environment. For example, a view can be - embedded in some other window, or represent an embedded system where there - is no concept of multiple windows at all. - - @{ -*/ - -/** - A drawable region that receives events. -*/ -typedef struct PuglViewImpl PuglView; - -/** - A graphics backend. - - The backend dictates how graphics are set up for a view, and how drawing is - performed. A backend must be set by calling puglSetBackend() before - realising a view. - - If you are using a local copy of Pugl, it is possible to implement a custom - backend. See the definition of `PuglBackendImpl` in the source code for - details. -*/ -typedef struct PuglBackendImpl PuglBackend; - -/** - A native view handle. - - X11: This is a `Window`. - - MacOS: This is a pointer to an `NSView*`. - - Windows: This is a `HWND`. -*/ -typedef uintptr_t PuglNativeView; - -/** - Handle for a view's opaque user data. -*/ -typedef void* PuglHandle; - -/** - A hint for configuring a view. -*/ -typedef enum { - PUGL_USE_COMPAT_PROFILE, ///< Use compatible (not core) OpenGL profile - PUGL_USE_DEBUG_CONTEXT, ///< True to use a debug OpenGL context - PUGL_CONTEXT_VERSION_MAJOR, ///< OpenGL context major version - PUGL_CONTEXT_VERSION_MINOR, ///< OpenGL context minor version - PUGL_RED_BITS, ///< Number of bits for red channel - PUGL_GREEN_BITS, ///< Number of bits for green channel - PUGL_BLUE_BITS, ///< Number of bits for blue channel - PUGL_ALPHA_BITS, ///< Number of bits for alpha channel - PUGL_DEPTH_BITS, ///< Number of bits for depth buffer - PUGL_STENCIL_BITS, ///< Number of bits for stencil buffer - PUGL_SAMPLES, ///< Number of samples per pixel (AA) - PUGL_DOUBLE_BUFFER, ///< True if double buffering should be used - PUGL_SWAP_INTERVAL, ///< Number of frames between buffer swaps - PUGL_RESIZABLE, ///< True if view should be resizable - PUGL_IGNORE_KEY_REPEAT, ///< True if key repeat events are ignored - PUGL_REFRESH_RATE, ///< Refresh rate in Hz - - PUGL_NUM_VIEW_HINTS -} PuglViewHint; - -/** - A special view hint value. -*/ -typedef enum { - PUGL_DONT_CARE = -1, ///< Use best available value - PUGL_FALSE = 0, ///< Explicitly false - PUGL_TRUE = 1 ///< Explicitly true -} PuglViewHintValue; - -/** - A function called when an event occurs. -*/ -typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event); - -/** - @name Setup - Functions for creating and destroying a view. - @{ -*/ - -/** - Create a new view. - - A newly created view does not correspond to a real system view or window. - It must first be configured, then the system view can be created with - puglRealize(). -*/ -PUGL_API PuglView* -puglNewView(PuglWorld* world); - -/** - Free a view created with puglNewView(). -*/ -PUGL_API void -puglFreeView(PuglView* view); - -/** - Return the world that `view` is a part of. -*/ -PUGL_API PuglWorld* -puglGetWorld(PuglView* view); - -/** - Set the user data for a view. - - This is usually a pointer to a struct that contains all the state which must - be accessed by a view. Everything needed to process events should be stored - here, not in static variables. - - The handle is opaque to Pugl and is not interpreted in any way. -*/ -PUGL_API void -puglSetHandle(PuglView* view, PuglHandle handle); - -/** - Get the user data for a view. -*/ -PUGL_API PuglHandle -puglGetHandle(PuglView* view); - -/** - Set the graphics backend to use for a view. - - This must be called once to set the graphics backend before calling - puglRealize(). - - Pugl includes the following backends: - - - puglGlBackend(), declared in pugl_gl.h - - puglCairoBackend(), declared in pugl_cairo.h - - Note that backends are modular and not compiled into the main Pugl library - to avoid unnecessary dependencies. To use a particular backend, - applications must link against the appropriate backend library, or be sure - to compile in the appropriate code if using a local copy of Pugl. -*/ -PUGL_API PuglStatus -puglSetBackend(PuglView* view, const PuglBackend* backend); - -/** - Set the function to call when an event occurs. -*/ -PUGL_API PuglStatus -puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc); - -/** - Set a hint to configure view properties. - - This only has an effect when called before puglRealize(). -*/ -PUGL_API PuglStatus -puglSetViewHint(PuglView* view, PuglViewHint hint, int value); - -/** - Get the value for a view hint. - - If the view has been realized, this can be used to get the actual value of a - hint which was initially set to PUGL_DONT_CARE, or has been adjusted from - the suggested value. -*/ -PUGL_API int -puglGetViewHint(const PuglView* view, PuglViewHint hint); - -/** - @} - @anchor frame - @name Frame - Functions for working with the position and size of a view. - @{ -*/ - -/** - Get the current position and size of the view. - - The position is in screen coordinates with an upper left origin. -*/ -PUGL_API PuglRect -puglGetFrame(const PuglView* view); - -/** - Set the current position and size of the view. - - The position is in screen coordinates with an upper left origin. - - @return #PUGL_UNKNOWN_ERROR on failure, in which case the view frame is - unchanged. -*/ -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. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -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 - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -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. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -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 - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currenty work on MacOS (the minimum is used), so only setting a fixed aspect - ratio works properly across all platforms. - - If an initial aspect ratio is known, this should be called before - puglRealize() to avoid stutter, though it can be called afterwards as well. - - @return #PUGL_UNKNOWN_ERROR on failure, but always succeeds if the view is - not yet realized. -*/ -PUGL_API PuglStatus -puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY); - -/** - @} - @name Windows - Functions for working with system views and the window hierarchy. - @{ -*/ - -/** - Set the title of the window. - - This only makes sense for non-embedded views that will have a corresponding - top-level window, and sets the title, typically displayed in the title bar - or in window switchers. -*/ -PUGL_API PuglStatus -puglSetWindowTitle(PuglView* view, const char* title); - -/** - Set the parent window for embedding a view in an existing window. - - This must be called before puglRealize(), reparenting is not supported. -*/ -PUGL_API PuglStatus -puglSetParentWindow(PuglView* view, PuglNativeView parent); - -/** - Set the transient parent of the window. - - 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); - -/** - Realise a view by creating a corresponding system view or window. - - After this call, the (initially invisible) underlying system view exists and - can be accessed with puglGetNativeWindow(). There is currently no - corresponding unrealize function, the system view will be destroyed along - with the view when puglFreeView() is called. - - The view should be fully configured using the above functions before this is - called. This function may only be called once per view. -*/ -PUGL_API PuglStatus -puglRealize(PuglView* view); - -/** - Show the view. - - If the view has not yet been realized, the first call to this function will - do so automatically. - - If the view is currently hidden, it will be shown and possibly raised to the - top depending on the platform. -*/ -PUGL_API PuglStatus -puglShowWindow(PuglView* view); - -/** - Hide the current window. -*/ -PUGL_API PuglStatus -puglHideWindow(PuglView* view); - -/** - Return true iff the view is currently visible. -*/ -PUGL_API bool -puglGetVisible(const PuglView* view); - -/** - Return the native window handle. -*/ -PUGL_API PuglNativeView -puglGetNativeWindow(PuglView* view); - -/** - @} - @name Graphics - Functions for working with the graphics context and scheduling redisplays. - @{ -*/ - -/** - Get the graphics context. - - This is a backend-specific context used for drawing if the backend graphics - API requires one. It is only available during an expose. - - @return - - Cairo: A pointer to a - [`cairo_t`](http://www.cairographics.org/manual/cairo-cairo-t.html). - - OpenGL: `NULL`. - - Stub: `NULL`. -*/ -PUGL_API void* -puglGetContext(PuglView* view); - -/** - Request a redisplay for the entire view. - - This will cause an expose event to be dispatched later. If called from - within the event handler, the expose should arrive at the end of the current - event loop iteration, though this is not strictly guaranteed on all - platforms. If called elsewhere, an expose will be enqueued to be processed - in the next event loop iteration. -*/ -PUGL_API PuglStatus -puglPostRedisplay(PuglView* view); - -/** - Request a redisplay of the given rectangle within the view. - - This has the same semantics as puglPostRedisplay(), but allows giving a - precise region for redrawing only a portion of the view. -*/ -PUGL_API PuglStatus -puglPostRedisplayRect(PuglView* view, PuglRect rect); - -/** - @} - @anchor interaction - @name Interaction - Functions for interacting with the user and window system. - @{ -*/ - -/** - 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 -puglGrabFocus(PuglView* view); - -/** - Return whether `view` has the keyboard input focus. -*/ -PUGL_API bool -puglHasFocus(const PuglView* view); - -/** - Set the clipboard contents. - - This sets the system clipboard contents, which can be retrieved with - puglGetClipboard() or pasted into other applications. - - @param view The view. - @param type The MIME type of the data, "text/plain" is assumed if `NULL`. - @param data The data to copy to the clipboard. - @param len The length of data in bytes (including terminator if necessary). -*/ -PUGL_API PuglStatus -puglSetClipboard(PuglView* view, - const char* type, - const void* data, - size_t len); - -/** - Get the clipboard contents. - - This gets the system clipboard contents, which may have been set with - puglSetClipboard() or copied from another application. - - @param view The view. - @param[out] type Set to the MIME type of the data. - @param[out] len Set to the length of the data in bytes. - @return The clipboard contents, or `NULL`. -*/ -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. - - @return - - #PUGL_BAD_PARAMETER if the given cursor is invalid. - - #PUGL_FAILURE if the cursor isknown but loading it from the system fails. -*/ -PUGL_API PuglStatus -puglSetCursor(PuglView* view, PuglCursor cursor); - -/** - Request user attention. - - This hints to the system that the window or application requires attention - from the user. The exact effect depends on the platform, but is usually - something like a flashing task bar entry or bouncing application icon. -*/ -PUGL_API PuglStatus -puglRequestAttention(PuglView* view); - -/** - Activate a repeating timer event. - - This starts a timer which will send a #PuglEventTimer to `view` every - `timeout` seconds. This can be used to perform some action in a view at a - regular interval with relatively low frequency. Note that the frequency of - timer events may be limited by how often puglUpdate() is called. - - If the given timer already exists, it is replaced. - - @param view The view to begin seding #PUGL_TIMER events to. - - @param id The identifier for this timer. This is an application-specific ID - that should be a low number, typically the value of a constant or `enum` - that starts from 0. There is a platform-specific limit to the number of - supported timers, and overhead associated with each, so applications should - create only a few timers and perform several tasks in one if necessary. - - @param timeout The period, in seconds, of this timer. This is not - guaranteed to have a resolution better than 10ms (the maximum timer - resolution on Windows) and may be rounded up if it is too short. On X11 and - MacOS, a resolution of about 1ms can usually be relied on. - - @return - - #PUGL_FAILURE if timers are not supported by this system or build. - - #PUGL_UNKNOWN_ERROR if setting the timer failed. -*/ -PUGL_API PuglStatus -puglStartTimer(PuglView* view, uintptr_t id, double timeout); - -/** - Stop an active timer. - - @param view The view that the timer is set for. - @param id The ID previously passed to puglStartTimer(). - - @return - - #PUGL_FAILURE if timers are not supported by this system or build. - - #PUGL_UNKNOWN_ERROR if stopping the timer failed. -*/ -PUGL_API PuglStatus -puglStopTimer(PuglView* view, uintptr_t id); - -/** - Send an event to a view via the window system. - - If supported, the event will be delivered to the view via the event loop - like other events. Note that this function only works for certain event - types. - - Currently, only #PUGL_CLIENT events are supported on all platforms. - - X11: A #PUGL_EXPOSE event can be sent, which is similar to calling - puglPostRedisplayRect(), but will always send a message to the X server, - even when called in an event handler. - - @return - - #PUGL_UNSUPPORTED_TYPE if sending events of this type is not supported. - - #PUGL_UNKNOWN_ERROR if sending the event failed. -*/ -PUGL_API PuglStatus -puglSendEvent(PuglView* view, const PuglEvent* event); - -/** - @} -*/ - -#ifndef PUGL_DISABLE_DEPRECATED - -/** - @} - @name Deprecated API - @{ -*/ - -/** - A native window handle. - - X11: This is a `Window`. - - MacOS: This is a pointer to an `NSView*`. - - Windows: This is a `HWND`. -*/ -typedef uintptr_t PuglNativeWindow; - -/** - Create a Pugl application and view. - - To create a window, call the various puglInit* functions as necessary, then - call puglRealize(). - - @deprecated Use puglNewApp() and puglNewView(). - - @param pargc Pointer to argument count (currently unused). - @param argv Arguments (currently unused). - @return A newly created view. -*/ -static inline PUGL_DEPRECATED_BY("puglNewView") PuglView* -puglInit(const int* pargc, char** argv) -{ - (void)pargc; - (void)argv; - - return puglNewView(puglNewWorld(PUGL_MODULE, 0)); -} - -/** - Destroy an app and view created with `puglInit()`. - - @deprecated Use puglFreeApp() and puglFreeView(). -*/ -static inline PUGL_DEPRECATED_BY("puglFreeView") void -puglDestroy(PuglView* view) -{ - PuglWorld* const world = puglGetWorld(view); - - puglFreeView(view); - puglFreeWorld(world); -} - -/** - Set the window class name before creating a window. -*/ -static inline PUGL_DEPRECATED_BY("puglSetClassName") void -puglInitWindowClass(PuglView* view, const char* name) -{ - puglSetClassName(puglGetWorld(view), name); -} - -/** - Set the window size before creating a window. - - @deprecated Use puglSetFrame(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetFrame") void -puglInitWindowSize(PuglView* view, int width, int height) -{ - PuglRect frame = puglGetFrame(view); - - frame.width = width; - frame.height = height; - - puglSetFrame(view, frame); -} - -/** - Set the minimum window size before creating a window. -*/ -static inline PUGL_DEPRECATED_BY("puglSetMinSize") void -puglInitWindowMinSize(PuglView* view, int width, int height) -{ - puglSetMinSize(view, width, height); -} - -/** - Set the window aspect ratio range before creating a window. - - The x and y values here represent a ratio of width to height. To set a - fixed aspect ratio, set the minimum and maximum values to the same ratio. - - Note that setting different minimum and maximum constraints does not - currenty work on MacOS (the minimum is used), so only setting a fixed aspect - ratio works properly across all platforms. -*/ -static inline PUGL_DEPRECATED_BY("puglSetAspectRatio") void -puglInitWindowAspectRatio(PuglView* view, - int minX, - int minY, - int maxX, - int maxY) -{ - puglSetAspectRatio(view, minX, minY, maxX, maxY); -} - -/** - Set transient parent before creating a window. - - On X11, parent must be a Window. - On OSX, parent must be an NSView*. -*/ -static inline PUGL_DEPRECATED_BY("puglSetTransientFor") void -puglInitTransientFor(PuglView* view, uintptr_t parent) -{ - puglSetTransientFor(view, (PuglNativeWindow)parent); -} - -/** - Enable or disable resizing before creating a window. - - @deprecated Use puglSetViewHint() with #PUGL_RESIZABLE. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") void -puglInitResizable(PuglView* view, bool resizable) -{ - puglSetViewHint(view, PUGL_RESIZABLE, resizable); -} - -/** - Get the current size of the view. - - @deprecated Use puglGetFrame(). - -*/ -static inline PUGL_DEPRECATED_BY("puglGetFrame") void -puglGetSize(PuglView* view, int* width, int* height) -{ - const PuglRect frame = puglGetFrame(view); - - *width = (int)frame.width; - *height = (int)frame.height; -} - -/** - Ignore synthetic repeated key events. - - @deprecated Use puglSetViewHint() with #PUGL_IGNORE_KEY_REPEAT. -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") void -puglIgnoreKeyRepeat(PuglView* view, bool ignore) -{ - puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, ignore); -} - -/** - Set a hint before creating a window. - - @deprecated Use puglSetWindowHint(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetViewHint") void -puglInitWindowHint(PuglView* view, PuglViewHint hint, int value) -{ - puglSetViewHint(view, hint, value); -} - -/** - Set the parent window before creating a window (for embedding). - - @deprecated Use puglSetWindowParent(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetParentWindow") void -puglInitWindowParent(PuglView* view, PuglNativeWindow parent) -{ - puglSetParentWindow(view, parent); -} - -/** - Set the graphics backend to use. - - @deprecated Use puglSetBackend(). -*/ -static inline PUGL_DEPRECATED_BY("puglSetBackend") int -puglInitBackend(PuglView* view, const PuglBackend* backend) -{ - return (int)puglSetBackend(view, backend); -} - -/** - Realise a view by creating a corresponding system view or window. - - The view should be fully configured using the above functions before this is - called. This function may only be called once per view. - - @deprecated Use puglRealize(), or just show the view. -*/ -static inline PUGL_DEPRECATED_BY("puglRealize") PuglStatus -puglCreateWindow(PuglView* view, const char* title) -{ - puglSetWindowTitle(view, title); - return puglRealize(view); -} - -/** - Block and wait for an event to be ready. - - This can be used in a loop to only process events via puglProcessEvents when - necessary. This function will block indefinitely if no events are - available, so is not appropriate for use in programs that need to perform - regular updates (e.g. animation). - - @deprecated Use puglPollEvents(). -*/ -PUGL_API PUGL_DEPRECATED_BY("puglPollEvents") PuglStatus -puglWaitForEvent(PuglView* view); - -/** - Process all pending window events. - - This handles input events as well as rendering, so it should be called - regularly and rapidly enough to keep the UI responsive. This function does - not block if no events are pending. - - @deprecated Use puglDispatchEvents(). -*/ -PUGL_API PUGL_DEPRECATED_BY("puglDispatchEvents") PuglStatus -puglProcessEvents(PuglView* view); - -/** - Poll for events that are ready to be processed. - - This polls for events that are ready for any view in the world, potentially - blocking depending on `timeout`. - - @param world The world to poll for events. - - @param timeout Maximum time to wait, in seconds. If zero, the call returns - immediately, if negative, the call blocks indefinitely. - - @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error. - - @deprecated Use puglUpdate(). -*/ -PUGL_API PUGL_DEPRECATED_BY("puglUpdate") PuglStatus -puglPollEvents(PuglWorld* world, double timeout); - -/** - Dispatch any pending events to views. - - This processes all pending events, dispatching them to the appropriate - views. View event handlers will be called in the scope of this call. This - function does not block, if no events are pending then it will return - immediately. - - @deprecated Use puglUpdate(). -*/ -PUGL_API PUGL_DEPRECATED_BY("puglUpdate") PuglStatus -puglDispatchEvents(PuglWorld* world); - -/** - Enter the graphics context. - - Note that, unlike some similar libraries, Pugl automatically enters and - leaves the graphics context when required and application should not - normally do this. Drawing in Pugl is only allowed during the processing of - an expose event. - - However, this can be used to enter the graphics context elsewhere, for - example to call any GL functions during setup. - - @param view The view being entered. - @param drawing If true, prepare for drawing. - - @deprecated Set up graphics when a #PUGL_CREATE event is received. -*/ -PUGL_API PUGL_DEPRECATED_BY("PUGL_CREATE") PuglStatus -puglEnterContext(PuglView* view, bool drawing); - -/** - Leave the graphics context. - - This must be called after puglEnterContext() with a matching `drawing` - parameter. - - @param view The view being left. - @param drawing If true, finish drawing, for example by swapping buffers. - - @deprecated Shut down graphics when a #PUGL_DESTROY event is received. -*/ -PUGL_API PUGL_DEPRECATED_BY("PUGL_DESTROY") PuglStatus -puglLeaveContext(PuglView* view, bool drawing); - -#endif /* PUGL_DISABLE_DEPRECATED */ - -/** - @} - @} - @} -*/ - -PUGL_END_DECLS - -#endif /* PUGL_PUGL_H */ diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp deleted file mode 100644 index 9709e51..0000000 --- a/pugl/pugl.hpp +++ /dev/null @@ -1,726 +0,0 @@ -/* - 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.hpp - @brief Pugl C++ API wrapper. -*/ - -#ifndef PUGL_PUGL_HPP -#define PUGL_PUGL_HPP - -#include "pugl/pugl.h" - -#include <cassert> -#include <chrono> -#include <functional> -#include <memory> -#include <stdexcept> -#include <type_traits> - -/** - @defgroup pugl_cxx C++ API - C++ API wrapper. - - @ingroup pugl - @{ -*/ - -/** - Pugl C++ API namespace. -*/ -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: - Wrapper(const Wrapper&) = delete; - Wrapper& operator=(const Wrapper&) = delete; - - Wrapper(Wrapper&&) = default; - Wrapper& operator=(Wrapper&&) = default; - - 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 - -/** - @defgroup eventsxx Events - @ingroup pugl_cxx - @copydoc events - @{ -*/ - -/** - A strongly-typed analogue of PuglEvent. - - 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. -*/ -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: - 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} - {} - - Clock(const Clock&) = delete; - Clock& operator=(const Clock&) = delete; - - Clock(Clock&&) = delete; - Clock& operator=(Clock&&) = delete; - - /// Return the current time - time_point now() const; - -private: - const pugl::World& _world; -}; - -/// @copydoc PuglWorld -class World : public detail::Wrapper<PuglWorld, puglFreeWorld> -{ -public: - World(const World&) = delete; - World& operator=(const World&) = delete; - - World(World&&) = delete; - World& operator=(World&&) = delete; - - 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, {}} - { - if (!cobj()) { - throw std::runtime_error("Failed to create pugl::World"); - } - } - - /// @copydoc puglGetNativeWorld - void* nativeWorld() { return puglGetNativeWorld(cobj()); } - - // TODO: setLogFunc - - Status setLogLevel(const LogLevel level) - { - return static_cast<Status>( - puglSetLogLevel(cobj(), static_cast<PuglLogLevel>(level))); - } - - /// @copydoc puglSetClassName - Status setClassName(const char* const name) - { - return static_cast<Status>(puglSetClassName(cobj(), name)); - } - - /// @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 - refreshRate, ///< @copydoc PUGL_REFRESH_RATE -}; - -static_assert(ViewHint(PUGL_REFRESH_RATE) == ViewHint::refreshRate, ""); - -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)); - } - - /// @copydoc puglGetViewHint - int getHint(ViewHint hint) - { - return puglGetViewHint(cobj(), static_cast<PuglViewHint>(hint)); - } - - /** - @} - @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)); - } - - /// @copydoc puglSetMaxSize - Status setMaxSize(int width, int height) - { - return static_cast<Status>(puglSetMaxSize(cobj(), width, height)); - } - - /// @copydoc puglSetAspectRatio - Status setAspectRatio(int minX, int minY, int maxX, int maxY) - { - return static_cast<Status>( - puglSetAspectRatio(cobj(), minX, minY, maxX, maxY)); - } - - /** - @} - @name Windows - Methods for working with top-level windows. - @{ - */ - - /// @copydoc puglSetWindowTitle - Status setWindowTitle(const char* title) - { - return static_cast<Status>(puglSetWindowTitle(cobj(), title)); - } - - /// @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())); - } - - /// @copydoc puglPostRedisplayRect - Status postRedisplayRect(const Rect rect) - { - return static_cast<Status>(puglPostRedisplayRect(cobj(), rect)); - } - - /** - @} - @name Interaction - Methods for interacting with the user and window system. - @{ - */ - - /// @copydoc puglGrabFocus - Status grabFocus() { return static_cast<Status>(puglGrabFocus(cobj())); } - - /// @copydoc puglHasFocus - bool hasFocus() const { return puglHasFocus(cobj()); } - - /// @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: - 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; - } - - 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 - -/** - @} -*/ - -#endif /* PUGL_PUGL_HPP */ diff --git a/pugl/pugl.ipp b/pugl/pugl.ipp deleted file mode 100644 index 0ed8c4d..0000000 --- a/pugl/pugl.ipp +++ /dev/null @@ -1,154 +0,0 @@ -/* - 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; -} - -} // namespace pugl diff --git a/pugl/pugl_cairo.h b/pugl/pugl_cairo.h deleted file mode 100644 index 4b3b621..0000000 --- a/pugl/pugl_cairo.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - 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_cairo.h - @brief Declaration of Cairo backend accessor. -*/ - -#ifndef PUGL_PUGL_CAIRO_H -#define PUGL_PUGL_CAIRO_H - -#include "pugl/pugl.h" - -PUGL_BEGIN_DECLS - -/** - @defgroup cairo Cairo - Cairo graphics support. - @ingroup pugl_c - @{ -*/ - -/** - Cairo graphics backend accessor. - - Pass the return value to puglInitBackend() to draw to a view with Cairo. -*/ -PUGL_API PUGL_CONST_FUNC const PuglBackend* -puglCairoBackend(void); - -/** - @} -*/ - -PUGL_END_DECLS - -#endif // PUGL_PUGL_CAIRO_H diff --git a/pugl/pugl_cairo.hpp b/pugl/pugl_cairo.hpp deleted file mode 100644 index 5b17ab7..0000000 --- a/pugl/pugl_cairo.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - 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_cairo.hpp - @brief Declaration of Cairo backend accessor for C++. -*/ - -#ifndef PUGL_PUGL_CAIRO_HPP -#define PUGL_PUGL_CAIRO_HPP - -#include "pugl/pugl.h" -#include "pugl/pugl_cairo.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_gl.h b/pugl/pugl_gl.h deleted file mode 100644 index 471c5ac..0000000 --- a/pugl/pugl_gl.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - 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_gl.h - @brief OpenGL-specific API. -*/ - -#ifndef PUGL_PUGL_GL_H -#define PUGL_PUGL_GL_H - -#include "pugl/pugl.h" - -PUGL_BEGIN_DECLS - -/** - @defgroup gl OpenGL - OpenGL graphics support. - @ingroup pugl_c - @{ -*/ - -/** - OpenGL extension function. -*/ -typedef void (*PuglGlFunc)(void); - -/** - Return the address of an OpenGL extension function. -*/ -PUGL_API PuglGlFunc -puglGetProcAddress(const char* name); - -/** - OpenGL graphics backend. - - Pass the return value to puglSetBackend() to draw to a view with OpenGL. -*/ -PUGL_API PUGL_CONST_FUNC const PuglBackend* -puglGlBackend(void); - -PUGL_END_DECLS - -/** - @} -*/ - -#endif // PUGL_PUGL_GL_H diff --git a/pugl/pugl_gl.hpp b/pugl/pugl_gl.hpp deleted file mode 100644 index 4bc5bbd..0000000 --- a/pugl/pugl_gl.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - 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_gl.hpp - @brief OpenGL-specific C++ API. -*/ - -#ifndef PUGL_PUGL_GL_HPP -#define PUGL_PUGL_GL_HPP - -#include "pugl/pugl.h" -#include "pugl/pugl_gl.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_stub.h b/pugl/pugl_stub.h deleted file mode 100644 index f50418e..0000000 --- a/pugl/pugl_stub.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - 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 - 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_stub.h - @brief Stub backend functions and accessor declaration. -*/ - -#ifndef PUGL_PUGL_STUB_H -#define PUGL_PUGL_STUB_H - -#include "pugl/pugl.h" - -PUGL_BEGIN_DECLS - -/** - @defgroup stub Stub - - Stub graphics backend. - - The stub backend functions do nothing and always - return success. These do not make for a usable backend on their own since - the platform implementation would fail to create a window, but are useful - for other backends to reuse since not all need non-trivial implementations - of every backend function. - - @ingroup pugl_c - @{ -*/ - -/** - Stub graphics backend. - - This backend just creates a simple native window without setting up any - portable graphics API. -*/ -PUGL_API PUGL_CONST_FUNC -const PuglBackend* -puglStubBackend(void); - -/** - @} -*/ - -PUGL_END_DECLS - -#endif // PUGL_PUGL_STUB_H diff --git a/pugl/pugl_stub.hpp b/pugl/pugl_stub.hpp deleted file mode 100644 index c5f3901..0000000 --- a/pugl/pugl_stub.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - 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_stub.hpp - @brief Declaration of Stub backend accessor for C++. -*/ - -#ifndef PUGL_PUGL_STUB_HPP -#define PUGL_PUGL_STUB_HPP - -#include "pugl/pugl.h" -#include "pugl/pugl_stub.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 |