cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_cocoawindow.m (49483B)


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