• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//========================================================================
2// GLFW 3.5 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
27#include "internal.h"
28
29#if defined(_GLFW_COCOA)
30
31#include <sys/param.h> // For MAXPATHLEN
32
33// Needed for _NSGetProgname
34#include <crt_externs.h>
35
36// Change to our application bundle's resources directory, if present
37//
38static void changeToResourcesDirectory(void)
39{
40    char resourcesPath[MAXPATHLEN];
41
42    CFBundleRef bundle = CFBundleGetMainBundle();
43    if (!bundle)
44        return;
45
46    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
47
48    CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
49    if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo)
50    {
51        CFRelease(last);
52        CFRelease(resourcesURL);
53        return;
54    }
55
56    CFRelease(last);
57
58    if (!CFURLGetFileSystemRepresentation(resourcesURL,
59                                          true,
60                                          (UInt8*) resourcesPath,
61                                          MAXPATHLEN))
62    {
63        CFRelease(resourcesURL);
64        return;
65    }
66
67    CFRelease(resourcesURL);
68
69    chdir(resourcesPath);
70}
71
72// Set up the menu bar (manually)
73// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that
74// could go away at any moment, lots of stuff that really should be
75// localize(d|able), etc.  Add a nib to save us this horror.
76//
77static void createMenuBar(void)
78{
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 (size_t 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//
178static void createKeyTables(void)
179{
180    memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
181    memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
182
183    _glfw.ns.keycodes[0x1D] = GLFW_KEY_0;
184    _glfw.ns.keycodes[0x12] = GLFW_KEY_1;
185    _glfw.ns.keycodes[0x13] = GLFW_KEY_2;
186    _glfw.ns.keycodes[0x14] = GLFW_KEY_3;
187    _glfw.ns.keycodes[0x15] = GLFW_KEY_4;
188    _glfw.ns.keycodes[0x17] = GLFW_KEY_5;
189    _glfw.ns.keycodes[0x16] = GLFW_KEY_6;
190    _glfw.ns.keycodes[0x1A] = GLFW_KEY_7;
191    _glfw.ns.keycodes[0x1C] = GLFW_KEY_8;
192    _glfw.ns.keycodes[0x19] = GLFW_KEY_9;
193    _glfw.ns.keycodes[0x00] = GLFW_KEY_A;
194    _glfw.ns.keycodes[0x0B] = GLFW_KEY_B;
195    _glfw.ns.keycodes[0x08] = GLFW_KEY_C;
196    _glfw.ns.keycodes[0x02] = GLFW_KEY_D;
197    _glfw.ns.keycodes[0x0E] = GLFW_KEY_E;
198    _glfw.ns.keycodes[0x03] = GLFW_KEY_F;
199    _glfw.ns.keycodes[0x05] = GLFW_KEY_G;
200    _glfw.ns.keycodes[0x04] = GLFW_KEY_H;
201    _glfw.ns.keycodes[0x22] = GLFW_KEY_I;
202    _glfw.ns.keycodes[0x26] = GLFW_KEY_J;
203    _glfw.ns.keycodes[0x28] = GLFW_KEY_K;
204    _glfw.ns.keycodes[0x25] = GLFW_KEY_L;
205    _glfw.ns.keycodes[0x2E] = GLFW_KEY_M;
206    _glfw.ns.keycodes[0x2D] = GLFW_KEY_N;
207    _glfw.ns.keycodes[0x1F] = GLFW_KEY_O;
208    _glfw.ns.keycodes[0x23] = GLFW_KEY_P;
209    _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q;
210    _glfw.ns.keycodes[0x0F] = GLFW_KEY_R;
211    _glfw.ns.keycodes[0x01] = GLFW_KEY_S;
212    _glfw.ns.keycodes[0x11] = GLFW_KEY_T;
213    _glfw.ns.keycodes[0x20] = GLFW_KEY_U;
214    _glfw.ns.keycodes[0x09] = GLFW_KEY_V;
215    _glfw.ns.keycodes[0x0D] = GLFW_KEY_W;
216    _glfw.ns.keycodes[0x07] = GLFW_KEY_X;
217    _glfw.ns.keycodes[0x10] = GLFW_KEY_Y;
218    _glfw.ns.keycodes[0x06] = GLFW_KEY_Z;
219
220    _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE;
221    _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH;
222    _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA;
223    _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL;
224    _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT;
225    _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET;
226    _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS;
227    _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD;
228    _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET;
229    _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON;
230    _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH;
231    _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1;
232
233    _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE;
234    _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK;
235    _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE;
236    _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN;
237    _glfw.ns.keycodes[0x77] = GLFW_KEY_END;
238    _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER;
239    _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE;
240    _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1;
241    _glfw.ns.keycodes[0x78] = GLFW_KEY_F2;
242    _glfw.ns.keycodes[0x63] = GLFW_KEY_F3;
243    _glfw.ns.keycodes[0x76] = GLFW_KEY_F4;
244    _glfw.ns.keycodes[0x60] = GLFW_KEY_F5;
245    _glfw.ns.keycodes[0x61] = GLFW_KEY_F6;
246    _glfw.ns.keycodes[0x62] = GLFW_KEY_F7;
247    _glfw.ns.keycodes[0x64] = GLFW_KEY_F8;
248    _glfw.ns.keycodes[0x65] = GLFW_KEY_F9;
249    _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10;
250    _glfw.ns.keycodes[0x67] = GLFW_KEY_F11;
251    _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12;
252    _glfw.ns.keycodes[0x69] = GLFW_KEY_PRINT_SCREEN;
253    _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14;
254    _glfw.ns.keycodes[0x71] = GLFW_KEY_F15;
255    _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16;
256    _glfw.ns.keycodes[0x40] = GLFW_KEY_F17;
257    _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18;
258    _glfw.ns.keycodes[0x50] = GLFW_KEY_F19;
259    _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20;
260    _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME;
261    _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT;
262    _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT;
263    _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT;
264    _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL;
265    _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT;
266    _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER;
267    _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU;
268    _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK;
269    _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN;
270    _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP;
271    _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT;
272    _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT;
273    _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL;
274    _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT;
275    _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER;
276    _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE;
277    _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB;
278    _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP;
279
280    _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0;
281    _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1;
282    _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2;
283    _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3;
284    _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4;
285    _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5;
286    _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6;
287    _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7;
288    _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8;
289    _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9;
290    _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD;
291    _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL;
292    _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE;
293    _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER;
294    _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL;
295    _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY;
296    _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT;
297
298    for (int scancode = 0;  scancode < 256;  scancode++)
299    {
300        // Store the reverse translation for faster key name lookup
301        if (_glfw.ns.keycodes[scancode] >= 0)
302            _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode;
303    }
304}
305
306// Retrieve Unicode data for the current keyboard layout
307//
308static GLFWbool updateUnicodeData(void)
309{
310    if (_glfw.ns.inputSource)
311    {
312        CFRelease(_glfw.ns.inputSource);
313        _glfw.ns.inputSource = NULL;
314        _glfw.ns.unicodeData = nil;
315    }
316
317    _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource();
318    if (!_glfw.ns.inputSource)
319    {
320        _glfwInputError(GLFW_PLATFORM_ERROR,
321                        "Cocoa: Failed to retrieve keyboard layout input source");
322        return GLFW_FALSE;
323    }
324
325    _glfw.ns.unicodeData =
326        TISGetInputSourceProperty(_glfw.ns.inputSource,
327                                  kTISPropertyUnicodeKeyLayoutData);
328    if (!_glfw.ns.unicodeData)
329    {
330        _glfwInputError(GLFW_PLATFORM_ERROR,
331                        "Cocoa: Failed to retrieve keyboard layout Unicode data");
332        return GLFW_FALSE;
333    }
334
335    return GLFW_TRUE;
336}
337
338// Load HIToolbox.framework and the TIS symbols we need from it
339//
340static GLFWbool initializeTIS(void)
341{
342    // This works only because Cocoa has already loaded it properly
343    _glfw.ns.tis.bundle =
344        CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox"));
345    if (!_glfw.ns.tis.bundle)
346    {
347        _glfwInputError(GLFW_PLATFORM_ERROR,
348                        "Cocoa: Failed to load HIToolbox.framework");
349        return GLFW_FALSE;
350    }
351
352    CFStringRef* kPropertyUnicodeKeyLayoutData =
353        CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
354                                      CFSTR("kTISPropertyUnicodeKeyLayoutData"));
355    _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
356        CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
357                                          CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
358    _glfw.ns.tis.GetInputSourceProperty =
359        CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
360                                          CFSTR("TISGetInputSourceProperty"));
361    _glfw.ns.tis.GetKbdType =
362        CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
363                                          CFSTR("LMGetKbdType"));
364
365    if (!kPropertyUnicodeKeyLayoutData ||
366        !TISCopyCurrentKeyboardLayoutInputSource ||
367        !TISGetInputSourceProperty ||
368        !LMGetKbdType)
369    {
370        _glfwInputError(GLFW_PLATFORM_ERROR,
371                        "Cocoa: Failed to load TIS API symbols");
372        return GLFW_FALSE;
373    }
374
375    _glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
376        *kPropertyUnicodeKeyLayoutData;
377
378    return updateUnicodeData();
379}
380
381@interface GLFWHelper : NSObject
382@end
383
384@implementation GLFWHelper
385
386- (void)selectedKeyboardInputSourceChanged:(NSObject* )object
387{
388    updateUnicodeData();
389}
390
391- (void)doNothing:(id)object
392{
393}
394
395@end // GLFWHelper
396
397@interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate>
398@end
399
400@implementation GLFWApplicationDelegate
401
402- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
403{
404    for (_GLFWwindow* window = _glfw.windowListHead;  window;  window = window->next)
405        _glfwInputWindowCloseRequest(window);
406
407    return NSTerminateCancel;
408}
409
410- (void)applicationDidChangeScreenParameters:(NSNotification *) notification
411{
412    for (_GLFWwindow* window = _glfw.windowListHead;  window;  window = window->next)
413    {
414        if (window->context.client != GLFW_NO_API)
415            [window->context.nsgl.object update];
416    }
417
418    _glfwPollMonitorsCocoa();
419}
420
421- (void)applicationWillFinishLaunching:(NSNotification *)notification
422{
423    if (_glfw.hints.init.ns.menubar)
424    {
425        // Menu bar setup must go between sharedApplication and finishLaunching
426        // in order to properly emulate the behavior of NSApplicationMain
427
428        if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"])
429        {
430            [[NSBundle mainBundle] loadNibNamed:@"MainMenu"
431                                          owner:NSApp
432                                topLevelObjects:&_glfw.ns.nibObjects];
433        }
434        else
435            createMenuBar();
436    }
437}
438
439- (void)applicationDidFinishLaunching:(NSNotification *)notification
440{
441    _glfwPostEmptyEventCocoa();
442    [NSApp stop:nil];
443}
444
445- (void)applicationDidHide:(NSNotification *)notification
446{
447    for (int i = 0;  i < _glfw.monitorCount;  i++)
448        _glfwRestoreVideoModeCocoa(_glfw.monitors[i]);
449}
450
451@end // GLFWApplicationDelegate
452
453
454//////////////////////////////////////////////////////////////////////////
455//////                       GLFW internal API                      //////
456//////////////////////////////////////////////////////////////////////////
457
458void* _glfwLoadLocalVulkanLoaderCocoa(void)
459{
460    CFBundleRef bundle = CFBundleGetMainBundle();
461    if (!bundle)
462        return NULL;
463
464    CFURLRef frameworksUrl = CFBundleCopyPrivateFrameworksURL(bundle);
465    if (!frameworksUrl)
466        return NULL;
467
468    CFURLRef loaderUrl = CFURLCreateCopyAppendingPathComponent(
469        kCFAllocatorDefault, frameworksUrl, CFSTR("libvulkan.1.dylib"), false);
470    if (!loaderUrl)
471    {
472        CFRelease(frameworksUrl);
473        return NULL;
474    }
475
476    char path[PATH_MAX];
477    void* handle = NULL;
478
479    if (CFURLGetFileSystemRepresentation(loaderUrl, true, (UInt8*) path, sizeof(path) - 1))
480        handle = _glfwPlatformLoadModule(path);
481
482    CFRelease(loaderUrl);
483    CFRelease(frameworksUrl);
484    return handle;
485}
486
487
488//////////////////////////////////////////////////////////////////////////
489//////                       GLFW platform API                      //////
490//////////////////////////////////////////////////////////////////////////
491
492GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform)
493{
494    const _GLFWplatform cocoa =
495    {
496        .platformID = GLFW_PLATFORM_COCOA,
497        .init = _glfwInitCocoa,
498        .terminate = _glfwTerminateCocoa,
499        .getCursorPos = _glfwGetCursorPosCocoa,
500        .setCursorPos = _glfwSetCursorPosCocoa,
501        .setCursorMode = _glfwSetCursorModeCocoa,
502        .setRawMouseMotion = _glfwSetRawMouseMotionCocoa,
503        .rawMouseMotionSupported = _glfwRawMouseMotionSupportedCocoa,
504        .createCursor = _glfwCreateCursorCocoa,
505        .createStandardCursor = _glfwCreateStandardCursorCocoa,
506        .destroyCursor = _glfwDestroyCursorCocoa,
507        .setCursor = _glfwSetCursorCocoa,
508        .getScancodeName = _glfwGetScancodeNameCocoa,
509        .getKeyScancode = _glfwGetKeyScancodeCocoa,
510        .setClipboardString = _glfwSetClipboardStringCocoa,
511        .getClipboardString = _glfwGetClipboardStringCocoa,
512        .initJoysticks = _glfwInitJoysticksCocoa,
513        .terminateJoysticks = _glfwTerminateJoysticksCocoa,
514        .pollJoystick = _glfwPollJoystickCocoa,
515        .getMappingName = _glfwGetMappingNameCocoa,
516        .updateGamepadGUID = _glfwUpdateGamepadGUIDCocoa,
517        .freeMonitor = _glfwFreeMonitorCocoa,
518        .getMonitorPos = _glfwGetMonitorPosCocoa,
519        .getMonitorContentScale = _glfwGetMonitorContentScaleCocoa,
520        .getMonitorWorkarea = _glfwGetMonitorWorkareaCocoa,
521        .getVideoModes = _glfwGetVideoModesCocoa,
522        .getVideoMode = _glfwGetVideoModeCocoa,
523        .getGammaRamp = _glfwGetGammaRampCocoa,
524        .setGammaRamp = _glfwSetGammaRampCocoa,
525        .createWindow = _glfwCreateWindowCocoa,
526        .destroyWindow = _glfwDestroyWindowCocoa,
527        .setWindowTitle = _glfwSetWindowTitleCocoa,
528        .setWindowIcon = _glfwSetWindowIconCocoa,
529        .getWindowPos = _glfwGetWindowPosCocoa,
530        .setWindowPos = _glfwSetWindowPosCocoa,
531        .getWindowSize = _glfwGetWindowSizeCocoa,
532        .setWindowSize = _glfwSetWindowSizeCocoa,
533        .setWindowSizeLimits = _glfwSetWindowSizeLimitsCocoa,
534        .setWindowAspectRatio = _glfwSetWindowAspectRatioCocoa,
535        .getFramebufferSize = _glfwGetFramebufferSizeCocoa,
536        .getWindowFrameSize = _glfwGetWindowFrameSizeCocoa,
537        .getWindowContentScale = _glfwGetWindowContentScaleCocoa,
538        .iconifyWindow = _glfwIconifyWindowCocoa,
539        .restoreWindow = _glfwRestoreWindowCocoa,
540        .maximizeWindow = _glfwMaximizeWindowCocoa,
541        .showWindow = _glfwShowWindowCocoa,
542        .hideWindow = _glfwHideWindowCocoa,
543        .requestWindowAttention = _glfwRequestWindowAttentionCocoa,
544        .focusWindow = _glfwFocusWindowCocoa,
545        .setWindowMonitor = _glfwSetWindowMonitorCocoa,
546        .windowFocused = _glfwWindowFocusedCocoa,
547        .windowIconified = _glfwWindowIconifiedCocoa,
548        .windowVisible = _glfwWindowVisibleCocoa,
549        .windowMaximized = _glfwWindowMaximizedCocoa,
550        .windowHovered = _glfwWindowHoveredCocoa,
551        .framebufferTransparent = _glfwFramebufferTransparentCocoa,
552        .getWindowOpacity = _glfwGetWindowOpacityCocoa,
553        .setWindowResizable = _glfwSetWindowResizableCocoa,
554        .setWindowDecorated = _glfwSetWindowDecoratedCocoa,
555        .setWindowFloating = _glfwSetWindowFloatingCocoa,
556        .setWindowOpacity = _glfwSetWindowOpacityCocoa,
557        .setWindowMousePassthrough = _glfwSetWindowMousePassthroughCocoa,
558        .pollEvents = _glfwPollEventsCocoa,
559        .waitEvents = _glfwWaitEventsCocoa,
560        .waitEventsTimeout = _glfwWaitEventsTimeoutCocoa,
561        .postEmptyEvent = _glfwPostEmptyEventCocoa,
562        .getEGLPlatform = _glfwGetEGLPlatformCocoa,
563        .getEGLNativeDisplay = _glfwGetEGLNativeDisplayCocoa,
564        .getEGLNativeWindow = _glfwGetEGLNativeWindowCocoa,
565        .getRequiredInstanceExtensions = _glfwGetRequiredInstanceExtensionsCocoa,
566        .getPhysicalDevicePresentationSupport = _glfwGetPhysicalDevicePresentationSupportCocoa,
567        .createWindowSurface = _glfwCreateWindowSurfaceCocoa
568    };
569
570    *platform = cocoa;
571    return GLFW_TRUE;
572}
573
574int _glfwInitCocoa(void)
575{
576    @autoreleasepool {
577
578    _glfw.ns.helper = [[GLFWHelper alloc] init];
579
580    [NSThread detachNewThreadSelector:@selector(doNothing:)
581                             toTarget:_glfw.ns.helper
582                           withObject:nil];
583
584    [NSApplication sharedApplication];
585
586    _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];
587    if (_glfw.ns.delegate == nil)
588    {
589        _glfwInputError(GLFW_PLATFORM_ERROR,
590                        "Cocoa: Failed to create application delegate");
591        return GLFW_FALSE;
592    }
593
594    [NSApp setDelegate:_glfw.ns.delegate];
595
596    NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
597    {
598        if ([event modifierFlags] & NSEventModifierFlagCommand)
599            [[NSApp keyWindow] sendEvent:event];
600
601        return event;
602    };
603
604    _glfw.ns.keyUpMonitor =
605        [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp
606                                              handler:block];
607
608    if (_glfw.hints.init.ns.chdir)
609        changeToResourcesDirectory();
610
611    // Press and Hold prevents some keys from emitting repeated characters
612    NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
613    [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
614
615    [[NSNotificationCenter defaultCenter]
616        addObserver:_glfw.ns.helper
617           selector:@selector(selectedKeyboardInputSourceChanged:)
618               name:NSTextInputContextKeyboardSelectionDidChangeNotification
619             object:nil];
620
621    createKeyTables();
622
623    _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
624    if (!_glfw.ns.eventSource)
625        return GLFW_FALSE;
626
627    CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0);
628
629    if (!initializeTIS())
630        return GLFW_FALSE;
631
632    _glfwPollMonitorsCocoa();
633
634    if (![[NSRunningApplication currentApplication] isFinishedLaunching])
635        [NSApp run];
636
637    // In case we are unbundled, make us a proper UI application
638    if (_glfw.hints.init.ns.menubar)
639        [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
640
641    return GLFW_TRUE;
642
643    } // autoreleasepool
644}
645
646void _glfwTerminateCocoa(void)
647{
648    @autoreleasepool {
649
650    if (_glfw.ns.inputSource)
651    {
652        CFRelease(_glfw.ns.inputSource);
653        _glfw.ns.inputSource = NULL;
654        _glfw.ns.unicodeData = nil;
655    }
656
657    if (_glfw.ns.eventSource)
658    {
659        CFRelease(_glfw.ns.eventSource);
660        _glfw.ns.eventSource = NULL;
661    }
662
663    if (_glfw.ns.delegate)
664    {
665        [NSApp setDelegate:nil];
666        [_glfw.ns.delegate release];
667        _glfw.ns.delegate = nil;
668    }
669
670    if (_glfw.ns.helper)
671    {
672        [[NSNotificationCenter defaultCenter]
673            removeObserver:_glfw.ns.helper
674                      name:NSTextInputContextKeyboardSelectionDidChangeNotification
675                    object:nil];
676        [[NSNotificationCenter defaultCenter]
677            removeObserver:_glfw.ns.helper];
678        [_glfw.ns.helper release];
679        _glfw.ns.helper = nil;
680    }
681
682    if (_glfw.ns.keyUpMonitor)
683        [NSEvent removeMonitor:_glfw.ns.keyUpMonitor];
684
685    _glfw_free(_glfw.ns.clipboardString);
686
687    _glfwTerminateNSGL();
688    _glfwTerminateEGL();
689    _glfwTerminateOSMesa();
690
691    } // autoreleasepool
692}
693
694#endif // _GLFW_COCOA
695
696