src/video/cocoa/SDL_cocoawindow.m
author Sam Lantinga <slouken@libsdl.org>
Thu, 28 May 2015 12:55:01 -0700
changeset 9679 7fc4a8be47a8
parent 9643 413b222c4b64
child 9685 525f13ccf27f
permissions -rw-r--r--
Fixed bug 2054 - SDL_GetError: "Unknown touch device"

Volumetric

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