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