diff options
Diffstat (limited to 'include/pugl/detail/mac.m')
-rw-r--r-- | include/pugl/detail/mac.m | 1449 |
1 files changed, 0 insertions, 1449 deletions
diff --git a/include/pugl/detail/mac.m b/include/pugl/detail/mac.m deleted file mode 100644 index a807761..0000000 --- a/include/pugl/detail/mac.m +++ /dev/null @@ -1,1449 +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/mac.h" - -#include "pugl/detail/implementation.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; - 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 -{ - puglDispatchSimpleEvent(puglview, PUGL_LOOP_ENTER); -} - -- (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 -{ - puglDispatchSimpleEvent(puglview, PUGL_LOOP_LEAVE); -} - -@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; -} |