src/video/cocoa/SDL_cocoawindow.m
author Ryan C. Gordon <icculus@icculus.org>
Wed, 26 Jun 2019 13:21:43 -0400
changeset 12907 6a3b2cc9d66c
parent 12863 bddd4ba329cc
child 12923 73091a9e72f7
permissions -rw-r--r--
cocoa: Check for capslock in -[NSResponder flagsChanged], not with IOKit.

Using IOKit for this pops up a warning at startup on macOS 10.15 ("Catalina"),
asking the user to authorize the app to listen to all keyboard input in the
system, which is unacceptable.

I _think_ we were using IOKit under incorrect presumptions here; the Stack
Overflow link mentioned in it was complaining about not being able to use
flagsChanged to differentiate between left and right mod keys, but that's not
an issue for capslock.

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