hs

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | LICENSE

cocoa_window.m (53441B)


      1 //========================================================================
      2 // GLFW 3.4 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 = (char **)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 (!createNativeWindow(window, wndconfig, fbconfig))
    888         return GLFW_FALSE;
    889 
    890     if (ctxconfig->client != GLFW_NO_API)
    891     {
    892         if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
    893         {
    894             if (!_glfwInitNSGL())
    895                 return GLFW_FALSE;
    896             if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig))
    897                 return GLFW_FALSE;
    898         }
    899         else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
    900         {
    901             if (!_glfwInitEGL())
    902                 return GLFW_FALSE;
    903             if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
    904                 return GLFW_FALSE;
    905         }
    906         else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
    907         {
    908             if (!_glfwInitOSMesa())
    909                 return GLFW_FALSE;
    910             if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
    911                 return GLFW_FALSE;
    912         }
    913     }
    914 
    915     if (window->monitor)
    916     {
    917         _glfwPlatformShowWindow(window);
    918         _glfwPlatformFocusWindow(window);
    919         acquireMonitor(window);
    920     }
    921 
    922     return GLFW_TRUE;
    923 
    924     } // autoreleasepool
    925 }
    926 
    927 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
    928 {
    929     @autoreleasepool {
    930 
    931     if (_glfw.ns.disabledCursorWindow == window)
    932         _glfw.ns.disabledCursorWindow = NULL;
    933 
    934     [window->ns.object orderOut:nil];
    935 
    936     if (window->monitor)
    937         releaseMonitor(window);
    938 
    939     if (window->context.destroy)
    940         window->context.destroy(window);
    941 
    942     [window->ns.object setDelegate:nil];
    943     [window->ns.delegate release];
    944     window->ns.delegate = nil;
    945 
    946     [window->ns.view release];
    947     window->ns.view = nil;
    948 
    949     [window->ns.object close];
    950     window->ns.object = nil;
    951 
    952     // HACK: Allow Cocoa to catch up before returning
    953     _glfwPlatformPollEvents();
    954 
    955     } // autoreleasepool
    956 }
    957 
    958 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
    959 {
    960     @autoreleasepool {
    961     NSString* string = @(title);
    962     [window->ns.object setTitle:string];
    963     // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it
    964     //       if the window lacks NSWindowStyleMaskTitled
    965     [window->ns.object setMiniwindowTitle:string];
    966     } // autoreleasepool
    967 }
    968 
    969 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
    970                                 int count, const GLFWimage* images)
    971 {
    972     // Regular windows do not have icons
    973 }
    974 
    975 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
    976 {
    977     @autoreleasepool {
    978 
    979     const NSRect contentRect =
    980         [window->ns.object contentRectForFrameRect:[window->ns.object frame]];
    981 
    982     if (xpos)
    983         *xpos = contentRect.origin.x;
    984     if (ypos)
    985         *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1);
    986 
    987     } // autoreleasepool
    988 }
    989 
    990 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y)
    991 {
    992     @autoreleasepool {
    993 
    994     const NSRect contentRect = [window->ns.view frame];
    995     const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0);
    996     const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect];
    997     [window->ns.object setFrameOrigin:frameRect.origin];
    998 
    999     } // autoreleasepool
   1000 }
   1001 
   1002 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
   1003 {
   1004     @autoreleasepool {
   1005 
   1006     const NSRect contentRect = [window->ns.view frame];
   1007 
   1008     if (width)
   1009         *width = contentRect.size.width;
   1010     if (height)
   1011         *height = contentRect.size.height;
   1012 
   1013     } // autoreleasepool
   1014 }
   1015 
   1016 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
   1017 {
   1018     @autoreleasepool {
   1019 
   1020     if (window->monitor)
   1021     {
   1022         if (window->monitor->window == window)
   1023             acquireMonitor(window);
   1024     }
   1025     else
   1026     {
   1027         NSRect contentRect =
   1028             [window->ns.object contentRectForFrameRect:[window->ns.object frame]];
   1029         contentRect.origin.y += contentRect.size.height - height;
   1030         contentRect.size = NSMakeSize(width, height);
   1031         [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect]
   1032                             display:YES];
   1033     }
   1034 
   1035     } // autoreleasepool
   1036 }
   1037 
   1038 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
   1039                                       int minwidth, int minheight,
   1040                                       int maxwidth, int maxheight)
   1041 {
   1042     @autoreleasepool {
   1043 
   1044     if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
   1045         [window->ns.object setContentMinSize:NSMakeSize(0, 0)];
   1046     else
   1047         [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)];
   1048 
   1049     if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
   1050         [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)];
   1051     else
   1052         [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)];
   1053 
   1054     } // autoreleasepool
   1055 }
   1056 
   1057 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
   1058 {
   1059     @autoreleasepool {
   1060     if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
   1061         [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];
   1062     else
   1063         [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)];
   1064     } // autoreleasepool
   1065 }
   1066 
   1067 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
   1068 {
   1069     @autoreleasepool {
   1070 
   1071     const NSRect contentRect = [window->ns.view frame];
   1072     const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
   1073 
   1074     if (width)
   1075         *width = (int) fbRect.size.width;
   1076     if (height)
   1077         *height = (int) fbRect.size.height;
   1078 
   1079     } // autoreleasepool
   1080 }
   1081 
   1082 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
   1083                                      int* left, int* top,
   1084                                      int* right, int* bottom)
   1085 {
   1086     @autoreleasepool {
   1087 
   1088     const NSRect contentRect = [window->ns.view frame];
   1089     const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect];
   1090 
   1091     if (left)
   1092         *left = contentRect.origin.x - frameRect.origin.x;
   1093     if (top)
   1094         *top = frameRect.origin.y + frameRect.size.height -
   1095                contentRect.origin.y - contentRect.size.height;
   1096     if (right)
   1097         *right = frameRect.origin.x + frameRect.size.width -
   1098                  contentRect.origin.x - contentRect.size.width;
   1099     if (bottom)
   1100         *bottom = contentRect.origin.y - frameRect.origin.y;
   1101 
   1102     } // autoreleasepool
   1103 }
   1104 
   1105 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
   1106                                         float* xscale, float* yscale)
   1107 {
   1108     @autoreleasepool {
   1109 
   1110     const NSRect points = [window->ns.view frame];
   1111     const NSRect pixels = [window->ns.view convertRectToBacking:points];
   1112 
   1113     if (xscale)
   1114         *xscale = (float) (pixels.size.width / points.size.width);
   1115     if (yscale)
   1116         *yscale = (float) (pixels.size.height / points.size.height);
   1117 
   1118     } // autoreleasepool
   1119 }
   1120 
   1121 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
   1122 {
   1123     @autoreleasepool {
   1124     [window->ns.object miniaturize:nil];
   1125     } // autoreleasepool
   1126 }
   1127 
   1128 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
   1129 {
   1130     @autoreleasepool {
   1131     if ([window->ns.object isMiniaturized])
   1132         [window->ns.object deminiaturize:nil];
   1133     else if ([window->ns.object isZoomed])
   1134         [window->ns.object zoom:nil];
   1135     } // autoreleasepool
   1136 }
   1137 
   1138 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
   1139 {
   1140     @autoreleasepool {
   1141     if (![window->ns.object isZoomed])
   1142         [window->ns.object zoom:nil];
   1143     } // autoreleasepool
   1144 }
   1145 
   1146 void _glfwPlatformShowWindow(_GLFWwindow* window)
   1147 {
   1148     @autoreleasepool {
   1149     [window->ns.object orderFront:nil];
   1150     } // autoreleasepool
   1151 }
   1152 
   1153 void _glfwPlatformHideWindow(_GLFWwindow* window)
   1154 {
   1155     @autoreleasepool {
   1156     [window->ns.object orderOut:nil];
   1157     } // autoreleasepool
   1158 }
   1159 
   1160 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
   1161 {
   1162     @autoreleasepool {
   1163     [NSApp requestUserAttention:NSInformationalRequest];
   1164     } // autoreleasepool
   1165 }
   1166 
   1167 void _glfwPlatformFocusWindow(_GLFWwindow* window)
   1168 {
   1169     @autoreleasepool {
   1170     // Make us the active application
   1171     // HACK: This is here to prevent applications using only hidden windows from
   1172     //       being activated, but should probably not be done every time any
   1173     //       window is shown
   1174     [NSApp activateIgnoringOtherApps:YES];
   1175     [window->ns.object makeKeyAndOrderFront:nil];
   1176     } // autoreleasepool
   1177 }
   1178 
   1179 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
   1180                                    _GLFWmonitor* monitor,
   1181                                    int xpos, int ypos,
   1182                                    int width, int height,
   1183                                    int refreshRate)
   1184 {
   1185     @autoreleasepool {
   1186 
   1187     if (window->monitor == monitor)
   1188     {
   1189         if (monitor)
   1190         {
   1191             if (monitor->window == window)
   1192                 acquireMonitor(window);
   1193         }
   1194         else
   1195         {
   1196             const NSRect contentRect =
   1197                 NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height);
   1198             const NSRect frameRect =
   1199                 [window->ns.object frameRectForContentRect:contentRect
   1200                                                  styleMask:getStyleMask(window)];
   1201 
   1202             [window->ns.object setFrame:frameRect display:YES];
   1203         }
   1204 
   1205         return;
   1206     }
   1207 
   1208     if (window->monitor)
   1209         releaseMonitor(window);
   1210 
   1211     _glfwInputWindowMonitor(window, monitor);
   1212 
   1213     // HACK: Allow the state cached in Cocoa to catch up to reality
   1214     // TODO: Solve this in a less terrible way
   1215     _glfwPlatformPollEvents();
   1216 
   1217     const NSUInteger styleMask = getStyleMask(window);
   1218     [window->ns.object setStyleMask:styleMask];
   1219     // HACK: Changing the style mask can cause the first responder to be cleared
   1220     [window->ns.object makeFirstResponder:window->ns.view];
   1221 
   1222     if (window->monitor)
   1223     {
   1224         [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
   1225         [window->ns.object setHasShadow:NO];
   1226 
   1227         acquireMonitor(window);
   1228     }
   1229     else
   1230     {
   1231         NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1),
   1232                                         width, height);
   1233         NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect
   1234                                                             styleMask:styleMask];
   1235         [window->ns.object setFrame:frameRect display:YES];
   1236 
   1237         if (window->numer != GLFW_DONT_CARE &&
   1238             window->denom != GLFW_DONT_CARE)
   1239         {
   1240             [window->ns.object setContentAspectRatio:NSMakeSize(window->numer,
   1241                                                                 window->denom)];
   1242         }
   1243 
   1244         if (window->minwidth != GLFW_DONT_CARE &&
   1245             window->minheight != GLFW_DONT_CARE)
   1246         {
   1247             [window->ns.object setContentMinSize:NSMakeSize(window->minwidth,
   1248                                                             window->minheight)];
   1249         }
   1250 
   1251         if (window->maxwidth != GLFW_DONT_CARE &&
   1252             window->maxheight != GLFW_DONT_CARE)
   1253         {
   1254             [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth,
   1255                                                             window->maxheight)];
   1256         }
   1257 
   1258         if (window->floating)
   1259             [window->ns.object setLevel:NSFloatingWindowLevel];
   1260         else
   1261             [window->ns.object setLevel:NSNormalWindowLevel];
   1262 
   1263         [window->ns.object setHasShadow:YES];
   1264         // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window
   1265         //       title property but the miniwindow title property is unaffected
   1266         [window->ns.object setTitle:[window->ns.object miniwindowTitle]];
   1267     }
   1268 
   1269     } // autoreleasepool
   1270 }
   1271 
   1272 int _glfwPlatformWindowFocused(_GLFWwindow* window)
   1273 {
   1274     @autoreleasepool {
   1275     return [window->ns.object isKeyWindow];
   1276     } // autoreleasepool
   1277 }
   1278 
   1279 int _glfwPlatformWindowIconified(_GLFWwindow* window)
   1280 {
   1281     @autoreleasepool {
   1282     return [window->ns.object isMiniaturized];
   1283     } // autoreleasepool
   1284 }
   1285 
   1286 int _glfwPlatformWindowVisible(_GLFWwindow* window)
   1287 {
   1288     @autoreleasepool {
   1289     return [window->ns.object isVisible];
   1290     } // autoreleasepool
   1291 }
   1292 
   1293 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
   1294 {
   1295     @autoreleasepool {
   1296     return [window->ns.object isZoomed];
   1297     } // autoreleasepool
   1298 }
   1299 
   1300 int _glfwPlatformWindowHovered(_GLFWwindow* window)
   1301 {
   1302     @autoreleasepool {
   1303 
   1304     const NSPoint point = [NSEvent mouseLocation];
   1305 
   1306     if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] !=
   1307         [window->ns.object windowNumber])
   1308     {
   1309         return GLFW_FALSE;
   1310     }
   1311 
   1312     return NSMouseInRect(point,
   1313         [window->ns.object convertRectToScreen:[window->ns.view frame]], NO);
   1314 
   1315     } // autoreleasepool
   1316 }
   1317 
   1318 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
   1319 {
   1320     @autoreleasepool {
   1321     return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];
   1322     } // autoreleasepool
   1323 }
   1324 
   1325 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
   1326 {
   1327     @autoreleasepool {
   1328     [window->ns.object setStyleMask:getStyleMask(window)];
   1329     } // autoreleasepool
   1330 }
   1331 
   1332 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
   1333 {
   1334     @autoreleasepool {
   1335     [window->ns.object setStyleMask:getStyleMask(window)];
   1336     [window->ns.object makeFirstResponder:window->ns.view];
   1337     } // autoreleasepool
   1338 }
   1339 
   1340 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
   1341 {
   1342     @autoreleasepool {
   1343     if (enabled)
   1344         [window->ns.object setLevel:NSFloatingWindowLevel];
   1345     else
   1346         [window->ns.object setLevel:NSNormalWindowLevel];
   1347     } // autoreleasepool
   1348 }
   1349 
   1350 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
   1351 {
   1352     @autoreleasepool {
   1353     return (float) [window->ns.object alphaValue];
   1354     } // autoreleasepool
   1355 }
   1356 
   1357 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
   1358 {
   1359     @autoreleasepool {
   1360     [window->ns.object setAlphaValue:opacity];
   1361     } // autoreleasepool
   1362 }
   1363 
   1364 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
   1365 {
   1366 }
   1367 
   1368 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
   1369 {
   1370     return GLFW_FALSE;
   1371 }
   1372 
   1373 void _glfwPlatformPollEvents(void)
   1374 {
   1375     @autoreleasepool {
   1376 
   1377     for (;;)
   1378     {
   1379         NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
   1380                                             untilDate:[NSDate distantPast]
   1381                                                inMode:NSDefaultRunLoopMode
   1382                                               dequeue:YES];
   1383         if (event == nil)
   1384             break;
   1385 
   1386         [NSApp sendEvent:event];
   1387     }
   1388 
   1389     } // autoreleasepool
   1390 }
   1391 
   1392 void _glfwPlatformWaitEvents(void)
   1393 {
   1394     @autoreleasepool {
   1395 
   1396     // I wanted to pass NO to dequeue:, and rely on PollEvents to
   1397     // dequeue and send.  For reasons not at all clear to me, passing
   1398     // NO to dequeue: causes this method never to return.
   1399     NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
   1400                                         untilDate:[NSDate distantFuture]
   1401                                            inMode:NSDefaultRunLoopMode
   1402                                           dequeue:YES];
   1403     [NSApp sendEvent:event];
   1404 
   1405     _glfwPlatformPollEvents();
   1406 
   1407     } // autoreleasepool
   1408 }
   1409 
   1410 void _glfwPlatformWaitEventsTimeout(double timeout)
   1411 {
   1412     @autoreleasepool {
   1413 
   1414     NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout];
   1415     NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
   1416                                         untilDate:date
   1417                                            inMode:NSDefaultRunLoopMode
   1418                                           dequeue:YES];
   1419     if (event)
   1420         [NSApp sendEvent:event];
   1421 
   1422     _glfwPlatformPollEvents();
   1423 
   1424     } // autoreleasepool
   1425 }
   1426 
   1427 void _glfwPlatformPostEmptyEvent(void)
   1428 {
   1429     @autoreleasepool {
   1430 
   1431     NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
   1432                                         location:NSMakePoint(0, 0)
   1433                                    modifierFlags:0
   1434                                        timestamp:0
   1435                                     windowNumber:0
   1436                                          context:nil
   1437                                          subtype:0
   1438                                            data1:0
   1439                                            data2:0];
   1440     [NSApp postEvent:event atStart:YES];
   1441 
   1442     } // autoreleasepool
   1443 }
   1444 
   1445 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
   1446 {
   1447     @autoreleasepool {
   1448 
   1449     const NSRect contentRect = [window->ns.view frame];
   1450     // NOTE: The returned location uses base 0,1 not 0,0
   1451     const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
   1452 
   1453     if (xpos)
   1454         *xpos = pos.x;
   1455     if (ypos)
   1456         *ypos = contentRect.size.height - pos.y;
   1457 
   1458     } // autoreleasepool
   1459 }
   1460 
   1461 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
   1462 {
   1463     @autoreleasepool {
   1464 
   1465     updateCursorImage(window);
   1466 
   1467     const NSRect contentRect = [window->ns.view frame];
   1468     // NOTE: The returned location uses base 0,1 not 0,0
   1469     const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
   1470 
   1471     window->ns.cursorWarpDeltaX += x - pos.x;
   1472     window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y;
   1473 
   1474     if (window->monitor)
   1475     {
   1476         CGDisplayMoveCursorToPoint(window->monitor->ns.displayID,
   1477                                    CGPointMake(x, y));
   1478     }
   1479     else
   1480     {
   1481         const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0);
   1482         const NSRect globalRect = [window->ns.object convertRectToScreen:localRect];
   1483         const NSPoint globalPoint = globalRect.origin;
   1484 
   1485         CGWarpMouseCursorPosition(CGPointMake(globalPoint.x,
   1486                                               _glfwTransformYNS(globalPoint.y)));
   1487     }
   1488 
   1489     } // autoreleasepool
   1490 }
   1491 
   1492 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
   1493 {
   1494     @autoreleasepool {
   1495     if (_glfwPlatformWindowFocused(window))
   1496         updateCursorMode(window);
   1497     } // autoreleasepool
   1498 }
   1499 
   1500 const char* _glfwPlatformGetScancodeName(int scancode)
   1501 {
   1502     @autoreleasepool {
   1503 
   1504     if (scancode < 0 || scancode > 0xff ||
   1505         _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN)
   1506     {
   1507         _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
   1508         return NULL;
   1509     }
   1510 
   1511     const int key = _glfw.ns.keycodes[scancode];
   1512 
   1513     UInt32 deadKeyState = 0;
   1514     UniChar characters[4];
   1515     UniCharCount characterCount = 0;
   1516 
   1517     if (UCKeyTranslate((const UCKeyboardLayout *)[(NSData*) _glfw.ns.unicodeData bytes],
   1518                        scancode,
   1519                        kUCKeyActionDisplay,
   1520                        0,
   1521                        LMGetKbdType(),
   1522                        kUCKeyTranslateNoDeadKeysBit,
   1523                        &deadKeyState,
   1524                        sizeof(characters) / sizeof(characters[0]),
   1525                        &characterCount,
   1526                        characters) != noErr)
   1527     {
   1528         return NULL;
   1529     }
   1530 
   1531     if (!characterCount)
   1532         return NULL;
   1533 
   1534     CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
   1535                                                             characters,
   1536                                                             characterCount,
   1537                                                             kCFAllocatorNull);
   1538     CFStringGetCString(string,
   1539                        _glfw.ns.keynames[key],
   1540                        sizeof(_glfw.ns.keynames[key]),
   1541                        kCFStringEncodingUTF8);
   1542     CFRelease(string);
   1543 
   1544     return _glfw.ns.keynames[key];
   1545 
   1546     } // autoreleasepool
   1547 }
   1548 
   1549 int _glfwPlatformGetKeyScancode(int key)
   1550 {
   1551     return _glfw.ns.scancodes[key];
   1552 }
   1553 
   1554 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
   1555                               const GLFWimage* image,
   1556                               int xhot, int yhot)
   1557 {
   1558     @autoreleasepool {
   1559 
   1560     NSImage* native;
   1561     NSBitmapImageRep* rep;
   1562 
   1563     rep = [[NSBitmapImageRep alloc]
   1564         initWithBitmapDataPlanes:NULL
   1565                       pixelsWide:image->width
   1566                       pixelsHigh:image->height
   1567                    bitsPerSample:8
   1568                  samplesPerPixel:4
   1569                         hasAlpha:YES
   1570                         isPlanar:NO
   1571                   colorSpaceName:NSCalibratedRGBColorSpace
   1572                     bitmapFormat:NSBitmapFormatAlphaNonpremultiplied
   1573                      bytesPerRow:image->width * 4
   1574                     bitsPerPixel:32];
   1575 
   1576     if (rep == nil)
   1577         return GLFW_FALSE;
   1578 
   1579     memcpy([rep bitmapData], image->pixels, image->width * image->height * 4);
   1580 
   1581     native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)];
   1582     [native addRepresentation:rep];
   1583 
   1584     cursor->ns.object = [[NSCursor alloc] initWithImage:native
   1585                                                 hotSpot:NSMakePoint(xhot, yhot)];
   1586 
   1587     [native release];
   1588     [rep release];
   1589 
   1590     if (cursor->ns.object == nil)
   1591         return GLFW_FALSE;
   1592 
   1593     return GLFW_TRUE;
   1594 
   1595     } // autoreleasepool
   1596 }
   1597 
   1598 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
   1599 {
   1600     @autoreleasepool {
   1601 
   1602     SEL cursorSelector = NULL;
   1603 
   1604     // HACK: Try to use a private message
   1605     if (shape == GLFW_RESIZE_EW_CURSOR)
   1606         cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor");
   1607     else if (shape == GLFW_RESIZE_NS_CURSOR)
   1608         cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor");
   1609     else if (shape == GLFW_RESIZE_NWSE_CURSOR)
   1610         cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor");
   1611     else if (shape == GLFW_RESIZE_NESW_CURSOR)
   1612         cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor");
   1613 
   1614     if (cursorSelector && [NSCursor respondsToSelector:cursorSelector])
   1615     {
   1616         id object = [NSCursor performSelector:cursorSelector];
   1617         if ([object isKindOfClass:[NSCursor class]])
   1618             cursor->ns.object = object;
   1619     }
   1620 
   1621     if (!cursor->ns.object)
   1622     {
   1623         if (shape == GLFW_ARROW_CURSOR)
   1624             cursor->ns.object = [NSCursor arrowCursor];
   1625         else if (shape == GLFW_IBEAM_CURSOR)
   1626             cursor->ns.object = [NSCursor IBeamCursor];
   1627         else if (shape == GLFW_CROSSHAIR_CURSOR)
   1628             cursor->ns.object = [NSCursor crosshairCursor];
   1629         else if (shape == GLFW_POINTING_HAND_CURSOR)
   1630             cursor->ns.object = [NSCursor pointingHandCursor];
   1631         else if (shape == GLFW_RESIZE_EW_CURSOR)
   1632             cursor->ns.object = [NSCursor resizeLeftRightCursor];
   1633         else if (shape == GLFW_RESIZE_NS_CURSOR)
   1634             cursor->ns.object = [NSCursor resizeUpDownCursor];
   1635         else if (shape == GLFW_RESIZE_ALL_CURSOR)
   1636             cursor->ns.object = [NSCursor closedHandCursor];
   1637         else if (shape == GLFW_NOT_ALLOWED_CURSOR)
   1638             cursor->ns.object = [NSCursor operationNotAllowedCursor];
   1639     }
   1640 
   1641     if (!cursor->ns.object)
   1642     {
   1643         _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
   1644                         "Cocoa: Standard cursor shape unavailable");
   1645         return GLFW_FALSE;
   1646     }
   1647 
   1648     [cursor->ns.object retain];
   1649     return GLFW_TRUE;
   1650 
   1651     } // autoreleasepool
   1652 }
   1653 
   1654 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
   1655 {
   1656     @autoreleasepool {
   1657     if (cursor->ns.object)
   1658         [(NSCursor*) cursor->ns.object release];
   1659     } // autoreleasepool
   1660 }
   1661 
   1662 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
   1663 {
   1664     @autoreleasepool {
   1665     if (cursorInContentArea(window))
   1666         updateCursorImage(window);
   1667     } // autoreleasepool
   1668 }
   1669 
   1670 void _glfwPlatformSetClipboardString(const char* string)
   1671 {
   1672     @autoreleasepool {
   1673     NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
   1674     [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
   1675     [pasteboard setString:@(string) forType:NSPasteboardTypeString];
   1676     } // autoreleasepool
   1677 }
   1678 
   1679 const char* _glfwPlatformGetClipboardString(void)
   1680 {
   1681     @autoreleasepool {
   1682 
   1683     NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
   1684 
   1685     if (![[pasteboard types] containsObject:NSPasteboardTypeString])
   1686     {
   1687         _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
   1688                         "Cocoa: Failed to retrieve string from pasteboard");
   1689         return NULL;
   1690     }
   1691 
   1692     NSString* object = [pasteboard stringForType:NSPasteboardTypeString];
   1693     if (!object)
   1694     {
   1695         _glfwInputError(GLFW_PLATFORM_ERROR,
   1696                         "Cocoa: Failed to retrieve object from pasteboard");
   1697         return NULL;
   1698     }
   1699 
   1700     free(_glfw.ns.clipboardString);
   1701     _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]);
   1702 
   1703     return _glfw.ns.clipboardString;
   1704 
   1705     } // autoreleasepool
   1706 }
   1707 
   1708 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
   1709 {
   1710     if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface)
   1711     {
   1712         extensions[0] = "VK_KHR_surface";
   1713         extensions[1] = "VK_EXT_metal_surface";
   1714     }
   1715     else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface)
   1716     {
   1717         extensions[0] = "VK_KHR_surface";
   1718         extensions[1] = "VK_MVK_macos_surface";
   1719     }
   1720 }
   1721 
   1722 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
   1723                                                       VkPhysicalDevice device,
   1724                                                       uint32_t queuefamily)
   1725 {
   1726     return GLFW_TRUE;
   1727 }
   1728 
   1729 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
   1730                                           _GLFWwindow* window,
   1731                                           const VkAllocationCallbacks* allocator,
   1732                                           VkSurfaceKHR* surface)
   1733 {
   1734     @autoreleasepool {
   1735 
   1736 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
   1737     // HACK: Dynamically load Core Animation to avoid adding an extra
   1738     //       dependency for the majority who don't use MoltenVK
   1739     NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"];
   1740     if (!bundle)
   1741     {
   1742         _glfwInputError(GLFW_PLATFORM_ERROR,
   1743                         "Cocoa: Failed to find QuartzCore.framework");
   1744         return VK_ERROR_EXTENSION_NOT_PRESENT;
   1745     }
   1746 
   1747     // NOTE: Create the layer here as makeBackingLayer should not return nil
   1748     window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer];
   1749     if (!window->ns.layer)
   1750     {
   1751         _glfwInputError(GLFW_PLATFORM_ERROR,
   1752                         "Cocoa: Failed to create layer for view");
   1753         return VK_ERROR_EXTENSION_NOT_PRESENT;
   1754     }
   1755 
   1756     if (window->ns.retina)
   1757         [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
   1758 
   1759     [window->ns.view setLayer:window->ns.layer];
   1760     [window->ns.view setWantsLayer:YES];
   1761 
   1762     VkResult err;
   1763 
   1764     if (_glfw.vk.EXT_metal_surface)
   1765     {
   1766         VkMetalSurfaceCreateInfoEXT sci;
   1767 
   1768         PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT;
   1769         vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT)
   1770             vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT");
   1771         if (!vkCreateMetalSurfaceEXT)
   1772         {
   1773             _glfwInputError(GLFW_API_UNAVAILABLE,
   1774                             "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension");
   1775             return VK_ERROR_EXTENSION_NOT_PRESENT;
   1776         }
   1777 
   1778         memset(&sci, 0, sizeof(sci));
   1779         sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
   1780         sci.pLayer = window->ns.layer;
   1781 
   1782         err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface);
   1783     }
   1784     else
   1785     {
   1786         VkMacOSSurfaceCreateInfoMVK sci;
   1787 
   1788         PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK;
   1789         vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)
   1790             vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK");
   1791         if (!vkCreateMacOSSurfaceMVK)
   1792         {
   1793             _glfwInputError(GLFW_API_UNAVAILABLE,
   1794                             "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension");
   1795             return VK_ERROR_EXTENSION_NOT_PRESENT;
   1796         }
   1797 
   1798         memset(&sci, 0, sizeof(sci));
   1799         sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
   1800         sci.pView = window->ns.view;
   1801 
   1802         err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface);
   1803     }
   1804 
   1805     if (err)
   1806     {
   1807         _glfwInputError(GLFW_PLATFORM_ERROR,
   1808                         "Cocoa: Failed to create Vulkan surface: %s",
   1809                         _glfwGetVulkanResultString(err));
   1810     }
   1811 
   1812     return err;
   1813 #else
   1814     return VK_ERROR_EXTENSION_NOT_PRESENT;
   1815 #endif
   1816 
   1817     } // autoreleasepool
   1818 }
   1819 
   1820 
   1821 //////////////////////////////////////////////////////////////////////////
   1822 //////                        GLFW native API                       //////
   1823 //////////////////////////////////////////////////////////////////////////
   1824 
   1825 GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
   1826 {
   1827     _GLFWwindow* window = (_GLFWwindow*) handle;
   1828     _GLFW_REQUIRE_INIT_OR_RETURN(nil);
   1829     return window->ns.object;
   1830 }
   1831