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