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