src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 17 Aug 2014 14:57:52 -0700
changeset 9086 c5e33f9a0d03
parent 9041 89e97caa2387
child 9236 a845edf98a80
permissions -rw-r--r--
Fixed bug 2655 - OSX: Window position and global mouse coord spaces are different

Tim McDaniel

On OSX, with revision 8729, the coordinate space for window position and the coordinate space for global mouse position don't match. For a non-fullscreen window, the window position is global relative to the bottom of the menubar. The global mouse position is relative to the top of the screen. This affects Cocoa_WarpMouse and potentially other things as well. Further, the coordinate system for window position is now affected by what screen it is on. For example, if I have two equal size screens oriented side by side such that the tops of the screens are equal in global space, with the menubar on one screen, and a window straddles the two screens, the window's y position makes no sense. The window's y position depends on what screen "most" of the window is on. So if I move the window horizontally just a bit, the y position of my window is now different by the size of the menubar, even though the window was not moved vertically.

I'd like to reiterate that this was a fairly fundamental change (and a breaking change for us). If SDL OSX is to really support multi-display configurations, this is especially problematic.

If the real concern is preventing windows from going under the menubar, then perhaps a solution involving something like overriding [NSWindow constrainFrameRect] would be less problematic than redefining the global window coord space for the main display.
slouken@1933
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 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"
slouken@1933
    37
#include "SDL_cocoavideo.h"
eligottlieb@4811
    38
#include "SDL_cocoashape.h"
gzjjgod@5057
    39
#include "SDL_cocoamouse.h"
jorgen@7594
    40
#include "SDL_cocoaopengl.h"
icculus@8295
    41
#include "SDL_assert.h"
slouken@1933
    42
jorgen@8261
    43
/* #define DEBUG_COCOAWINDOW */
jorgen@8261
    44
jorgen@8261
    45
#ifdef DEBUG_COCOAWINDOW
jorgen@8261
    46
#define DLog(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
jorgen@8261
    47
#else
jorgen@8261
    48
#define DLog(...) do { } while (0)
jorgen@8261
    49
#endif
jorgen@8261
    50
jorgen@8261
    51
slouken@8801
    52
#define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
slouken@8801
    53
slouken@8801
    54
jorgen@8260
    55
@interface SDLWindow : NSWindow
jorgen@8260
    56
/* These are needed for borderless/fullscreen windows */
jorgen@8260
    57
- (BOOL)canBecomeKeyWindow;
jorgen@8260
    58
- (BOOL)canBecomeMainWindow;
jorgen@8260
    59
- (void)sendEvent:(NSEvent *)event;
slouken@8809
    60
- (void)doCommandBySelector:(SEL)aSelector;
jorgen@8260
    61
@end
jorgen@8260
    62
jorgen@8260
    63
@implementation SDLWindow
jorgen@8260
    64
- (BOOL)canBecomeKeyWindow
jorgen@8260
    65
{
jorgen@8260
    66
    return YES;
jorgen@8260
    67
}
jorgen@8260
    68
jorgen@8260
    69
- (BOOL)canBecomeMainWindow
jorgen@8260
    70
{
jorgen@8260
    71
    return YES;
jorgen@8260
    72
}
jorgen@8260
    73
jorgen@8260
    74
- (void)sendEvent:(NSEvent *)event
jorgen@8260
    75
{
jorgen@8260
    76
  [super sendEvent:event];
jorgen@8260
    77
jorgen@8260
    78
  if ([event type] != NSLeftMouseUp) {
jorgen@8260
    79
      return;
jorgen@8260
    80
  }
jorgen@8260
    81
jorgen@8260
    82
  id delegate = [self delegate];
jorgen@8260
    83
  if (![delegate isKindOfClass:[Cocoa_WindowListener class]]) {
jorgen@8260
    84
      return;
jorgen@8260
    85
  }
jorgen@8260
    86
jorgen@8260
    87
  if ([delegate isMoving]) {
jorgen@8260
    88
      [delegate windowDidFinishMoving];
jorgen@8260
    89
  }
jorgen@8260
    90
}
slouken@8809
    91
slouken@8809
    92
/* We'll respond to selectors by doing nothing so we don't beep.
slouken@8809
    93
 * The escape key gets converted to a "cancel" selector, etc.
slouken@8809
    94
 */
slouken@8809
    95
- (void)doCommandBySelector:(SEL)aSelector
slouken@8809
    96
{
slouken@8809
    97
    /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
slouken@8809
    98
}
jorgen@8260
    99
@end
jorgen@8260
   100
jorgen@8260
   101
slouken@5398
   102
static Uint32 s_moveHack;
slouken@5398
   103
slouken@8801
   104
static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
slouken@1933
   105
{
slouken@9086
   106
    r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
slouken@1933
   107
}
slouken@1933
   108
slouken@7952
   109
static void
slouken@7952
   110
ScheduleContextUpdates(SDL_WindowData *data)
jorgen@7595
   111
{
jorgen@8258
   112
    NSOpenGLContext *currentContext = [NSOpenGLContext currentContext];
jorgen@7595
   113
    NSMutableArray *contexts = data->nscontexts;
jorgen@7595
   114
    @synchronized (contexts) {
jorgen@7595
   115
        for (SDLOpenGLContext *context in contexts) {
jorgen@8258
   116
            if (context == currentContext) {
jorgen@8258
   117
                [context update];
jorgen@8258
   118
            } else {
jorgen@8258
   119
                [context scheduleUpdate];
jorgen@8258
   120
            }
jorgen@7595
   121
        }
jorgen@7595
   122
    }
jorgen@7595
   123
}
jorgen@7595
   124
slouken@7952
   125
static int
slouken@7952
   126
GetHintCtrlClickEmulateRightClick()
slouken@7915
   127
{
slouken@7915
   128
	const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK );
slouken@7915
   129
	return hint != NULL && *hint != '0';
slouken@7915
   130
}
slouken@7915
   131
slouken@7952
   132
static unsigned int
slouken@7952
   133
GetWindowStyle(SDL_Window * window)
slouken@7952
   134
{
slouken@7952
   135
    unsigned int style;
slouken@7952
   136
icculus@8295
   137
    if (window->flags & SDL_WINDOW_FULLSCREEN) {
slouken@7952
   138
        style = NSBorderlessWindowMask;
slouken@7952
   139
    } else {
slouken@7952
   140
        if (window->flags & SDL_WINDOW_BORDERLESS) {
slouken@7952
   141
            style = NSBorderlessWindowMask;
slouken@7952
   142
        } else {
slouken@7952
   143
            style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
slouken@7952
   144
        }
slouken@7952
   145
        if (window->flags & SDL_WINDOW_RESIZABLE) {
slouken@7952
   146
            style |= NSResizableWindowMask;
slouken@7952
   147
        }
slouken@7952
   148
    }
slouken@7952
   149
    return style;
slouken@7952
   150
}
slouken@7952
   151
slouken@7990
   152
static SDL_bool
slouken@7990
   153
SetWindowStyle(SDL_Window * window, unsigned int style)
slouken@7990
   154
{
slouken@7990
   155
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@7990
   156
    NSWindow *nswindow = data->nswindow;
slouken@7990
   157
slouken@7990
   158
    if (![nswindow respondsToSelector: @selector(setStyleMask:)]) {
slouken@7990
   159
        return SDL_FALSE;
slouken@7990
   160
    }
slouken@7990
   161
slouken@7990
   162
    /* The view responder chain gets messed with during setStyleMask */
slouken@7990
   163
    if ([[nswindow contentView] nextResponder] == data->listener) {
slouken@7990
   164
        [[nswindow contentView] setNextResponder:nil];
slouken@7990
   165
    }
slouken@7990
   166
slouken@7990
   167
    [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)style];
slouken@7990
   168
slouken@7990
   169
    /* The view responder chain gets messed with during setStyleMask */
slouken@7990
   170
    if ([[nswindow contentView] nextResponder] != data->listener) {
slouken@7990
   171
        [[nswindow contentView] setNextResponder:data->listener];
slouken@7990
   172
    }
slouken@7990
   173
slouken@7990
   174
    return SDL_TRUE;
slouken@7990
   175
}
slouken@7990
   176
slouken@7952
   177
slouken@1933
   178
@implementation Cocoa_WindowListener
slouken@1933
   179
slouken@1933
   180
