medfall

A super great game engine
Log | Files | Refs

cocoa_window.m (53534B)


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