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