cocoa_init.m (20854B)
1 //======================================================================== 2 // GLFW 3.4 macOS - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2009-2019 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 // It is fine to use C99 in this file because it will not be built with VS 27 //======================================================================== 28 29 #include "internal.h" 30 #include <sys/param.h> // For MAXPATHLEN 31 32 // Needed for _NSGetProgname 33 #include <crt_externs.h> 34 35 // Change to our application bundle's resources directory, if present 36 // 37 static void changeToResourcesDirectory(void) 38 { 39 char resourcesPath[MAXPATHLEN]; 40 41 CFBundleRef bundle = CFBundleGetMainBundle(); 42 if (!bundle) 43 return; 44 45 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); 46 47 CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); 48 if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo) 49 { 50 CFRelease(last); 51 CFRelease(resourcesURL); 52 return; 53 } 54 55 CFRelease(last); 56 57 if (!CFURLGetFileSystemRepresentation(resourcesURL, 58 true, 59 (UInt8*) resourcesPath, 60 MAXPATHLEN)) 61 { 62 CFRelease(resourcesURL); 63 return; 64 } 65 66 CFRelease(resourcesURL); 67 68 chdir(resourcesPath); 69 } 70 71 // Set up the menu bar (manually) 72 // This is nasty, nasty stuff -- calls to undocumented semi-private APIs that 73 // could go away at any moment, lots of stuff that really should be 74 // localize(d|able), etc. Add a nib to save us this horror. 75 // 76 static void createMenuBar(void) 77 { 78 size_t i; 79 NSString* appName = nil; 80 NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; 81 NSString* nameKeys[] = 82 { 83 @"CFBundleDisplayName", 84 @"CFBundleName", 85 @"CFBundleExecutable", 86 }; 87 88 // Try to figure out what the calling application is called 89 90 for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) 91 { 92 id name = bundleInfo[nameKeys[i]]; 93 if (name && 94 [name isKindOfClass:[NSString class]] && 95 ![name isEqualToString:@""]) 96 { 97 appName = name; 98 break; 99 } 100 } 101 102 if (!appName) 103 { 104 char** progname = _NSGetProgname(); 105 if (progname && *progname) 106 appName = @(*progname); 107 else 108 appName = @"GLFW Application"; 109 } 110 111 NSMenu* bar = [[NSMenu alloc] init]; 112 [NSApp setMainMenu:bar]; 113 114 NSMenuItem* appMenuItem = 115 [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; 116 NSMenu* appMenu = [[NSMenu alloc] init]; 117 [appMenuItem setSubmenu:appMenu]; 118 119 [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] 120 action:@selector(orderFrontStandardAboutPanel:) 121 keyEquivalent:@""]; 122 [appMenu addItem:[NSMenuItem separatorItem]]; 123 NSMenu* servicesMenu = [[NSMenu alloc] init]; 124 [NSApp setServicesMenu:servicesMenu]; 125 [[appMenu addItemWithTitle:@"Services" 126 action:NULL 127 keyEquivalent:@""] setSubmenu:servicesMenu]; 128 [servicesMenu release]; 129 [appMenu addItem:[NSMenuItem separatorItem]]; 130 [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] 131 action:@selector(hide:) 132 keyEquivalent:@"h"]; 133 [[appMenu addItemWithTitle:@"Hide Others" 134 action:@selector(hideOtherApplications:) 135 keyEquivalent:@"h"] 136 setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; 137 [appMenu addItemWithTitle:@"Show All" 138 action:@selector(unhideAllApplications:) 139 keyEquivalent:@""]; 140 [appMenu addItem:[NSMenuItem separatorItem]]; 141 [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] 142 action:@selector(terminate:) 143 keyEquivalent:@"q"]; 144 145 NSMenuItem* windowMenuItem = 146 [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; 147 [bar release]; 148 NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; 149 [NSApp setWindowsMenu:windowMenu]; 150 [windowMenuItem setSubmenu:windowMenu]; 151 152 [windowMenu addItemWithTitle:@"Minimize" 153 action:@selector(performMiniaturize:) 154 keyEquivalent:@"m"]; 155 [windowMenu addItemWithTitle:@"Zoom" 156 action:@selector(performZoom:) 157 keyEquivalent:@""]; 158 [windowMenu addItem:[NSMenuItem separatorItem]]; 159 [windowMenu addItemWithTitle:@"Bring All to Front" 160 action:@selector(arrangeInFront:) 161 keyEquivalent:@""]; 162 163 // TODO: Make this appear at the bottom of the menu (for consistency) 164 [windowMenu addItem:[NSMenuItem separatorItem]]; 165 [[windowMenu addItemWithTitle:@"Enter Full Screen" 166 action:@selector(toggleFullScreen:) 167 keyEquivalent:@"f"] 168 setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; 169 170 // Prior to Snow Leopard, we need to use this oddly-named semi-private API 171 // to get the application menu working properly. 172 SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); 173 [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; 174 } 175 176 // Create key code translation tables 177 // 178 static void createKeyTables(void) 179 { 180 int scancode; 181 182 memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); 183 memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); 184 185 _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; 186 _glfw.ns.keycodes[0x12] = GLFW_KEY_1; 187 _glfw.ns.keycodes[0x13] = GLFW_KEY_2; 188 _glfw.ns.keycodes[0x14] = GLFW_KEY_3; 189 _glfw.ns.keycodes[0x15] = GLFW_KEY_4; 190 _glfw.ns.keycodes[0x17] = GLFW_KEY_5; 191 _glfw.ns.keycodes[0x16] = GLFW_KEY_6; 192 _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; 193 _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; 194 _glfw.ns.keycodes[0x19] = GLFW_KEY_9; 195 _glfw.ns.keycodes[0x00] = GLFW_KEY_A; 196 _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; 197 _glfw.ns.keycodes[0x08] = GLFW_KEY_C; 198 _glfw.ns.keycodes[0x02] = GLFW_KEY_D; 199 _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; 200 _glfw.ns.keycodes[0x03] = GLFW_KEY_F; 201 _glfw.ns.keycodes[0x05] = GLFW_KEY_G; 202 _glfw.ns.keycodes[0x04] = GLFW_KEY_H; 203 _glfw.ns.keycodes[0x22] = GLFW_KEY_I; 204 _glfw.ns.keycodes[0x26] = GLFW_KEY_J; 205 _glfw.ns.keycodes[0x28] = GLFW_KEY_K; 206 _glfw.ns.keycodes[0x25] = GLFW_KEY_L; 207 _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; 208 _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; 209 _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; 210 _glfw.ns.keycodes[0x23] = GLFW_KEY_P; 211 _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; 212 _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; 213 _glfw.ns.keycodes[0x01] = GLFW_KEY_S; 214 _glfw.ns.keycodes[0x11] = GLFW_KEY_T; 215 _glfw.ns.keycodes[0x20] = GLFW_KEY_U; 216 _glfw.ns.keycodes[0x09] = GLFW_KEY_V; 217 _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; 218 _glfw.ns.keycodes[0x07] = GLFW_KEY_X; 219 _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; 220 _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; 221 222 _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; 223 _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; 224 _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; 225 _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; 226 _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; 227 _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; 228 _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; 229 _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; 230 _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; 231 _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; 232 _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; 233 _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; 234 235 _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; 236 _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; 237 _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; 238 _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; 239 _glfw.ns.keycodes[0x77] = GLFW_KEY_END; 240 _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; 241 _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; 242 _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; 243 _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; 244 _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; 245 _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; 246 _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; 247 _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; 248 _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; 249 _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; 250 _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; 251 _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; 252 _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; 253 _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; 254 _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; 255 _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; 256 _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; 257 _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; 258 _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; 259 _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; 260 _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; 261 _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; 262 _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; 263 _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; 264 _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; 265 _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; 266 _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; 267 _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; 268 _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; 269 _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; 270 _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; 271 _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; 272 _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; 273 _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; 274 _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; 275 _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; 276 _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; 277 _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; 278 _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; 279 _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; 280 _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; 281 282 _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; 283 _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; 284 _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; 285 _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; 286 _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; 287 _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; 288 _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; 289 _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; 290 _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; 291 _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; 292 _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; 293 _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; 294 _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; 295 _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; 296 _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; 297 _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; 298 _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; 299 300 for (scancode = 0; scancode < 256; scancode++) 301 { 302 // Store the reverse translation for faster key name lookup 303 if (_glfw.ns.keycodes[scancode] >= 0) 304 _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; 305 } 306 } 307 308 // Retrieve Unicode data for the current keyboard layout 309 // 310 static GLFWbool updateUnicodeDataNS(void) 311 { 312 if (_glfw.ns.inputSource) 313 { 314 CFRelease(_glfw.ns.inputSource); 315 _glfw.ns.inputSource = NULL; 316 _glfw.ns.unicodeData = nil; 317 } 318 319 _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource(); 320 if (!_glfw.ns.inputSource) 321 { 322 _glfwInputError(GLFW_PLATFORM_ERROR, 323 "Cocoa: Failed to retrieve keyboard layout input source"); 324 return GLFW_FALSE; 325 } 326 327 _glfw.ns.unicodeData = (id) 328 TISGetInputSourceProperty(_glfw.ns.inputSource, 329 kTISPropertyUnicodeKeyLayoutData); 330 if (!_glfw.ns.unicodeData) 331 { 332 _glfwInputError(GLFW_PLATFORM_ERROR, 333 "Cocoa: Failed to retrieve keyboard layout Unicode data"); 334 return GLFW_FALSE; 335 } 336 337 return GLFW_TRUE; 338 } 339 340 // Load HIToolbox.framework and the TIS symbols we need from it 341 // 342 static GLFWbool initializeTIS(void) 343 { 344 // This works only because Cocoa has already loaded it properly 345 _glfw.ns.tis.bundle = 346 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); 347 if (!_glfw.ns.tis.bundle) 348 { 349 _glfwInputError(GLFW_PLATFORM_ERROR, 350 "Cocoa: Failed to load HIToolbox.framework"); 351 return GLFW_FALSE; 352 } 353 354 CFStringRef* kPropertyUnicodeKeyLayoutData = 355 (CFStringRef*)CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, 356 CFSTR("kTISPropertyUnicodeKeyLayoutData")); 357 _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = (PFN_TISCopyCurrentKeyboardLayoutInputSource) 358 CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, 359 CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); 360 _glfw.ns.tis.GetInputSourceProperty = (PFN_TISGetInputSourceProperty) 361 CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, 362 CFSTR("TISGetInputSourceProperty")); 363 _glfw.ns.tis.GetKbdType = (PFN_LMGetKbdType) 364 CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, 365 CFSTR("LMGetKbdType")); 366 367 if (!kPropertyUnicodeKeyLayoutData || 368 !TISCopyCurrentKeyboardLayoutInputSource || 369 !TISGetInputSourceProperty || 370 !LMGetKbdType) 371 { 372 _glfwInputError(GLFW_PLATFORM_ERROR, 373 "Cocoa: Failed to load TIS API symbols"); 374 return GLFW_FALSE; 375 } 376 377 _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = 378 *kPropertyUnicodeKeyLayoutData; 379 380 return updateUnicodeDataNS(); 381 } 382 383 @interface GLFWHelper : NSObject 384 @end 385 386 @implementation GLFWHelper 387 388 - (void)selectedKeyboardInputSourceChanged:(NSObject*)object 389 { 390 updateUnicodeDataNS(); 391 } 392 393 - (void)doNothing:(id)object 394 { 395 } 396 397 @end // GLFWHelper 398 399 @interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate> 400 @end 401 402 @implementation GLFWApplicationDelegate 403 404 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender 405 { 406 _GLFWwindow* window; 407 408 for (window = _glfw.windowListHead; window; window = window->next) 409 _glfwInputWindowCloseRequest(window); 410 411 return NSTerminateCancel; 412 } 413 414 - (void)applicationDidChangeScreenParameters:(NSNotification *) notification 415 { 416 _GLFWwindow* window; 417 418 for (window = _glfw.windowListHead; window; window = window->next) 419 { 420 if (window->context.client != GLFW_NO_API) 421 [window->context.nsgl.object update]; 422 } 423 424 _glfwPollMonitorsNS(); 425 } 426 427 - (void)applicationWillFinishLaunching:(NSNotification *)notification 428 { 429 if (_glfw.hints.init.ns.menubar) 430 { 431 // In case we are unbundled, make us a proper UI application 432 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 433 434 // Menu bar setup must go between sharedApplication and finishLaunching 435 // in order to properly emulate the behavior of NSApplicationMain 436 437 if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) 438 { 439 [[NSBundle mainBundle] loadNibNamed:@"MainMenu" 440 owner:NSApp 441 topLevelObjects:&_glfw.ns.nibObjects]; 442 } 443 else 444 createMenuBar(); 445 } 446 } 447 448 - (void)applicationDidFinishLaunching:(NSNotification *)notification 449 { 450 _glfwPlatformPostEmptyEvent(); 451 [NSApp stop:nil]; 452 } 453 454 - (void)applicationDidHide:(NSNotification *)notification 455 { 456 int i; 457 458 for (i = 0; i < _glfw.monitorCount; i++) 459 _glfwRestoreVideoModeNS(_glfw.monitors[i]); 460 } 461 462 @end // GLFWApplicationDelegate 463 464 465 ////////////////////////////////////////////////////////////////////////// 466 ////// GLFW internal API ////// 467 ////////////////////////////////////////////////////////////////////////// 468 469 void* _glfwLoadLocalVulkanLoaderNS(void) 470 { 471 CFBundleRef bundle = CFBundleGetMainBundle(); 472 if (!bundle) 473 return NULL; 474 475 CFURLRef url = 476 CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib")); 477 if (!url) 478 return NULL; 479 480 char path[PATH_MAX]; 481 void* handle = NULL; 482 483 if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1)) 484 handle = _glfw_dlopen(path); 485 486 CFRelease(url); 487 return handle; 488 } 489 490 491 ////////////////////////////////////////////////////////////////////////// 492 ////// GLFW platform API ////// 493 ////////////////////////////////////////////////////////////////////////// 494 495 int _glfwPlatformInit(void) 496 { 497 @autoreleasepool { 498 499 _glfw.ns.helper = [[GLFWHelper alloc] init]; 500 501 [NSThread detachNewThreadSelector:@selector(doNothing:) 502 toTarget:_glfw.ns.helper 503 withObject:nil]; 504 505 [NSApplication sharedApplication]; 506 507 _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; 508 if (_glfw.ns.delegate == nil) 509 { 510 _glfwInputError(GLFW_PLATFORM_ERROR, 511 "Cocoa: Failed to create application delegate"); 512 return GLFW_FALSE; 513 } 514 515 [NSApp setDelegate:_glfw.ns.delegate]; 516 517 NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) 518 { 519 if ([event modifierFlags] & NSEventModifierFlagCommand) 520 [[NSApp keyWindow] sendEvent:event]; 521 522 return event; 523 }; 524 525 _glfw.ns.keyUpMonitor = 526 [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp 527 handler:block]; 528 529 if (_glfw.hints.init.ns.chdir) 530 changeToResourcesDirectory(); 531 532 // Press and Hold prevents some keys from emitting repeated characters 533 NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO}; 534 [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; 535 536 [[NSNotificationCenter defaultCenter] 537 addObserver:_glfw.ns.helper 538 selector:@selector(selectedKeyboardInputSourceChanged:) 539 name:NSTextInputContextKeyboardSelectionDidChangeNotification 540 object:nil]; 541 542 createKeyTables(); 543 544 _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 545 if (!_glfw.ns.eventSource) 546 return GLFW_FALSE; 547 548 CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); 549 550 if (!initializeTIS()) 551 return GLFW_FALSE; 552 553 _glfwInitTimerNS(); 554 _glfwInitJoysticksNS(); 555 556 _glfwPollMonitorsNS(); 557 558 if (![[NSRunningApplication currentApplication] isFinishedLaunching]) 559 [NSApp run]; 560 561 return GLFW_TRUE; 562 563 } // autoreleasepool 564 } 565 566 void _glfwPlatformTerminate(void) 567 { 568 @autoreleasepool { 569 570 if (_glfw.ns.inputSource) 571 { 572 CFRelease(_glfw.ns.inputSource); 573 _glfw.ns.inputSource = NULL; 574 _glfw.ns.unicodeData = nil; 575 } 576 577 if (_glfw.ns.eventSource) 578 { 579 CFRelease(_glfw.ns.eventSource); 580 _glfw.ns.eventSource = NULL; 581 } 582 583 if (_glfw.ns.delegate) 584 { 585 [NSApp setDelegate:nil]; 586 [_glfw.ns.delegate release]; 587 _glfw.ns.delegate = nil; 588 } 589 590 if (_glfw.ns.helper) 591 { 592 [[NSNotificationCenter defaultCenter] 593 removeObserver:_glfw.ns.helper 594 name:NSTextInputContextKeyboardSelectionDidChangeNotification 595 object:nil]; 596 [[NSNotificationCenter defaultCenter] 597 removeObserver:_glfw.ns.helper]; 598 [_glfw.ns.helper release]; 599 _glfw.ns.helper = nil; 600 } 601 602 if (_glfw.ns.keyUpMonitor) 603 [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; 604 605 free(_glfw.ns.clipboardString); 606 607 _glfwTerminateNSGL(); 608 _glfwTerminateJoysticksNS(); 609 610 } // autoreleasepool 611 } 612 613 const char* _glfwPlatformGetVersionString(void) 614 { 615 return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa" 616 #if defined(_GLFW_BUILD_DLL) 617 " dynamic" 618 #endif 619 ; 620 } 621