zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

cocoa_window.m (53331B)


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