- (void)listen:(SDL_WindowData *)data
slouken@1933
   181
{
slouken@1933
   182
    NSNotificationCenter *center;
slouken@5371
   183
    NSWindow *window = data->nswindow;
slouken@5371
   184
    NSView *view = [window contentView];
slouken@1933
   185
slouken@1933
   186
    _data = data;
jorgen@7087
   187
    observingVisible = YES;
slouken@7740
   188
    wasCtrlLeft = NO;
jorgen@7087
   189
    wasVisible = [window isVisible];
slouken@7967
   190
    isFullscreenSpace = NO;
slouken@7952
   191
    inFullscreenTransition = NO;
slouken@7963
   192
    pendingWindowOperation = PENDING_OPERATION_NONE;
jorgen@8260
   193
    isMoving = NO;
icculus@8931
   194
    isDragAreaRunning = NO;
slouken@1933
   195
slouken@1933
   196
    center = [NSNotificationCenter defaultCenter];
slouken@1933
   197
slouken@5374
   198
    if ([window delegate] != nil) {
slouken@5374
   199
        [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
slouken@5374
   200
        [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
slouken@5374
   201
        [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
slouken@5374
   202
        [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
slouken@5374
   203
        [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
slouken@5374
   204
        [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
slouken@5374
   205
        [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
slouken@7952
   206
        [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
slouken@7952
   207
        [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
slouken@7952
   208
        [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
slouken@7952
   209
        [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
slouken@5374
   210
    } else {
slouken@5374
   211
        [window setDelegate:self];
slouken@5374
   212
    }
slouken@1933
   213
slouken@7191
   214
    /* Haven't found a delegate / notification that triggers when the window is
slouken@7191
   215
     * ordered out (is not visible any more). You can be ordered out without
slouken@7191
   216
     * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
slouken@7191
   217
     */
jorgen@7084
   218
    [window addObserver:self
jorgen@7084
   219
             forKeyPath:@"visible"
jorgen@7084
   220
                options:NSKeyValueObservingOptionNew
jorgen@7084
   221
                context:NULL];
jorgen@7084
   222
slouken@5371
   223
    [window setNextResponder:self];
slouken@5371
   224
    [window setAcceptsMouseMovedEvents:YES];
slouken@5371
   225
slouken@5371
   226
    [view setNextResponder:self];
icculus@6108
   227
icculus@6108
   228
    if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
icculus@6108
   229
        [view setAcceptsTouchEvents:YES];
icculus@6108
   230
    }
slouken@1933
   231
}
slouken@1933
   232
jorgen@7084
   233
- (void)observeValueForKeyPath:(NSString *)keyPath
jorgen@7084
   234
                      ofObject:(id)object
jorgen@7084
   235
                        change:(NSDictionary *)change
jorgen@7084
   236
                       context:(void *)context
jorgen@7084
   237
{
jorgen@7087
   238
    if (!observingVisible) {
jorgen@7087
   239
        return;
jorgen@7087
   240
    }
jorgen@7087
   241
jorgen@7084
   242
    if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
jorgen@7084
   243
        int newVisibility = [[change objectForKey:@"new"] intValue];
jorgen@7084
   244
        if (newVisibility) {
jorgen@7084
   245
            SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
jorgen@7084
   246
        } else {
jorgen@7084
   247
            SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
jorgen@7084
   248
        }
jorgen@7084
   249
    }
jorgen@7084
   250
}
jorgen@7084
   251
jorgen@7087
   252
-(void) pauseVisibleObservation
jorgen@7087
   253
{
jorgen@7087
   254
    observingVisible = NO;
jorgen@7087
   255
    wasVisible = [_data->nswindow isVisible];
jorgen@7087
   256
}
jorgen@7087
   257
jorgen@7087
   258
-(void) resumeVisibleObservation
jorgen@7087
   259
{
jorgen@7087
   260
    BOOL isVisible = [_data->nswindow isVisible];
jorgen@7087
   261
    observingVisible = YES;
jorgen@7087
   262
    if (wasVisible != isVisible) {
jorgen@7087
   263
        if (isVisible) {
jorgen@7087
   264
            SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
jorgen@7087
   265
        } else {
jorgen@7087
   266
            SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
jorgen@7087
   267
        }
jorgen@7087
   268
jorgen@7087
   269
        wasVisible = isVisible;
jorgen@7087
   270
    }
jorgen@7087
   271
}
jorgen@7087
   272
icculus@8284
   273
-(BOOL) setFullscreenSpace:(BOOL) state
slouken@7961
   274
{
slouken@7961
   275
    SDL_Window *window = _data->window;
slouken@7961
   276
    NSWindow *nswindow = _data->nswindow;
icculus@8295
   277
    SDL_VideoData *videodata = ((SDL_WindowData *) window->driverdata)->videodata;
slouken@7961
   278
icculus@8295
   279
    if (!videodata->allow_spaces) {
icculus@8295
   280
        return NO;  /* Spaces are forcibly disabled. */
icculus@8295
   281
    } else if (state && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
icculus@8292
   282
        return NO;  /* we only allow you to make a Space on FULLSCREEN_DESKTOP windows. */
slouken@8798
   283
    } else if (!state && ((window->last_fullscreen_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP)) {
slouken@8798
   284
        return NO;  /* we only handle leaving the Space on windows that were previously FULLSCREEN_DESKTOP. */
icculus@8284
   285
    } else if (state == isFullscreenSpace) {
icculus@8284
   286
        return YES;  /* already there. */
slouken@7961
   287
    }
slouken@7961
   288
slouken@7961
   289
    if (inFullscreenTransition) {
slouken@7961
   290
        if (state) {
slouken@7963
   291
            [self addPendingWindowOperation:PENDING_OPERATION_ENTER_FULLSCREEN];
slouken@7961
   292
        } else {
slouken@7963
   293
            [self addPendingWindowOperation:PENDING_OPERATION_LEAVE_FULLSCREEN];
slouken@7961
   294
        }
slouken@7961
   295
        return YES;
slouken@7961
   296
    }
slouken@7968
   297
    inFullscreenTransition = YES;
slouken@7968
   298
icculus@8295
   299
    /* you need to be FullScreenPrimary, or toggleFullScreen doesn't work. Unset it again in windowDidExitFullScreen. */
icculus@8284
   300
    [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
slouken@7965
   301
    [nswindow performSelectorOnMainThread: @selector(toggleFullScreen:) withObject:nswindow waitUntilDone:NO];
slouken@7961
   302
    return YES;
slouken@7952
   303
}
slouken@7952
   304
slouken@7968
   305
-(BOOL) isInFullscreenSpace
slouken@7968
   306
{
slouken@7968
   307
    return isFullscreenSpace;
slouken@7968
   308
}
slouken@7968
   309
slouken@7968
   310
-(BOOL) isInFullscreenSpaceTransition
slouken@7963
   311
{
slouken@7963
   312
    return inFullscreenTransition;
slouken@7963
   313
}
slouken@7963
   314
slouken@7963
   315
-(void) addPendingWindowOperation:(PendingWindowOperation) operation
slouken@7963
   316
{
slouken@7963
   317
    pendingWindowOperation = operation;
slouken@7963
   318
}
slouken@7963
   319
slouken@1933
   320
- (void)close
slouken@1933
   321
{
slouken@1933
   322
    NSNotificationCenter *center;
slouken@5371
   323
    NSWindow *window = _data->nswindow;
slouken@5371
   324
    NSView *view = [window contentView];
icculus@7534
   325
    NSArray *windows = nil;
slouken@1933
   326
slouken@1933
   327
    center = [NSNotificationCenter defaultCenter];
slouken@1933
   328
slouken@5374
   329
    if ([window delegate] != self) {
slouken@5374
   330
        [center removeObserver:self name:NSWindowDidExposeNotification object:window];
slouken@5374
   331
        [center removeObserver:self name:NSWindowDidMoveNotification object:window];
slouken@5374
   332
        [center removeObserver:self name:NSWindowDidResizeNotification object:window];
slouken@5374
   333
        [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
slouken@5374
   334
        [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
slouken@5374
   335
        [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
slouken@5374
   336
        [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
slouken@7952
   337
        [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window];
slouken@7952
   338
        [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window];
slouken@7952
   339
        [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window];
slouken@7952
   340
        [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window];
slouken@5374
   341
    } else {
slouken@5374
   342
        [window setDelegate:nil];
slouken@5374
   343
    }
slouken@5371
   344
slouken@7961
   345
    [window removeObserver:self forKeyPath:@"visible"];
jorgen@7084
   346
slouken@5374
   347
    if ([window nextResponder] == self) {
slouken@5374
   348
        [window setNextResponder:nil];
slouken@5374
   349
    }
slouken@5374
   350
    if ([view nextResponder] == self) {
slouken@5374
   351
        [view setNextResponder:nil];
slouken@5374
   352
    }
icculus@7534
   353
icculus@7534
   354
    /* Make the next window in the z-order Key. If we weren't the foreground
icculus@7535
   355
       when closed, this is a no-op.
icculus@7535
   356
       !!! FIXME: Note that this is a hack, and there are corner cases where
icculus@7535
   357
       !!! FIXME:  this fails (such as the About box). The typical nib+RunLoop
icculus@7535
   358
       !!! FIXME:  handles this for Cocoa apps, but we bypass all that in SDL.
icculus@7535
   359
       !!! FIXME:  We should remove this code when we find a better way to
icculus@7535
   360
       !!! FIXME:  have the system do this for us. See discussion in
icculus@7535
   361
       !!! FIXME:   http://bugzilla.libsdl.org/show_bug.cgi?id=1825
icculus@7535
   362
    */
icculus@7534
   363
    windows = [NSApp orderedWindows];
slouken@8986
   364
    for (NSWindow *win in windows) {
jorgen@8111
   365
        if (win == window) {
jorgen@8111
   366
            continue;
jorgen@8111
   367
        }
jorgen@8111
   368
icculus@7534
   369
        [win makeKeyAndOrderFront:self];
jorgen@8111
   370
        break;
icculus@7534
   371
    }
slouken@1933
   372
}
slouken@1933
   373
jorgen@8260
   374
- (BOOL)isMoving
jorgen@8260
   375
{
jorgen@8260
   376
    return isMoving;
jorgen@8260
   377
}
jorgen@8260
   378
jorgen@8260
   379
-(void) setPendingMoveX:(int)x Y:(int)y
jorgen@8260
   380
{
jorgen@8260
   381
    pendingWindowWarpX = x;
jorgen@8260
   382
    pendingWindowWarpY = y;
jorgen@8260
   383
}
jorgen@8260
   384
jorgen@8260
   385
- (void)windowDidFinishMoving
jorgen@8260
   386
{
slouken@8986
   387
    if ([self isMoving]) {
jorgen@8260
   388
        isMoving = NO;
jorgen@8260
   389
jorgen@8260
   390
        SDL_Mouse *mouse = SDL_GetMouse();
slouken@9086
   391
        if (pendingWindowWarpX != INT_MAX && pendingWindowWarpY != INT_MAX) {
slouken@9086
   392
            mouse->WarpMouseGlobal(pendingWindowWarpX, pendingWindowWarpY);
slouken@9086
   393
            pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
jorgen@8260
   394
        }
slouken@9086
   395
        if (mouse->relative_mode && !mouse->relative_mode_warp && mouse->focus == _data->window) {
jorgen@8260
   396
            mouse->SetRelativeMouseMode(SDL_TRUE);
jorgen@8260
   397
        }
jorgen@8260
   398
    }
jorgen@8260
   399
}
jorgen@8260
   400
slouken@1933
   401
- (BOOL)windowShouldClose:(id)sender
slouken@1933
   402
{
slouken@3685
   403
    SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
slouken@1933
   404
    return NO;
slouken@1933
   405
}
slouken@1933
   406
slouken@1933
   407
- (void)windowDidExpose:(NSNotification *)aNotification
slouken@1933
   408
{
slouken@3685
   409
    SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
slouken@1933
   410
}
slouken@1933
   411
jorgen@8260
   412
- (void)windowWillMove:(NSNotification *)aNotification
jorgen@8260
   413
{
jorgen@8260
   414
    if ([_data->nswindow isKindOfClass:[SDLWindow class]]) {
slouken@9086
   415
        pendingWindowWarpX = pendingWindowWarpY = INT_MAX;
jorgen@8260
   416
        isMoving = YES;
jorgen@8260
   417
    }
jorgen@8260
   418
}
jorgen@8260
   419
slouken@1933
   420
- (void)windowDidMove:(NSNotification *)aNotification
slouken@1933
   421
{
slouken@1933
   422
    int x, y;
slouken@5398
   423
    SDL_Window *window = _data->window;
slouken@5398
   424
    NSWindow *nswindow = _data->nswindow;
slouken@8801
   425
    BOOL fullscreen = window->flags & FULLSCREEN_MASK;
slouken@5398
   426
    NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
slouken@8801
   427
    ConvertNSRect([nswindow screen], fullscreen, &rect);
slouken@5398
   428
slouken@5398
   429
    if (s_moveHack) {
slouken@5398
   430
        SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
slouken@5398
   431
slouken@5398
   432
        s_moveHack = 0;
slouken@5398
   433
slouken@5398
   434
        if (blockMove) {
slouken@5398
   435
            /* Cocoa is adjusting the window in response to a mode change */
slouken@5398
   436
            rect.origin.x = window->x;
slouken@5398
   437
            rect.origin.y = window->y;
slouken@8801
   438
            ConvertNSRect([nswindow screen], fullscreen, &rect);
slouken@5398
   439
            [nswindow setFrameOrigin:rect.origin];
slouken@5398
   440
            return;
slouken@5398
   441
        }
slouken@5398
   442
    }
slouken@5398
   443
slouken@3507
   444
    x = (int)rect.origin.x;
slouken@3507
   445
    y = (int)rect.origin.y;
icculus@5564
   446
jorgen@7595
   447
    ScheduleContextUpdates(_data);
icculus@5564
   448
slouken@5398
   449
    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
slouken@1933
   450
}
slouken@1933
   451
slouken@1933
   452
- (void)windowDidResize:(NSNotification *)aNotification
slouken@1933
   453
{
icculus@8284
   454
    if (inFullscreenTransition) {
icculus@8284
   455
        /* We'll take care of this at the end of the transition */
icculus@8284
   456
        return;
icculus@8284
   457
    }
icculus@8284
   458
slouken@7963
   459
    SDL_Window *window = _data->window;
slouken@7963
   460
    NSWindow *nswindow = _data->nswindow;
slouken@6231
   461
    int x, y, w, h;
slouken@7963
   462
    NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
slouken@8801
   463
    ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
slouken@6231
   464
    x = (int)rect.origin.x;
slouken@6231
   465
    y = (int)rect.origin.y;
slouken@1933
   466
    w = (int)rect.size.width;
slouken@1933
   467
    h = (int)rect.size.height;
slouken@7952
   468
slouken@7963
   469
    if (SDL_IsShapedWindow(window)) {
slouken@7963
   470
        Cocoa_ResizeWindowShape(window);
slouken@7952
   471
    }
icculus@5564
   472
jorgen@7595
   473
    ScheduleContextUpdates(_data);
icculus@5564
   474
slouken@6231
   475
    /* The window can move during a resize event, such as when maximizing
slouken@6231
   476
       or resizing from a corner */
slouken@7963
   477
    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
slouken@7963
   478
    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
icculus@7566
   479
slouken@7963
   480
    const BOOL zoomed = [nswindow isZoomed];
icculus@7566
   481
    if (!zoomed) {
slouken@7963
   482
        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0);
icculus@7566
   483
    } else if (zoomed) {
slouken@7963
   484
        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
icculus@7566
   485
    }
slouken@1933
   486
}
slouken@1933
   487
slouken@1933
   488
- (void)windowDidMiniaturize:(NSNotification *)aNotification
slouken@1933
   489
{
slouken@3685
   490
    SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
slouken@1933
   491
}
slouken@1933
   492
slouken@1933
   493
- (void)windowDidDeminiaturize:(NSNotification *)aNotification
slouken@1933
   494
{
slouken@3685
   495
    SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
slouken@1933
   496
}
slouken@1933
   497
slouken@1933
   498
- (void)windowDidBecomeKey:(NSNotification *)aNotification
slouken@1933
   499
{
slouken@5367
   500
    SDL_Window *window = _data->window;
jorgen@7271
   501
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@9086
   502
    if (mouse->relative_mode && !mouse->relative_mode_warp && ![self isMoving]) {
jorgen@8260
   503
        mouse->SetRelativeMouseMode(SDL_TRUE);
jorgen@8260
   504
    }
slouken@5367
   505
slouken@3688
   506
    /* We're going to get keyboard events, since we're key. */
slouken@5367
   507
    SDL_SetKeyboardFocus(window);
slouken@5367
   508
slouken@5367
   509
    /* If we just gained focus we need the updated mouse position */
jorgen@7271
   510
    if (!mouse->relative_mode) {
slouken@5367
   511
        NSPoint point;
slouken@5396
   512
        int x, y;
slouken@5396
   513
slouken@5396
   514
        point = [_data->nswindow mouseLocationOutsideOfEventStream];
slouken@5396
   515
        x = (int)point.x;
slouken@5396
   516
        y = (int)(window->h - point.y);
slouken@5396
   517
slouken@5396
   518
        if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
slouken@6950
   519
            SDL_SendMouseMotion(window, 0, 0, x, y);
slouken@5396
   520
        }
slouken@5367
   521
    }
slouken@1962
   522
slouken@4503
   523
    /* Check to see if someone updated the clipboard */
slouken@4503
   524
    Cocoa_CheckClipboardUpdate(_data->videodata);
icculus@8288
   525
icculus@8293
   526
    if ((isFullscreenSpace) && ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)) {
icculus@8288
   527
        [NSMenu setMenuBarVisible:NO];
icculus@8288
   528
    }
slouken@1933
   529
}
slouken@1933
   530
slouken@1933
   531
- (void)windowDidResignKey:(NSNotification *)aNotification
slouken@1933
   532
{
jorgen@8260
   533
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@9086
   534
    if (mouse->relative_mode && !mouse->relative_mode_warp) {
jorgen@8260
   535
        mouse->SetRelativeMouseMode(SDL_FALSE);
jorgen@8260
   536
    }
jorgen@8260
   537
slouken@2059
   538
    /* Some other window will get mouse events, since we're not key. */
slouken@4465
   539
    if (SDL_GetMouseFocus() == _data->window) {
slouken@4465
   540
        SDL_SetMouseFocus(NULL);
slouken@2059
   541
    }
slouken@2059
   542
slouken@2059
   543
    /* Some other window will get keyboard events, since we're not key. */
slouken@4465
   544
    if (SDL_GetKeyboardFocus() == _data->window) {
slouken@4465
   545
        SDL_SetKeyboardFocus(NULL);
slouken@4465
   546
    }
icculus@8288
   547
icculus@8288
   548
    if (isFullscreenSpace) {
icculus@8288
   549
        [NSMenu setMenuBarVisible:YES];
icculus@8288
   550
    }
slouken@1933
   551
}
slouken@1933
   552
slouken@7952
   553
- (void)windowWillEnterFullScreen:(NSNotification *)aNotification
slouken@7952
   554
{
slouken@7952
   555
    SDL_Window *window = _data->window;
slouken@7952
   556
slouken@7990
   557
    SetWindowStyle(window, (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask));
slouken@7965
   558
slouken@7967
   559
    isFullscreenSpace = YES;
slouken@7952
   560
    inFullscreenTransition = YES;
slouken@7952
   561
}
slouken@7952
   562
slouken@7952
   563
- (void)windowDidEnterFullScreen:(NSNotification *)aNotification
slouken@7952
   564
{
slouken@7965
   565
    SDL_Window *window = _data->window;
slouken@7965
   566
slouken@7952
   567
    inFullscreenTransition = NO;
slouken@7961
   568
slouken@7963
   569
    if (pendingWindowOperation == PENDING_OPERATION_LEAVE_FULLSCREEN) {
slouken@7963
   570
        pendingWindowOperation = PENDING_OPERATION_NONE;
slouken@7967
   571
        [self setFullscreenSpace:NO];
slouken@7961
   572
    } else {
icculus@8284
   573
        if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
icculus@8284
   574
            [NSMenu setMenuBarVisible:NO];
icculus@8284
   575
        }
icculus@8284
   576
slouken@7963
   577
        pendingWindowOperation = PENDING_OPERATION_NONE;
slouken@7965
   578
        /* Force the size change event in case it was delivered earlier
slouken@7965
   579
           while the window was still animating into place.
slouken@7965
   580
         */
slouken@7965
   581
        window->w = 0;
slouken@7965
   582
        window->h = 0;
slouken@7961
   583
        [self windowDidResize:aNotification];
slouken@7961
   584
    }
slouken@7952
   585
}
slouken@7952
   586
slouken@7952
   587
- (void)windowWillExitFullScreen:(NSNotification *)aNotification
slouken@7952
   588
{
slouken@7964
   589
    SDL_Window *window = _data->window;
slouken@7964
   590
slouken@7990
   591
    SetWindowStyle(window, GetWindowStyle(window));
slouken@7964
   592
slouken@7967
   593
    isFullscreenSpace = NO;
slouken@7952
   594
    inFullscreenTransition = YES;
slouken@7952
   595
}
slouken@7952
   596
slouken@7952
   597
- (void)windowDidExitFullScreen:(NSNotification *)aNotification
slouken@7952
   598
{
slouken@7965
   599
    SDL_Window *window = _data->window;
slouken@7952
   600
    NSWindow *nswindow = _data->nswindow;
slouken@7952
   601
slouken@7952
   602
    inFullscreenTransition = NO;
slouken@7961
   603
slouken@8618
   604
    [nswindow setLevel:kCGNormalWindowLevel];
slouken@8618
   605
slouken@7963
   606
    if (pendingWindowOperation == PENDING_OPERATION_ENTER_FULLSCREEN) {
slouken@7963
   607
        pendingWindowOperation = PENDING_OPERATION_NONE;
slouken@7967
   608
        [self setFullscreenSpace:YES];
slouken@7963
   609
    } else if (pendingWindowOperation == PENDING_OPERATION_MINIMIZE) {
slouken@7963
   610
        pendingWindowOperation = PENDING_OPERATION_NONE;
slouken@7963
   611
        [nswindow miniaturize:nil];
slouken@7961
   612
    } else {
icculus@8291
   613
        /* Adjust the fullscreen toggle button and readd menu now that we're here. */
icculus@8291
   614
        if (window->flags & SDL_WINDOW_RESIZABLE) {
icculus@8291
   615
            /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
icculus@8291
   616
            [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
icculus@8291
   617
        } else {
icculus@8284
   618
            [nswindow setCollectionBehavior:NSWindowCollectionBehaviorManaged];
icculus@8284
   619
        }
icculus@8291
   620
        [NSMenu setMenuBarVisible:YES];
icculus@8284
   621
slouken@7963
   622
        pendingWindowOperation = PENDING_OPERATION_NONE;
slouken@7965
   623
        /* Force the size change event in case it was delivered earlier
slouken@7965
   624
           while the window was still animating into place.
slouken@7965
   625
         */
slouken@7965
   626
        window->w = 0;
slouken@7965
   627
        window->h = 0;
slouken@7961
   628
        [self windowDidResize:aNotification];
icculus@8622
   629
slouken@8623
   630
        /* FIXME: Why does the window get hidden? */
slouken@8623
   631
        if (window->flags & SDL_WINDOW_SHOWN) {
slouken@8623
   632
            Cocoa_ShowWindow(SDL_GetVideoDevice(), window);
slouken@8623
   633
        }
slouken@7961
   634
    }
slouken@7952
   635
}
slouken@7952
   636
icculus@8291
   637
-(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
icculus@8291
   638
{
icculus@8291
   639
    if ((_data->window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
icculus@8291
   640
        return NSApplicationPresentationFullScreen | NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
icculus@8291
   641
    } else {
icculus@8291
   642
        return proposedOptions;
icculus@8291
   643
    }
icculus@8291
   644
}
icculus@8291
   645
icculus@8291
   646
slouken@7191
   647
/* We'll respond to key events by doing nothing so we don't beep.
slouken@7191
   648
 * We could handle key messages here, but we lose some in the NSApp dispatch,
slouken@7191
   649
 * where they get converted to action messages, etc.
slouken@7191
   650
 */
slouken@6514
   651
- (void)flagsChanged:(NSEvent *)theEvent
slouken@6514
   652
{
slouken@7191
   653
    /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
slouken@6514
   654
}
slouken@6514
   655
- (void)keyDown:(NSEvent *)theEvent
slouken@6514
   656
{
slouken@7191
   657
    /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
slouken@6514
   658
}
slouken@6514
   659
- (void)keyUp:(NSEvent *)theEvent
slouken@6514
   660
{
slouken@7191
   661
    /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
slouken@6514
   662
}
slouken@6514
   663
slouken@7191
   664
/* We'll respond to selectors by doing nothing so we don't beep.
slouken@7191
   665
 * The escape key gets converted to a "cancel" selector, etc.
slouken@7191
   666
 */
slouken@6514
   667
- (void)doCommandBySelector:(SEL)aSelector
slouken@6514
   668
{
slouken@7191
   669
    /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
slouken@6514
   670
}
slouken@6514
   671
icculus@8935
   672
- (BOOL)processHitTest:(NSEvent *)theEvent
icculus@8931
   673
{
icculus@8935
   674
    SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
icculus@8931
   675
icculus@8935
   676
    if (_data->window->hit_test) {  /* if no hit-test, skip this. */
icculus@8931
   677
        const NSPoint location = [theEvent locationInWindow];
icculus@8931
   678
        const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
icculus@8935
   679
        const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
icculus@8935
   680
        if (rc == SDL_HITTEST_DRAGGABLE) {
icculus@8935
   681
            if (!isDragAreaRunning) {
icculus@8935
   682
                isDragAreaRunning = YES;
icculus@8935
   683
                [_data->nswindow setMovableByWindowBackground:YES];
icculus@8931
   684
            }
icculus@8935
   685
            return YES;  /* dragging! */
icculus@8931
   686
        }
icculus@8931
   687
    }
icculus@8931
   688
icculus@8931
   689
    if (isDragAreaRunning) {
icculus@8931
   690
        isDragAreaRunning = NO;
icculus@8931
   691
        [_data->nswindow setMovableByWindowBackground:NO];
icculus@8931
   692
        return YES;  /* was dragging, drop event. */
icculus@8931
   693
    }
icculus@8931
   694
icculus@8935
   695
    return NO;  /* not a special area, carry on. */
icculus@8931
   696
}
icculus@8931
   697
slouken@1933
   698
- (void)mouseDown:(NSEvent *)theEvent
slouken@1933
   699
{
slouken@1959
   700
    int button;
slouken@1933
   701
icculus@8935
   702
    if ([self processHitTest:theEvent]) {
icculus@8931
   703
        return;  /* dragging, drop event. */
icculus@8931
   704
    }
icculus@8931
   705
slouken@1959
   706
    switch ([theEvent buttonNumber]) {
slouken@1959
   707
    case 0:
slouken@7915
   708
        if (([theEvent modifierFlags] & NSControlKeyMask) &&
slouken@7915
   709
		    GetHintCtrlClickEmulateRightClick()) {
slouken@7740
   710
            wasCtrlLeft = YES;
slouken@7740
   711
            button = SDL_BUTTON_RIGHT;
slouken@7740
   712
        } else {
slouken@7740
   713
            wasCtrlLeft = NO;
slouken@7740
   714
            button = SDL_BUTTON_LEFT;
slouken@7740
   715
        }
slouken@1959
   716
        break;
slouken@1959
   717
    case 1:
slouken@1959
   718
        button = SDL_BUTTON_RIGHT;
slouken@1959
   719
        break;
slouken@1959
   720
    case 2:
slouken@1959
   721
        button = SDL_BUTTON_MIDDLE;
slouken@1959
   722
        break;
slouken@1959
   723
    default:
slouken@5061
   724
        button = [theEvent buttonNumber] + 1;
slouken@1959
   725
        break;
slouken@1959
   726
    }
slouken@6950
   727
    SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
slouken@1933
   728
}
slouken@1933
   729
slouken@1933
   730
- (void)rightMouseDown:(NSEvent *)theEvent
slouken@1933
   731
{
slouken@1959
   732
    [self mouseDown:theEvent];
slouken@1933
   733
}
slouken@1933
   734
slouken@1933
   735
- (void)otherMouseDown:(NSEvent *)theEvent
slouken@1933
   736
{
slouken@1959
   737
    [self mouseDown:theEvent];
slouken@1933
   738
}
slouken@1933
   739
slouken@1933
   740
- (void)mouseUp:(NSEvent *)theEvent
slouken@1933
   741
{
slouken@1959
   742
    int button;
slouken@1933
   743
icculus@8935
   744
    if ([self processHitTest:theEvent]) {
icculus@8931
   745
        return;  /* stopped dragging, drop event. */
icculus@8931
   746
    }
icculus@8931
   747
slouken@1959
   748
    switch ([theEvent buttonNumber]) {
slouken@1959
   749
    case 0:
slouken@7740
   750
        if (wasCtrlLeft) {
slouken@7740
   751
            button = SDL_BUTTON_RIGHT;
slouken@7740
   752
            wasCtrlLeft = NO;
slouken@7740
   753
        } else {
slouken@7740
   754
            button = SDL_BUTTON_LEFT;
slouken@7740
   755
        }
slouken@1959
   756
        break;
slouken@1959
   757
    case 1:
slouken@1959
   758
        button = SDL_BUTTON_RIGHT;
slouken@1959
   759
        break;
slouken@1959
   760
    case 2:
slouken@1959
   761
        button = SDL_BUTTON_MIDDLE;
slouken@1959
   762
        break;
slouken@1959
   763
    default:
slouken@5061
   764
        button = [theEvent buttonNumber] + 1;
slouken@1959
   765
        break;
slouken@1959
   766
    }
slouken@6950
   767
    SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
slouken@1933
   768
}
slouken@1933
   769
slouken@1933
   770
- (void)rightMouseUp:(NSEvent *)theEvent
slouken@1933
   771
{
slouken@1959
   772
    [self mouseUp:theEvent];
slouken@1933
   773
}
slouken@1933
   774
slouken@1933
   775
- (void)otherMouseUp:(NSEvent *)theEvent
slouken@1933
   776
{
slouken@1959
   777
    [self mouseUp:theEvent];
slouken@1933
   778
}
slouken@1933
   779
slouken@1933
   780
- (void)mouseMoved:(NSEvent *)theEvent
slouken@1933
   781
{
slouken@5406
   782
    SDL_Mouse *mouse = SDL_GetMouse();
slouken@3685
   783
    SDL_Window *window = _data->window;
slouken@5396
   784
    NSPoint point;
slouken@5396
   785
    int x, y;
slouken@1933
   786
icculus@8935
   787
    if ([self processHitTest:theEvent]) {
icculus@8931
   788
        return;  /* dragging, drop event. */
icculus@8931
   789
    }
icculus@8931
   790
slouken@5406
   791
    if (mouse->relative_mode) {
gzjjgod@5059
   792
        return;
slouken@5371
   793
    }
gzjjgod@5059
   794
slouken@5396
   795
    point = [theEvent locationInWindow];
slouken@5396
   796
    x = (int)point.x;
slouken@5396
   797
    y = (int)(window->h - point.y);
slouken@5371
   798
icculus@8929
   799
    if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
icculus@8929
   800
        if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
slouken@6666
   801
            if (x < 0) {
slouken@6666
   802
                x = 0;
slouken@6666
   803
            } else if (x >= window->w) {
slouken@6666
   804
                x = window->w - 1;
slouken@6666
   805
            }
slouken@6666
   806
            if (y < 0) {
slouken@6666
   807
                y = 0;
slouken@6666
   808
            } else if (y >= window->h) {
slouken@6666
   809
                y = window->h - 1;
slouken@6666
   810
            }
slouken@6666
   811
jorgen@7593
   812
#if !SDL_MAC_NO_SANDBOX
jorgen@8260
   813
            CGPoint cgpoint;
jorgen@8260
   814
jorgen@7593
   815
            /* When SDL_MAC_NO_SANDBOX is set, this is handled by
jorgen@7593
   816
             * SDL_cocoamousetap.m.
jorgen@7593
   817
             */
jorgen@7593
   818
slouken@6666
   819
            cgpoint.x = window->x + x;
slouken@6666
   820
            cgpoint.y = window->y + y;
jorgen@7098
   821
jorgen@7113
   822
            /* According to the docs, this was deprecated in 10.6, but it's still
jorgen@7113
   823
             * around. The substitute requires a CGEventSource, but I'm not entirely
jorgen@7113
   824
             * sure how we'd procure the right one for this event.
jorgen@7098
   825
             */
jorgen@7113
   826
            CGSetLocalEventsSuppressionInterval(0.0);
slouken@6666
   827
            CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
jorgen@7113
   828
            CGSetLocalEventsSuppressionInterval(0.25);
jorgen@8261
   829
jorgen@8261
   830
            Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
jorgen@7593
   831
#endif
slouken@5396
   832
        }
slouken@2059
   833
    }
slouken@6950
   834
    SDL_SendMouseMotion(window, 0, 0, x, y);
slouken@1933
   835
}
slouken@1933
   836
slouken@1957
   837
- (void)mouseDragged:(NSEvent *)theEvent
slouken@1957
   838
{
slouken@1957
   839
    [self mouseMoved:theEvent];
slouken@1957
   840
}
slouken@1957
   841
slouken@1958
   842
- (void)rightMouseDragged:(NSEvent *)theEvent
slouken@1958
   843
{
slouken@1958
   844
    [self mouseMoved:theEvent];
slouken@1958
   845
}
slouken@1958
   846
slouken@1958
   847
- (void)otherMouseDragged:(NSEvent *)theEvent
slouken@1958
   848
{
slouken@1958
   849
    [self mouseMoved:theEvent];
slouken@1958
   850
}
slouken@1958
   851
slouken@1933
   852
- (void)scrollWheel:(NSEvent *)theEvent
slouken@1933
   853
{
gzjjgod@5057
   854
    Cocoa_HandleMouseWheel(_data->window, theEvent);
slouken@3688
   855
}
slouken@3688
   856
slouken@4673
   857
- (void)touchesBeganWithEvent:(NSEvent *) theEvent
slouken@4673
   858
{
slouken@8986
   859
    [self handleTouches:NSTouchPhaseBegan withEvent:theEvent];
slouken@4673
   860
}
slouken@4673
   861
slouken@4673
   862
- (void)touchesMovedWithEvent:(NSEvent *) theEvent
slouken@4673
   863
{
slouken@8986
   864
    [self handleTouches:NSTouchPhaseMoved withEvent:theEvent];
slouken@4673
   865
}
slouken@4673
   866
slouken@4673
   867
- (void)touchesEndedWithEvent:(NSEvent *) theEvent
slouken@4673
   868
{
slouken@8986
   869
    [self handleTouches:NSTouchPhaseEnded withEvent:theEvent];
slouken@4673
   870
}
slouken@4673
   871
slouken@4673
   872
- (void)touchesCancelledWithEvent:(NSEvent *) theEvent
slouken@4673
   873
{
slouken@8986
   874
    [self handleTouches:NSTouchPhaseCancelled withEvent:theEvent];
slouken@4673
   875
}
slouken@4673
   876
slouken@8986
   877
- (void)handleTouches:(NSTouchPhase) phase withEvent:(NSEvent *) theEvent
slouken@4673
   878
{
slouken@8986
   879
    NSSet *touches = [theEvent touchesMatchingPhase:phase inView:nil];
slouken@4673
   880
slouken@8986
   881
    for (NSTouch *touch in touches) {
slouken@6953
   882
        const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
slouken@4673
   883
        if (!SDL_GetTouch(touchId)) {
slouken@6951
   884
            if (SDL_AddTouch(touchId, "") < 0) {
slouken@4680
   885
                return;
slouken@4673
   886
            }
slouken@7191
   887
        }
slouken@4687
   888
slouken@6953
   889
        const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
slouken@4673
   890
        float x = [touch normalizedPosition].x;
slouken@4673
   891
        float y = [touch normalizedPosition].y;
slouken@5261
   892
        /* Make the origin the upper left instead of the lower left */
slouken@5261
   893
        y = 1.0f - y;
slouken@4687
   894
slouken@8986
   895
        switch (phase) {
slouken@8986
   896
        case NSTouchPhaseBegan:
slouken@6951
   897
            SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
slouken@4673
   898
            break;
slouken@8986
   899
        case NSTouchPhaseEnded:
slouken@8986
   900
        case NSTouchPhaseCancelled:
slouken@6951
   901
            SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
slouken@4673
   902
            break;
slouken@8986
   903
        case NSTouchPhaseMoved:
slouken@6951
   904
            SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
slouken@4673
   905
            break;
slouken@8986
   906
        default:
slouken@8986
   907
            break;
slouken@4673
   908
        }
slouken@4673
   909
    }
slouken@1933
   910
}
slouken@1933
   911
slouken@1933
   912
@end
slouken@1933
   913
slouken@5379
   914
@interface SDLView : NSView
jorgen@7158
   915
slouken@5379
   916
/* The default implementation doesn't pass rightMouseDown to responder chain */
slouken@5379
   917
- (void)rightMouseDown:(NSEvent *)theEvent;
icculus@8931
   918
- (BOOL)mouseDownCanMoveWindow;
gzjjgod@4915
   919
@end
gzjjgod@4915
   920
gzjjgod@4915
   921
@implementation SDLView
gzjjgod@4915
   922
- (void)rightMouseDown:(NSEvent *)theEvent
gzjjgod@4915
   923
{
slouken@5371
   924
    [[self nextResponder] rightMouseDown:theEvent];
gzjjgod@4915
   925
}
jorgen@7158
   926
icculus@8931
   927
- (BOOL)mouseDownCanMoveWindow
icculus@8931
   928
{
icculus@8931
   929
    /* Always say YES, but this doesn't do anything until we call
icculus@8931
   930
       -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
icculus@8931
   931
       during mouse events when we're using a drag area. */
icculus@8931
   932
    return YES;
icculus@8931
   933
}
icculus@8931
   934
jorgen@7158
   935
- (void)resetCursorRects
jorgen@7158
   936
{
jorgen@7158
   937
    [super resetCursorRects];
jorgen@7158
   938
    SDL_Mouse *mouse = SDL_GetMouse();
jorgen@7158
   939
jorgen@7270
   940
    if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
jorgen@7158
   941
        [self addCursorRect:[self bounds]
jorgen@7158
   942
                     cursor:mouse->cur_cursor->driverdata];
jorgen@7158
   943
    } else {
jorgen@7158
   944
        [self addCursorRect:[self bounds]
jorgen@7158
   945
                     cursor:[NSCursor invisibleCursor]];
jorgen@7158
   946
    }
jorgen@7158
   947
}
gzjjgod@4915
   948
@end
gzjjgod@4915
   949
slouken@1933
   950
static int
slouken@1951
   951
SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
slouken@1933
   952
{
slouken@6848
   953
    NSAutoreleasePool *pool;
slouken@1951
   954
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
slouken@1933
   955
    SDL_WindowData *data;
slouken@1933
   956
slouken@1933
   957
    /* Allocate the window data */
slouken@8926
   958
    window->driverdata = data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
slouken@1933
   959
    if (!data) {
icculus@7037
   960
        return SDL_OutOfMemory();
slouken@1933
   961
    }
slouken@3685
   962
    data->window = window;
slouken@3688
   963
    data->nswindow = nswindow;
slouken@1933
   964
    data->created = created;
slouken@1951
   965
    data->videodata = videodata;
jorgen@7595
   966
    data->nscontexts = [[NSMutableArray alloc] init];
slouken@1933
   967
slouken@6848
   968
    pool = [[NSAutoreleasePool alloc] init];
slouken@1933
   969
slouken@6848
   970
    /* Create an event listener for the window */
slouken@6848
   971
    data->listener = [[Cocoa_WindowListener alloc] init];
slouken@6848
   972
slouken@6848
   973
    /* Fill in the SDL window with the window data */
slouken@6848
   974
    {
slouken@6848
   975
        NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
slouken@8801
   976
        ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
slouken@6848
   977
        window->x = (int)rect.origin.x;
slouken@6848
   978
        window->y = (int)rect.origin.y;
slouken@6848
   979
        window->w = (int)rect.size.width;
slouken@6848
   980
        window->h = (int)rect.size.height;
slouken@6848
   981
    }
slouken@6848
   982
slouken@6848
   983
    /* Set up the listener after we create the view */
slouken@6848
   984
    [data->listener listen:data];
slouken@6848
   985
slouken@6848
   986
    if ([nswindow isVisible]) {
slouken@6848
   987
        window->flags |= SDL_WINDOW_SHOWN;
slouken@6848
   988
    } else {
slouken@6848
   989
        window->flags &= ~SDL_WINDOW_SHOWN;
slouken@6848
   990
    }
jorgen@7084
   991
slouken@6848
   992
    {
slouken@6848
   993
        unsigned int style = [nswindow styleMask];
slouken@6848
   994
slouken@6848
   995
        if (style == NSBorderlessWindowMask) {
slouken@6848
   996
            window->flags |= SDL_WINDOW_BORDERLESS;
slouken@6848
   997
        } else {
slouken@6848
   998
            window->flags &= ~SDL_WINDOW_BORDERLESS;
alexey@6832
   999
        }
slouken@6848
  1000
        if (style & NSResizableWindowMask) {
slouken@6848
  1001
            window->flags |= SDL_WINDOW_RESIZABLE;
slouken@6848
  1002
        } else {
slouken@6848
  1003
            window->flags &= ~SDL_WINDOW_RESIZABLE;
slouken@6848
  1004
        }
slouken@6848
  1005
    }
jorgen@7084
  1006
slouken@6848
  1007
    /* isZoomed always returns true if the window is not resizable */
slouken@6848
  1008
    if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
slouken@6848
  1009
        window->flags |= SDL_WINDOW_MAXIMIZED;
slouken@6848
  1010
    } else {
slouken@6848
  1011
        window->flags &= ~SDL_WINDOW_MAXIMIZED;
slouken@6848
  1012
    }
jorgen@7084
  1013
slouken@6848
  1014
    if ([nswindow isMiniaturized]) {
slouken@6848
  1015
        window->flags |= SDL_WINDOW_MINIMIZED;
slouken@6848
  1016
    } else {
slouken@6848
  1017
        window->flags &= ~SDL_WINDOW_MINIMIZED;
slouken@6848
  1018
    }
jorgen@7084
  1019
slouken@6848
  1020
    if ([nswindow isKeyWindow]) {
slouken@6848
  1021
        window->flags |= SDL_WINDOW_INPUT_FOCUS;
slouken@6848
  1022
        SDL_SetKeyboardFocus(data->window);
slouken@6848
  1023
    }
slouken@1933
  1024
jorgen@7085
  1025
    /* Prevents the window's "window device" from being destroyed when it is
jorgen@7085
  1026
     * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
jorgen@7085
  1027
     */
jorgen@7085
  1028
    [nswindow setOneShot:NO];
jorgen@7085
  1029
slouken@6848
  1030
    /* All done! */
slouken@6848
  1031
    [pool release];
slouken@6848
  1032
    window->driverdata = data;
slouken@6848
  1033
    return 0;
slouken@1933
  1034
}
slouken@1933
  1035
slouken@1933
  1036
int
slouken@1933
  1037
Cocoa_CreateWindow(_THIS, SDL_Window * window)
slouken@1933
  1038
{
icculus@8295
  1039
    SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
slouken@6848
  1040
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1041
    NSWindow *nswindow;
slouken@6848
  1042
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
slouken@6848
  1043
    NSRect rect;
slouken@6848
  1044
    SDL_Rect bounds;
slouken@6848
  1045
    unsigned int style;
slouken@8986
  1046
    NSArray *screens = [NSScreen screens];
slouken@1933
  1047
slouken@6848
  1048
    Cocoa_GetDisplayBounds(_this, display, &bounds);
slouken@6848
  1049
    rect.origin.x = window->x;
slouken@6848
  1050
    rect.origin.y = window->y;
slouken@6848
  1051
    rect.size.width = window->w;
slouken@6848
  1052
    rect.size.height = window->h;
slouken@8986
  1053
    ConvertNSRect([screens objectAtIndex:0], (window->flags & FULLSCREEN_MASK), &rect);
slouken@1933
  1054
slouken@6848
  1055
    style = GetWindowStyle(window);
slouken@1933
  1056
slouken@6848
  1057
    /* Figure out which screen to place this window */
slouken@6848
  1058
    NSScreen *screen = nil;
slouken@8986
  1059
    for (NSScreen *candidate in screens) {
slouken@6848
  1060
        NSRect screenRect = [candidate frame];
slouken@6848
  1061
        if (rect.origin.x >= screenRect.origin.x &&
slouken@6848
  1062
            rect.origin.x < screenRect.origin.x + screenRect.size.width &&
slouken@6848
  1063
            rect.origin.y >= screenRect.origin.y &&
slouken@6848
  1064
            rect.origin.y < screenRect.origin.y + screenRect.size.height) {
slouken@6848
  1065
            screen = candidate;
slouken@6848
  1066
            rect.origin.x -= screenRect.origin.x;
slouken@6848
  1067
            rect.origin.y -= screenRect.origin.y;
slouken@3506
  1068
        }
slouken@6848
  1069
    }
slouken@7946
  1070
slouken@7946
  1071
    @try {
slouken@7946
  1072
        nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
slouken@7946
  1073
    }
slouken@7946
  1074
    @catch (NSException *e) {
slouken@7946
  1075
        SDL_SetError("%s", [[e reason] UTF8String]);
slouken@7948
  1076
        [pool release];
slouken@7946
  1077
        return -1;
slouken@7946
  1078
    }
icculus@7205
  1079
    [nswindow setBackgroundColor:[NSColor blackColor]];
icculus@8284
  1080
icculus@8295
  1081
    if (videodata->allow_spaces) {
slouken@8986
  1082
        SDL_assert(floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6);
icculus@8295
  1083
        SDL_assert([nswindow respondsToSelector:@selector(toggleFullScreen:)]);
icculus@8284
  1084
        /* we put FULLSCREEN_DESKTOP windows in their own Space, without a toggle button or menubar, later */
icculus@8284
  1085
        if (window->flags & SDL_WINDOW_RESIZABLE) {
icculus@8284
  1086
            /* resizable windows are Spaces-friendly: they get the "go fullscreen" toggle button on their titlebar. */
slouken@7968
  1087
            [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
slouken@7968
  1088
        }
slouken@7955
  1089
    }
alexey@6832
  1090
slouken@7191
  1091
    /* Create a default view for this window */
slouken@6848
  1092
    rect = [nswindow contentRectForFrameRect:[nswindow frame]];
slouken@6848
  1093
    NSView *contentView = [[SDLView alloc] initWithFrame:rect];
urkle@7746
  1094
slouken@7955
  1095
    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
urkle@7746
  1096
        if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
urkle@7746
  1097
            [contentView setWantsBestResolutionOpenGLSurface:YES];
urkle@7746
  1098
        }
urkle@7746
  1099
    }
urkle@7746
  1100
slouken@6848
  1101
    [nswindow setContentView: contentView];
slouken@6848
  1102
    [contentView release];
alexey@6832
  1103
slouken@6848
  1104
    [pool release];
slouken@6848
  1105
slouken@6848
  1106
    if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
slouken@6848
  1107
        [nswindow release];
slouken@6848
  1108
        return -1;
slouken@3506
  1109
    }
slouken@6848
  1110
    return 0;
slouken@1933
  1111
}
slouken@1933
  1112
slouken@1933
  1113
int
slouken@1933
  1114
Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
slouken@1933
  1115
{
slouken@6848
  1116
    NSAutoreleasePool *pool;
slouken@1933
  1117
    NSWindow *nswindow = (NSWindow *) data;
slouken@1933
  1118
    NSString *title;
slouken@1933
  1119
slouken@6848
  1120
    pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1121
slouken@6848
  1122
    /* Query the title from the existing window */
slouken@6848
  1123
    title = [nswindow title];
slouken@6848
  1124
    if (title) {
slouken@6848
  1125
        window->title = SDL_strdup([title UTF8String]);
slouken@1933
  1126
    }
slouken@1933
  1127
slouken@6848
  1128
    [pool release];
slouken@6848
  1129
slouken@1951
  1130
    return SetupWindowData(_this, window, nswindow, SDL_FALSE);
slouken@1933
  1131
}
slouken@1933
  1132
slouken@1933
  1133
void
slouken@1933
  1134
Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
slouken@1933
  1135
{
slouken@6848
  1136
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1137
    NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
slouken@6848
  1138
    NSString *string;
slouken@1933
  1139
slouken@6848
  1140
    if(window->title) {
slouken@6848
  1141
        string = [[NSString alloc] initWithUTF8String:window->title];
slouken@6848
  1142
    } else {
slouken@6848
  1143
        string = [[NSString alloc] init];
slouken@1956
  1144
    }
slouken@6848
  1145
    [nswindow setTitle:string];
slouken@6848
  1146
    [string release];
slouken@6848
  1147
slouken@6848
  1148
    [pool release];
slouken@1933
  1149
}
slouken@1933
  1150
slouken@1933
  1151
void
slouken@5375
  1152
Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
slouken@5375
  1153
{
slouken@6848
  1154
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1155
    NSImage *nsimage = Cocoa_CreateImage(icon);
slouken@5375
  1156
slouken@6848
  1157
    if (nsimage) {
slouken@6848
  1158
        [NSApp setApplicationIconImage:nsimage];
slouken@5375
  1159
    }
slouken@6848
  1160
slouken@6848
  1161
    [pool release];
slouken@5375
  1162
}
slouken@5375
  1163
slouken@5375
  1164
void
slouken@1933
  1165
Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
slouken@1933
  1166
{
slouken@6848
  1167
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
jorgen@7594
  1168
    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
jorgen@7594
  1169
    NSWindow *nswindow = windata->nswindow;
slouken@6848
  1170
    NSRect rect;
slouken@6848
  1171
    Uint32 moveHack;
slouken@1933
  1172
slouken@6848
  1173
    rect.origin.x = window->x;
slouken@6848
  1174
    rect.origin.y = window->y;
slouken@6848
  1175
    rect.size.width = window->w;
slouken@6848
  1176
    rect.size.height = window->h;
slouken@8801
  1177
    ConvertNSRect([nswindow screen], (window->flags & FULLSCREEN_MASK), &rect);
slouken@5478
  1178
slouken@6848
  1179
    moveHack = s_moveHack;
slouken@6848
  1180
    s_moveHack = 0;
slouken@6848
  1181
    [nswindow setFrameOrigin:rect.origin];
slouken@6848
  1182
    s_moveHack = moveHack;
slouken@5478
  1183
jorgen@7595
  1184
    ScheduleContextUpdates(windata);
slouken@6848
  1185
slouken@6848
  1186
    [pool release];
slouken@1933
  1187
}
slouken@1933
  1188
slouken@1933
  1189
void
slouken@1933
  1190
Cocoa_SetWindowSize(_THIS, SDL_Window * window)
slouken@1933
  1191
{
slouken@6848
  1192
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1193
    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
slouken@6848
  1194
    NSWindow *nswindow = windata->nswindow;
slouken@6848
  1195
    NSSize size;
slouken@1933
  1196
slouken@6848
  1197
    size.width = window->w;
slouken@6848
  1198
    size.height = window->h;
slouken@6848
  1199
    [nswindow setContentSize:size];
icculus@5564
  1200
jorgen@7595
  1201
    ScheduleContextUpdates(windata);
slouken@6848
  1202
slouken@6848
  1203
    [pool release];
slouken@1933
  1204
}
slouken@1933
  1205
slouken@1933
  1206
void
stopiccot@6681
  1207
Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
stopiccot@6681
  1208
{
slouken@6848
  1209
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1210
    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
slouken@7191
  1211
slouken@6848
  1212
    NSSize minSize;
slouken@6848
  1213
    minSize.width = window->min_w;
slouken@6848
  1214
    minSize.height = window->min_h;
slouken@7191
  1215
slouken@6848
  1216
    [windata->nswindow setContentMinSize:minSize];
slouken@7191
  1217
slouken@6848
  1218
    [pool release];
slouken@6788
  1219
}
slouken@6788
  1220
slouken@6788
  1221
void
slouken@6788
  1222
Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
slouken@6788
  1223
{
slouken@6848
  1224
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1225
    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
slouken@7191
  1226
slouken@6848
  1227
    NSSize maxSize;
slouken@6848
  1228
    maxSize.width = window->max_w;
slouken@6848
  1229
    maxSize.height = window->max_h;
slouken@7191
  1230
slouken@6848
  1231
    [windata->nswindow setContentMaxSize:maxSize];
slouken@7191
  1232
slouken@6848
  1233
    [pool release];
stopiccot@6681
  1234
}
stopiccot@6681
  1235
stopiccot@6681
  1236
void
slouken@1933
  1237
Cocoa_ShowWindow(_THIS, SDL_Window * window)
slouken@1933
  1238
{
slouken@6848
  1239
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
jorgen@7087
  1240
    SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
jorgen@7087
  1241
    NSWindow *nswindow = windowData->nswindow;
slouken@1933
  1242
slouken@6848
  1243
    if (![nswindow isMiniaturized]) {
jorgen@7087
  1244
        [windowData->listener pauseVisibleObservation];
slouken@6848
  1245
        [nswindow makeKeyAndOrderFront:nil];
jorgen@7087
  1246
        [windowData->listener resumeVisibleObservation];
slouken@1956
  1247
    }
slouken@6848
  1248
    [pool release];
slouken@1933
  1249
}
slouken@1933
  1250
slouken@1933
  1251
void
slouken@1933
  1252
Cocoa_HideWindow(_THIS, SDL_Window * window)
slouken@1933
  1253
{
slouken@6848
  1254
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1255
    NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
slouken@6848
  1256
slouken@6848
  1257
    [nswindow orderOut:nil];
slouken@6848
  1258
    [pool release];
slouken@1933
  1259
}
slouken@1933
  1260
slouken@1933
  1261
void
slouken@1933
  1262
Cocoa_RaiseWindow(_THIS, SDL_Window * window)
slouken@1933
  1263
{
slouken@6848
  1264
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
jorgen@7087
  1265
    SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
jorgen@7087
  1266
    NSWindow *nswindow = windowData->nswindow;
slouken@6848
  1267
slouken@7771
  1268
    /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
slouken@7771
  1269
       a minimized or hidden window, so check for that before showing it.
slouken@7771
  1270
     */
jorgen@7087
  1271
    [windowData->listener pauseVisibleObservation];
jorgen@7469
  1272
    if (![nswindow isMiniaturized] && [nswindow isVisible]) {
alfred@9041
  1273
        [NSApp activateIgnoringOtherApps:YES];
jorgen@7469
  1274
        [nswindow makeKeyAndOrderFront:nil];
jorgen@7469
  1275
    }
jorgen@7087
  1276
    [windowData->listener resumeVisibleObservation];
jorgen@7087
  1277
slouken@6848
  1278
    [pool release];
slouken@1933
  1279
}
slouken@1933
  1280
slouken@1933
  1281
void
slouken@1933
  1282
Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
slouken@1933
  1283
{
slouken@6848
  1284
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
jorgen@7594
  1285
    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
jorgen@7594
  1286
    NSWindow *nswindow = windata->nswindow;
slouken@1933
  1287
slouken@6848
  1288
    [nswindow zoom:nil];
slouken@6848
  1289
jorgen@7595
  1290
    ScheduleContextUpdates(windata);
slouken@6848
  1291
slouken@6848
  1292
    [pool release];
slouken@1933
  1293
}
slouken@1933
  1294
slouken@1933
  1295
void
slouken@1933
  1296
Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
slouken@1933
  1297
{
slouken@6848
  1298
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@7963
  1299
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@7963
  1300
    NSWindow *nswindow = data->nswindow;
slouken@6848
  1301
slouken@7968
  1302
    if ([data->listener isInFullscreenSpaceTransition]) {
slouken@7963
  1303
        [data->listener addPendingWindowOperation:PENDING_OPERATION_MINIMIZE];
slouken@7963
  1304
    } else {
slouken@7963
  1305
        [nswindow miniaturize:nil];
slouken@7963
  1306
    }
slouken@6848
  1307
    [pool release];
slouken@1933
  1308
}
slouken@1933
  1309
slouken@1933
  1310
void
slouken@1933
  1311
Cocoa_RestoreWindow(_THIS, SDL_Window * window)
slouken@1933
  1312
{
slouken@6848
  1313
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1314
    NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
slouken@1933
  1315
slouken@6848
  1316
    if ([nswindow isMiniaturized]) {
slouken@6848
  1317
        [nswindow deminiaturize:nil];
slouken@6848
  1318
    } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
slouken@6848
  1319
        [nswindow zoom:nil];
slouken@1956
  1320
    }
slouken@6848
  1321
    [pool release];
slouken@1933
  1322
}
slouken@1933
  1323
slouken@5400
  1324
static NSWindow *
slouken@5400
  1325
Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
slouken@5400
  1326
{
slouken@5400
  1327
    if (!data->created) {
slouken@5400
  1328
        /* Don't mess with other people's windows... */
slouken@5400
  1329
        return nswindow;
slouken@5400
  1330
    }
slouken@5400
  1331
slouken@5400
  1332
    [data->listener close];
jorgen@7085
  1333
    data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
slouken@5400
  1334
    [data->nswindow setContentView:[nswindow contentView]];
jorgen@7085
  1335
    /* See comment in SetupWindowData. */
jorgen@7085
  1336
    [data->nswindow setOneShot:NO];
slouken@5400
  1337
    [data->listener listen:data];
slouken@5400
  1338
slouken@5400
  1339
    [nswindow close];
slouken@5400
  1340
slouken@5400
  1341
    return data->nswindow;
slouken@5400
  1342
}
slouken@5400
  1343
slouken@1933
  1344
void
icculus@6422
  1345
Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
icculus@6422
  1346
{
slouken@6848
  1347
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@7990
  1348
    if (SetWindowStyle(window, GetWindowStyle(window))) {
slouken@6848
  1349
        if (bordered) {
slouken@7191
  1350
            Cocoa_SetWindowTitle(_this, window);  /* this got blanked out. */
icculus@6426
  1351
        }
icculus@6422
  1352
    }
slouken@6848
  1353
    [pool release];
icculus@6422
  1354
}
icculus@6422
  1355
slouken@7952
  1356
slouken@7968
  1357
void
slouken@7968
  1358
Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
slouken@7952
  1359
{
slouken@7968
  1360
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1361
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@6848
  1362
    NSWindow *nswindow = data->nswindow;
slouken@6848
  1363
    NSRect rect;
slouken@5249
  1364
slouken@6848
  1365
    /* The view responder chain gets messed with during setStyleMask */
slouken@6848
  1366
    if ([[nswindow contentView] nextResponder] == data->listener) {
slouken@6848
  1367
        [[nswindow contentView] setNextResponder:nil];
slouken@6848
  1368
    }
slouken@6848
  1369
slouken@6848
  1370
    if (fullscreen) {
slouken@6848
  1371
        SDL_Rect bounds;
slouken@6848
  1372
slouken@6848
  1373
        Cocoa_GetDisplayBounds(_this, display, &bounds);
slouken@6848
  1374
        rect.origin.x = bounds.x;
slouken@6848
  1375
        rect.origin.y = bounds.y;
slouken@6848
  1376
        rect.size.width = bounds.w;
slouken@6848
  1377
        rect.size.height = bounds.h;
slouken@8801
  1378
        ConvertNSRect([nswindow screen], fullscreen, &rect);
slouken@6848
  1379
slouken@6848
  1380
        /* Hack to fix origin on Mac OS X 10.4 */
slouken@6848
  1381
        NSRect screenRect = [[nswindow screen] frame];
slouken@6848
  1382
        if (screenRect.size.height >= 1.0f) {
slouken@6848
  1383
            rect.origin.y += (screenRect.size.height - rect.size.height);
slouken@5401
  1384
        }
slouken@5401
  1385
slouken@6848
  1386
        if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
slouken@6848
  1387
            [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
slouken@6848
  1388
        } else {
slouken@6848
  1389
            nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
slouken@6848
  1390
        }
slouken@6848
  1391
    } else {
slouken@6848
  1392
        rect.origin.x = window->windowed.x;
slouken@6848
  1393
        rect.origin.y = window->windowed.y;
slouken@6848
  1394
        rect.size.width = window->windowed.w;
slouken@6848
  1395
        rect.size.height = window->windowed.h;
slouken@8801
  1396
        ConvertNSRect([nswindow screen], fullscreen, &rect);
alexey@6832
  1397
slouken@6848
  1398
        if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
slouken@6848
  1399
            [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
slouken@5400
  1400
        } else {
slouken@6848
  1401
            nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
slouken@5361
  1402
        }
slouken@5398
  1403
    }
slouken@6848
  1404
slouken@6848
  1405
    /* The view responder chain gets messed with during setStyleMask */
slouken@6848
  1406
    if ([[nswindow contentView] nextResponder] != data->listener) {
slouken@6848
  1407
        [[nswindow contentView] setNextResponder:data->listener];
slouken@6848
  1408
    }
slouken@6848
  1409
slouken@6848
  1410
    s_moveHack = 0;
slouken@7873
  1411
    [nswindow setContentSize:rect.size];
slouken@6848
  1412
    [nswindow setFrameOrigin:rect.origin];
slouken@6848
  1413
    s_moveHack = SDL_GetTicks();
slouken@6848
  1414
slouken@6848
  1415
    /* When the window style changes the title is cleared */
slouken@6848
  1416
    if (!fullscreen) {
slouken@6848
  1417
        Cocoa_SetWindowTitle(_this, window);
slouken@6848
  1418
    }
slouken@6848
  1419
slouken@6848
  1420
    if (SDL_ShouldAllowTopmost() && fullscreen) {
slouken@6848
  1421
        /* OpenGL is rendering to the window, so make it visible! */
slouken@6848
  1422
        [nswindow setLevel:CGShieldingWindowLevel()];
slouken@6848
  1423
    } else {
slouken@6848
  1424
        [nswindow setLevel:kCGNormalWindowLevel];
slouken@6848
  1425
    }
jorgen@7087
  1426
jorgen@7636
  1427
    if ([nswindow isVisible] || fullscreen) {
jorgen@7636
  1428
        [data->listener pauseVisibleObservation];
jorgen@7636
  1429
        [nswindow makeKeyAndOrderFront:nil];
jorgen@7636
  1430
        [data->listener resumeVisibleObservation];
jorgen@7636
  1431
    }
slouken@6848
  1432
jorgen@7595
  1433
    ScheduleContextUpdates(data);
slouken@6848
  1434
slouken@6848
  1435
    [pool release];
slouken@5249
  1436
}
slouken@5249
  1437
slouken@5466
  1438
int
slouken@5466
  1439
Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
slouken@5466
  1440
{
slouken@5466
  1441
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
slouken@5466
  1442
    CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
slouken@5466
  1443
    const uint32_t tableSize = 256;
slouken@5466
  1444
    CGGammaValue redTable[tableSize];
slouken@5466
  1445
    CGGammaValue greenTable[tableSize];
slouken@5466
  1446
    CGGammaValue blueTable[tableSize];
slouken@5466
  1447
    uint32_t i;
slouken@5466
  1448
    float inv65535 = 1.0f / 65535.0f;
slouken@5466
  1449
slouken@5466
  1450
    /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
slouken@5466
  1451
    for (i = 0; i < 256; i++) {
slouken@5466
  1452
        redTable[i] = ramp[0*256+i] * inv65535;
slouken@5466
  1453
        greenTable[i] = ramp[1*256+i] * inv65535;
slouken@5466
  1454
        blueTable[i] = ramp[2*256+i] * inv65535;
slouken@5466
  1455
    }
slouken@5466
  1456
slouken@5466
  1457
    if (CGSetDisplayTransferByTable(display_id, tableSize,
slouken@5466
  1458
                                    redTable, greenTable, blueTable) != CGDisplayNoErr) {
icculus@7037
  1459
        return SDL_SetError("CGSetDisplayTransferByTable()");
slouken@5466
  1460
    }
slouken@5466
  1461
    return 0;
slouken@5466
  1462
}
slouken@5466
  1463
slouken@5466
  1464
int
slouken@5466
  1465
Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
slouken@5466
  1466
{
slouken@5466
  1467
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
slouken@5466
  1468
    CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
slouken@5466
  1469
    const uint32_t tableSize = 256;
slouken@5466
  1470
    CGGammaValue redTable[tableSize];
slouken@5466
  1471
    CGGammaValue greenTable[tableSize];
slouken@5466
  1472
    CGGammaValue blueTable[tableSize];
slouken@5466
  1473
    uint32_t i, tableCopied;
slouken@5466
  1474
slouken@5466
  1475
    if (CGGetDisplayTransferByTable(display_id, tableSize,
slouken@5466
  1476
                                    redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
icculus@7037
  1477
        return SDL_SetError("CGGetDisplayTransferByTable()");
slouken@5466
  1478
    }
slouken@5466
  1479
slouken@5466
  1480
    for (i = 0; i < tableCopied; i++) {
slouken@5466
  1481
        ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
slouken@5466
  1482
        ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
slouken@5466
  1483
        ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
slouken@5466
  1484
    }
slouken@5466
  1485
    return 0;
slouken@5466
  1486
}
slouken@5466
  1487
slouken@5249
  1488
void
slouken@6662
  1489
Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
slouken@1933
  1490
{
slouken@5371
  1491
    /* Move the cursor to the nearest point in the window */
jorgen@8260
  1492
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
jorgen@8260
  1493
    if (grabbed && data && ![data->listener isMoving]) {
slouken@5371
  1494
        int x, y;
slouken@5371
  1495
        CGPoint cgpoint;
slouken@5371
  1496
slouken@5371
  1497
        SDL_GetMouseState(&x, &y);
slouken@5371
  1498
        cgpoint.x = window->x + x;
slouken@5371
  1499
        cgpoint.y = window->y + y;
jorgen@8261
  1500
jorgen@8261
  1501
        Cocoa_HandleMouseWarp(cgpoint.x, cgpoint.y);
jorgen@8261
  1502
jorgen@8261
  1503
        DLog("Returning cursor to (%g, %g)", cgpoint.x, cgpoint.y);
slouken@5371
  1504
        CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
slouken@5371
  1505
    }
slouken@7191
  1506
icculus@8652
  1507
    if ( data && (window->flags & SDL_WINDOW_FULLSCREEN) ) {
slouken@7191
  1508
        if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
slouken@7191
  1509
            /* OpenGL is rendering to the window, so make it visible! */
slouken@7191
  1510
            [data->nswindow setLevel:CGShieldingWindowLevel()];
slouken@7191
  1511
        } else {
slouken@7191
  1512
            [data->nswindow setLevel:kCGNormalWindowLevel];
slouken@7191
  1513
        }
slouken@7191
  1514
    }
slouken@1933
  1515
}
slouken@1933
  1516
slouken@1933
  1517
void
slouken@1933
  1518
Cocoa_DestroyWindow(_THIS, SDL_Window * window)
slouken@1933
  1519
{
slouken@6848
  1520
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@6848
  1521
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@1933
  1522
slouken@6848
  1523
    if (data) {
slouken@6848
  1524
        [data->listener close];
slouken@6848
  1525
        [data->listener release];
slouken@6848
  1526
        if (data->created) {
slouken@6848
  1527
            [data->nswindow close];
slouken@1933
  1528
        }
jorgen@7595
  1529
jorgen@7595
  1530
        NSArray *contexts = [[data->nscontexts copy] autorelease];
jorgen@7595
  1531
        for (SDLOpenGLContext *context in contexts) {
jorgen@7595
  1532
            /* Calling setWindow:NULL causes the context to remove itself from the context list. */            
jorgen@7595
  1533
            [context setWindow:NULL];
jorgen@7595
  1534
        }
jorgen@7595
  1535
        [data->nscontexts release];
jorgen@7595
  1536
slouken@6848
  1537
        SDL_free(data);
slouken@1933
  1538
    }
slouken@8978
  1539
    window->driverdata = NULL;
slouken@8978
  1540
slouken@6848
  1541
    [pool release];
slouken@1933
  1542
}
slouken@1933
  1543
slouken@1933
  1544
SDL_bool
slouken@1933
  1545
Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
slouken@1933
  1546
{
slouken@4900
  1547
    NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
slouken@1933
  1548
slouken@1933
  1549
    if (info->version.major <= SDL_MAJOR_VERSION) {
slouken@4900
  1550
        info->subsystem = SDL_SYSWM_COCOA;
slouken@5056
  1551
        info->info.cocoa.window = nswindow;
slouken@1933
  1552
        return SDL_TRUE;
slouken@1933
  1553
    } else {
slouken@1933
  1554
        SDL_SetError("Application not compiled with SDL %d.%d\n",
slouken@1933
  1555
                     SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
slouken@1933
  1556
        return SDL_FALSE;
slouken@1933
  1557
    }
slouken@1933
  1558
}
slouken@1933
  1559
slouken@7968
  1560
SDL_bool
slouken@7969
  1561
Cocoa_IsWindowInFullscreenSpace(SDL_Window * window)
slouken@7969
  1562
{
slouken@7969
  1563
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@7969
  1564
slouken@7969
  1565
    if ([data->listener isInFullscreenSpace]) {
slouken@7969
  1566
        return SDL_TRUE;
slouken@7969
  1567
    } else {
slouken@7969
  1568
        return SDL_FALSE;
slouken@7969
  1569
    }
slouken@7969
  1570
}
slouken@7969
  1571
slouken@7969
  1572
SDL_bool
slouken@7968
  1573
Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
slouken@7968
  1574
{
slouken@7968
  1575
    SDL_bool succeeded = SDL_FALSE;
slouken@7968
  1576
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
slouken@7968
  1577
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
slouken@7968
  1578
slouken@7968
  1579
    if ([data->listener setFullscreenSpace:(state ? YES : NO)]) {
slouken@7968
  1580
        succeeded = SDL_TRUE;
slouken@8810
  1581
slouken@8810
  1582
        /* Wait for the transition to complete, so application changes
slouken@8810
  1583
           take effect properly (e.g. setting the window size, etc.)
slouken@8810
  1584
         */
slouken@8810
  1585
        const int limit = 10000;
slouken@8810
  1586
        int count = 0;
slouken@8810
  1587
        while ([data->listener isInFullscreenSpaceTransition]) {
slouken@8810
  1588
            if ( ++count == limit ) {
slouken@8810
  1589
                /* Uh oh, transition isn't completing. Should we assert? */
slouken@8810
  1590
                break;
slouken@8810
  1591
            }
slouken@8810
  1592
            SDL_Delay(1);
slouken@8810
  1593
            SDL_PumpEvents();
slouken@8810
  1594
        }
slouken@7968
  1595
    }
slouken@7968
  1596
slouken@7968
  1597
    [pool release];
slouken@7968
  1598
slouken@7968
  1599
    return succeeded;
slouken@7968
  1600
}
slouken@7968
  1601
icculus@8931
  1602
int
icculus@8935
  1603
Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
icculus@8931
  1604
{
icculus@8931
  1605
    return 0;  /* just succeed, the real work is done elsewhere. */
icculus@8931
  1606
}
icculus@8931
  1607
slouken@6044
  1608
#endif /* SDL_VIDEO_DRIVER_COCOA */
slouken@6044
  1609
slouken@1933
  1610
/* vi: set ts=4 sw=4 expandtab: */