1 //========================================================================
2 // GLFW 3.4 macOS - www.glfw.org
3 //------------------------------------------------------------------------
4 // Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
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.
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:
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.
19 // 2. Altered source versions must be plainly marked as such, and must not
20 // be misrepresented as being the original software.
22 // 3. This notice may not be removed or altered from any source
25 //========================================================================
26 // It is fine to use C99 in this file because it will not be built with VS
27 //========================================================================
34 // Returns the style mask corresponding to the window settings
36 static NSUInteger getStyleMask(_GLFWwindow* window)
38 NSUInteger styleMask = NSWindowStyleMaskMiniaturizable;
40 if (window->monitor || !window->decorated)
41 styleMask |= NSWindowStyleMaskBorderless;
44 styleMask |= NSWindowStyleMaskTitled |
45 NSWindowStyleMaskClosable;
47 if (window->resizable)
48 styleMask |= NSWindowStyleMaskResizable;
54 // Returns whether the cursor is in the content area of the specified window
56 static GLFWbool cursorInContentArea(_GLFWwindow* window)
58 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
59 return [window->ns.view mouse:pos inRect:[window->ns.view frame]];
62 // Hides the cursor if not already hidden
64 static void hideCursor(_GLFWwindow* window)
66 if (!_glfw.ns.cursorHidden)
69 _glfw.ns.cursorHidden = GLFW_TRUE;
73 // Shows the cursor if not already shown
75 static void showCursor(_GLFWwindow* window)
77 if (_glfw.ns.cursorHidden)
80 _glfw.ns.cursorHidden = GLFW_FALSE;
84 // Updates the cursor image according to its cursor mode
86 static void updateCursorImage(_GLFWwindow* window)
88 if (window->cursorMode == GLFW_CURSOR_NORMAL)
93 [(NSCursor*) window->cursor->ns.object set];
95 [[NSCursor arrowCursor] set];
101 // Apply chosen cursor mode to a focused window
103 static void updateCursorMode(_GLFWwindow* window)
105 if (window->cursorMode == GLFW_CURSOR_DISABLED)
107 _glfw.ns.disabledCursorWindow = window;
108 _glfwPlatformGetCursorPos(window,
109 &_glfw.ns.restoreCursorPosX,
110 &_glfw.ns.restoreCursorPosY);
111 _glfwCenterCursorInContentArea(window);
112 CGAssociateMouseAndMouseCursorPosition(false);
114 else if (_glfw.ns.disabledCursorWindow == window)
116 _glfw.ns.disabledCursorWindow = NULL;
117 CGAssociateMouseAndMouseCursorPosition(true);
118 _glfwPlatformSetCursorPos(window,
119 _glfw.ns.restoreCursorPosX,
120 _glfw.ns.restoreCursorPosY);
123 if (cursorInContentArea(window))
124 updateCursorImage(window);
127 // Make the specified window and its video mode active on its monitor
129 static void acquireMonitor(_GLFWwindow* window)
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),
138 [window->ns.object setFrame:frame display:YES];
140 _glfwInputMonitorWindow(window->monitor, window);
143 // Remove the window and restore the original video mode
145 static void releaseMonitor(_GLFWwindow* window)
147 if (window->monitor->window != window)
150 _glfwInputMonitorWindow(window->monitor, NULL);
151 _glfwRestoreVideoModeNS(window->monitor);
154 // Translates macOS key modifiers into GLFW ones
156 static int translateFlags(NSUInteger flags)
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;
174 // Translates a macOS keycode to a GLFW keycode
176 static int translateKey(unsigned int key)
178 if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0]))
179 return GLFW_KEY_UNKNOWN;
181 return _glfw.ns.keycodes[key];
184 // Translate a GLFW keycode to a Cocoa modifier flag
186 static NSUInteger translateKeyToModifierFlag(int key)
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;
209 // Defines a constant for empty ranges in NSTextInputClient
211 static const NSRange kEmptyRange = { NSNotFound, 0 };
214 //------------------------------------------------------------------------
215 // Delegate for window related notifications
216 //------------------------------------------------------------------------
218 @interface GLFWWindowDelegate : NSObject
223 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;
227 @implementation GLFWWindowDelegate
229 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow
238 - (BOOL)windowShouldClose:(id)sender
240 _glfwInputWindowCloseRequest(window);
244 - (void)windowDidResize:(NSNotification *)notification
246 if (window->context.client != GLFW_NO_API)
247 [window->context.nsgl.object update];
249 if (_glfw.ns.disabledCursorWindow == window)
250 _glfwCenterCursorInContentArea(window);
252 const int maximized = [window->ns.object isZoomed];
253 if (window->ns.maximized != maximized)
255 window->ns.maximized = maximized;
256 _glfwInputWindowMaximize(window, maximized);
259 const NSRect contentRect = [window->ns.view frame];
260 const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
262 if (fbRect.size.width != window->ns.fbWidth ||
263 fbRect.size.height != window->ns.fbHeight)
265 window->ns.fbWidth = fbRect.size.width;
266 window->ns.fbHeight = fbRect.size.height;
267 _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
270 if (contentRect.size.width != window->ns.width ||
271 contentRect.size.height != window->ns.height)
273 window->ns.width = contentRect.size.width;
274 window->ns.height = contentRect.size.height;
275 _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height);
279 - (void)windowDidMove:(NSNotification *)notification
281 if (window->context.client != GLFW_NO_API)
282 [window->context.nsgl.object update];
284 if (_glfw.ns.disabledCursorWindow == window)
285 _glfwCenterCursorInContentArea(window);
288 _glfwPlatformGetWindowPos(window, &x, &y);
289 _glfwInputWindowPos(window, x, y);
292 - (void)windowDidMiniaturize:(NSNotification *)notification
295 releaseMonitor(window);
297 _glfwInputWindowIconify(window, GLFW_TRUE);
300 - (void)windowDidDeminiaturize:(NSNotification *)notification
303 acquireMonitor(window);
305 _glfwInputWindowIconify(window, GLFW_FALSE);
308 - (void)windowDidBecomeKey:(NSNotification *)notification
310 if (_glfw.ns.disabledCursorWindow == window)
311 _glfwCenterCursorInContentArea(window);
313 _glfwInputWindowFocus(window, GLFW_TRUE);
314 updateCursorMode(window);
317 - (void)windowDidResignKey:(NSNotification *)notification
319 if (window->monitor && window->autoIconify)
320 _glfwPlatformIconifyWindow(window);
322 _glfwInputWindowFocus(window, GLFW_FALSE);
325 - (void)windowDidChangeOcclusionState:(NSNotification* )notification
327 if ([window->ns.object occlusionState] & NSWindowOcclusionStateVisible)
328 window->ns.occluded = GLFW_FALSE;
330 window->ns.occluded = GLFW_TRUE;
336 //------------------------------------------------------------------------
337 // Content view class for the GLFW window
338 //------------------------------------------------------------------------
340 @interface GLFWContentView : NSView <NSTextInputClient>
343 NSTrackingArea* trackingArea;
344 NSMutableAttributedString* markedText;
347 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow;
351 @implementation GLFWContentView
353 - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow
360 markedText = [[NSMutableAttributedString alloc] init];
362 [self updateTrackingAreas];
363 // NOTE: kUTTypeURL corresponds to NSPasteboardTypeURL but is available
364 // on 10.7 without having been deprecated yet
365 [self registerForDraggedTypes:@[(__bridge NSString*) kUTTypeURL]];
373 [trackingArea release];
374 [markedText release];
380 return [window->ns.object isOpaque];
383 - (BOOL)canBecomeKeyView
388 - (BOOL)acceptsFirstResponder
393 - (BOOL)wantsUpdateLayer
400 if (window->context.client != GLFW_NO_API)
401 [window->context.nsgl.object update];
403 _glfwInputWindowDamage(window);
406 - (void)cursorUpdate:(NSEvent *)event
408 updateCursorImage(window);
411 - (BOOL)acceptsFirstMouse:(NSEvent *)event
416 - (void)mouseDown:(NSEvent *)event
418 _glfwInputMouseClick(window,
419 GLFW_MOUSE_BUTTON_LEFT,
421 translateFlags([event modifierFlags]));
424 - (void)mouseDragged:(NSEvent *)event
426 [self mouseMoved:event];
429 - (void)mouseUp:(NSEvent *)event
431 _glfwInputMouseClick(window,
432 GLFW_MOUSE_BUTTON_LEFT,
434 translateFlags([event modifierFlags]));
437 - (void)mouseMoved:(NSEvent *)event
439 if (window->cursorMode == GLFW_CURSOR_DISABLED)
441 const double dx = [event deltaX] - window->ns.cursorWarpDeltaX;
442 const double dy = [event deltaY] - window->ns.cursorWarpDeltaY;
444 _glfwInputCursorPos(window,
445 window->virtualCursorPosX + dx,
446 window->virtualCursorPosY + dy);
450 const NSRect contentRect = [window->ns.view frame];
451 // NOTE: The returned location uses base 0,1 not 0,0
452 const NSPoint pos = [event locationInWindow];
454 _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y);
457 window->ns.cursorWarpDeltaX = 0;
458 window->ns.cursorWarpDeltaY = 0;
461 - (void)rightMouseDown:(NSEvent *)event
463 _glfwInputMouseClick(window,
464 GLFW_MOUSE_BUTTON_RIGHT,
466 translateFlags([event modifierFlags]));
469 - (void)rightMouseDragged:(NSEvent *)event
471 [self mouseMoved:event];
474 - (void)rightMouseUp:(NSEvent *)event
476 _glfwInputMouseClick(window,
477 GLFW_MOUSE_BUTTON_RIGHT,
479 translateFlags([event modifierFlags]));
482 - (void)otherMouseDown:(NSEvent *)event
484 _glfwInputMouseClick(window,
485 (int) [event buttonNumber],
487 translateFlags([event modifierFlags]));
490 - (void)otherMouseDragged:(NSEvent *)event
492 [self mouseMoved:event];
495 - (void)otherMouseUp:(NSEvent *)event
497 _glfwInputMouseClick(window,
498 (int) [event buttonNumber],
500 translateFlags([event modifierFlags]));
503 - (void)mouseExited:(NSEvent *)event
505 if (window->cursorMode == GLFW_CURSOR_HIDDEN)
508 _glfwInputCursorEnter(window, GLFW_FALSE);
511 - (void)mouseEntered:(NSEvent *)event
513 if (window->cursorMode == GLFW_CURSOR_HIDDEN)
516 _glfwInputCursorEnter(window, GLFW_TRUE);
519 - (void)viewDidChangeBackingProperties
521 const NSRect contentRect = [window->ns.view frame];
522 const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
524 if (fbRect.size.width != window->ns.fbWidth ||
525 fbRect.size.height != window->ns.fbHeight)
527 window->ns.fbWidth = fbRect.size.width;
528 window->ns.fbHeight = fbRect.size.height;
529 _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height);
532 const float xscale = fbRect.size.width / contentRect.size.width;
533 const float yscale = fbRect.size.height / contentRect.size.height;
535 if (xscale != window->ns.xscale || yscale != window->ns.yscale)
537 window->ns.xscale = xscale;
538 window->ns.yscale = yscale;
539 _glfwInputWindowContentScale(window, xscale, yscale);
541 if (window->ns.retina && window->ns.layer)
542 [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
546 - (void)drawRect:(NSRect)rect
548 _glfwInputWindowDamage(window);
551 - (void)updateTrackingAreas
553 if (trackingArea != nil)
555 [self removeTrackingArea:trackingArea];
556 [trackingArea release];
559 const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited |
560 NSTrackingActiveInKeyWindow |
561 NSTrackingEnabledDuringMouseDrag |
562 NSTrackingCursorUpdate |
563 NSTrackingInVisibleRect |
564 NSTrackingAssumeInside;
566 trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
571 [self addTrackingArea:trackingArea];
572 [super updateTrackingAreas];
575 - (void)keyDown:(NSEvent *)event
577 const int key = translateKey([event keyCode]);
578 const int mods = translateFlags([event modifierFlags]);
580 _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods);
582 [self interpretKeyEvents:@[event]];
585 - (void)flagsChanged:(NSEvent *)event
588 const unsigned int modifierFlags =
589 [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
590 const int key = translateKey([event keyCode]);
591 const int mods = translateFlags(modifierFlags);
592 const NSUInteger keyFlag = translateKeyToModifierFlag(key);
594 if (keyFlag & modifierFlags)
596 if (window->keys[key] == GLFW_PRESS)
597 action = GLFW_RELEASE;
602 action = GLFW_RELEASE;
604 _glfwInputKey(window, key, [event keyCode], action, mods);
607 - (void)keyUp:(NSEvent *)event
609 const int key = translateKey([event keyCode]);
610 const int mods = translateFlags([event modifierFlags]);
611 _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods);
614 - (void)scrollWheel:(NSEvent *)event
616 double deltaX = [event scrollingDeltaX];
617 double deltaY = [event scrollingDeltaY];
619 if ([event hasPreciseScrollingDeltas])
625 if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0)
626 _glfwInputScroll(window, deltaX, deltaY);
629 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
631 // HACK: We don't know what to say here because we don't know what the
632 // application wants to do with the paths
633 return NSDragOperationGeneric;
636 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
638 const NSRect contentRect = [window->ns.view frame];
639 // NOTE: The returned location uses base 0,1 not 0,0
640 const NSPoint pos = [sender draggingLocation];
641 _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y);
643 NSPasteboard* pasteboard = [sender draggingPasteboard];
644 NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
645 NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]]
647 const NSUInteger count = [urls count];
650 char** paths = calloc(count, sizeof(char*));
652 for (NSUInteger i = 0; i < count; i++)
653 paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]);
655 _glfwInputDrop(window, (int) count, (const char**) paths);
657 for (NSUInteger i = 0; i < count; i++)
665 - (BOOL)hasMarkedText
667 return [markedText length] > 0;
670 - (NSRange)markedRange
672 if ([markedText length] > 0)
673 return NSMakeRange(0, [markedText length] - 1);
678 - (NSRange)selectedRange
683 - (void)setMarkedText:(id)string
684 selectedRange:(NSRange)selectedRange
685 replacementRange:(NSRange)replacementRange
687 [markedText release];
688 if ([string isKindOfClass:[NSAttributedString class]])
689 markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
691 markedText = [[NSMutableAttributedString alloc] initWithString:string];
696 [[markedText mutableString] setString:@""];
699 - (NSArray*)validAttributesForMarkedText
701 return [NSArray array];
704 - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
705 actualRange:(NSRangePointer)actualRange
710 - (NSUInteger)characterIndexForPoint:(NSPoint)point
715 - (NSRect)firstRectForCharacterRange:(NSRange)range
716 actualRange:(NSRangePointer)actualRange
718 const NSRect frame = [window->ns.view frame];
719 return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0);
722 - (void)insertText:(id)string replacementRange:(NSRange)replacementRange
724 NSString* characters;
725 NSEvent* event = [NSApp currentEvent];
726 const int mods = translateFlags([event modifierFlags]);
727 const int plain = !(mods & GLFW_MOD_SUPER);
729 if ([string isKindOfClass:[NSAttributedString class]])
730 characters = [string string];
732 characters = (NSString*) string;
734 NSRange range = NSMakeRange(0, [characters length]);
737 uint32_t codepoint = 0;
739 if ([characters getBytes:&codepoint
740 maxLength:sizeof(codepoint)
742 encoding:NSUTF32StringEncoding
745 remainingRange:&range])
747 if (codepoint >= 0xf700 && codepoint <= 0xf7ff)
750 _glfwInputChar(window, codepoint, mods, plain);
755 - (void)doCommandBySelector:(SEL)selector
762 //------------------------------------------------------------------------
764 //------------------------------------------------------------------------
766 @interface GLFWWindow : NSWindow {}
769 @implementation GLFWWindow
771 - (BOOL)canBecomeKeyWindow
773 // Required for NSWindowStyleMaskBorderless windows
777 - (BOOL)canBecomeMainWindow
785 // Create the Cocoa window
787 static GLFWbool createNativeWindow(_GLFWwindow* window,
788 const _GLFWwndconfig* wndconfig,
789 const _GLFWfbconfig* fbconfig)
791 window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window];
792 if (window->ns.delegate == nil)
794 _glfwInputError(GLFW_PLATFORM_ERROR,
795 "Cocoa: Failed to create window delegate");
806 _glfwPlatformGetVideoMode(window->monitor, &mode);
807 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
809 contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height);
812 contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height);
814 window->ns.object = [[GLFWWindow alloc]
815 initWithContentRect:contentRect
816 styleMask:getStyleMask(window)
817 backing:NSBackingStoreBuffered
820 if (window->ns.object == nil)
822 _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window");
827 [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
830 [(NSWindow*) window->ns.object center];
831 _glfw.ns.cascadePoint =
832 NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint:
833 NSPointFromCGPoint(_glfw.ns.cascadePoint)]);
835 if (wndconfig->resizable)
837 const NSWindowCollectionBehavior behavior =
838 NSWindowCollectionBehaviorFullScreenPrimary |
839 NSWindowCollectionBehaviorManaged;
840 [window->ns.object setCollectionBehavior:behavior];
843 if (wndconfig->floating)
844 [window->ns.object setLevel:NSFloatingWindowLevel];
846 if (wndconfig->maximized)
847 [window->ns.object zoom:nil];
850 if (strlen(wndconfig->ns.frameName))
851 [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)];
853 window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window];
854 window->ns.retina = wndconfig->ns.retina;
856 if (fbconfig->transparent)
858 [window->ns.object setOpaque:NO];
859 [window->ns.object setHasShadow:NO];
860 [window->ns.object setBackgroundColor:[NSColor clearColor]];
863 [window->ns.object setContentView:window->ns.view];
864 [window->ns.object makeFirstResponder:window->ns.view];
865 [window->ns.object setTitle:@(wndconfig->title)];
866 [window->ns.object setDelegate:window->ns.delegate];
867 [window->ns.object setAcceptsMouseMovedEvents:YES];
868 [window->ns.object setRestorable:NO];
870 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
871 if ([window->ns.object respondsToSelector:@selector(setTabbingMode:)])
872 [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed];
875 _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height);
876 _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight);
882 //////////////////////////////////////////////////////////////////////////
883 ////// GLFW internal API //////
884 //////////////////////////////////////////////////////////////////////////
886 // Transforms a y-coordinate between the CG display and NS screen spaces
888 float _glfwTransformYNS(float y)
890 return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1;
894 //////////////////////////////////////////////////////////////////////////
895 ////// GLFW platform API //////
896 //////////////////////////////////////////////////////////////////////////
898 int _glfwPlatformCreateWindow(_GLFWwindow* window,
899 const _GLFWwndconfig* wndconfig,
900 const _GLFWctxconfig* ctxconfig,
901 const _GLFWfbconfig* fbconfig)
905 if (!createNativeWindow(window, wndconfig, fbconfig))
908 if (ctxconfig->client != GLFW_NO_API)
910 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
912 if (!_glfwInitNSGL())
914 if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig))
917 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
919 // EGL implementation on macOS use CALayer* EGLNativeWindowType so we
920 // need to get the layer for EGL window surface creation.
921 [window->ns.view setWantsLayer:YES];
922 window->ns.layer = [window->ns.view layer];
926 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
929 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
931 if (!_glfwInitOSMesa())
933 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
940 _glfwPlatformShowWindow(window);
941 _glfwPlatformFocusWindow(window);
942 acquireMonitor(window);
950 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
954 if (_glfw.ns.disabledCursorWindow == window)
955 _glfw.ns.disabledCursorWindow = NULL;
957 [window->ns.object orderOut:nil];
960 releaseMonitor(window);
962 if (window->context.destroy)
963 window->context.destroy(window);
965 [window->ns.object setDelegate:nil];
966 [window->ns.delegate release];
967 window->ns.delegate = nil;
969 [window->ns.view release];
970 window->ns.view = nil;
972 [window->ns.object close];
973 window->ns.object = nil;
975 // HACK: Allow Cocoa to catch up before returning
976 _glfwPlatformPollEvents();
981 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
984 NSString* string = @(title);
985 [window->ns.object setTitle:string];
986 // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it
987 // if the window lacks NSWindowStyleMaskTitled
988 [window->ns.object setMiniwindowTitle:string];
992 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
993 int count, const GLFWimage* images)
995 _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
996 "Cocoa: Regular windows do not have icons on macOS");
999 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
1003 const NSRect contentRect =
1004 [window->ns.object contentRectForFrameRect:[window->ns.object frame]];
1007 *xpos = contentRect.origin.x;
1009 *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1);
1011 } // autoreleasepool
1014 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y)
1018 const NSRect contentRect = [window->ns.view frame];
1019 const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0);
1020 const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect];
1021 [window->ns.object setFrameOrigin:frameRect.origin];
1023 } // autoreleasepool
1026 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
1030 const NSRect contentRect = [window->ns.view frame];
1033 *width = contentRect.size.width;
1035 *height = contentRect.size.height;
1037 } // autoreleasepool
1040 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
1044 if (window->monitor)
1046 if (window->monitor->window == window)
1047 acquireMonitor(window);
1051 NSRect contentRect =
1052 [window->ns.object contentRectForFrameRect:[window->ns.object frame]];
1053 contentRect.origin.y += contentRect.size.height - height;
1054 contentRect.size = NSMakeSize(width, height);
1055 [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect]
1059 } // autoreleasepool
1062 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
1063 int minwidth, int minheight,
1064 int maxwidth, int maxheight)
1068 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
1069 [window->ns.object setContentMinSize:NSMakeSize(0, 0)];
1071 [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)];
1073 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
1074 [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)];
1076 [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)];
1078 } // autoreleasepool
1081 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
1084 if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
1085 [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)];
1087 [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)];
1088 } // autoreleasepool
1091 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
1095 const NSRect contentRect = [window->ns.view frame];
1096 const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect];
1099 *width = (int) fbRect.size.width;
1101 *height = (int) fbRect.size.height;
1103 } // autoreleasepool
1106 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
1107 int* left, int* top,
1108 int* right, int* bottom)
1112 const NSRect contentRect = [window->ns.view frame];
1113 const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect];
1116 *left = contentRect.origin.x - frameRect.origin.x;
1118 *top = frameRect.origin.y + frameRect.size.height -
1119 contentRect.origin.y - contentRect.size.height;
1121 *right = frameRect.origin.x + frameRect.size.width -
1122 contentRect.origin.x - contentRect.size.width;
1124 *bottom = contentRect.origin.y - frameRect.origin.y;
1126 } // autoreleasepool
1129 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
1130 float* xscale, float* yscale)
1134 const NSRect points = [window->ns.view frame];
1135 const NSRect pixels = [window->ns.view convertRectToBacking:points];
1138 *xscale = (float) (pixels.size.width / points.size.width);
1140 *yscale = (float) (pixels.size.height / points.size.height);
1142 } // autoreleasepool
1145 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
1148 [window->ns.object miniaturize:nil];
1149 } // autoreleasepool
1152 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
1155 if ([window->ns.object isMiniaturized])
1156 [window->ns.object deminiaturize:nil];
1157 else if ([window->ns.object isZoomed])
1158 [window->ns.object zoom:nil];
1159 } // autoreleasepool
1162 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
1165 if (![window->ns.object isZoomed])
1166 [window->ns.object zoom:nil];
1167 } // autoreleasepool
1170 void _glfwPlatformShowWindow(_GLFWwindow* window)
1173 [window->ns.object orderFront:nil];
1174 } // autoreleasepool
1177 void _glfwPlatformHideWindow(_GLFWwindow* window)
1180 [window->ns.object orderOut:nil];
1181 } // autoreleasepool
1184 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
1187 [NSApp requestUserAttention:NSInformationalRequest];
1188 } // autoreleasepool
1191 void _glfwPlatformFocusWindow(_GLFWwindow* window)
1194 // Make us the active application
1195 // HACK: This is here to prevent applications using only hidden windows from
1196 // being activated, but should probably not be done every time any
1198 [NSApp activateIgnoringOtherApps:YES];
1199 [window->ns.object makeKeyAndOrderFront:nil];
1200 } // autoreleasepool
1203 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
1204 _GLFWmonitor* monitor,
1206 int width, int height,
1211 if (window->monitor == monitor)
1215 if (monitor->window == window)
1216 acquireMonitor(window);
1220 const NSRect contentRect =
1221 NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height);
1222 const NSRect frameRect =
1223 [window->ns.object frameRectForContentRect:contentRect
1224 styleMask:getStyleMask(window)];
1226 [window->ns.object setFrame:frameRect display:YES];
1232 if (window->monitor)
1233 releaseMonitor(window);
1235 _glfwInputWindowMonitor(window, monitor);
1237 // HACK: Allow the state cached in Cocoa to catch up to reality
1238 // TODO: Solve this in a less terrible way
1239 _glfwPlatformPollEvents();
1241 const NSUInteger styleMask = getStyleMask(window);
1242 [window->ns.object setStyleMask:styleMask];
1243 // HACK: Changing the style mask can cause the first responder to be cleared
1244 [window->ns.object makeFirstResponder:window->ns.view];
1246 if (window->monitor)
1248 [window->ns.object setLevel:NSMainMenuWindowLevel + 1];
1249 [window->ns.object setHasShadow:NO];
1251 acquireMonitor(window);
1255 NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1),
1257 NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect
1258 styleMask:styleMask];
1259 [window->ns.object setFrame:frameRect display:YES];
1261 if (window->numer != GLFW_DONT_CARE &&
1262 window->denom != GLFW_DONT_CARE)
1264 [window->ns.object setContentAspectRatio:NSMakeSize(window->numer,
1268 if (window->minwidth != GLFW_DONT_CARE &&
1269 window->minheight != GLFW_DONT_CARE)
1271 [window->ns.object setContentMinSize:NSMakeSize(window->minwidth,
1272 window->minheight)];
1275 if (window->maxwidth != GLFW_DONT_CARE &&
1276 window->maxheight != GLFW_DONT_CARE)
1278 [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth,
1279 window->maxheight)];
1282 if (window->floating)
1283 [window->ns.object setLevel:NSFloatingWindowLevel];
1285 [window->ns.object setLevel:NSNormalWindowLevel];
1287 [window->ns.object setHasShadow:YES];
1288 // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window
1289 // title property but the miniwindow title property is unaffected
1290 [window->ns.object setTitle:[window->ns.object miniwindowTitle]];
1293 } // autoreleasepool
1296 int _glfwPlatformWindowFocused(_GLFWwindow* window)
1299 return [window->ns.object isKeyWindow];
1300 } // autoreleasepool
1303 int _glfwPlatformWindowIconified(_GLFWwindow* window)
1306 return [window->ns.object isMiniaturized];
1307 } // autoreleasepool
1310 int _glfwPlatformWindowVisible(_GLFWwindow* window)
1313 return [window->ns.object isVisible];
1314 } // autoreleasepool
1317 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
1320 return [window->ns.object isZoomed];
1321 } // autoreleasepool
1324 int _glfwPlatformWindowHovered(_GLFWwindow* window)
1328 const NSPoint point = [NSEvent mouseLocation];
1330 if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] !=
1331 [window->ns.object windowNumber])
1336 return NSMouseInRect(point,
1337 [window->ns.object convertRectToScreen:[window->ns.view frame]], NO);
1339 } // autoreleasepool
1342 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
1345 return ![window->ns.object isOpaque] && ![window->ns.view isOpaque];
1346 } // autoreleasepool
1349 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
1352 [window->ns.object setStyleMask:getStyleMask(window)];
1353 } // autoreleasepool
1356 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
1359 [window->ns.object setStyleMask:getStyleMask(window)];
1360 [window->ns.object makeFirstResponder:window->ns.view];
1361 } // autoreleasepool
1364 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
1368 [window->ns.object setLevel:NSFloatingWindowLevel];
1370 [window->ns.object setLevel:NSNormalWindowLevel];
1371 } // autoreleasepool
1374 void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
1377 [window->ns.object setIgnoresMouseEvents:enabled];
1381 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
1384 return (float) [window->ns.object alphaValue];
1385 } // autoreleasepool
1388 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
1391 [window->ns.object setAlphaValue:opacity];
1392 } // autoreleasepool
1395 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
1397 _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
1398 "Cocoa: Raw mouse motion not yet implemented");
1401 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
1406 void _glfwPlatformPollEvents(void)
1412 NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
1413 untilDate:[NSDate distantPast]
1414 inMode:NSDefaultRunLoopMode
1419 [NSApp sendEvent:event];
1422 } // autoreleasepool
1425 void _glfwPlatformWaitEvents(void)
1429 // I wanted to pass NO to dequeue:, and rely on PollEvents to
1430 // dequeue and send. For reasons not at all clear to me, passing
1431 // NO to dequeue: causes this method never to return.
1432 NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
1433 untilDate:[NSDate distantFuture]
1434 inMode:NSDefaultRunLoopMode
1436 [NSApp sendEvent:event];
1438 _glfwPlatformPollEvents();
1440 } // autoreleasepool
1443 void _glfwPlatformWaitEventsTimeout(double timeout)
1447 NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout];
1448 NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
1450 inMode:NSDefaultRunLoopMode
1453 [NSApp sendEvent:event];
1455 _glfwPlatformPollEvents();
1457 } // autoreleasepool
1460 void _glfwPlatformPostEmptyEvent(void)
1464 NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
1465 location:NSMakePoint(0, 0)
1473 [NSApp postEvent:event atStart:YES];
1475 } // autoreleasepool
1478 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
1482 const NSRect contentRect = [window->ns.view frame];
1483 // NOTE: The returned location uses base 0,1 not 0,0
1484 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
1489 *ypos = contentRect.size.height - pos.y;
1491 } // autoreleasepool
1494 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
1498 updateCursorImage(window);
1500 const NSRect contentRect = [window->ns.view frame];
1501 // NOTE: The returned location uses base 0,1 not 0,0
1502 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream];
1504 window->ns.cursorWarpDeltaX += x - pos.x;
1505 window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y;
1507 if (window->monitor)
1509 CGDisplayMoveCursorToPoint(window->monitor->ns.displayID,
1514 const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0);
1515 const NSRect globalRect = [window->ns.object convertRectToScreen:localRect];
1516 const NSPoint globalPoint = globalRect.origin;
1518 CGWarpMouseCursorPosition(CGPointMake(globalPoint.x,
1519 _glfwTransformYNS(globalPoint.y)));
1522 } // autoreleasepool
1525 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
1528 if (_glfwPlatformWindowFocused(window))
1529 updateCursorMode(window);
1530 } // autoreleasepool
1533 const char* _glfwPlatformGetScancodeName(int scancode)
1537 if (scancode < 0 || scancode > 0xff ||
1538 _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN)
1540 _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
1544 const int key = _glfw.ns.keycodes[scancode];
1546 UInt32 deadKeyState = 0;
1547 UniChar characters[4];
1548 UniCharCount characterCount = 0;
1550 if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes],
1552 kUCKeyActionDisplay,
1555 kUCKeyTranslateNoDeadKeysBit,
1557 sizeof(characters) / sizeof(characters[0]),
1559 characters) != noErr)
1564 if (!characterCount)
1567 CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
1571 CFStringGetCString(string,
1572 _glfw.ns.keynames[key],
1573 sizeof(_glfw.ns.keynames[key]),
1574 kCFStringEncodingUTF8);
1577 return _glfw.ns.keynames[key];
1579 } // autoreleasepool
1582 int _glfwPlatformGetKeyScancode(int key)
1584 return _glfw.ns.scancodes[key];
1587 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
1588 const GLFWimage* image,
1594 NSBitmapImageRep* rep;
1596 rep = [[NSBitmapImageRep alloc]
1597 initWithBitmapDataPlanes:NULL
1598 pixelsWide:image->width
1599 pixelsHigh:image->height
1604 colorSpaceName:NSCalibratedRGBColorSpace
1605 bitmapFormat:NSBitmapFormatAlphaNonpremultiplied
1606 bytesPerRow:image->width * 4
1612 memcpy([rep bitmapData], image->pixels, image->width * image->height * 4);
1614 native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)];
1615 [native addRepresentation:rep];
1617 cursor->ns.object = [[NSCursor alloc] initWithImage:native
1618 hotSpot:NSMakePoint(xhot, yhot)];
1623 if (cursor->ns.object == nil)
1628 } // autoreleasepool
1631 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
1635 SEL cursorSelector = NULL;
1637 // HACK: Try to use a private message
1638 if (shape == GLFW_RESIZE_EW_CURSOR)
1639 cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor");
1640 else if (shape == GLFW_RESIZE_NS_CURSOR)
1641 cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor");
1642 else if (shape == GLFW_RESIZE_NWSE_CURSOR)
1643 cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor");
1644 else if (shape == GLFW_RESIZE_NESW_CURSOR)
1645 cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor");
1647 if (cursorSelector && [NSCursor respondsToSelector:cursorSelector])
1649 id object = [NSCursor performSelector:cursorSelector];
1650 if ([object isKindOfClass:[NSCursor class]])
1651 cursor->ns.object = object;
1654 if (!cursor->ns.object)
1656 if (shape == GLFW_ARROW_CURSOR)
1657 cursor->ns.object = [NSCursor arrowCursor];
1658 else if (shape == GLFW_IBEAM_CURSOR)
1659 cursor->ns.object = [NSCursor IBeamCursor];
1660 else if (shape == GLFW_CROSSHAIR_CURSOR)
1661 cursor->ns.object = [NSCursor crosshairCursor];
1662 else if (shape == GLFW_POINTING_HAND_CURSOR)
1663 cursor->ns.object = [NSCursor pointingHandCursor];
1664 else if (shape == GLFW_RESIZE_EW_CURSOR)
1665 cursor->ns.object = [NSCursor resizeLeftRightCursor];
1666 else if (shape == GLFW_RESIZE_NS_CURSOR)
1667 cursor->ns.object = [NSCursor resizeUpDownCursor];
1668 else if (shape == GLFW_RESIZE_ALL_CURSOR)
1669 cursor->ns.object = [NSCursor closedHandCursor];
1670 else if (shape == GLFW_NOT_ALLOWED_CURSOR)
1671 cursor->ns.object = [NSCursor operationNotAllowedCursor];
1674 if (!cursor->ns.object)
1676 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
1677 "Cocoa: Standard cursor shape unavailable");
1681 [cursor->ns.object retain];
1684 } // autoreleasepool
1687 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
1690 if (cursor->ns.object)
1691 [(NSCursor*) cursor->ns.object release];
1692 } // autoreleasepool
1695 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
1698 if (cursorInContentArea(window))
1699 updateCursorImage(window);
1700 } // autoreleasepool
1703 void _glfwPlatformSetClipboardString(const char* string)
1706 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
1707 [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil];
1708 [pasteboard setString:@(string) forType:NSPasteboardTypeString];
1709 } // autoreleasepool
1712 const char* _glfwPlatformGetClipboardString(void)
1716 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
1718 if (![[pasteboard types] containsObject:NSPasteboardTypeString])
1720 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
1721 "Cocoa: Failed to retrieve string from pasteboard");
1725 NSString* object = [pasteboard stringForType:NSPasteboardTypeString];
1728 _glfwInputError(GLFW_PLATFORM_ERROR,
1729 "Cocoa: Failed to retrieve object from pasteboard");
1733 free(_glfw.ns.clipboardString);
1734 _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]);
1736 return _glfw.ns.clipboardString;
1738 } // autoreleasepool
1741 EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
1743 if (_glfw.egl.ANGLE_platform_angle)
1747 if (_glfw.egl.ANGLE_platform_angle_opengl)
1749 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
1750 type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
1753 if (_glfw.egl.ANGLE_platform_angle_metal)
1755 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_METAL)
1756 type = EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE;
1761 *attribs = calloc(3, sizeof(EGLint));
1762 (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
1763 (*attribs)[1] = type;
1764 (*attribs)[2] = EGL_NONE;
1765 return EGL_PLATFORM_ANGLE_ANGLE;
1772 EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
1774 return EGL_DEFAULT_DISPLAY;
1777 EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
1779 return window->ns.layer;
1782 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
1784 if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface)
1786 extensions[0] = "VK_KHR_surface";
1787 extensions[1] = "VK_EXT_metal_surface";
1789 else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface)
1791 extensions[0] = "VK_KHR_surface";
1792 extensions[1] = "VK_MVK_macos_surface";
1796 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
1797 VkPhysicalDevice device,
1798 uint32_t queuefamily)
1803 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
1804 _GLFWwindow* window,
1805 const VkAllocationCallbacks* allocator,
1806 VkSurfaceKHR* surface)
1810 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
1811 // HACK: Dynamically load Core Animation to avoid adding an extra
1812 // dependency for the majority who don't use MoltenVK
1813 NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"];
1816 _glfwInputError(GLFW_PLATFORM_ERROR,
1817 "Cocoa: Failed to find QuartzCore.framework");
1818 return VK_ERROR_EXTENSION_NOT_PRESENT;
1821 // NOTE: Create the layer here as makeBackingLayer should not return nil
1822 window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer];
1823 if (!window->ns.layer)
1825 _glfwInputError(GLFW_PLATFORM_ERROR,
1826 "Cocoa: Failed to create layer for view");
1827 return VK_ERROR_EXTENSION_NOT_PRESENT;
1830 if (window->ns.retina)
1831 [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]];
1833 [window->ns.view setLayer:window->ns.layer];
1834 [window->ns.view setWantsLayer:YES];
1838 if (_glfw.vk.EXT_metal_surface)
1840 VkMetalSurfaceCreateInfoEXT sci;
1842 PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT;
1843 vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT)
1844 vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT");
1845 if (!vkCreateMetalSurfaceEXT)
1847 _glfwInputError(GLFW_API_UNAVAILABLE,
1848 "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension");
1849 return VK_ERROR_EXTENSION_NOT_PRESENT;
1852 memset(&sci, 0, sizeof(sci));
1853 sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
1854 sci.pLayer = window->ns.layer;
1856 err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface);
1860 VkMacOSSurfaceCreateInfoMVK sci;
1862 PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK;
1863 vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)
1864 vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK");
1865 if (!vkCreateMacOSSurfaceMVK)
1867 _glfwInputError(GLFW_API_UNAVAILABLE,
1868 "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension");
1869 return VK_ERROR_EXTENSION_NOT_PRESENT;
1872 memset(&sci, 0, sizeof(sci));
1873 sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
1874 sci.pView = window->ns.view;
1876 err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface);
1881 _glfwInputError(GLFW_PLATFORM_ERROR,
1882 "Cocoa: Failed to create Vulkan surface: %s",
1883 _glfwGetVulkanResultString(err));
1888 return VK_ERROR_EXTENSION_NOT_PRESENT;
1891 } // autoreleasepool
1895 //////////////////////////////////////////////////////////////////////////
1896 ////// GLFW native API //////
1897 //////////////////////////////////////////////////////////////////////////
1899 GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle)
1901 _GLFWwindow* window = (_GLFWwindow*) handle;
1902 _GLFW_REQUIRE_INIT_OR_RETURN(nil);
1903 return window->ns.object;