cocoa_init.m (12782B)
1 //======================================================================== 2 // GLFW 3.3 macOS - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org> 5 // 6 // This software is provided 'as-is', without any express or implied 7 // warranty. In no event will the authors be held liable for any damages 8 // arising from the use of this software. 9 // 10 // Permission is granted to anyone to use this software for any purpose, 11 // including commercial applications, and to alter it and redistribute it 12 // freely, subject to the following restrictions: 13 // 14 // 1. The origin of this software must not be misrepresented; you must not 15 // claim that you wrote the original software. If you use this software 16 // in a product, an acknowledgment in the product documentation would 17 // be appreciated but is not required. 18 // 19 // 2. Altered source versions must be plainly marked as such, and must not 20 // be misrepresented as being the original software. 21 // 22 // 3. This notice may not be removed or altered from any source 23 // distribution. 24 // 25 //======================================================================== 26 27 #include "internal.h" 28 #include <sys/param.h> // For MAXPATHLEN 29 30 31 // Change to our application bundle's resources directory, if present 32 // 33 static void changeToResourcesDirectory(void) 34 { 35 char resourcesPath[MAXPATHLEN]; 36 37 CFBundleRef bundle = CFBundleGetMainBundle(); 38 if (!bundle) 39 return; 40 41 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); 42 43 CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); 44 if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo) 45 { 46 CFRelease(last); 47 CFRelease(resourcesURL); 48 return; 49 } 50 51 CFRelease(last); 52 53 if (!CFURLGetFileSystemRepresentation(resourcesURL, 54 true, 55 (UInt8*) resourcesPath, 56 MAXPATHLEN)) 57 { 58 CFRelease(resourcesURL); 59 return; 60 } 61 62 CFRelease(resourcesURL); 63 64 chdir(resourcesPath); 65 } 66 67 // Create key code translation tables 68 // 69 static void createKeyTables(void) 70 { 71 int scancode; 72 73 memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); 74 memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); 75 76 _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; 77 _glfw.ns.keycodes[0x12] = GLFW_KEY_1; 78 _glfw.ns.keycodes[0x13] = GLFW_KEY_2; 79 _glfw.ns.keycodes[0x14] = GLFW_KEY_3; 80 _glfw.ns.keycodes[0x15] = GLFW_KEY_4; 81 _glfw.ns.keycodes[0x17] = GLFW_KEY_5; 82 _glfw.ns.keycodes[0x16] = GLFW_KEY_6; 83 _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; 84 _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; 85 _glfw.ns.keycodes[0x19] = GLFW_KEY_9; 86 _glfw.ns.keycodes[0x00] = GLFW_KEY_A; 87 _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; 88 _glfw.ns.keycodes[0x08] = GLFW_KEY_C; 89 _glfw.ns.keycodes[0x02] = GLFW_KEY_D; 90 _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; 91 _glfw.ns.keycodes[0x03] = GLFW_KEY_F; 92 _glfw.ns.keycodes[0x05] = GLFW_KEY_G; 93 _glfw.ns.keycodes[0x04] = GLFW_KEY_H; 94 _glfw.ns.keycodes[0x22] = GLFW_KEY_I; 95 _glfw.ns.keycodes[0x26] = GLFW_KEY_J; 96 _glfw.ns.keycodes[0x28] = GLFW_KEY_K; 97 _glfw.ns.keycodes[0x25] = GLFW_KEY_L; 98 _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; 99 _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; 100 _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; 101 _glfw.ns.keycodes[0x23] = GLFW_KEY_P; 102 _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; 103 _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; 104 _glfw.ns.keycodes[0x01] = GLFW_KEY_S; 105 _glfw.ns.keycodes[0x11] = GLFW_KEY_T; 106 _glfw.ns.keycodes[0x20] = GLFW_KEY_U; 107 _glfw.ns.keycodes[0x09] = GLFW_KEY_V; 108 _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; 109 _glfw.ns.keycodes[0x07] = GLFW_KEY_X; 110 _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; 111 _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; 112 113 _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; 114 _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; 115 _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; 116 _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; 117 _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; 118 _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; 119 _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; 120 _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; 121 _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; 122 _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; 123 _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; 124 _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; 125 126 _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; 127 _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; 128 _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; 129 _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; 130 _glfw.ns.keycodes[0x77] = GLFW_KEY_END; 131 _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; 132 _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; 133 _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; 134 _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; 135 _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; 136 _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; 137 _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; 138 _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; 139 _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; 140 _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; 141 _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; 142 _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; 143 _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; 144 _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; 145 _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; 146 _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; 147 _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; 148 _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; 149 _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; 150 _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; 151 _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; 152 _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; 153 _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; 154 _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; 155 _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; 156 _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; 157 _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; 158 _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; 159 _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; 160 _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; 161 _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; 162 _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; 163 _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; 164 _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; 165 _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; 166 _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; 167 _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; 168 _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; 169 _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; 170 _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; 171 _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; 172 173 _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; 174 _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; 175 _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; 176 _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; 177 _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; 178 _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; 179 _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; 180 _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; 181 _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; 182 _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; 183 _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; 184 _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; 185 _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; 186 _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; 187 _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; 188 _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; 189 _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; 190 191 for (scancode = 0; scancode < 256; scancode++) 192 { 193 // Store the reverse translation for faster key name lookup 194 if (_glfw.ns.keycodes[scancode] >= 0) 195 _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; 196 } 197 } 198 199 // Retrieve Unicode data for the current keyboard layout 200 // 201 static GLFWbool updateUnicodeDataNS(void) 202 { 203 if (_glfw.ns.inputSource) 204 { 205 CFRelease(_glfw.ns.inputSource); 206 _glfw.ns.inputSource = NULL; 207 _glfw.ns.unicodeData = nil; 208 } 209 210 _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource(); 211 if (!_glfw.ns.inputSource) 212 { 213 _glfwInputError(GLFW_PLATFORM_ERROR, 214 "Cocoa: Failed to retrieve keyboard layout input source"); 215 return GLFW_FALSE; 216 } 217 218 _glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, 219 kTISPropertyUnicodeKeyLayoutData); 220 if (!_glfw.ns.unicodeData) 221 { 222 _glfwInputError(GLFW_PLATFORM_ERROR, 223 "Cocoa: Failed to retrieve keyboard layout Unicode data"); 224 return GLFW_FALSE; 225 } 226 227 return GLFW_TRUE; 228 } 229 230 // Load HIToolbox.framework and the TIS symbols we need from it 231 // 232 static GLFWbool initializeTIS(void) 233 { 234 // This works only because Cocoa has already loaded it properly 235 _glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); 236 if (!_glfw.ns.tis.bundle) 237 { 238 _glfwInputError(GLFW_PLATFORM_ERROR, 239 "Cocoa: Failed to load HIToolbox.framework"); 240 return GLFW_FALSE; 241 } 242 243 CFStringRef* kPropertyUnicodeKeyLayoutData = 244 CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, 245 CFSTR("kTISPropertyUnicodeKeyLayoutData")); 246 _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = 247 CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, 248 CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); 249 _glfw.ns.tis.GetInputSourceProperty = 250 CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, 251 CFSTR("TISGetInputSourceProperty")); 252 _glfw.ns.tis.GetKbdType = 253 CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, 254 CFSTR("LMGetKbdType")); 255 256 if (!kPropertyUnicodeKeyLayoutData || 257 !TISCopyCurrentKeyboardLayoutInputSource || 258 !TISGetInputSourceProperty || 259 !LMGetKbdType) 260 { 261 _glfwInputError(GLFW_PLATFORM_ERROR, 262 "Cocoa: Failed to load TIS API symbols"); 263 return GLFW_FALSE; 264 } 265 266 _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = 267 *kPropertyUnicodeKeyLayoutData; 268 269 return updateUnicodeDataNS(); 270 } 271 272 @interface GLFWLayoutListener : NSObject 273 @end 274 275 @implementation GLFWLayoutListener 276 277 - (void)selectedKeyboardInputSourceChanged:(NSObject* )object 278 { 279 updateUnicodeDataNS(); 280 } 281 282 @end 283 284 285 ////////////////////////////////////////////////////////////////////////// 286 ////// GLFW platform API ////// 287 ////////////////////////////////////////////////////////////////////////// 288 289 int _glfwPlatformInit(void) 290 { 291 _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; 292 293 if (_glfw.hints.init.ns.chdir) 294 changeToResourcesDirectory(); 295 296 _glfw.ns.listener = [[GLFWLayoutListener alloc] init]; 297 [[NSNotificationCenter defaultCenter] 298 addObserver:_glfw.ns.listener 299 selector:@selector(selectedKeyboardInputSourceChanged:) 300 name:NSTextInputContextKeyboardSelectionDidChangeNotification 301 object:nil]; 302 303 createKeyTables(); 304 305 _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); 306 if (!_glfw.ns.eventSource) 307 return GLFW_FALSE; 308 309 CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); 310 311 if (!initializeTIS()) 312 return GLFW_FALSE; 313 314 _glfwInitTimerNS(); 315 _glfwInitJoysticksNS(); 316 317 _glfwPollMonitorsNS(); 318 return GLFW_TRUE; 319 } 320 321 void _glfwPlatformTerminate(void) 322 { 323 if (_glfw.ns.inputSource) 324 { 325 CFRelease(_glfw.ns.inputSource); 326 _glfw.ns.inputSource = NULL; 327 _glfw.ns.unicodeData = nil; 328 } 329 330 if (_glfw.ns.eventSource) 331 { 332 CFRelease(_glfw.ns.eventSource); 333 _glfw.ns.eventSource = NULL; 334 } 335 336 if (_glfw.ns.delegate) 337 { 338 [NSApp setDelegate:nil]; 339 [_glfw.ns.delegate release]; 340 _glfw.ns.delegate = nil; 341 } 342 343 if (_glfw.ns.listener) 344 { 345 [[NSNotificationCenter defaultCenter] 346 removeObserver:_glfw.ns.listener 347 name:NSTextInputContextKeyboardSelectionDidChangeNotification 348 object:nil]; 349 [[NSNotificationCenter defaultCenter] 350 removeObserver:_glfw.ns.listener]; 351 [_glfw.ns.listener release]; 352 _glfw.ns.listener = nil; 353 } 354 355 [_glfw.ns.cursor release]; 356 _glfw.ns.cursor = nil; 357 358 free(_glfw.ns.clipboardString); 359 360 _glfwTerminateNSGL(); 361 _glfwTerminateJoysticksNS(); 362 363 [_glfw.ns.autoreleasePool release]; 364 _glfw.ns.autoreleasePool = nil; 365 } 366 367 const char* _glfwPlatformGetVersionString(void) 368 { 369 return _GLFW_VERSION_NUMBER " Cocoa NSGL" 370 #if defined(_GLFW_BUILD_DLL) 371 " dynamic" 372 #endif 373 ; 374 } 375