sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_cocoawindow.m (69516B)


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