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 { 1140 NSRect contentRect = 1141 [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; 1142 contentRect.origin.y += contentRect.size.height - height; 1143 contentRect.size = NSMakeSize(width, height); 1144 [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect] 1145 display:YES]; 1146 } 1147} 1148 1149void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, 1150 int minwidth, int minheight, 1151 int maxwidth, int maxheight) 1152{ 1153 if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) 1154 [window->ns.object setContentMinSize:NSMakeSize(0, 0)]; 1155 else 1156 [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)]; 1157 1158 if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) 1159 [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; 1160 else 1161 [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; 1162} 1163 1164void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) 1165{ 1166 if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) 1167 [window->ns.object setContentAspectRatio:NSMakeSize(0, 0)]; 1168 else 1169 [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; 1170} 1171 1172void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) 1173{ 1174 const NSRect contentRect = [window->ns.view frame]; 1175 const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; 1176 1177 if (width) 1178 *width = (int) fbRect.size.width; 1179 if (height) 1180 *height = (int) fbRect.size.height; 1181} 1182 1183void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, 1184 int* left, int* top, 1185 int* right, int* bottom) 1186{ 1187 const NSRect contentRect = [window->ns.view frame]; 1188 const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect]; 1189 1190 if (left) 1191 *left = contentRect.origin.x - frameRect.origin.x; 1192 if (top) 1193 *top = frameRect.origin.y + frameRect.size.height - 1194 contentRect.origin.y - contentRect.size.height; 1195 if (right) 1196 *right = frameRect.origin.x + frameRect.size.width - 1197 contentRect.origin.x - contentRect.size.width; 1198 if (bottom) 1199 *bottom = contentRect.origin.y - frameRect.origin.y; 1200} 1201 1202void _glfwPlatformIconifyWindow(_GLFWwindow* window) 1203{ 1204 [window->ns.object miniaturize:nil]; 1205} 1206 1207void _glfwPlatformRestoreWindow(_GLFWwindow* window) 1208{ 1209 if ([window->ns.object isMiniaturized]) 1210 [window->ns.object deminiaturize:nil]; 1211 else if ([window->ns.object isZoomed]) 1212 [window->ns.object zoom:nil]; 1213} 1214 1215void _glfwPlatformMaximizeWindow(_GLFWwindow* window) 1216{ 1217 if (![window->ns.object isZoomed]) 1218 [window->ns.object zoom:nil]; 1219} 1220 1221void _glfwPlatformShowWindow(_GLFWwindow* window) 1222{ 1223 [window->ns.object orderFront:nil]; 1224} 1225 1226void _glfwPlatformHideWindow(_GLFWwindow* window) 1227{ 1228 [window->ns.object orderOut:nil]; 1229} 1230 1231void _glfwPlatformFocusWindow(_GLFWwindow* window) 1232{ 1233 // Make us the active application 1234 // HACK: This has been moved here from initializeAppKit to prevent 1235 // applications using only hidden windows from being activated, but 1236 // should probably not be done every time any window is shown 1237 [NSApp activateIgnoringOtherApps:YES]; 1238 1239 [window->ns.object makeKeyAndOrderFront:nil]; 1240} 1241 1242void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, 1243 _GLFWmonitor* monitor, 1244 int xpos, int ypos, 1245 int width, int height, 1246 int refreshRate) 1247{ 1248 if (window->monitor == monitor) 1249 { 1250 if (monitor) 1251 { 1252 if (monitor->window == window) 1253 acquireMonitor(window); 1254 } 1255 else 1256 { 1257 const NSRect contentRect = 1258 NSMakeRect(xpos, transformY(ypos + height), width, height); 1259 const NSRect frameRect = 1260 [window->ns.object frameRectForContentRect:contentRect 1261 styleMask:getStyleMask(window)]; 1262 1263 [window->ns.object setFrame:frameRect display:YES]; 1264 } 1265 1266 return; 1267 } 1268 1269 if (window->monitor) 1270 releaseMonitor(window); 1271 1272 _glfwInputWindowMonitorChange(window, monitor); 1273 1274 const NSUInteger styleMask = getStyleMask(window); 1275 [window->ns.object setStyleMask:styleMask]; 1276 [window->ns.object makeFirstResponder:window->ns.view]; 1277 1278 NSRect contentRect; 1279 1280 if (monitor) 1281 { 1282 GLFWvidmode mode; 1283 1284 _glfwPlatformGetVideoMode(window->monitor, &mode); 1285 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); 1286 1287 contentRect = NSMakeRect(xpos, transformY(ypos + mode.height), 1288 mode.width, mode.height); 1289 } 1290 else 1291 { 1292 contentRect = NSMakeRect(xpos, transformY(ypos + height), 1293 width, height); 1294 } 1295 1296 NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect 1297 styleMask:styleMask]; 1298 [window->ns.object setFrame:frameRect display:YES]; 1299 1300 if (monitor) 1301 { 1302 [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; 1303 [window->ns.object setHasShadow:NO]; 1304 1305 acquireMonitor(window); 1306 } 1307 else 1308 { 1309 if (window->numer != GLFW_DONT_CARE && 1310 window->denom != GLFW_DONT_CARE) 1311 { 1312 [window->ns.object setContentAspectRatio:NSMakeSize(window->numer, 1313 window->denom)]; 1314 } 1315 1316 if (window->minwidth != GLFW_DONT_CARE && 1317 window->minheight != GLFW_DONT_CARE) 1318 { 1319 [window->ns.object setContentMinSize:NSMakeSize(window->minwidth, 1320 window->minheight)]; 1321 } 1322 1323 if (window->maxwidth != GLFW_DONT_CARE && 1324 window->maxheight != GLFW_DONT_CARE) 1325 { 1326 [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth, 1327 window->maxheight)]; 1328 } 1329 1330 if (window->floating) 1331 [window->ns.object setLevel:NSFloatingWindowLevel]; 1332 else 1333 [window->ns.object setLevel:NSNormalWindowLevel]; 1334 1335 [window->ns.object setHasShadow:YES]; 1336 } 1337} 1338 1339int _glfwPlatformWindowFocused(_GLFWwindow* window) 1340{ 1341 return [window->ns.object isKeyWindow]; 1342} 1343 1344int _glfwPlatformWindowIconified(_GLFWwindow* window) 1345{ 1346 return [window->ns.object isMiniaturized]; 1347} 1348 1349int _glfwPlatformWindowVisible(_GLFWwindow* window) 1350{ 1351 return [window->ns.object isVisible]; 1352} 1353 1354int _glfwPlatformWindowMaximized(_GLFWwindow* window) 1355{ 1356 return [window->ns.object isZoomed]; 1357} 1358 1359void _glfwPlatformPollEvents(void) 1360{ 1361 for (;;) 1362 { 1363 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 1364 untilDate:[NSDate distantPast] 1365 inMode:NSDefaultRunLoopMode 1366 dequeue:YES]; 1367 if (event == nil) 1368 break; 1369 1370 [NSApp sendEvent:event]; 1371 } 1372 1373 [_glfw.ns.autoreleasePool drain]; 1374 _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; 1375} 1376 1377void _glfwPlatformWaitEvents(void) 1378{ 1379 // I wanted to pass NO to dequeue:, and rely on PollEvents to 1380 // dequeue and send. For reasons not at all clear to me, passing 1381 // NO to dequeue: causes this method never to return. 1382 NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask 1383 untilDate:[NSDate distantFuture] 1384 inMode:NSDefaultRunLoopMode 1385 dequeue:YES]; 1386 [NSApp sendEvent:event]; 1387 1388 _glfwPlatformPollEvents(); 1389} 1390 1391void _glfwPlatformWaitEventsTimeout(double timeout) 1392{ 1393 NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; 1394 NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask 1395 untilDate:date 1396 inMode:NSDefaultRunLoopMode 1397 dequeue:YES]; 1398 if (event) 1399 [NSApp sendEvent:event]; 1400 1401 _glfwPlatformPollEvents(); 1402} 1403 1404void _glfwPlatformPostEmptyEvent(void) 1405{ 1406 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 1407 NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined 1408 location:NSMakePoint(0, 0) 1409 modifierFlags:0 1410 timestamp:0 1411 windowNumber:0 1412 context:nil 1413 subtype:0 1414 data1:0 1415 data2:0]; 1416 [NSApp postEvent:event atStart:YES]; 1417 [pool drain]; 1418} 1419 1420void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) 1421{ 1422 const NSRect contentRect = [window->ns.view frame]; 1423 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; 1424 1425 if (xpos) 1426 *xpos = pos.x; 1427 if (ypos) 1428 *ypos = contentRect.size.height - pos.y - 1; 1429} 1430 1431void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) 1432{ 1433 updateCursorImage(window); 1434 1435 const NSRect contentRect = [window->ns.view frame]; 1436 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; 1437 1438 window->ns.cursorWarpDeltaX += x - pos.x; 1439 window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y; 1440 1441 if (window->monitor) 1442 { 1443 CGDisplayMoveCursorToPoint(window->monitor->ns.displayID, 1444 CGPointMake(x, y)); 1445 } 1446 else 1447 { 1448 const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0); 1449 const NSRect globalRect = [window->ns.object convertRectToScreen:localRect]; 1450 const NSPoint globalPoint = globalRect.origin; 1451 1452 CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, 1453 transformY(globalPoint.y))); 1454 } 1455} 1456 1457void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) 1458{ 1459 if (mode == GLFW_CURSOR_DISABLED) 1460 { 1461 _glfw.ns.disabledCursorWindow = window; 1462 _glfwPlatformGetCursorPos(window, 1463 &_glfw.ns.restoreCursorPosX, 1464 &_glfw.ns.restoreCursorPosY); 1465 centerCursor(window); 1466 CGAssociateMouseAndMouseCursorPosition(false); 1467 } 1468 else if (_glfw.ns.disabledCursorWindow == window) 1469 { 1470 _glfw.ns.disabledCursorWindow = NULL; 1471 CGAssociateMouseAndMouseCursorPosition(true); 1472 _glfwPlatformSetCursorPos(window, 1473 _glfw.ns.restoreCursorPosX, 1474 _glfw.ns.restoreCursorPosY); 1475 } 1476 1477 if (cursorInClientArea(window)) 1478 updateCursorImage(window); 1479} 1480 1481const char* _glfwPlatformGetKeyName(int key, int scancode) 1482{ 1483 if (key != GLFW_KEY_UNKNOWN) 1484 scancode = _glfw.ns.nativeKeys[key]; 1485 1486 if (!_glfwIsPrintable(_glfw.ns.publicKeys[scancode])) 1487 return NULL; 1488 1489 UInt32 deadKeyState = 0; 1490 UniChar characters[8]; 1491 UniCharCount characterCount = 0; 1492 1493 if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes], 1494 scancode, 1495 kUCKeyActionDisplay, 1496 0, 1497 LMGetKbdType(), 1498 kUCKeyTranslateNoDeadKeysBit, 1499 &deadKeyState, 1500 sizeof(characters) / sizeof(characters[0]), 1501 &characterCount, 1502 characters) != noErr) 1503 { 1504 return NULL; 1505 } 1506 1507 if (!characterCount) 1508 return NULL; 1509 1510 CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, 1511 characters, 1512 characterCount, 1513 kCFAllocatorNull); 1514 CFStringGetCString(string, 1515 _glfw.ns.keyName, 1516 sizeof(_glfw.ns.keyName), 1517 kCFStringEncodingUTF8); 1518 CFRelease(string); 1519 1520 return _glfw.ns.keyName; 1521} 1522 1523int _glfwPlatformCreateCursor(_GLFWcursor* cursor, 1524 const GLFWimage* image, 1525 int xhot, int yhot) 1526{ 1527 NSImage* native; 1528 NSBitmapImageRep* rep; 1529 1530 if (!initializeAppKit()) 1531 return GLFW_FALSE; 1532 1533 rep = [[NSBitmapImageRep alloc] 1534 initWithBitmapDataPlanes:NULL 1535 pixelsWide:image->width 1536 pixelsHigh:image->height 1537 bitsPerSample:8 1538 samplesPerPixel:4 1539 hasAlpha:YES 1540 isPlanar:NO 1541 colorSpaceName:NSCalibratedRGBColorSpace 1542 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat 1543 bytesPerRow:image->width * 4 1544 bitsPerPixel:32]; 1545 1546 if (rep == nil) 1547 return GLFW_FALSE; 1548 1549 memcpy([rep bitmapData], image->pixels, image->width * image->height * 4); 1550 1551 native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)]; 1552 [native addRepresentation:rep]; 1553 1554 cursor->ns.object = [[NSCursor alloc] initWithImage:native 1555 hotSpot:NSMakePoint(xhot, yhot)]; 1556 1557 [native release]; 1558 [rep release]; 1559 1560 if (cursor->ns.object == nil) 1561 return GLFW_FALSE; 1562 1563 return GLFW_TRUE; 1564} 1565 1566int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 1567{ 1568 if (!initializeAppKit()) 1569 return GLFW_FALSE; 1570 1571 cursor->ns.object = getStandardCursor(shape); 1572 if (!cursor->ns.object) 1573 { 1574 _glfwInputError(GLFW_PLATFORM_ERROR, 1575 "Cocoa: Failed to retrieve standard cursor"); 1576 return GLFW_FALSE; 1577 } 1578 1579 [cursor->ns.object retain]; 1580 return GLFW_TRUE; 1581} 1582 1583void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) 1584{ 1585 if (cursor->ns.object) 1586 [(NSCursor*) cursor->ns.object release]; 1587} 1588 1589void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 1590{ 1591 if (cursorInClientArea(window)) 1592 updateCursorImage(window); 1593} 1594 1595void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) 1596{ 1597 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; 1598 1599 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; 1600 [pasteboard declareTypes:types owner:nil]; 1601 [pasteboard setString:[NSString stringWithUTF8String:string] 1602 forType:NSStringPboardType]; 1603} 1604 1605const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) 1606{ 1607 NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; 1608 1609 if (![[pasteboard types] containsObject:NSStringPboardType]) 1610 { 1611 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 1612 "Cocoa: Failed to retrieve string from pasteboard"); 1613 return NULL; 1614 } 1615 1616 NSString* object = [pasteboard stringForType:NSStringPboardType]; 1617 if (!object) 1618 { 1619 _glfwInputError(GLFW_PLATFORM_ERROR, 1620 "Cocoa: Failed to retrieve object from pasteboard"); 1621 return NULL; 1622 } 1623 1624 free(_glfw.ns.clipboardString); 1625 _glfw.ns.clipboardString = strdup([object UTF8String]); 1626 1627 return _glfw.ns.clipboardString; 1628} 1629 1630char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count) 1631{ 1632 *count = 0; 1633 return NULL; 1634} 1635 1636int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, 1637 VkPhysicalDevice device, 1638 uint32_t queuefamily) 1639{ 1640 return GLFW_FALSE; 1641} 1642 1643VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, 1644 _GLFWwindow* window, 1645 const VkAllocationCallbacks* allocator, 1646 VkSurfaceKHR* surface) 1647{ 1648 return VK_ERROR_EXTENSION_NOT_PRESENT; 1649} 1650 1651 1652////////////////////////////////////////////////////////////////////////// 1653////// GLFW native API ////// 1654////////////////////////////////////////////////////////////////////////// 1655 1656GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) 1657{ 1658 _GLFWwindow* window = (_GLFWwindow*) handle; 1659 _GLFW_REQUIRE_INIT_OR_RETURN(nil); 1660 return window->ns.object; 1661} 1662 1663