src/video/cocoa/SDL_cocoawindow.m
author Alex Szpakowski <slime73@gmail.com>
Sat, 13 Jul 2019 17:04:02 -0300
changeset 12940 1e6980ce45c0
parent 12937 41528cbd80ab
child 12979 bbbb30026158
permissions -rw-r--r--
macOS: Fix SDL_GL_CreateContext/MakeCurrent on non-main threads causing a Main Thread Checker warning when built with Xcode 11 / the macOS 10.15 SDK.

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