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