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