• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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