twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

cocoa_window.m (52532B)


      1 //========================================================================
      2 // GLFW 3.3 macOS - www.glfw.org
      3 //------------------------------------------------------------------------
      4 // Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
      5 //
      6 // This software is provided 'as-is', without any express or implied
      7 // warranty. In no event will the authors be held liable for any damages
      8 // arising from the use of this software.
      9 //
     10 // Permission is granted to anyone to use this software for any purpose,
     11 // including commercial applications, and to alter it and redistribute it
     12 // freely, subject to the following restrictions:
     13 //
     14 // 1. The origin of this software must not be misrepresented; you must not
     15 //    claim that you wrote the original software. If you use this software
     16 //    in a product, an acknowledgment in the product documentation would
     17 //    be appreciated but is not required.
     18 //
     19 // 2. Altered source versions must be plainly marked as such, and must not
     20 //    be misrepresented as being the original software.
     21 //
     22 // 3. This notice may not be removed or altered from any source
     23 //    distribution.
     24 //
     25 //========================================================================
     26 // It is fine to use C99 in this file because it will not be built with VS
     27 //========================================================================
     28 
     29 #include "internal.h"
     30 
     31 #include <float.h>
     32 #include <string.h>
     33 
     34 // Returns the style mask corresponding to the window settings
     35 //
     36 static NSUInteger getStyleMask(_GLFWwindow* window)
     37 {
     38     NSUInteger styleMask = NSWindowStyleMaskMiniaturizable;
     39 
     40     if (window->monitor || !window->decorated)
     41         styleMask |= NSWindowStyleMaskBorderless;
     42     else
     43     {
     44         styleMask |= NSWindowStyleMaskTitled |
     45                      NSWindowStyleMaskClosable;
     46 
     47         if (window->resizable)
     48             styleMask |= NSWindowStyleMaskResizable;
     49     }
     50 
     51     return styleMask;
     52 }
     53 
     54 // Returns whether the cursor is in the content area of the specified window
     55 //
     56 static GLFWbool cursorInContentArea(_GLFWwindow* window)
     57 {
     58     const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
     59     return [window->ns.view mouse:pos inRect:[window->ns.view frame]];
     60 }
     61 
     62 // Hides the cursor if not already hidden
     63 //
     64 static void hideCursor(_GLFWwindow* window)
     65 {
     66     if (!_glfw.ns.cursorHidden)
     67     {
     68         [NSCursor hide];
     69         _glfw.ns.cursorHidden = GLFW_TRUE;
     70     }
     71 }
     72 
     73 // Shows the cursor if not already shown
     74 //
     75 static void showCursor(_GLFWwindow* window)
     76 {
     77     if (_glfw.ns.cursorHidden)
     78     {
     79         [NSCursor unhide];
     80         _glfw.ns.cursorHidden = GLFW_FALSE;
     81     }
     82 }
     83 
     84 // Updates the cursor image according to its cursor mode
     85 //
     86 static void updateCursorImage(_GLFWwindow* window)
     87 {
     88     if (window->cursorMode == GLFW_CURSOR_NORMAL)
     89     {
     90         showCursor(window);
     91 
     92         if (window->cursor)
     93             [(NSCursor*) window->cursor->ns.object set];
     94         else
     95             [[NSCursor arrowCursor] set];
     96     }
     97     else
     98         hideCursor(window);
     99 }
    100 
    101 // Apply chosen cursor mode to a focused window
    102 //
    103 static void updateCursorMode(_GLFWwindow* window)
    104 {
    105     if (window->cursorMode == GLFW_CURSOR_DISABLED)
    106     {
    107         _glfw.ns.disabledCursorWindow = window;
    108         _glfwPlatformGetCursorPos(window,
    109                                   &_glfw.ns.restoreCursorPosX,
    110                                   &_glfw.ns.restoreCursorPosY);
    111         _glfwCenterCursorInContentArea(window);
    112         CGAssociateMouseAndMouseCursorPosition(false);
    113     }
    114     else if (_glfw.ns.disabledCursorWindow == window)
    115     {
    116         _glfw.ns.disabledCursorWindow = NULL;
    117         CGAssociateMouseAndMouseCursorPosition(true);
    118         _glfwPlatformSetCursorPos(window,
    119                                   _glfw.ns.restoreCursorPosX,
    120                                   _glfw.ns.restoreCursorPosY);
    121     }
    122 
    123     if (cursorInContentArea(window))
    124         updateCursorImage(window);
    125 }
    126 
    127 // Make the specified window and its video mode active on its monitor
    128 //
    129 static void acquireMonitor(_GLFWwindow* window)
    130 {
    131     _glfwSetVideoModeNS(window->monitor, &window->videoMode);
    132     const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID);
    133     const NSRect frame = NSMakeRect(bounds.origin.x,
    134                                     _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1),
    135                                     bounds.size.width,
    136                                     bounds.size.height);
    137 
    138     [window->ns.object setFrame:frame display:YES];
    139 
    140     _glfwInputMonitorWindow(window->monitor, window);
    141 }
    142 
    143 // Remove the window and restore the original video mode
    144 //
    145 static void releaseMonitor(_GLFWwindow* window)
    146 {
    147     if (window->monitor->window != window)
    148         return;
    149 
    150     _glfwInputMonitorWindow(window->monitor, NULL);
    151     _glfwRestoreVideoModeNS(window->monitor);
    152 }
    153 
    154 // Translates macOS key modifiers into GLFW ones
    155 //
    156 static int translateFlags(NSUInteger flags)
    157 {
    158     int mods = 0;
    159 
    160     if (flags & NSEventModifierFlagShift)
    161         mods |= GLFW_MOD_SHIFT;
    162     if (flags & NSEventModifierFlagControl)
    163         mods |= GLFW_MOD_CONTROL;
    164     if (flags & NSEventModifierFlagOption)
    165         mods |= GLFW_MOD_ALT;
    166     if (flags & NSEventModifierFlagCommand)
    167         mods |= GLFW_MOD_SUPER;
    168     if (flags & NSEventModifierFlagCapsLock)
    169         mods |= GLFW_MOD_CAPS_LOCK;
    170 
    171     return mods;
    172 }
    173 
    174 // Translates a macOS keycode to a GLFW keycode
    175 //
    176 static int translateKey(unsigned int key)
    177 {
    178     if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
    179         return GLFW_KEY_UNKNOWN;
    180 
    181     return _glfw.ns.keycodes[key];
    182 }
    183 
    184 // Translate a GLFW keycode to a Cocoa modifier flag
    185 //
    186 static NSUInteger translateKeyToModifierFlag(int key)
    187 {
    188     switch (key)
    189     {
    190         case GLFW_KEY_LEFT_SHIFT:
    191         case GLFW_KEY_RIGHT_SHIFT:
    192             return NSEventModifierFlagShift;
    193         case GLFW_KEY_LEFT_CONTROL:
    194         case GLFW_KEY_RIGHT_CONTROL:
    195             return NSEventModifierFlagControl;
    196         case GLFW_KEY_LEFT_ALT:
    197         case GLFW_KEY_RIGHT_ALT:
    198             return NSEventModifierFlagOption;
    199         case GLFW_KEY_LEFT_SUPER:
    200         case GLFW_KEY_RIGHT_SUPER:
    201             return NSEventModifierFlagCommand;
    202         case GLFW_KEY_CAPS_LOCK:
    203             return NSEventModifierFlagCapsLock;
    204     }
    205 
    206     return 0;
    207 }
    208 
    209 // Defines a constant for empty ranges in NSTextInputClient
    210 //
    211 static const NSRange kEmptyRange = { NSNotFound, 0 };
    212 
    213 
    214 //------------------------------------------------------------------------
    215 // Delegate for window related notifications
    216 //------------------------------------------------------------------------
    217 
    218 @interface GLFWWindowDelegate : NSObject
    219 {
    220     _GLFWwindow* window;
    221 }
    222 
    223 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;
    224 
    225 @end
    226 
    227 @implementation GLFWWindowDelegate
    228 
    229 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow
    230 {
    231     self = [super init];
    232     if (self != nil)
    233         window = initWindow;
    234 
    235     return self;
    236 }
    237 
    238 - (BOOL)windowShouldClose:(id)sender
    239 {
    240     _glfwInputWindowCloseRequest(window);
    241     return NO;
    242 }
    243 
    244 - (void)windowDidResize:(NSNotification *)notification
    245 {
    246     if (window->context.client != GLFW_NO_API)
    247         [window->context.nsgl.object update];
    248 
    249     if (_glfw.ns.disabledCursorWindow == window)
    250         _glfwCenterCursorInContentArea(window);
    251 
    252     const int maximized = [window->ns.object isZoomed];
    253     if (window->ns.maximized != maximized)
    254     {
    255         window->ns.maximized = maximized;
    256         _glfwInputWindowMaximize(window, maximized);
    257     }
    258 
    259     const NSRect contentRect = [window->ns.view frame];
    260     const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
    261 
    262     if (fbRect.size.width != window->ns.fbWidth ||
    263         fbRect.size.height != window->ns.fbHeight)
    264     {
    265         window->ns.fbWidth  = fbRect.size.width;
    266         window->ns.fbHeight = fbRect.size.height;
    267         _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
    268     }
    269 
    270     if (contentRect.size.width != window->ns.width ||
    271         contentRect.size.height != window->ns.height)
    272     {
    273         window->ns.width  = contentRect.size.width;
    274         window->ns.height = contentRect.size.height;
    275         _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height);
    276     }
    277 }
    278 
    279 - (void)windowDidMove:(NSNotification *)notification
    280 {
    281     if (window->context.client != GLFW_NO_API)
    282         [window->context.nsgl.object update];
    283 
    284     if (_glfw.ns.disabledCursorWindow == window)
    285         _glfwCenterCursorInContentArea(window);
    286 
    287     int x, y;
    288     _glfwPlatformGetWindowPos(window, &x, &y);
    289     _glfwInputWindowPos(window, x, y);
    290 }
    291 
    292 - (void)windowDidMiniaturize:(NSNotification *)notification
    293 {
    294     if (window->monitor)
    295         releaseMonitor(window);
    296 
    297     _glfwInputWindowIconify(window, GLFW_TRUE);
    298 }
    299 
    300 - (void)windowDidDeminiaturize:(NSNotification *)notification
    301 {
    302     if (window->monitor)
    303         acquireMonitor(window);
    304 
    305     _glfwInputWindowIconify(window, GLFW_FALSE);
    306 }
    307 
    308 - (void)windowDidBecomeKey:(NSNotification *)notification
    309 {
    310     if (_glfw.ns.disabledCursorWindow == window)
    311         _glfwCenterCursorInContentArea(window);
    312 
    313     _glfwInputWindowFocus(window, GLFW_TRUE);
    314     updateCursorMode(window);
    315 }
    316 
    317 - (void)windowDidResignKey:(NSNotification *)notification
    318 {
    319     if (window->monitor && window->autoIconify)
    320         _glfwPlatformIconifyWindow(window);
    321 
    322     _glfwInputWindowFocus(window, GLFW_FALSE);
    323 }
    324 
    325 @end
    326 
    327 
    328 //------------------------------------------------------------------------
    329 // Content view class for the GLFW window
    330 //------------------------------------------------------------------------
    331 
    332 @interface GLFWContentView : NSView <NSTextInputClient>
    333 {
    334     _GLFWwindow* window;
    335     NSTrackingArea* trackingArea;
    336     NSMutableAttributedString* markedText;
    337 }
    338 
    339 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;
    340 
    341 @end
    342 
    343 @implementation GLFWContentView
    344 
    345 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow
    346 {
    347     self = [super init];
    348     if (self != nil)
    349     {
    350         window = initWindow;
    351         trackingArea = nil;
    352         markedText = [[NSMutableAttributedString alloc] init];
    353 
    354         [self updateTrackingAreas];
    355         // NOTE: kUTTypeURL corresponds to NSPasteboardTypeURL but is available
    356         //       on 10.7 without having been deprecated yet
    357         [self registerForDraggedTypes:@[(__bridge NSString*) kUTTypeURL]];
    358     }
    359 
    360     return self;
    361 }
    362 
    363 - (void)dealloc
    364 {
    365     [trackingArea release];
    366     [markedText release];
    367     [super dealloc];
    368 }
    369 
    370 - (BOOL)isOpaque
    371 {
    372     return [window->ns.object isOpaque];
    373 }
    374 
    375 - (BOOL)canBecomeKeyView
    376 {
    377     return YES;
    378 }
    379 
    380 - (BOOL)acceptsFirstResponder
    381 {
    382     return YES;
    383 }
    384 
    385 - (BOOL)wantsUpdateLayer
    386 {
    387     return YES;
    388 }
    389 
    390 - (void)updateLayer
    391 {
    392     if (window->context.client != GLFW_NO_API)
    393         [window->context.nsgl.object update];
    394 
    395     _glfwInputWindowDamage(window);
    396 }
    397 
    398 - (void)cursorUpdate:(NSEvent *)event
    399 {
    400     updateCursorImage(window);
    401 }
    402 
    403 - (BOOL)acceptsFirstMouse:(NSEvent *)event
    404 {
    405     return YES;
    406 }
    407 
    408 - (void)mouseDown:(NSEvent *)event
    409 {
    410     _glfwInputMouseClick(window,
    411                          GLFW_MOUSE_BUTTON_LEFT,
    412                          GLFW_PRESS,
    413                          translateFlags([event modifierFlags]));
    414 }
    415 
    416 - (void)mouseDragged:(NSEvent *)event
    417 {
    418     [self mouseMoved:event];
    419 }
    420 
    421 - (void)mouseUp:(NSEvent *)event
    422 {
    423     _glfwInputMouseClick(window,
    424                          GLFW_MOUSE_BUTTON_LEFT,
    425                          GLFW_RELEASE,
    426                          translateFlags([event modifierFlags]));
    427 }
    428 
    429 - (void)mouseMoved:(NSEvent *)event
    430 {
    431     if (window->cursorMode == GLFW_CURSOR_DISABLED)
    432     {
    433         const double dx = [event deltaX] - window->ns.cursorWarpDeltaX;
    434         const double dy = [event deltaY] - window->ns.cursorWarpDeltaY;
    435 
    436         _glfwInputCursorPos(window,
    437                             window->virtualCursorPosX + dx,
    438                             window->virtualCursorPosY + dy);
    439     }
    440     else
    441     {
    442         const NSRect contentRect = [window->ns.view frame];
    443         // NOTE: The returned location uses base 0,1 not 0,0
    444         const NSPoint pos = [event locationInWindow];
    445 
    446         _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y);
    447     }
    448 
    449     window->ns.cursorWarpDeltaX = 0;
    450     window->ns.cursorWarpDeltaY = 0;
    451 }
    452 
    453 - (void)rightMouseDown:(NSEvent *)event
    454 {
    455     _glfwInputMouseClick(window,
    456                          GLFW_MOUSE_BUTTON_RIGHT,
    457                          GLFW_PRESS,
    458                          translateFlags([event modifierFlags]));
    459 }
    460 
    461 - (void)rightMouseDragged:(NSEvent *)event
    462 {
    463     [self mouseMoved:event];
    464 }
    465 
    466 - (void)rightMouseUp:(NSEvent *)event
    467 {
    468     _glfwInputMouseClick(window,
    469                          GLFW_MOUSE_BUTTON_RIGHT,
    470                          GLFW_RELEASE,
    471                          translateFlags([event modifierFlags]));
    472 }
    473 
    474 - (void)otherMouseDown:(NSEvent *)event
    475 {
    476     _glfwInputMouseClick(window,
    477                          (int) [event buttonNumber],
    478                          GLFW_PRESS,
    479                          translateFlags([event modifierFlags]));
    480 }
    481 
    482 - (void)otherMouseDragged:(NSEvent *)event
    483 {
    484     [self mouseMoved:event];
    485 }
    486 
    487 - (void)otherMouseUp:(NSEvent *)event
    488 {
    489     _glfwInputMouseClick(window,
    490                          (int) [event buttonNumber],
    491                          GLFW_RELEASE,
    492                          translateFlags([event modifierFlags]));
    493 }
    494 
    495 - (void)mouseExited:(NSEvent *)event
    496 {
    497     if (window->cursorMode == GLFW_CURSOR_HIDDEN)
    498         showCursor(window);
    499 
    500     _glfwInputCursorEnter(window, GLFW_FALSE);
    501 }
    502 
    503 - (void)mouseEntered:(NSEvent *)event
    504 {
    505     if (window->cursorMode == GLFW_CURSOR_HIDDEN)
    506         hideCursor(window);
    507 
    508     _glfwInputCursorEnter(window, GLFW_TRUE);
    509 }
    510 
    511 - (void)viewDidChangeBackingProperties
    512 {
    513     const NSRect contentRect = [window->ns.view frame];
    514     const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
    515 
    516     if (fbRect.size.width != window->ns.fbWidth ||
    517         fbRect.size.height != window->ns.fbHeight)
    518     {
    519         window->ns.fbWidth  = fbRect.size.width;
    520         window->ns.fbHeight = fbRect.size.height;
    521         _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
    522     }
    523 
    524     const float xscale = fbRect.size.width / contentRect.size.width;
    525     const float yscale = fbRect.size.height / contentRect.size.height;
    526 
    527     if (xscale != window->ns.xscale || yscale != window->ns.yscale)
    528     {
    529         window->ns.xscale = xscale;
    530         window->ns.yscale = yscale;
    531         _glfwInputWindowContentScale(window, xscale, yscale);
    532 
    533         if (window->ns.retina && window->ns.layer)
    534             [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
    535     }
    536 }
    537 
    538 - (void)drawRect:(NSRect)rect
    539 {
    540     _glfwInputWindowDamage(window);
    541 }
    542 
    543 - (void)updateTrackingAreas
    544 {
    545     if (trackingArea != nil)
    546     {
    547         [self removeTrackingArea:trackingArea];
    548         [trackingArea release];
    549     }
    550 
    551     const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
    552                                           NSTrackingActiveInKeyWindow |
    553                                           NSTrackingEnabledDuringMouseDrag |
    554                                           NSTrackingCursorUpdate |
    555                                           NSTrackingInVisibleRect |
    556                                           NSTrackingAssumeInside;
    557 
    558     trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
    559                                                 options:options
    560                                                   owner:self
    561                                                userInfo:nil];
    562 
    563     [self addTrackingArea:trackingArea];
    564     [super updateTrackingAreas];
    565 }
    566 
    567 - (void)keyDown:(NSEvent *)event
    568 {
    569     const int key = translateKey([event keyCode]);
    570     const int mods = translateFlags([event modifierFlags]);
    571 
    572     _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
    573 
    574     [self interpretKeyEvents:@[event]];
    575 }
    576 
    577 - (void)flagsChanged:(NSEvent *)event
    578 {
    579     int action;
    580     const unsigned int modifierFlags =
    581         [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
    582     const int key = translateKey([event keyCode]);
    583     const int mods = translateFlags(modifierFlags);
    584     const NSUInteger keyFlag = translateKeyToModifierFlag(key);
    585 
    586     if (keyFlag & modifierFlags)
    587     {
    588         if (window->keys[key] == GLFW_PRESS)
    589             action = GLFW_RELEASE;
    590         else
    591             action = GLFW_PRESS;
    592     }
    593     else
    594         action = GLFW_RELEASE;
    595 
    596     _glfwInputKey(window, key, [event keyCode], action, mods);
    597 }
    598 
    599 - (void)keyUp:(NSEvent *)event
    600 {
    601     const int key = translateKey([event keyCode]);
    602     const int mods = translateFlags([event modifierFlags]);
    603     _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);
    604 }
    605 
    606 - (void)scrollWheel:(NSEvent *)event
    607 {
    608     double deltaX = [event scrollingDeltaX];
    609     double deltaY = [event scrollingDeltaY];
    610 
    611     if ([event hasPreciseScrollingDeltas])
    612     {
    613         deltaX *= 0.1;
    614         deltaY *= 0.1;
    615     }
    616 
    617     if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0)
    618         _glfwInputScroll(window, deltaX, deltaY);
    619 }
    620 
    621 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
    622 {
    623     // HACK: We don't know what to say here because we don't know what the
    624     //       application wants to do with the paths
    625     return NSDragOperationGeneric;
    626 }
    627 
    628 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
    629 {
    630     const NSRect contentRect = [window->ns.view frame];
    631     // NOTE: The returned location uses base 0,1 not 0,0
    632     const NSPoint pos = [sender draggingLocation];
    633     _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y);
    634 
    635     NSPasteboard* pasteboard = [sender draggingPasteboard];
    636     NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
    637     NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]]
    638                                               options:options];
    639     const NSUInteger count = [urls count];
    640     if (count)
    641     {
    642         char** paths = calloc(count, sizeof(char*));
    643 
    644         for (NSUInteger i = 0;  i < count;  i++)
    645             paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]);
    646 
    647         _glfwInputDrop(window, (int) count, (const char**) paths);
    648 
    649         for (NSUInteger i = 0;  i < count;  i++)
    650             free(paths[i]);
    651         free(paths);
    652     }
    653 
    654     return YES;
    655 }
    656 
    657 - (BOOL)hasMarkedText
    658 {
    659     return [markedText length] > 0;
    660 }
    661 
    662 - (NSRange)markedRange
    663 {
    664     if ([markedText length] > 0)
    665         return NSMakeRange(0, [markedText length] - 1);
    666     else
    667         return kEmptyRange;
    668 }
    669 
    670 - (NSRange)selectedRange
    671 {
    672     return kEmptyRange;
    673 }
    674 
    675 - (void)setMarkedText:(id)string
    676         selectedRange:(NSRange)selectedRange
    677      replacementRange:(NSRange)replacementRange
    678 {
    679     [markedText release];
    680     if ([string isKindOfClass:[NSAttributedString class]])
    681         markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
    682     else
    683         markedText = [[NSMutableAttributedString alloc] initWithString:string];
    684 }
    685 
    686 - (void)unmarkText
    687 {
    688     [[markedText mutableString] setString:@""];
    689 }
    690 
    691 - (NSArray*)validAttributesForMarkedText
    692 {
    693     return [NSArray array];
    694 }
    695 
    696 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
    697                                                actualRange:(NSRangePointer)actualRange
    698 {
    699     return nil;
    700 }
    701 
    702 - (NSUInteger)characterIndexForPoint:(NSPoint)point
    703 {
    704     return 0;
    705 }
    706 
    707 - (NSRect)firstRectForCharacterRange:(NSRange)range
    708                          actualRange:(NSRangePointer)actualRange
    709 {
    710     const NSRect frame = [window->ns.view frame];
    711     return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0);
    712 }
    713 
    714 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
    715 {
    716     NSString* characters;
    717     NSEvent* event = [NSApp currentEvent];
    718     const int mods = translateFlags([event modifierFlags]);
    719     const int plain = !(mods & GLFW_MOD_SUPER);
    720 
    721     if ([string isKindOfClass:[NSAttributedString class]])
    722         characters = [string string];
    723     else
    724         characters = (NSString*) string;
    725 
    726     const NSUInteger length = [characters length];
    727     for (NSUInteger i = 0;  i < length;  i++)
    728     {
    729         const unichar codepoint = [characters characterAtIndex:i];
    730         if ((codepoint & 0xff00) == 0xf700)
    731             continue;
    732 
    733         _glfwInputChar(window, codepoint, mods, plain);
    734     }
    735 }
    736 
    737 - (void)doCommandBySelector:(SEL)selector
    738 {
    739 }
    740 
    741 @end
    742 
    743 
    744 //------------------------------------------------------------------------
    745 // GLFW window class
    746 //------------------------------------------------------------------------
    747 
    748 @interface GLFWWindow : NSWindow {}
    749 @end
    750 
    751 @implementation GLFWWindow
    752 
    753 - (BOOL)canBecomeKeyWindow
    754 {
    755     // Required for NSWindowStyleMaskBorderless windows
    756     return YES;
    757 }
    758 
    759 - (BOOL)canBecomeMainWindow
    760 {
    761     return YES;
    762 }
    763 
    764 @end
    765 
    766 
    767 // Create the Cocoa window
    768 //
    769 static GLFWbool createNativeWindow(_GLFWwindow* window,
    770                                    const _GLFWwndconfig* wndconfig,
    771                                    const _GLFWfbconfig* fbconfig)
    772 {
    773     window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window];
    774     if (window->ns.delegate == nil)
    775     {
    776         _glfwInputError(GLFW_PLATFORM_ERROR,
    777                         "Cocoa: Failed to create window delegate");
    778         return GLFW_FALSE;
    779     }
    780 
    781     NSRect contentRect;
    782 
    783     if (window->monitor)
    784     {
    785         GLFWvidmode mode;
    786         int xpos, ypos;
    787 
    788         _glfwPlatformGetVideoMode(window->monitor, &mode);
    789         _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
    790 
    791         contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height);
    792     }
    793     else
    794         contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height);
    795 
    796     window->ns.object = [[GLFWWindow alloc]
    797         initWithContentRect:contentRect
    798                   styleMask:getStyleMask(window)
    799                     backing:NSBackingStoreBuffered
    800                       defer:NO];
    801 
    802     if (window->ns.object == nil)
    803     {
    804         _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window");
    805         return GLFW_FALSE;
    806     }
    807 
    808     if (window->monitor)
    809         [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
    810     else
    811     {
    812         [(NSWindow*) window->ns.object center];
    813         _glfw.ns.cascadePoint =
    814             NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint:
    815                               NSPointFromCGPoint(_glfw.ns.cascadePoint)]);
    816 
    817         if (wndconfig->resizable)
    818         {
    819             const NSWindowCollectionBehavior behavior =
    820                 NSWindowCollectionBehaviorFullScreenPrimary |
    821                 NSWindowCollectionBehaviorManaged;
    822             [window->ns.object setCollectionBehavior:behavior];
    823         }
    824 
    825         if (wndconfig->floating)
    826             [window->ns.object setLevel:NSFloatingWindowLevel];
    827 
    828         if (wndconfig->maximized)
    829             [window->ns.object zoom:nil];
    830     }
    831 
    832     if (strlen(wndconfig->ns.frameName))
    833         [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)];
    834 
    835     window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window];
    836     window->ns.retina = wndconfig->ns.retina;
    837 
    838     if (fbconfig->transparent)
    839     {
    840         [window->ns.object setOpaque:NO];
    841         [window->ns.object setHasShadow:NO];
    842         [window->ns.object setBackgroundColor:[NSColor clearColor]];
    843     }
    844 
    845     [window->ns.object setContentView:window->ns.view];
    846     [window->ns.object makeFirstResponder:window->ns.view];
    847     [window->ns.object setTitle:@(wndconfig->title)];
    848     [window->ns.object setDelegate:window->ns.delegate];
    849     [window->ns.object setAcceptsMouseMovedEvents:YES];
    850     [window->ns.object setRestorable:NO];
    851 
    852 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
    853     if ([window->ns.object respondsToSelector:@selector(setTabbingMode:)])
    854         [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed];
    855 #endif
    856 
    857     _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height);
    858     _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight);
    859 
    860     return GLFW_TRUE;
    861 }
    862 
    863 
    864 //////////////////////////////////////////////////////////////////////////
    865 //////                       GLFW internal API                      //////
    866 //////////////////////////////////////////////////////////////////////////
    867 
    868 // Transforms a y-coordinate between the CG display and NS screen spaces
    869 //
    870 float _glfwTransformYNS(float y)
    871 {
    872     return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1;
    873 }
    874 
    875 
    876 //////////////////////////////////////////////////////////////////////////
    877 //////                       GLFW platform API                      //////
    878 //////////////////////////////////////////////////////////////////////////
    879 
    880 int _glfwPlatformCreateWindow(_GLFWwindow* window,
    881                               const _GLFWwndconfig* wndconfig,
    882                               const _GLFWctxconfig* ctxconfig,
    883                               const _GLFWfbconfig* fbconfig)
    884 {
    885     @autoreleasepool {
    886 
    887     if (!_glfw.ns.finishedLaunching)
    888         [NSApp run];
    889 
    890     if (!createNativeWindow(window, wndconfig, fbconfig))
    891         return GLFW_FALSE;
    892 
    893     if (ctxconfig->client != GLFW_NO_API)
    894     {
    895         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
    896         {
    897             if (!_glfwInitNSGL())
    898                 return GLFW_FALSE;
    899             if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig))
    900                 return GLFW_FALSE;
    901         }
    902         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
    903         {
    904             if (!_glfwInitEGL())
    905                 return GLFW_FALSE;
    906             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
    907                 return GLFW_FALSE;
    908         }
    909         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
    910         {
    911             if (!_glfwInitOSMesa())
    912                 return GLFW_FALSE;
    913             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
    914                 return GLFW_FALSE;
    915         }
    916     }
    917 
    918     if (window->monitor)
    919     {
    920         _glfwPlatformShowWindow(window);
    921         _glfwPlatformFocusWindow(window);
    922         acquireMonitor(window);
    923     }
    924 
    925     return GLFW_TRUE;
    926 
    927     } // autoreleasepool
    928 }
    929 
    930 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
    931 {
    932     @autoreleasepool {
    933 
    934     if (_glfw.ns.disabledCursorWindow == window)
    935         _glfw.ns.disabledCursorWindow = NULL;
    936 
    937     [window->ns.object orderOut:nil];
    938 
    939     if (window->monitor)
    940         releaseMonitor(window);
    941 
    942     if (window->context.destroy)
    943         window->context.destroy(window);
    944 
    945     [window->ns.object setDelegate:nil];
    946     [window->ns.delegate release];
    947     window->ns.delegate = nil;
    948 
    949     [window->ns.view release];
    950     window->ns.view = nil;
    951 
    952     [window->ns.object close];
    953     window->ns.object = nil;
    954 
    955     // HACK: Allow Cocoa to catch up before returning
    956     _glfwPlatformPollEvents();
    957 
    958     } // autoreleasepool
    959 }
    960 
    961 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
    962 {
    963     @autoreleasepool {
    964     NSString* string = @(title);
    965     [window->ns.object setTitle:string];
    966     // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it
    967     //       if the window lacks NSWindowStyleMaskTitled
    968     [window->ns.object setMiniwindowTitle:string];
    969     } // autoreleasepool
    970 }
    971 
    972 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
    973                                 int count, const GLFWimage* images)
    974 {
    975     // Regular windows do not have icons
    976 }
    977 
    978 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
    979 {
    980     @autoreleasepool {
    981 
    982     const NSRect contentRect =
    983         [window->ns.object contentRectForFrameRect:[window->ns.object frame]];
    984 
    985     if (xpos)
    986         *xpos = contentRect.origin.x;
    987     if (ypos)
    988         *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1);
    989 
    990     } // autoreleasepool
    991 }
    992 
    993 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y)
    994 {
    995     @autoreleasepool {
    996 
    997     const NSRect contentRect = [window->ns.view frame];
    998     const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0);
    999     const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect];
   1000     [window->ns.object setFrameOrigin:frameRect.origin];
   1001 
   1002     } // autoreleasepool
   1003 }
   1004 
   1005 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   1006 {
   1007     @autoreleasepool {
   1008 
   1009     const NSRect contentRect = [window->ns.view frame];
   1010 
   1011     if (width)
   1012         *width = contentRect.size.width;
   1013     if (height)
   1014         *height = contentRect.size.height;
   1015 
   1016     } // autoreleasepool
   1017 }
   1018 
   1019 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   1020 {
   1021     @autoreleasepool {
   1022 
   1023     if (window->monitor)
   1024     {
   1025         if (window->monitor->window == window)
   1026             acquireMonitor(window);
   1027     }
   1028     else
   1029     {
   1030         NSRect contentRect =
   1031             [window->ns.object contentRectForFrameRect:[window->ns.object frame]];
   1032         contentRect.origin.y += contentRect.size.height - height;
   1033         contentRect.size = NSMakeSize(width, height);
   1034         [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect]
   1035                             display:YES];
   1036     }
   1037 
   1038     } // autoreleasepool
   1039 }
   1040 
   1041 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   1042                                       int minwidth, int minheight,
   1043                                       int maxwidth, int maxheight)
   1044 {
   1045     @autoreleasepool {
   1046 
   1047     if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
   1048         [window->ns.object setContentMinSize:NSMakeSize(0, 0)];
   1049     else
   1050         [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)];
   1051 
   1052     if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
   1053         [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)];
   1054     else
   1055         [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)];
   1056 
   1057     } // autoreleasepool
   1058 }
   1059 
   1060 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
   1061 {
   1062     @autoreleasepool {
   1063     if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
   1064         [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];
   1065     else
   1066         [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)];
   1067     } // autoreleasepool
   1068 }
   1069 
   1070 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
   1071 {
   1072     @autoreleasepool {
   1073 
   1074     const NSRect contentRect = [window->ns.view frame];
   1075     const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
   1076 
   1077     if (width)
   1078         *width = (int) fbRect.size.width;
   1079     if (height)
   1080         *height = (int) fbRect.size.height;
   1081 
   1082     } // autoreleasepool
   1083 }
   1084 
   1085 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   1086                                      int* left, int* top,
   1087                                      int* right, int* bottom)
   1088 {
   1089     @autoreleasepool {
   1090 
   1091     const NSRect contentRect = [window->ns.view frame];
   1092     const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect];
   1093 
   1094     if (left)
   1095         *left = contentRect.origin.x - frameRect.origin.x;
   1096     if (top)
   1097         *top = frameRect.origin.y + frameRect.size.height -
   1098                contentRect.origin.y - contentRect.size.height;
   1099     if (right)
   1100         *right = frameRect.origin.x + frameRect.size.width -
   1101                  contentRect.origin.x - contentRect.size.width;
   1102     if (bottom)
   1103         *bottom = contentRect.origin.y - frameRect.origin.y;
   1104 
   1105     } // autoreleasepool
   1106 }
   1107 
   1108 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   1109                                         float* xscale, float* yscale)
   1110 {
   1111     @autoreleasepool {
   1112 
   1113     const NSRect points = [window->ns.view frame];
   1114     const NSRect pixels = [window->ns.view convertRectToBacking:points];
   1115 
   1116     if (xscale)
   1117         *xscale = (float) (pixels.size.width / points.size.width);
   1118     if (yscale)
   1119         *yscale = (float) (pixels.size.height / points.size.height);
   1120 
   1121     } // autoreleasepool
   1122 }
   1123 
   1124 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   1125 {
   1126     @autoreleasepool {
   1127     [window->ns.object miniaturize:nil];
   1128     } // autoreleasepool
   1129 }
   1130 
   1131 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   1132 {
   1133     @autoreleasepool {
   1134     if ([window->ns.object isMiniaturized])
   1135         [window->ns.object deminiaturize:nil];
   1136     else if ([window->ns.object isZoomed])
   1137         [window->ns.object zoom:nil];
   1138     } // autoreleasepool
   1139 }
   1140 
   1141 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   1142 {
   1143     @autoreleasepool {
   1144     if (![window->ns.object isZoomed])
   1145         [window->ns.object zoom:nil];
   1146     } // autoreleasepool
   1147 }
   1148 
   1149 void _glfwPlatformShowWindow(_GLFWwindow* window)
   1150 {
   1151     @autoreleasepool {
   1152     [window->ns.object orderFront:nil];
   1153     } // autoreleasepool
   1154 }
   1155 
   1156 void _glfwPlatformHideWindow(_GLFWwindow* window)
   1157 {
   1158     @autoreleasepool {
   1159     [window->ns.object orderOut:nil];
   1160     } // autoreleasepool
   1161 }
   1162 
   1163 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   1164 {
   1165     @autoreleasepool {
   1166     [NSApp requestUserAttention:NSInformationalRequest];
   1167     } // autoreleasepool
   1168 }
   1169 
   1170 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   1171 {
   1172     @autoreleasepool {
   1173     // Make us the active application
   1174     // HACK: This is here to prevent applications using only hidden windows from
   1175     //       being activated, but should probably not be done every time any
   1176     //       window is shown
   1177     [NSApp activateIgnoringOtherApps:YES];
   1178     [window->ns.object makeKeyAndOrderFront:nil];
   1179     } // autoreleasepool
   1180 }
   1181 
   1182 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   1183                                    _GLFWmonitor* monitor,
   1184                                    int xpos, int ypos,
   1185                                    int width, int height,
   1186                                    int refreshRate)
   1187 {
   1188     @autoreleasepool {
   1189 
   1190     if (window->monitor == monitor)
   1191     {
   1192         if (monitor)
   1193         {
   1194             if (monitor->window == window)
   1195                 acquireMonitor(window);
   1196         }
   1197         else
   1198         {
   1199             const NSRect contentRect =
   1200                 NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height);
   1201             const NSRect frameRect =
   1202                 [window->ns.object frameRectForContentRect:contentRect
   1203                                                  styleMask:getStyleMask(window)];
   1204 
   1205             [window->ns.object setFrame:frameRect display:YES];
   1206         }
   1207 
   1208         return;
   1209     }
   1210 
   1211     if (window->monitor)
   1212         releaseMonitor(window);
   1213 
   1214     _glfwInputWindowMonitor(window, monitor);
   1215 
   1216     // HACK: Allow the state cached in Cocoa to catch up to reality
   1217     // TODO: Solve this in a less terrible way
   1218     _glfwPlatformPollEvents();
   1219 
   1220     const NSUInteger styleMask = getStyleMask(window);
   1221     [window->ns.object setStyleMask:styleMask];
   1222     // HACK: Changing the style mask can cause the first responder to be cleared
   1223     [window->ns.object makeFirstResponder:window->ns.view];
   1224 
   1225     if (window->monitor)
   1226     {
   1227         [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
   1228         [window->ns.object setHasShadow:NO];
   1229 
   1230         acquireMonitor(window);
   1231     }
   1232     else
   1233     {
   1234         NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1),
   1235                                         width, height);
   1236         NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect
   1237                                                             styleMask:styleMask];
   1238         [window->ns.object setFrame:frameRect display:YES];
   1239 
   1240         if (window->numer != GLFW_DONT_CARE &&
   1241             window->denom != GLFW_DONT_CARE)
   1242         {
   1243             [window->ns.object setContentAspectRatio:NSMakeSize(window->numer,
   1244                                                                 window->denom)];
   1245         }
   1246 
   1247         if (window->minwidth != GLFW_DONT_CARE &&
   1248             window->minheight != GLFW_DONT_CARE)
   1249         {
   1250             [window->ns.object setContentMinSize:NSMakeSize(window->minwidth,
   1251                                                             window->minheight)];
   1252         }
   1253 
   1254         if (window->maxwidth != GLFW_DONT_CARE &&
   1255             window->maxheight != GLFW_DONT_CARE)
   1256         {
   1257             [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth,
   1258                                                             window->maxheight)];
   1259         }
   1260 
   1261         if (window->floating)
   1262             [window->ns.object setLevel:NSFloatingWindowLevel];
   1263         else
   1264             [window->ns.object setLevel:NSNormalWindowLevel];
   1265 
   1266         [window->ns.object setHasShadow:YES];
   1267         // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window
   1268         //       title property but the miniwindow title property is unaffected
   1269         [window->ns.object setTitle:[window->ns.object miniwindowTitle]];
   1270     }
   1271 
   1272     } // autoreleasepool
   1273 }
   1274 
   1275 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   1276 {
   1277     @autoreleasepool {
   1278     return [window->ns.object isKeyWindow];
   1279     } // autoreleasepool
   1280 }
   1281 
   1282 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   1283 {
   1284     @autoreleasepool {
   1285     return [window->ns.object isMiniaturized];
   1286     } // autoreleasepool
   1287 }
   1288 
   1289 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   1290 {
   1291     @autoreleasepool {
   1292     return [window->ns.object isVisible];
   1293     } // autoreleasepool
   1294 }
   1295 
   1296 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   1297 {
   1298     @autoreleasepool {
   1299     return [window->ns.object isZoomed];
   1300     } // autoreleasepool
   1301 }
   1302 
   1303 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   1304 {
   1305     @autoreleasepool {
   1306 
   1307     const NSPoint point = [NSEvent mouseLocation];
   1308 
   1309     if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] !=
   1310         [window->ns.object windowNumber])
   1311     {
   1312         return GLFW_FALSE;
   1313     }
   1314 
   1315     return NSMouseInRect(point,
   1316         [window->ns.object convertRectToScreen:[window->ns.view frame]], NO);
   1317 
   1318     } // autoreleasepool
   1319 }
   1320 
   1321 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   1322 {
   1323     @autoreleasepool {
   1324     return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];
   1325     } // autoreleasepool
   1326 }
   1327 
   1328 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   1329 {
   1330     @autoreleasepool {
   1331     [window->ns.object setStyleMask:getStyleMask(window)];
   1332     } // autoreleasepool
   1333 }
   1334 
   1335 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   1336 {
   1337     @autoreleasepool {
   1338     [window->ns.object setStyleMask:getStyleMask(window)];
   1339     [window->ns.object makeFirstResponder:window->ns.view];
   1340     } // autoreleasepool
   1341 }
   1342 
   1343 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   1344 {
   1345     @autoreleasepool {
   1346     if (enabled)
   1347         [window->ns.object setLevel:NSFloatingWindowLevel];
   1348     else
   1349         [window->ns.object setLevel:NSNormalWindowLevel];
   1350     } // autoreleasepool
   1351 }
   1352 
   1353 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   1354 {
   1355     @autoreleasepool {
   1356     return (float) [window->ns.object alphaValue];
   1357     } // autoreleasepool
   1358 }
   1359 
   1360 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   1361 {
   1362     @autoreleasepool {
   1363     [window->ns.object setAlphaValue:opacity];
   1364     } // autoreleasepool
   1365 }
   1366 
   1367 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   1368 {
   1369 }
   1370 
   1371 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   1372 {
   1373     return GLFW_FALSE;
   1374 }
   1375 
   1376 void _glfwPlatformPollEvents(void)
   1377 {
   1378     @autoreleasepool {
   1379 
   1380     if (!_glfw.ns.finishedLaunching)
   1381         [NSApp run];
   1382 
   1383     for (;;)
   1384     {
   1385         NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
   1386                                             untilDate:[NSDate distantPast]
   1387                                                inMode:NSDefaultRunLoopMode
   1388                                               dequeue:YES];
   1389         if (event == nil)
   1390             break;
   1391 
   1392         [NSApp sendEvent:event];
   1393     }
   1394 
   1395     } // autoreleasepool
   1396 }
   1397 
   1398 void _glfwPlatformWaitEvents(void)
   1399 {
   1400     @autoreleasepool {
   1401 
   1402     if (!_glfw.ns.finishedLaunching)
   1403         [NSApp run];
   1404 
   1405     // I wanted to pass NO to dequeue:, and rely on PollEvents to
   1406     // dequeue and send.  For reasons not at all clear to me, passing
   1407     // NO to dequeue: causes this method never to return.
   1408     NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
   1409                                         untilDate:[NSDate distantFuture]
   1410                                            inMode:NSDefaultRunLoopMode
   1411                                           dequeue:YES];
   1412     [NSApp sendEvent:event];
   1413 
   1414     _glfwPlatformPollEvents();
   1415 
   1416     } // autoreleasepool
   1417 }
   1418 
   1419 void _glfwPlatformWaitEventsTimeout(double timeout)
   1420 {
   1421     @autoreleasepool {
   1422 
   1423     if (!_glfw.ns.finishedLaunching)
   1424         [NSApp run];
   1425 
   1426     NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout];
   1427     NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
   1428                                         untilDate:date
   1429                                            inMode:NSDefaultRunLoopMode
   1430                                           dequeue:YES];
   1431     if (event)
   1432         [NSApp sendEvent:event];
   1433 
   1434     _glfwPlatformPollEvents();
   1435 
   1436     } // autoreleasepool
   1437 }
   1438 
   1439 void _glfwPlatformPostEmptyEvent(void)
   1440 {
   1441     @autoreleasepool {
   1442 
   1443     if (!_glfw.ns.finishedLaunching)
   1444         [NSApp run];
   1445 
   1446     NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
   1447                                         location:NSMakePoint(0, 0)
   1448                                    modifierFlags:0
   1449                                        timestamp:0
   1450                                     windowNumber:0
   1451                                          context:nil
   1452                                          subtype:0
   1453                                            data1:0
   1454                                            data2:0];
   1455     [NSApp postEvent:event atStart:YES];
   1456 
   1457     } // autoreleasepool
   1458 }
   1459 
   1460 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   1461 {
   1462     @autoreleasepool {
   1463 
   1464     const NSRect contentRect = [window->ns.view frame];
   1465     // NOTE: The returned location uses base 0,1 not 0,0
   1466     const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
   1467 
   1468     if (xpos)
   1469         *xpos = pos.x;
   1470     if (ypos)
   1471         *ypos = contentRect.size.height - pos.y;
   1472 
   1473     } // autoreleasepool
   1474 }
   1475 
   1476 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   1477 {
   1478     @autoreleasepool {
   1479 
   1480     updateCursorImage(window);
   1481 
   1482     const NSRect contentRect = [window->ns.view frame];
   1483     // NOTE: The returned location uses base 0,1 not 0,0
   1484     const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
   1485 
   1486     window->ns.cursorWarpDeltaX += x - pos.x;
   1487     window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y;
   1488 
   1489     if (window->monitor)
   1490     {
   1491         CGDisplayMoveCursorToPoint(window->monitor->ns.displayID,
   1492                                    CGPointMake(x, y));
   1493     }
   1494     else
   1495     {
   1496         const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0);
   1497         const NSRect globalRect = [window->ns.object convertRectToScreen:localRect];
   1498         const NSPoint globalPoint = globalRect.origin;
   1499 
   1500         CGWarpMouseCursorPosition(CGPointMake(globalPoint.x,
   1501                                               _glfwTransformYNS(globalPoint.y)));
   1502     }
   1503 
   1504     } // autoreleasepool
   1505 }
   1506 
   1507 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   1508 {
   1509     @autoreleasepool {
   1510     if (_glfwPlatformWindowFocused(window))
   1511         updateCursorMode(window);
   1512     } // autoreleasepool
   1513 }
   1514 
   1515 const char* _glfwPlatformGetScancodeName(int scancode)
   1516 {
   1517     @autoreleasepool {
   1518 
   1519     if (scancode < 0 || scancode > 0xff ||
   1520         _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN)
   1521     {
   1522         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
   1523         return NULL;
   1524     }
   1525 
   1526     const int key = _glfw.ns.keycodes[scancode];
   1527 
   1528     UInt32 deadKeyState = 0;
   1529     UniChar characters[4];
   1530     UniCharCount characterCount = 0;
   1531 
   1532     if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],
   1533                        scancode,
   1534                        kUCKeyActionDisplay,
   1535                        0,
   1536                        LMGetKbdType(),
   1537                        kUCKeyTranslateNoDeadKeysBit,
   1538                        &deadKeyState,
   1539                        sizeof(characters) / sizeof(characters[0]),
   1540                        &characterCount,
   1541                        characters) != noErr)
   1542     {
   1543         return NULL;
   1544     }
   1545 
   1546     if (!characterCount)
   1547         return NULL;
   1548 
   1549     CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
   1550                                                             characters,
   1551                                                             characterCount,
   1552                                                             kCFAllocatorNull);
   1553     CFStringGetCString(string,
   1554                        _glfw.ns.keynames[key],
   1555                        sizeof(_glfw.ns.keynames[key]),
   1556                        kCFStringEncodingUTF8);
   1557     CFRelease(string);
   1558 
   1559     return _glfw.ns.keynames[key];
   1560 
   1561     } // autoreleasepool
   1562 }
   1563 
   1564 int _glfwPlatformGetKeyScancode(int key)
   1565 {
   1566     return _glfw.ns.scancodes[key];
   1567 }
   1568 
   1569 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   1570                               const GLFWimage* image,
   1571                               int xhot, int yhot)
   1572 {
   1573     @autoreleasepool {
   1574 
   1575     NSImage* native;
   1576     NSBitmapImageRep* rep;
   1577 
   1578     rep = [[NSBitmapImageRep alloc]
   1579         initWithBitmapDataPlanes:NULL
   1580                       pixelsWide:image->width
   1581                       pixelsHigh:image->height
   1582                    bitsPerSample:8
   1583                  samplesPerPixel:4
   1584                         hasAlpha:YES
   1585                         isPlanar:NO
   1586                   colorSpaceName:NSCalibratedRGBColorSpace
   1587                     bitmapFormat:NSBitmapFormatAlphaNonpremultiplied
   1588                      bytesPerRow:image->width * 4
   1589                     bitsPerPixel:32];
   1590 
   1591     if (rep == nil)
   1592         return GLFW_FALSE;
   1593 
   1594     memcpy([rep bitmapData], image->pixels, image->width * image->height * 4);
   1595 
   1596     native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)];
   1597     [native addRepresentation:rep];
   1598 
   1599     cursor->ns.object = [[NSCursor alloc] initWithImage:native
   1600                                                 hotSpot:NSMakePoint(xhot, yhot)];
   1601 
   1602     [native release];
   1603     [rep release];
   1604 
   1605     if (cursor->ns.object == nil)
   1606         return GLFW_FALSE;
   1607 
   1608     return GLFW_TRUE;
   1609 
   1610     } // autoreleasepool
   1611 }
   1612 
   1613 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   1614 {
   1615     @autoreleasepool {
   1616 
   1617     if (shape == GLFW_ARROW_CURSOR)
   1618         cursor->ns.object = [NSCursor arrowCursor];
   1619     else if (shape == GLFW_IBEAM_CURSOR)
   1620         cursor->ns.object = [NSCursor IBeamCursor];
   1621     else if (shape == GLFW_CROSSHAIR_CURSOR)
   1622         cursor->ns.object = [NSCursor crosshairCursor];
   1623     else if (shape == GLFW_HAND_CURSOR)
   1624         cursor->ns.object = [NSCursor pointingHandCursor];
   1625     else if (shape == GLFW_HRESIZE_CURSOR)
   1626         cursor->ns.object = [NSCursor resizeLeftRightCursor];
   1627     else if (shape == GLFW_VRESIZE_CURSOR)
   1628         cursor->ns.object = [NSCursor resizeUpDownCursor];
   1629 
   1630     if (!cursor->ns.object)
   1631     {
   1632         _glfwInputError(GLFW_PLATFORM_ERROR,
   1633                         "Cocoa: Failed to retrieve standard cursor");
   1634         return GLFW_FALSE;
   1635     }
   1636 
   1637     [cursor->ns.object retain];
   1638     return GLFW_TRUE;
   1639 
   1640     } // autoreleasepool
   1641 }
   1642 
   1643 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   1644 {
   1645     @autoreleasepool {
   1646     if (cursor->ns.object)
   1647         [(NSCursor*) cursor->ns.object release];
   1648     } // autoreleasepool
   1649 }
   1650 
   1651 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   1652 {
   1653     @autoreleasepool {
   1654     if (cursorInContentArea(window))
   1655         updateCursorImage(window);
   1656     } // autoreleasepool
   1657 }
   1658 
   1659 void _glfwPlatformSetClipboardString(const char* string)
   1660 {
   1661     @autoreleasepool {
   1662     NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
   1663     [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
   1664     [pasteboard setString:@(string) forType:NSPasteboardTypeString];
   1665     } // autoreleasepool
   1666 }
   1667 
   1668 const char* _glfwPlatformGetClipboardString(void)
   1669 {
   1670     @autoreleasepool {
   1671 
   1672     NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
   1673 
   1674     if (![[pasteboard types] containsObject:NSPasteboardTypeString])
   1675     {
   1676         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
   1677                         "Cocoa: Failed to retrieve string from pasteboard");
   1678         return NULL;
   1679     }
   1680 
   1681     NSString* object = [pasteboard stringForType:NSPasteboardTypeString];
   1682     if (!object)
   1683     {
   1684         _glfwInputError(GLFW_PLATFORM_ERROR,
   1685                         "Cocoa: Failed to retrieve object from pasteboard");
   1686         return NULL;
   1687     }
   1688 
   1689     free(_glfw.ns.clipboardString);
   1690     _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]);
   1691 
   1692     return _glfw.ns.clipboardString;
   1693 
   1694     } // autoreleasepool
   1695 }
   1696 
   1697 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   1698 {
   1699     if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface)
   1700     {
   1701         extensions[0] = "VK_KHR_surface";
   1702         extensions[1] = "VK_EXT_metal_surface";
   1703     }
   1704     else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface)
   1705     {
   1706         extensions[0] = "VK_KHR_surface";
   1707         extensions[1] = "VK_MVK_macos_surface";
   1708     }
   1709 }
   1710 
   1711 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   1712                                                       VkPhysicalDevice device,
   1713                                                       uint32_t queuefamily)
   1714 {
   1715     return GLFW_TRUE;
   1716 }
   1717 
   1718 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   1719                                           _GLFWwindow* window,
   1720                                           const VkAllocationCallbacks* allocator,
   1721                                           VkSurfaceKHR* surface)
   1722 {
   1723     @autoreleasepool {
   1724 
   1725 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
   1726     // HACK: Dynamically load Core Animation to avoid adding an extra
   1727     //       dependency for the majority who don't use MoltenVK
   1728     NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"];
   1729     if (!bundle)
   1730     {
   1731         _glfwInputError(GLFW_PLATFORM_ERROR,
   1732                         "Cocoa: Failed to find QuartzCore.framework");
   1733         return VK_ERROR_EXTENSION_NOT_PRESENT;
   1734     }
   1735 
   1736     // NOTE: Create the layer here as makeBackingLayer should not return nil
   1737     window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer];
   1738     if (!window->ns.layer)
   1739     {
   1740         _glfwInputError(GLFW_PLATFORM_ERROR,
   1741                         "Cocoa: Failed to create layer for view");
   1742         return VK_ERROR_EXTENSION_NOT_PRESENT;
   1743     }
   1744 
   1745     if (window->ns.retina)
   1746         [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
   1747 
   1748     [window->ns.view setLayer:window->ns.layer];
   1749     [window->ns.view setWantsLayer:YES];
   1750 
   1751     VkResult err;
   1752 
   1753     if (_glfw.vk.EXT_metal_surface)
   1754     {
   1755         VkMetalSurfaceCreateInfoEXT sci;
   1756 
   1757         PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT;
   1758         vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT)
   1759             vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT");
   1760         if (!vkCreateMetalSurfaceEXT)
   1761         {
   1762             _glfwInputError(GLFW_API_UNAVAILABLE,
   1763                             "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension");
   1764             return VK_ERROR_EXTENSION_NOT_PRESENT;
   1765         }
   1766 
   1767         memset(&sci, 0, sizeof(sci));
   1768         sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
   1769         sci.pLayer = window->ns.layer;
   1770 
   1771         err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface);
   1772     }
   1773     else
   1774     {
   1775         VkMacOSSurfaceCreateInfoMVK sci;
   1776 
   1777         PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK;
   1778         vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)
   1779             vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK");
   1780         if (!vkCreateMacOSSurfaceMVK)
   1781         {
   1782             _glfwInputError(GLFW_API_UNAVAILABLE,
   1783                             "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension");
   1784             return VK_ERROR_EXTENSION_NOT_PRESENT;
   1785         }
   1786 
   1787         memset(&sci, 0, sizeof(sci));
   1788         sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
   1789         sci.pView = window->ns.view;
   1790 
   1791         err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface);
   1792     }
   1793 
   1794     if (err)
   1795     {
   1796         _glfwInputError(GLFW_PLATFORM_ERROR,
   1797                         "Cocoa: Failed to create Vulkan surface: %s",
   1798                         _glfwGetVulkanResultString(err));
   1799     }
   1800 
   1801     return err;
   1802 #else
   1803     return VK_ERROR_EXTENSION_NOT_PRESENT;
   1804 #endif
   1805 
   1806     } // autoreleasepool
   1807 }
   1808 
   1809 
   1810 //////////////////////////////////////////////////////////////////////////
   1811 //////                        GLFW native API                       //////
   1812 //////////////////////////////////////////////////////////////////////////
   1813 
   1814 GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
   1815 {
   1816     _GLFWwindow* window = (_GLFWwindow*) handle;
   1817     _GLFW_REQUIRE_INIT_OR_RETURN(nil);
   1818     return window->ns.object;
   1819 }
   1820