diff options
Diffstat (limited to 'pugl/detail')
-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 |
19 files changed, 0 insertions, 6506 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; -} |