src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Mon, 28 Dec 2015 13:07:44 -0500
changeset 9973 4d8a561cf978
parent 9972 734c90ea9990
child 9983 bbe9ef8c2ecb
permissions -rw-r--r--
Sync up the caps/numlock state properly without sending key events.

Partially fixes Bugzilla #2736 and #3125.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_COCOA
    24 
    25 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
    26 # error SDL for Mac OS X must be built with a 10.7 SDK or above.
    27 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1070 */
    28 
    29 #include "SDL_syswm.h"
    30 #include "SDL_timer.h"  /* For SDL_GetTicks() */
    31 #include "SDL_hints.h"
    32 #include "../SDL_sysvideo.h"
    33 #include "../../events/SDL_keyboard_c.h"
    34 #include "../../events/SDL_mouse_c.h"
    35 #include "../../events/SDL_touch_c.h"
    36 #include "../../events/SDL_windowevents_c.h"
    37 #include "../../events/SDL_dropevents_c.h"
    38 #include "SDL_cocoavideo.h"
    39 #include "SDL_cocoashape.h"
    40 #include "SDL_cocoamouse.h"
    41 #include "SDL_cocoaopengl.h"
    42 #include "SDL_assert.h"
    43 
    44 /* #define DEBUG_COCOAWINDOW */
    45 
    46 #ifdef DEBUG_COCOAWINDOW
    47 #define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
    48 #else
    49 #define DLog(...) do { } while (0)
    50 #endif
    51 
    52 
    53 #define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
    54 
    55 
    56 @interface SDLWindow : NSWindow <NSDraggingDestination>
    57 /* These are needed for borderless/fullscreen windows */
    58 - (BOOL)canBecomeKeyWindow;
    59 - (BOOL)canBecomeMainWindow;
    60 - (void)sendEvent:(NSEvent *)event;
    61 - (void)doCommandBySelector:(SEL)aSelector;
    62 
    63 /* Handle drag-and-drop of files onto the SDL window. */
    64 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
    65 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
    66 - (BOOL)wantsPeriodicDraggingUpdates;
    67 @end
    68 
    69 @implementation SDLWindow
    70 
    71 - (BOOL)canBecomeKeyWindow
    72 {
    73     return YES;
    74 }
    75 
    76 - (BOOL)canBecomeMainWindow
    77 {
    78     return YES;
    79 }
    80 
    81 - (void)sendEvent:(NSEvent *)event
    82 {
    83   [super sendEvent:event];
    84 
    85   if ([event type] != NSLeftMouseUp) {
    86       return;
    87   }
    88 
    89   id delegate = [self delegate];
    90   if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) {
    91       return;
    92   }
    93 
    94   if ([delegate isMoving]) {
    95       [delegate windowDidFinishMoving];
    96   }
    97 }
    98 
    99 /* We'll respond to selectors by doing nothing so we don't beep.
   100  * The escape key gets converted to a "cancel" selector, etc.
   101  */
   102 - (void)doCommandBySelector:(SEL)aSelector
   103 {
   104     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   105 }
   106 
   107 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
   108 {
   109     if (([sender draggingSourceOperationMask] & NSDragOperationGeneric) == NSDragOperationGeneric) {
   110         return NSDragOperationGeneric;
   111     }
   112 
   113     return NSDragOperationNone; /* no idea what to do with this, reject it. */
   114 }
   115 
   116 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
   117 { @autoreleasepool
   118 {
   119     NSPasteboard *pasteboard = [sender draggingPasteboard];
   120     NSArray *types = [NSArray arrayWithObject:NSFilenamesPboardType];
   121     NSString *desiredType = [pasteboard availableTypeFromArray:types];
   122     if (desiredType == nil) {
   123         return NO;  /* can't accept anything that's being dropped here. */
   124     }
   125 
   126     NSData *data = [pasteboard dataForType:desiredType];
   127     if (data == nil) {
   128         return NO;
   129     }
   130 
   131     SDL_assert([desiredType isEqualToString:NSFilenamesPboardType]);
   132     NSArray *array = [pasteboard propertyListForType:@"NSFilenamesPboardType"];
   133 
   134     for (NSString *path in array) {
   135         NSURL *fileURL = [[NSURL fileURLWithPath:path] autorelease];
   136         NSNumber *isAlias = nil;
   137 
   138         /* Functionality for resolving URL aliases was added with OS X 10.6. */
   139         if ([fileURL respondsToSelector:@selector(getResourceValue:forKey:error:)]) {
   140             [fileURL getResourceValue:&isAlias forKey:NSURLIsAliasFileKey error:nil];
   141         }
   142 
   143         /* If the URL is an alias, resolve it. */
   144         if ([isAlias boolValue]) {
   145             NSURLBookmarkResolutionOptions opts = NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI;
   146             NSData *bookmark = [NSURL bookmarkDataWithContentsOfURL:fileURL error:nil];
   147             if (bookmark != nil) {
   148                 NSURL *resolvedURL = [NSURL URLByResolvingBookmarkData:bookmark
   149                                                                options:opts
   150                                                          relativeToURL:nil
   151                                                    bookmarkDataIsStale:nil
   152                                                                  error:nil];
   153 
   154                 if (resolvedURL != nil) {
   155                     fileURL = resolvedURL;
   156                 }
   157             }
   158         }
   159 
   160         if (!SDL_SendDropFile([[fileURL path] UTF8String])) {
   161             return NO;
   162         }
   163     }
   164 
   165     return YES;
   166 }}
   167 
   168 - (BOOL)wantsPeriodicDraggingUpdates
   169 {
   170     return NO;
   171 }
   172 
   173 @end
   174 
   175 
   176 static Uint32 s_moveHack;
   177 
   178 static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
   179 {
   180     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
   181 }
   182 
   183 static void
   184 ScheduleContextUpdates(SDL_WindowData *data)
   185 {
   186     NSOpenGLContext *currentContext = [NSOpenGLContext currentContext];
   187     NSMutableArray *contexts = data->nscontexts;
   188     @synchronized (contexts) {
   189         for (SDLOpenGLContext *context in contexts) {
   190             if (context == currentContext) {
   191                 [context update];
   192             } else {
   193                 [context scheduleUpdate];
   194             }
   195         }
   196     }
   197 }
   198 
   199 static int
   200 GetHintCtrlClickEmulateRightClick()
   201 {
   202 	const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK );
   203 	return hint != NULL && *hint != '0';
   204 }
   205 
   206 static unsigned int
   207 GetWindowStyle(SDL_Window * window)
   208 {
   209     unsigned int style;
   210 
   211     if (window->flags & SDL_WINDOW_FULLSCREEN) {
   212         style = NSBorderlessWindowMask;
   213     } else {
   214         if (window->flags & SDL_WINDOW_BORDERLESS) {
   215             style = NSBorderlessWindowMask;
   216         } else {
   217             style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
   218         }
   219         if (window->flags & SDL_WINDOW_RESIZABLE) {
   220             style |= NSResizableWindowMask;
   221         }
   222     }
   223     return style;
   224 }
   225 
   226 static SDL_bool
   227 SetWindowStyle(SDL_Window * window, unsigned int style)
   228 {
   229     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   230     NSWindow *nswindow = data->nswindow;
   231 
   232     if (![nswindow respondsToSelector: @selector(setStyleMask:)]) {
   233         return SDL_FALSE;
   234     }
   235 
   236     /* The view responder chain gets messed with during setStyleMask */
   237     if ([[nswindow contentView] nextResponder] == data->listener) {
   238         [[nswindow contentView] setNextResponder:nil];
   239     }
   240 
   241     [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)style];
   242 
   243     /* The view responder chain gets messed with during setStyleMask */
   244     if ([[nswindow contentView] nextResponder] != data->listener) {
   245         [[nswindow contentView] setNextResponder:data->listener];
   246     }
   247 
   248     return SDL_TRUE;
   249 }
   250 
   251 
   252 @implementation Cocoa_WindowListener
   253 
   254 - (void)listen:(SDL_WindowData *)data
   255 {
   256     NSNotificationCenter *center;
   257     NSWindow *window = data->nswindow;
   258     NSView *view = [window contentView];
   259 
   260     _data = data;
   261     observingVisible = YES;
   262     wasCtrlLeft = NO;
   263     wasVisible = [window isVisible];
   264     isFullscreenSpace = NO;
   265     inFullscreenTransition = NO;
   266     pendingWindowOperation = PENDING_OPERATION_NONE;
   267     isMoving = NO;
   268     isDragAreaRunning = NO;
   269 
   270     center = [NSNotificationCenter defaultCenter];
   271 
   272     if ([window delegate] != nil) {
   273         [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
   274         [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
   275         [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
   276         [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
   277         [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
   278         [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
   279         [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
   280         [center addObserver:self selector:@selector(windowDidChangeBackingProperties:) name:NSWindowDidChangeBackingPropertiesNotification object:window];
   281         [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
   282         [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
   283         [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
   284         [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
   285         [center addObserver:self selector:@selector(windowDidFailToEnterFullScreen:) name:@"NSWindowDidFailToEnterFullScreenNotification" object:window];
   286         [center addObserver:self selector:@selector(windowDidFailToExitFullScreen:) name:@"NSWindowDidFailToExitFullScreenNotification" object:window];
   287     } else {
   288         [window setDelegate:self];
   289     }
   290 
   291     /* Haven't found a delegate / notification that triggers when the window is
   292      * ordered out (is not visible any more). You can be ordered out without
   293      * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
   294      */
   295     [window addObserver:self
   296              forKeyPath:@"visible"
   297                 options:NSKeyValueObservingOptionNew
   298                 context:NULL];
   299 
   300     [window setNextResponder:self];
   301     [window setAcceptsMouseMovedEvents:YES];
   302 
   303     [view setNextResponder:self];
   304 
   305     if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
   306         [view setAcceptsTouchEvents:YES];
   307     }
   308 }
   309 
   310 - (void)observeValueForKeyPath:(NSString *)keyPath
   311                       ofObject:(id)object
   312                         change:(NSDictionary *)change
   313                        context:(void *)context
   314 {
   315     if (!observingVisible) {
   316         return;
   317     }
   318 
   319     if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
   320         int newVisibility = [[change objectForKey:@"new"] intValue];
   321         if (newVisibility) {
   322             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   323         } else {
   324             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   325         }
   326     }
   327 }
   328 
   329 -(void) pauseVisibleObservation
   330 {
   331     observingVisible = NO;
   332     wasVisible = [_data->nswindow isVisible];
   333 }
   334 
   335 -(void) resumeVisibleObservation
   336 {
   337     BOOL isVisible = [_data->nswindow isVisible];
   338     observingVisible = YES;
   339     if (wasVisible != isVisible) {
   340         if (isVisible) {
   341             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   342         } else {
   343             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   344         }
   345 
   346         wasVisible = isVisible;
   347     }
   348 }
   349 
   350 -(BOOL) setFullscreenSpace:(BOOL) state
   351 {
   352     SDL_Window *window = _data->window;
   353     NSWindow *nswindow = _data->nswindow;
   354     SDL_VideoData *videodata = ((SDL_WindowData *) window->driverdata)->videodata;
   355 
   356     if (!videodata->allow_spaces) {
   357         return NO;  /* Spaces are forcibly disabled. */
   358     } else if (state && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   359         return NO;  /* we only allow you to make a Space on FULLSCREEN_DESKTOP windows. */
   360     } else if (!state && ((window->last_fullscreen_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   361         return NO;  /* we only handle leaving the Space on windows that were previously FULLSCREEN_DESKTOP. */
   362     } else if (state == isFullscreenSpace) {
   363         return YES;  /* already there. */
   364     }
   365 
   366     if (inFullscreenTransition) {
   367         if (state) {
   368             [self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
   369         } else {
   370             [self addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
   371         }
   372         return YES;
   373     }
   374     inFullscreenTransition = YES;
   375 
   376     /* you need to be FullScreenPrimary, or toggleFullScreen doesn't work. Unset it again in windowDidExitFullScreen. */
   377     [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
   378     [nswindow performSelectorOnMainThread: @selector(toggleFullScreen:) withObject:nswindow waitUntilDone:NO];
   379     return YES;
   380 }
   381 
   382 -(BOOL) isInFullscreenSpace
   383 {
   384     return isFullscreenSpace;
   385 }
   386 
   387 -(BOOL) isInFullscreenSpaceTransition
   388 {
   389     return inFullscreenTransition;
   390 }
   391 
   392 -(void) addPendingWindowOperation:(PendingWindowOperation) operation
   393 {
   394     pendingWindowOperation = operation;
   395 }
   396 
   397 - (void)close
   398 {
   399     NSNotificationCenter *center;
   400     NSWindow *window = _data->nswindow;
   401     NSView *view = [window contentView];
   402 
   403     center = [NSNotificationCenter defaultCenter];
   404 
   405     if ([window delegate] != self) {
   406         [center removeObserver:self name:NSWindowDidExposeNotification object:window];
   407         [center removeObserver:self name:NSWindowDidMoveNotification object:window];
   408         [center removeObserver:self name:NSWindowDidResizeNotification object:window];
   409         [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
   410         [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
   411         [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
   412         [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
   413         [center removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window];
   414         [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window];
   415         [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window];
   416         [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window];
   417         [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window];
   418         [center removeObserver:self name:@"NSWindowDidFailToEnterFullScreenNotification" object:window];
   419         [center removeObserver:self name:@"NSWindowDidFailToExitFullScreenNotification" object:window];
   420     } else {
   421         [window setDelegate:nil];
   422     }
   423 
   424     [window removeObserver:self forKeyPath:@"visible"];
   425 
   426     if ([window nextResponder] == self) {
   427         [window setNextResponder:nil];
   428     }
   429     if ([view nextResponder] == self) {
   430         [view setNextResponder:nil];
   431     }
   432 }
   433 
   434 - (BOOL)isMoving
   435 {
   436     return isMoving;
   437 }
   438 
   439 -(void) setPendingMoveX:(int)x Y:(int)y
   440 {
   441     pendingWindowWarpX = x;
   442     pendingWindowWarpY = y;
   443 }
   444 
   445 - (void)windowDidFinishMoving
   446 {
   447     if ([self isMoving]) {
   448         isMoving = NO;
   449 
   450         SDL_Mouse *mouse = SDL_GetMouse();
   451         if (pendingWindowWarpX != INT_MAX && pendingWindowWarpY != INT_MAX) {
   452             mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY);
   453             pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
   454         }
   455         if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data->window) {
   456             mouse->SetRelativeMouseMode(SDL_TRUE);
   457         }
   458     }
   459 }
   460 
   461 - (BOOL)windowShouldClose:(id)sender
   462 {
   463     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   464     return NO;
   465 }
   466 
   467 - (void)windowDidExpose:(NSNotification *)aNotification
   468 {
   469     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   470 }
   471 
   472 - (void)windowWillMove:(NSNotification *)aNotification
   473 {
   474     if ([_data->nswindow isKindOfClass:[SDLWindow class]]) {
   475         pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
   476         isMoving = YES;
   477     }
   478 }
   479 
   480 - (void)windowDidMove:(NSNotification *)aNotification
   481 {
   482     int x, y;
   483     SDL_Window *window = _data->window;
   484     NSWindow *nswindow = _data->nswindow;
   485     BOOL fullscreen = window->flags & FULLSCREEN_MASK;
   486     NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   487     ConvertNSRect([nswindow screen], fullscreen, &rect);
   488 
   489     if (s_moveHack) {
   490         SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
   491 
   492         s_moveHack = 0;
   493 
   494         if (blockMove) {
   495             /* Cocoa is adjusting the window in response to a mode change */
   496             rect.origin.x = window->x;
   497             rect.origin.y = window->y;
   498             ConvertNSRect([nswindow screen], fullscreen, &rect);
   499             [nswindow setFrameOrigin:rect.origin];
   500             return;
   501         }
   502     }
   503 
   504     x = (int)rect.origin.x;
   505     y = (int)rect.origin.y;
   506 
   507     ScheduleContextUpdates(_data);
   508 
   509     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
   510 }
   511 
   512 - (void)windowDidResize:(NSNotification *)aNotification
   513 {
   514     if (inFullscreenTransition) {
   515         /* We'll take care of this at the end of the transition */
   516         return;
   517     }
   518 
   519     SDL_Window *window = _data->window;
   520     NSWindow *nswindow = _data->nswindow;
   521     int x, y, w, h;
   522     NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   523     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
   524     x = (int)rect.origin.x;
   525     y = (int)rect.origin.y;
   526     w = (int)rect.size.width;
   527     h = (int)rect.size.height;
   528 
   529     if (SDL_IsShapedWindow(window)) {
   530         Cocoa_ResizeWindowShape(window);
   531     }
   532 
   533     ScheduleContextUpdates(_data);
   534 
   535     /* The window can move during a resize event, such as when maximizing
   536        or resizing from a corner */
   537     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
   538     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
   539 
   540     const BOOL zoomed = [nswindow isZoomed];
   541     if (!zoomed) {
   542         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   543     } else if (zoomed) {
   544         SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
   545     }
   546 }
   547 
   548 - (void)windowDidMiniaturize:(NSNotification *)aNotification
   549 {
   550     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   551 }
   552 
   553 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
   554 {
   555     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   556 }
   557 
   558 - (void)windowDidBecomeKey:(NSNotification *)aNotification
   559 {
   560     SDL_Window *window = _data->window;
   561     SDL_Mouse *mouse = SDL_GetMouse();
   562 
   563     /* We're going to get keyboard events, since we're key. */
   564     /* This needs to be done before restoring the relative mouse mode. */
   565     SDL_SetKeyboardFocus(window);
   566 
   567     if (mouse->relative_mode && !mouse->relative_mode_warp && ![self isMoving]) {
   568         mouse->SetRelativeMouseMode(SDL_TRUE);
   569     }
   570 
   571     /* If we just gained focus we need the updated mouse position */
   572     if (!mouse->relative_mode) {
   573         NSPoint point;
   574         int x, y;
   575 
   576         point = [_data->nswindow mouseLocationOutsideOfEventStream];
   577         x = (int)point.x;
   578         y = (int)(window->h - point.y);
   579 
   580         if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
   581             SDL_SendMouseMotion(window, 0, 0, x, y);
   582         }
   583     }
   584 
   585     /* Check to see if someone updated the clipboard */
   586     Cocoa_CheckClipboardUpdate(_data->videodata);
   587 
   588     if ((isFullscreenSpace) && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)) {
   589         [NSMenu setMenuBarVisible:NO];
   590     }
   591 
   592     /* On pre-10.6, you might have the capslock key state wrong now because we can't check here. */
   593     if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_6) {
   594         const unsigned int newflags = [NSEvent modifierFlags] & NSAlphaShiftKeyMask;
   595         _data->videodata->modifierFlags = (_data->videodata->modifierFlags & ~NSAlphaShiftKeyMask) | newflags;
   596         SDL_ToggleModState(KMOD_CAPS, newflags != 0);
   597     }
   598 }
   599 
   600 - (void)windowDidResignKey:(NSNotification *)aNotification
   601 {
   602     SDL_Mouse *mouse = SDL_GetMouse();
   603     if (mouse->relative_mode && !mouse->relative_mode_warp) {
   604         mouse->SetRelativeMouseMode(SDL_FALSE);
   605     }
   606 
   607     /* Some other window will get mouse events, since we're not key. */
   608     if (SDL_GetMouseFocus() == _data->window) {
   609         SDL_SetMouseFocus(NULL);
   610     }
   611 
   612     /* Some other window will get keyboard events, since we're not key. */
   613     if (SDL_GetKeyboardFocus() == _data->window) {
   614         SDL_SetKeyboardFocus(NULL);
   615     }
   616 
   617     if (isFullscreenSpace) {
   618         [NSMenu setMenuBarVisible:YES];
   619     }
   620 }
   621 
   622 - (void)windowDidChangeBackingProperties:(NSNotification *)aNotification
   623 {
   624     NSNumber *oldscale = [[aNotification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey];
   625 
   626     if (inFullscreenTransition) {
   627         return;
   628     }
   629 
   630     if ([oldscale doubleValue] != [_data->nswindow backingScaleFactor]) {
   631         /* Force a resize event when the backing scale factor changes. */
   632         _data->window->w = 0;
   633         _data->window->h = 0;
   634         [self windowDidResize:aNotification];
   635     }
   636 }
   637 
   638 - (void)windowWillEnterFullScreen:(NSNotification *)aNotification
   639 {
   640     SDL_Window *window = _data->window;
   641 
   642     SetWindowStyle(window, (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask));
   643 
   644     isFullscreenSpace = YES;
   645     inFullscreenTransition = YES;
   646 }
   647 
   648 - (void)windowDidFailToEnterFullScreen:(NSNotification *)aNotification
   649 {
   650     SDL_Window *window = _data->window;
   651 
   652     if (window->is_destroying) {
   653         return;
   654     }
   655 
   656     SetWindowStyle(window, GetWindowStyle(window));
   657 
   658     isFullscreenSpace = NO;
   659     inFullscreenTransition = NO;
   660     
   661     [self windowDidExitFullScreen:nil];
   662 }
   663 
   664 - (void)windowDidEnterFullScreen:(NSNotification *)aNotification
   665 {
   666     SDL_Window *window = _data->window;
   667 
   668     inFullscreenTransition = NO;
   669 
   670     if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) {
   671         pendingWindowOperation = PENDING_OPERATION_NONE;
   672         [self setFullscreenSpace:NO];
   673     } else {
   674         if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   675             [NSMenu setMenuBarVisible:NO];
   676         }
   677 
   678         pendingWindowOperation = PENDING_OPERATION_NONE;
   679         /* Force the size change event in case it was delivered earlier
   680            while the window was still animating into place.
   681          */
   682         window->w = 0;
   683         window->h = 0;
   684         [self windowDidResize:aNotification];
   685     }
   686 }
   687 
   688 - (void)windowWillExitFullScreen:(NSNotification *)aNotification
   689 {
   690     SDL_Window *window = _data->window;
   691 
   692     /* As of OS X 10.11, the window seems to need to be resizable when exiting
   693        a Space, in order for it to resize back to its windowed-mode size.
   694      */
   695     SetWindowStyle(window, GetWindowStyle(window) | NSResizableWindowMask);
   696 
   697     isFullscreenSpace = NO;
   698     inFullscreenTransition = YES;
   699 }
   700 
   701 - (void)windowDidFailToExitFullScreen:(NSNotification *)aNotification
   702 {
   703     SDL_Window *window = _data->window;
   704     
   705     if (window->is_destroying) {
   706         return;
   707     }
   708 
   709     SetWindowStyle(window, (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask));
   710     
   711     isFullscreenSpace = YES;
   712     inFullscreenTransition = NO;
   713     
   714     [self windowDidEnterFullScreen:nil];
   715 }
   716 
   717 - (void)windowDidExitFullScreen:(NSNotification *)aNotification
   718 {
   719     SDL_Window *window = _data->window;
   720     NSWindow *nswindow = _data->nswindow;
   721 
   722     inFullscreenTransition = NO;
   723 
   724     SetWindowStyle(window, GetWindowStyle(window));
   725 
   726     [nswindow setLevel:kCGNormalWindowLevel];
   727 
   728     if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
   729         pendingWindowOperation = PENDING_OPERATION_NONE;
   730         [self setFullscreenSpace:YES];
   731     } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
   732         pendingWindowOperation = PENDING_OPERATION_NONE;
   733         [nswindow miniaturize:nil];
   734     } else {
   735         /* Adjust the fullscreen toggle button and readd menu now that we're here. */
   736         if (window->flags & SDL_WINDOW_RESIZABLE) {
   737             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
   738             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
   739         } else {
   740             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged];
   741         }
   742         [NSMenu setMenuBarVisible:YES];
   743 
   744         pendingWindowOperation = PENDING_OPERATION_NONE;
   745         /* Force the size change event in case it was delivered earlier
   746            while the window was still animating into place.
   747          */
   748         window->w = 0;
   749         window->h = 0;
   750         [self windowDidResize:aNotification];
   751 
   752         /* FIXME: Why does the window get hidden? */
   753         if (window->flags & SDL_WINDOW_SHOWN) {
   754             Cocoa_ShowWindow(SDL_GetVideoDevice(), window);
   755         }
   756     }
   757 }
   758 
   759 -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
   760 {
   761     if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
   762         return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
   763     } else {
   764         return proposedOptions;
   765     }
   766 }
   767 
   768 
   769 /* We'll respond to key events by doing nothing so we don't beep.
   770  * We could handle key messages here, but we lose some in the NSApp dispatch,
   771  * where they get converted to action messages, etc.
   772  */
   773 - (void)flagsChanged:(NSEvent *)theEvent
   774 {
   775     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   776 }
   777 - (void)keyDown:(NSEvent *)theEvent
   778 {
   779     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   780 }
   781 - (void)keyUp:(NSEvent *)theEvent
   782 {
   783     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   784 }
   785 
   786 /* We'll respond to selectors by doing nothing so we don't beep.
   787  * The escape key gets converted to a "cancel" selector, etc.
   788  */
   789 - (void)doCommandBySelector:(SEL)aSelector
   790 {
   791     /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   792 }
   793 
   794 - (BOOL)processHitTest:(NSEvent *)theEvent
   795 {
   796     SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
   797 
   798     if (_data->window->hit_test) {  /* if no hit-test, skip this. */
   799         const NSPoint location = [theEvent locationInWindow];
   800         const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
   801         const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
   802         if (rc == SDL_HITTEST_DRAGGABLE) {
   803             if (!isDragAreaRunning) {
   804                 isDragAreaRunning = YES;
   805                 [_data->nswindow setMovableByWindowBackground:YES];
   806             }
   807             return YES;  /* dragging! */
   808         }
   809     }
   810 
   811     if (isDragAreaRunning) {
   812         isDragAreaRunning = NO;
   813         [_data->nswindow setMovableByWindowBackground:NO];
   814         return YES;  /* was dragging, drop event. */
   815     }
   816 
   817     return NO;  /* not a special area, carry on. */
   818 }
   819 
   820 - (void)mouseDown:(NSEvent *)theEvent
   821 {
   822     int button;
   823 
   824     /* Ignore events that aren't inside the client area (i.e. title bar.) */
   825     if ([theEvent window]) {
   826         const NSRect windowRect = [[[theEvent window] contentView] frame];
   827         if (!NSPointInRect([theEvent locationInWindow], windowRect)) {
   828             return;
   829         }
   830     }
   831 
   832     if ([self processHitTest:theEvent]) {
   833         return;  /* dragging, drop event. */
   834     }
   835 
   836     switch ([theEvent buttonNumber]) {
   837     case 0:
   838         if (([theEvent modifierFlags] & NSControlKeyMask) &&
   839 		    GetHintCtrlClickEmulateRightClick()) {
   840             wasCtrlLeft = YES;
   841             button = SDL_BUTTON_RIGHT;
   842         } else {
   843             wasCtrlLeft = NO;
   844             button = SDL_BUTTON_LEFT;
   845         }
   846         break;
   847     case 1:
   848         button = SDL_BUTTON_RIGHT;
   849         break;
   850     case 2:
   851         button = SDL_BUTTON_MIDDLE;
   852         break;
   853     default:
   854         button = [theEvent buttonNumber] + 1;
   855         break;
   856     }
   857     SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
   858 }
   859 
   860 - (void)rightMouseDown:(NSEvent *)theEvent
   861 {
   862     [self mouseDown:theEvent];
   863 }
   864 
   865 - (void)otherMouseDown:(NSEvent *)theEvent
   866 {
   867     [self mouseDown:theEvent];
   868 }
   869 
   870 - (void)mouseUp:(NSEvent *)theEvent
   871 {
   872     int button;
   873 
   874     if ([self processHitTest:theEvent]) {
   875         return;  /* stopped dragging, drop event. */
   876     }
   877 
   878     switch ([theEvent buttonNumber]) {
   879     case 0:
   880         if (wasCtrlLeft) {
   881             button = SDL_BUTTON_RIGHT;
   882             wasCtrlLeft = NO;
   883         } else {
   884             button = SDL_BUTTON_LEFT;
   885         }
   886         break;
   887     case 1:
   888         button = SDL_BUTTON_RIGHT;
   889         break;
   890     case 2:
   891         button = SDL_BUTTON_MIDDLE;
   892         break;
   893     default:
   894         button = [theEvent buttonNumber] + 1;
   895         break;
   896     }
   897     SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
   898 }
   899 
   900 - (void)rightMouseUp:(NSEvent *)theEvent
   901 {
   902     [self mouseUp:theEvent];
   903 }
   904 
   905 - (void)otherMouseUp:(NSEvent *)theEvent
   906 {
   907     [self mouseUp:theEvent];
   908 }
   909 
   910 - (void)mouseMoved:(NSEvent *)theEvent
   911 {
   912     SDL_Mouse *mouse = SDL_GetMouse();
   913     SDL_Window *window = _data->window;
   914     NSPoint point;
   915     int x, y;
   916 
   917     if ([self processHitTest:theEvent]) {
   918         return;  /* dragging, drop event. */
   919     }
   920 
   921     if (mouse->relative_mode) {
   922         return;
   923     }
   924 
   925     point = [theEvent locationInWindow];
   926     x = (int)point.x;
   927     y = (int)(window->h - point.y);
   928 
   929     if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   930         if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   931             if (x < 0) {
   932                 x = 0;
   933             } else if (x >= window->w) {
   934                 x = window->w - 1;
   935             }
   936             if (y < 0) {
   937                 y = 0;
   938             } else if (y >= window->h) {
   939                 y = window->h - 1;
   940             }
   941 
   942 #if !SDL_MAC_NO_SANDBOX
   943             CGPoint cgpoint;
   944 
   945             /* When SDL_MAC_NO_SANDBOX is set, this is handled by
   946              * SDL_cocoamousetap.m.
   947              */
   948 
   949             cgpoint.x = window->x + x;
   950             cgpoint.y = window->y + y;
   951 
   952             /* According to the docs, this was deprecated in 10.6, but it's still
   953              * around. The substitute requires a CGEventSource, but I'm not entirely
   954              * sure how we'd procure the right one for this event.
   955              */
   956             CGSetLocalEventsSuppressionInterval(0.0);
   957             CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
   958             CGSetLocalEventsSuppressionInterval(0.25);
   959 
   960             Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
   961 #endif
   962         }
   963     }
   964     SDL_SendMouseMotion(window, 0, 0, x, y);
   965 }
   966 
   967 - (void)mouseDragged:(NSEvent *)theEvent
   968 {
   969     [self mouseMoved:theEvent];
   970 }
   971 
   972 - (void)rightMouseDragged:(NSEvent *)theEvent
   973 {
   974     [self mouseMoved:theEvent];
   975 }
   976 
   977 - (void)otherMouseDragged:(NSEvent *)theEvent
   978 {
   979     [self mouseMoved:theEvent];
   980 }
   981 
   982 - (void)scrollWheel:(NSEvent *)theEvent
   983 {
   984     Cocoa_HandleMouseWheel(_data->window, theEvent);
   985 }
   986 
   987 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
   988 {
   989     NSSet *touches = [theEvent touchesMatchingPhase:NSTouchPhaseAny inView:nil];
   990     int existingTouchCount = 0;
   991 
   992     for (NSTouch* touch in touches) {
   993         if ([touch phase] != NSTouchPhaseBegan) {
   994             existingTouchCount++;
   995         }
   996     }
   997     if (existingTouchCount == 0) {
   998         SDL_TouchID touchID = (SDL_TouchID)(intptr_t)[[touches anyObject] device];
   999         int numFingers = SDL_GetNumTouchFingers(touchID);
  1000         DLog("Reset Lost Fingers: %d", numFingers);
  1001         for (--numFingers; numFingers >= 0; --numFingers) {
  1002             SDL_Finger* finger = SDL_GetTouchFinger(touchID, numFingers);
  1003             SDL_SendTouch(touchID, finger->id, SDL_FALSE, 0, 0, 0);
  1004         }
  1005     }
  1006 
  1007     DLog("Began Fingers: %lu .. existing: %d", (unsigned long)[touches count], existingTouchCount);
  1008     [self handleTouches:NSTouchPhaseBegan withEvent:theEvent];
  1009 }
  1010 
  1011 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
  1012 {
  1013     [self handleTouches:NSTouchPhaseMoved withEvent:theEvent];
  1014 }
  1015 
  1016 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
  1017 {
  1018     [self handleTouches:NSTouchPhaseEnded withEvent:theEvent];
  1019 }
  1020 
  1021 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
  1022 {
  1023     [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent];
  1024 }
  1025 
  1026 - (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent
  1027 {
  1028     NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];
  1029 
  1030     for (NSTouch *touch in touches) {
  1031         const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
  1032         if (SDL_AddTouch(touchId, "") < 0) {
  1033             return;
  1034         }
  1035 
  1036         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
  1037         float x = [touch normalizedPosition].x;
  1038         float y = [touch normalizedPosition].y;
  1039         /* Make the origin the upper left instead of the lower left */
  1040         y = 1.0f - y;
  1041 
  1042         switch (phase) {
  1043         case NSTouchPhaseBegan:
  1044             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
  1045             break;
  1046         case NSTouchPhaseEnded:
  1047         case NSTouchPhaseCancelled:
  1048             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
  1049             break;
  1050         case NSTouchPhaseMoved:
  1051             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
  1052             break;
  1053         default:
  1054             break;
  1055         }
  1056     }
  1057 }
  1058 
  1059 @end
  1060 
  1061 @interface SDLView : NSView {
  1062     SDL_Window *_sdlWindow;
  1063 }
  1064 
  1065 - (void)setSDLWindow:(SDL_Window*)window;
  1066 
  1067 /* The default implementation doesn't pass rightMouseDown to responder chain */
  1068 - (void)rightMouseDown:(NSEvent *)theEvent;
  1069 - (BOOL)mouseDownCanMoveWindow;
  1070 - (void)drawRect:(NSRect)dirtyRect;
  1071 @end
  1072 
  1073 @implementation SDLView
  1074 - (void)setSDLWindow:(SDL_Window*)window
  1075 {
  1076     _sdlWindow = window;
  1077 }
  1078 
  1079 - (void)drawRect:(NSRect)dirtyRect
  1080 {
  1081     SDL_SendWindowEvent(_sdlWindow, SDL_WINDOWEVENT_EXPOSED, 0, 0);
  1082 }
  1083 
  1084 - (void)rightMouseDown:(NSEvent *)theEvent
  1085 {
  1086     [[self nextResponder] rightMouseDown:theEvent];
  1087 }
  1088 
  1089 - (BOOL)mouseDownCanMoveWindow
  1090 {
  1091     /* Always say YES, but this doesn't do anything until we call
  1092        -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
  1093        during mouse events when we're using a drag area. */
  1094     return YES;
  1095 }
  1096 
  1097 - (void)resetCursorRects
  1098 {
  1099     [super resetCursorRects];
  1100     SDL_Mouse *mouse = SDL_GetMouse();
  1101 
  1102     if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
  1103         [self addCursorRect:[self bounds]
  1104                      cursor:mouse->cur_cursor->driverdata];
  1105     } else {
  1106         [self addCursorRect:[self bounds]
  1107                      cursor:[NSCursor invisibleCursor]];
  1108     }
  1109 }
  1110 @end
  1111 
  1112 static int
  1113 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
  1114 { @autoreleasepool
  1115 {
  1116     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1117     SDL_WindowData *data;
  1118 
  1119     /* Allocate the window data */
  1120     window->driverdata = data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
  1121     if (!data) {
  1122         return SDL_OutOfMemory();
  1123     }
  1124     data->window = window;
  1125     data->nswindow = nswindow;
  1126     data->created = created;
  1127     data->videodata = videodata;
  1128     data->nscontexts = [[NSMutableArray alloc] init];
  1129 
  1130     /* Create an event listener for the window */
  1131     data->listener = [[Cocoa_WindowListener alloc] init];
  1132 
  1133     /* Fill in the SDL window with the window data */
  1134     {
  1135         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1136         ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1137         window->x = (int)rect.origin.x;
  1138         window->y = (int)rect.origin.y;
  1139         window->w = (int)rect.size.width;
  1140         window->h = (int)rect.size.height;
  1141     }
  1142 
  1143     /* Set up the listener after we create the view */
  1144     [data->listener listen:data];
  1145 
  1146     if ([nswindow isVisible]) {
  1147         window->flags |= SDL_WINDOW_SHOWN;
  1148     } else {
  1149         window->flags &= ~SDL_WINDOW_SHOWN;
  1150     }
  1151 
  1152     {
  1153         unsigned int style = [nswindow styleMask];
  1154 
  1155         if (style == NSBorderlessWindowMask) {
  1156             window->flags |= SDL_WINDOW_BORDERLESS;
  1157         } else {
  1158             window->flags &= ~SDL_WINDOW_BORDERLESS;
  1159         }
  1160         if (style & NSResizableWindowMask) {
  1161             window->flags |= SDL_WINDOW_RESIZABLE;
  1162         } else {
  1163             window->flags &= ~SDL_WINDOW_RESIZABLE;
  1164         }
  1165     }
  1166 
  1167     /* isZoomed always returns true if the window is not resizable */
  1168     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1169         window->flags |= SDL_WINDOW_MAXIMIZED;
  1170     } else {
  1171         window->flags &= ~SDL_WINDOW_MAXIMIZED;
  1172     }
  1173 
  1174     if ([nswindow isMiniaturized]) {
  1175         window->flags |= SDL_WINDOW_MINIMIZED;
  1176     } else {
  1177         window->flags &= ~SDL_WINDOW_MINIMIZED;
  1178     }
  1179 
  1180     if ([nswindow isKeyWindow]) {
  1181         window->flags |= SDL_WINDOW_INPUT_FOCUS;
  1182         SDL_SetKeyboardFocus(data->window);
  1183     }
  1184 
  1185     /* Prevents the window's "window device" from being destroyed when it is
  1186      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
  1187      */
  1188     [nswindow setOneShot:NO];
  1189 
  1190     /* All done! */
  1191     window->driverdata = data;
  1192     return 0;
  1193 }}
  1194 
  1195 int
  1196 Cocoa_CreateWindow(_THIS, SDL_Window * window)
  1197 { @autoreleasepool
  1198 {
  1199     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  1200     NSWindow *nswindow;
  1201     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1202     NSRect rect;
  1203     SDL_Rect bounds;
  1204     unsigned int style;
  1205     NSArray *screens = [NSScreen screens];
  1206 
  1207     Cocoa_GetDisplayBounds(_this, display, &bounds);
  1208     rect.origin.x = window->x;
  1209     rect.origin.y = window->y;
  1210     rect.size.width = window->w;
  1211     rect.size.height = window->h;
  1212     ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
  1213 
  1214     style = GetWindowStyle(window);
  1215 
  1216     /* Figure out which screen to place this window */
  1217     NSScreen *screen = nil;
  1218     for (NSScreen *candidate in screens) {
  1219         NSRect screenRect = [candidate frame];
  1220         if (rect.origin.x >= screenRect.origin.x &&
  1221             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
  1222             rect.origin.y >= screenRect.origin.y &&
  1223             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
  1224             screen = candidate;
  1225             rect.origin.x -= screenRect.origin.x;
  1226             rect.origin.y -= screenRect.origin.y;
  1227         }
  1228     }
  1229 
  1230     @try {
  1231         nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
  1232     }
  1233     @catch (NSException *e) {
  1234         return SDL_SetError("%s", [[e reason] UTF8String]);
  1235     }
  1236     [nswindow setBackgroundColor:[NSColor blackColor]];
  1237 
  1238     if (videodata->allow_spaces) {
  1239         SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
  1240         SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
  1241         /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
  1242         if (window->flags & SDL_WINDOW_RESIZABLE) {
  1243             /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
  1244             [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
  1245         }
  1246     }
  1247 
  1248     /* Create a default view for this window */
  1249     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
  1250     SDLView *contentView = [[SDLView alloc] initWithFrame:rect];
  1251     [contentView setSDLWindow:window];
  1252 
  1253     if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
  1254         if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
  1255             [contentView setWantsBestResolutionOpenGLSurface:YES];
  1256         }
  1257     }
  1258 
  1259     [nswindow setContentView: contentView];
  1260     [contentView release];
  1261 
  1262     /* Allow files and folders to be dragged onto the window by users */
  1263     [nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1264 
  1265     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
  1266         [nswindow release];
  1267         return -1;
  1268     }
  1269     return 0;
  1270 }}
  1271 
  1272 int
  1273 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
  1274 { @autoreleasepool
  1275 {
  1276     NSWindow *nswindow = (NSWindow *) data;
  1277     NSString *title;
  1278 
  1279     /* Query the title from the existing window */
  1280     title = [nswindow title];
  1281     if (title) {
  1282         window->title = SDL_strdup([title UTF8String]);
  1283     }
  1284 
  1285     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
  1286 }}
  1287 
  1288 void
  1289 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
  1290 { @autoreleasepool
  1291 {
  1292     const char *title = window->title ? window->title : "";
  1293     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1294     NSString *string = [[NSString alloc] initWithUTF8String:title];
  1295     [nswindow setTitle:string];
  1296     [string release];
  1297 }}
  1298 
  1299 void
  1300 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
  1301 { @autoreleasepool
  1302 {
  1303     NSImage *nsimage = Cocoa_CreateImage(icon);
  1304 
  1305     if (nsimage) {
  1306         [NSApp setApplicationIconImage:nsimage];
  1307     }
  1308 }}
  1309 
  1310 void
  1311 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
  1312 { @autoreleasepool
  1313 {
  1314     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1315     NSWindow *nswindow = windata->nswindow;
  1316     NSRect rect;
  1317     Uint32 moveHack;
  1318 
  1319     rect.origin.x = window->x;
  1320     rect.origin.y = window->y;
  1321     rect.size.width = window->w;
  1322     rect.size.height = window->h;
  1323     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1324 
  1325     moveHack = s_moveHack;
  1326     s_moveHack = 0;
  1327     [nswindow setFrameOrigin:rect.origin];
  1328     s_moveHack = moveHack;
  1329 
  1330     ScheduleContextUpdates(windata);
  1331 }}
  1332 
  1333 void
  1334 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
  1335 { @autoreleasepool
  1336 {
  1337     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1338     NSWindow *nswindow = windata->nswindow;
  1339     NSRect rect;
  1340     Uint32 moveHack;
  1341 
  1342     /* Cocoa will resize the window from the bottom-left rather than the
  1343      * top-left when -[nswindow setContentSize:] is used, so we must set the
  1344      * entire frame based on the new size, in order to preserve the position.
  1345      */
  1346     rect.origin.x = window->x;
  1347     rect.origin.y = window->y;
  1348     rect.size.width = window->w;
  1349     rect.size.height = window->h;
  1350     ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
  1351 
  1352     moveHack = s_moveHack;
  1353     s_moveHack = 0;
  1354     [nswindow setFrame:[nswindow frameRectForContentRect:rect] display:YES];
  1355     s_moveHack = moveHack;
  1356 
  1357     ScheduleContextUpdates(windata);
  1358 }}
  1359 
  1360 void
  1361 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
  1362 { @autoreleasepool
  1363 {
  1364     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1365 
  1366     NSSize minSize;
  1367     minSize.width = window->min_w;
  1368     minSize.height = window->min_h;
  1369 
  1370     [windata->nswindow setContentMinSize:minSize];
  1371 }}
  1372 
  1373 void
  1374 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
  1375 { @autoreleasepool
  1376 {
  1377     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1378 
  1379     NSSize maxSize;
  1380     maxSize.width = window->max_w;
  1381     maxSize.height = window->max_h;
  1382 
  1383     [windata->nswindow setContentMaxSize:maxSize];
  1384 }}
  1385 
  1386 void
  1387 Cocoa_ShowWindow(_THIS, SDL_Window * window)
  1388 { @autoreleasepool
  1389 {
  1390     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1391     NSWindow *nswindow = windowData->nswindow;
  1392 
  1393     if (![nswindow isMiniaturized]) {
  1394         [windowData->listener pauseVisibleObservation];
  1395         [nswindow makeKeyAndOrderFront:nil];
  1396         [windowData->listener resumeVisibleObservation];
  1397     }
  1398 }}
  1399 
  1400 void
  1401 Cocoa_HideWindow(_THIS, SDL_Window * window)
  1402 { @autoreleasepool
  1403 {
  1404     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1405 
  1406     [nswindow orderOut:nil];
  1407 }}
  1408 
  1409 void
  1410 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
  1411 { @autoreleasepool
  1412 {
  1413     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
  1414     NSWindow *nswindow = windowData->nswindow;
  1415 
  1416     /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
  1417        a minimized or hidden window, so check for that before showing it.
  1418      */
  1419     [windowData->listener pauseVisibleObservation];
  1420     if (![nswindow isMiniaturized] && [nswindow isVisible]) {
  1421         [NSApp activateIgnoringOtherApps:YES];
  1422         [nswindow makeKeyAndOrderFront:nil];
  1423     }
  1424     [windowData->listener resumeVisibleObservation];
  1425 }}
  1426 
  1427 void
  1428 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
  1429 { @autoreleasepool
  1430 {
  1431     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
  1432     NSWindow *nswindow = windata->nswindow;
  1433 
  1434     [nswindow zoom:nil];
  1435 
  1436     ScheduleContextUpdates(windata);
  1437 }}
  1438 
  1439 void
  1440 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
  1441 { @autoreleasepool
  1442 {
  1443     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1444     NSWindow *nswindow = data->nswindow;
  1445 
  1446     if ([data->listener isInFullscreenSpaceTransition]) {
  1447         [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
  1448     } else {
  1449         [nswindow miniaturize:nil];
  1450     }
  1451 }}
  1452 
  1453 void
  1454 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
  1455 { @autoreleasepool
  1456 {
  1457     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1458 
  1459     if ([nswindow isMiniaturized]) {
  1460         [nswindow deminiaturize:nil];
  1461     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
  1462         [nswindow zoom:nil];
  1463     }
  1464 }}
  1465 
  1466 static NSWindow *
  1467 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
  1468 {
  1469     if (!data->created) {
  1470         /* Don't mess with other people's windows... */
  1471         return nswindow;
  1472     }
  1473 
  1474     [data->listener close];
  1475     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
  1476     [data->nswindow setContentView:[nswindow contentView]];
  1477     [data->nswindow registerForDraggedTypes:[NSArray arrayWithObject:(NSString *)kUTTypeFileURL]];
  1478     /* See comment in SetupWindowData. */
  1479     [data->nswindow setOneShot:NO];
  1480     [data->listener listen:data];
  1481 
  1482     [nswindow close];
  1483 
  1484     return data->nswindow;
  1485 }
  1486 
  1487 void
  1488 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
  1489 { @autoreleasepool
  1490 {
  1491     if (SetWindowStyle(window, GetWindowStyle(window))) {
  1492         if (bordered) {
  1493             Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
  1494         }
  1495     }
  1496 }}
  1497 
  1498 
  1499 void
  1500 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
  1501 { @autoreleasepool
  1502 {
  1503     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1504     NSWindow *nswindow = data->nswindow;
  1505     NSRect rect;
  1506 
  1507     /* The view responder chain gets messed with during setStyleMask */
  1508     if ([[nswindow contentView] nextResponder] == data->listener) {
  1509         [[nswindow contentView] setNextResponder:nil];
  1510     }
  1511 
  1512     if (fullscreen) {
  1513         SDL_Rect bounds;
  1514 
  1515         Cocoa_GetDisplayBounds(_this, display, &bounds);
  1516         rect.origin.x = bounds.x;
  1517         rect.origin.y = bounds.y;
  1518         rect.size.width = bounds.w;
  1519         rect.size.height = bounds.h;
  1520         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1521 
  1522         /* Hack to fix origin on Mac OS X 10.4 */
  1523         NSRect screenRect = [[nswindow screen] frame];
  1524         if (screenRect.size.height >= 1.0f) {
  1525             rect.origin.y += (screenRect.size.height - rect.size.height);
  1526         }
  1527 
  1528         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1529             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
  1530         } else {
  1531             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
  1532         }
  1533     } else {
  1534         rect.origin.x = window->windowed.x;
  1535         rect.origin.y = window->windowed.y;
  1536         rect.size.width = window->windowed.w;
  1537         rect.size.height = window->windowed.h;
  1538         ConvertNSRect([nswindow screen], fullscreen, &rect);
  1539 
  1540         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
  1541             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
  1542 
  1543             /* Hack to restore window decorations on Mac OS X 10.10 */
  1544             NSRect frameRect = [nswindow frame];
  1545             [nswindow setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
  1546             [nswindow setFrame:frameRect display:NO];
  1547         } else {
  1548             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
  1549         }
  1550     }
  1551 
  1552     /* The view responder chain gets messed with during setStyleMask */
  1553     if ([[nswindow contentView] nextResponder] != data->listener) {
  1554         [[nswindow contentView] setNextResponder:data->listener];
  1555     }
  1556 
  1557     s_moveHack = 0;
  1558     [nswindow setContentSize:rect.size];
  1559     [nswindow setFrameOrigin:rect.origin];
  1560     s_moveHack = SDL_GetTicks();
  1561 
  1562     /* When the window style changes the title is cleared */
  1563     if (!fullscreen) {
  1564         Cocoa_SetWindowTitle(_this, window);
  1565     }
  1566 
  1567     if (SDL_ShouldAllowTopmost() && fullscreen) {
  1568         /* OpenGL is rendering to the window, so make it visible! */
  1569         [nswindow setLevel:CGShieldingWindowLevel()];
  1570     } else {
  1571         [nswindow setLevel:kCGNormalWindowLevel];
  1572     }
  1573 
  1574     if ([nswindow isVisible] || fullscreen) {
  1575         [data->listener pauseVisibleObservation];
  1576         [nswindow makeKeyAndOrderFront:nil];
  1577         [data->listener resumeVisibleObservation];
  1578     }
  1579 
  1580     ScheduleContextUpdates(data);
  1581 }}
  1582 
  1583 int
  1584 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1585 {
  1586     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1587     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1588     const uint32_t tableSize = 256;
  1589     CGGammaValue redTable[tableSize];
  1590     CGGammaValue greenTable[tableSize];
  1591     CGGammaValue blueTable[tableSize];
  1592     uint32_t i;
  1593     float inv65535 = 1.0f / 65535.0f;
  1594 
  1595     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1596     for (i = 0; i < 256; i++) {
  1597         redTable[i] = ramp[0*256+i] * inv65535;
  1598         greenTable[i] = ramp[1*256+i] * inv65535;
  1599         blueTable[i] = ramp[2*256+i] * inv65535;
  1600     }
  1601 
  1602     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1603                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1604         return SDL_SetError("CGSetDisplayTransferByTable()");
  1605     }
  1606     return 0;
  1607 }
  1608 
  1609 int
  1610 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1611 {
  1612     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1613     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1614     const uint32_t tableSize = 256;
  1615     CGGammaValue redTable[tableSize];
  1616     CGGammaValue greenTable[tableSize];
  1617     CGGammaValue blueTable[tableSize];
  1618     uint32_t i, tableCopied;
  1619 
  1620     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1621                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1622         return SDL_SetError("CGGetDisplayTransferByTable()");
  1623     }
  1624 
  1625     for (i = 0; i < tableCopied; i++) {
  1626         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1627         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1628         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1629     }
  1630     return 0;
  1631 }
  1632 
  1633 void
  1634 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1635 {
  1636     /* Move the cursor to the nearest point in the window */
  1637     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1638     if (grabbed && data && ![data->listener isMoving]) {
  1639         int x, y;
  1640         CGPoint cgpoint;
  1641 
  1642         SDL_GetMouseState(&x, &y);
  1643         cgpoint.x = window->x + x;
  1644         cgpoint.y = window->y + y;
  1645 
  1646         Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
  1647 
  1648         DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
  1649         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1650     }
  1651 
  1652     if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
  1653         if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)
  1654             && ![data->listener isInFullscreenSpace]) {
  1655             /* OpenGL is rendering to the window, so make it visible! */
  1656             /* Doing this in 10.11 while in a Space breaks things (bug #3152) */
  1657             [data->nswindow setLevel:CGShieldingWindowLevel()];
  1658         } else {
  1659             [data->nswindow setLevel:kCGNormalWindowLevel];
  1660         }
  1661     }
  1662 }
  1663 
  1664 void
  1665 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1666 { @autoreleasepool
  1667 {
  1668     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1669 
  1670     if (data) {
  1671         if ([data->listener isInFullscreenSpace]) {
  1672             [NSMenu setMenuBarVisible:YES];
  1673         }
  1674         [data->listener close];
  1675         [data->listener release];
  1676         if (data->created) {
  1677             [data->nswindow close];
  1678         }
  1679 
  1680         NSArray *contexts = [[data->nscontexts copy] autorelease];
  1681         for (SDLOpenGLContext *context in contexts) {
  1682             /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
  1683             [context setWindow:NULL];
  1684         }
  1685         [data->nscontexts release];
  1686 
  1687         SDL_free(data);
  1688     }
  1689     window->driverdata = NULL;
  1690 }}
  1691 
  1692 SDL_bool
  1693 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1694 {
  1695     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1696 
  1697     if (info->version.major <= SDL_MAJOR_VERSION) {
  1698         info->subsystem = SDL_SYSWM_COCOA;
  1699         info->info.cocoa.window = nswindow;
  1700         return SDL_TRUE;
  1701     } else {
  1702         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1703                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1704         return SDL_FALSE;
  1705     }
  1706 }
  1707 
  1708 SDL_bool
  1709 Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
  1710 {
  1711     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1712 
  1713     if ([data->listener isInFullscreenSpace]) {
  1714         return SDL_TRUE;
  1715     } else {
  1716         return SDL_FALSE;
  1717     }
  1718 }
  1719 
  1720 SDL_bool
  1721 Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
  1722 { @autoreleasepool
  1723 {
  1724     SDL_bool succeeded = SDL_FALSE;
  1725     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1726 
  1727     if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
  1728         const int maxattempts = 3;
  1729         int attempt = 0;
  1730         while (++attempt <= maxattempts) {
  1731             /* Wait for the transition to complete, so application changes
  1732              take effect properly (e.g. setting the window size, etc.)
  1733              */
  1734             const int limit = 10000;
  1735             int count = 0;
  1736             while ([data->listener isInFullscreenSpaceTransition]) {
  1737                 if ( ++count == limit ) {
  1738                     /* Uh oh, transition isn't completing. Should we assert? */
  1739                     break;
  1740                 }
  1741                 SDL_Delay(1);
  1742                 SDL_PumpEvents();
  1743             }
  1744             if ([data->listener isInFullscreenSpace] == (state ? YES : NO))
  1745                 break;
  1746             /* Try again, the last attempt was interrupted by user gestures */
  1747             if (![data->listener setFullscreenSpace:(state ? YES : NO)])
  1748                 break; /* ??? */
  1749         }
  1750         /* Return TRUE to prevent non-space fullscreen logic from running */
  1751         succeeded = SDL_TRUE;
  1752     }
  1753 
  1754     return succeeded;
  1755 }}
  1756 
  1757 int
  1758 Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
  1759 {
  1760     return 0;  /* just succeed, the real work is done elsewhere. */
  1761 }
  1762 
  1763 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1764 
  1765 /* vi: set ts=4 sw=4 expandtab: */