src/video/cocoa/SDL_cocoawindow.m
author Jørgen P. Tjernø <jorgen@valvesoftware.com>
Tue, 23 Apr 2013 18:47:38 -0700
changeset 7098 f4b2c6fb0258
parent 7087 5639ac726076
child 7113 7b4b596b3cfb
permissions -rw-r--r--
Mac: Make mouse movement smooth at edge of window when grabbed.

There's a limit of one update every 250ms when warping the mouse, and we
can work around that by disassociating the cursor & the mouse before
issuing our warp, then re-associating them.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_COCOA
    24 
    25 #include "SDL_syswm.h"
    26 #include "SDL_timer.h"  /* For SDL_GetTicks() */
    27 #include "../SDL_sysvideo.h"
    28 #include "../../events/SDL_keyboard_c.h"
    29 #include "../../events/SDL_mouse_c.h"
    30 #include "../../events/SDL_touch_c.h"
    31 #include "../../events/SDL_windowevents_c.h"
    32 #include "SDL_cocoavideo.h"
    33 #include "SDL_cocoashape.h"
    34 #include "SDL_cocoamouse.h"
    35 
    36 
    37 static Uint32 s_moveHack;
    38 
    39 static __inline__ void ConvertNSRect(NSRect *r)
    40 {
    41     r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
    42 }
    43 
    44 @implementation Cocoa_WindowListener
    45 
    46 - (void)listen:(SDL_WindowData *)data
    47 {
    48     NSNotificationCenter *center;
    49     NSWindow *window = data->nswindow;
    50     NSView *view = [window contentView];
    51 
    52     _data = data;
    53     observingVisible = YES;
    54     wasVisible = [window isVisible];
    55 
    56     center = [NSNotificationCenter defaultCenter];
    57 
    58     if ([window delegate] != nil) {
    59         [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
    60         [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
    61         [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
    62         [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
    63         [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
    64         [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
    65         [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
    66     } else {
    67         [window setDelegate:self];
    68     }
    69 
    70     // Haven't found a delegate / notification that triggers when the window is
    71     // ordered out (is not visible any more). You can be ordered out without
    72     // minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
    73     [window addObserver:self
    74              forKeyPath:@"visible"
    75                 options:NSKeyValueObservingOptionNew
    76                 context:NULL];
    77 
    78     [window setNextResponder:self];
    79     [window setAcceptsMouseMovedEvents:YES];
    80 
    81     [view setNextResponder:self];
    82 
    83 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
    84     if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
    85         [view setAcceptsTouchEvents:YES];
    86     }
    87 #endif
    88 }
    89 
    90 - (void)observeValueForKeyPath:(NSString *)keyPath
    91                       ofObject:(id)object
    92                         change:(NSDictionary *)change
    93                        context:(void *)context
    94 {
    95     if (!observingVisible) {
    96         return;
    97     }
    98 
    99     if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
   100         int newVisibility = [[change objectForKey:@"new"] intValue];
   101         if (newVisibility) {
   102             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   103         } else {
   104             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   105         }
   106     }
   107 }
   108 
   109 -(void) pauseVisibleObservation
   110 {
   111     observingVisible = NO;
   112     wasVisible = [_data->nswindow isVisible];
   113 }
   114 
   115 -(void) resumeVisibleObservation
   116 {
   117     BOOL isVisible = [_data->nswindow isVisible];
   118     observingVisible = YES;
   119     if (wasVisible != isVisible) {
   120         if (isVisible) {
   121             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
   122         } else {
   123             SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
   124         }
   125 
   126         wasVisible = isVisible;
   127     }
   128 }
   129 
   130 - (void)close
   131 {
   132     NSNotificationCenter *center;
   133     NSWindow *window = _data->nswindow;
   134     NSView *view = [window contentView];
   135 
   136     center = [NSNotificationCenter defaultCenter];
   137 
   138     if ([window delegate] != self) {
   139         [center removeObserver:self name:NSWindowDidExposeNotification object:window];
   140         [center removeObserver:self name:NSWindowDidMoveNotification object:window];
   141         [center removeObserver:self name:NSWindowDidResizeNotification object:window];
   142         [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
   143         [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
   144         [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
   145         [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
   146     } else {
   147         [window setDelegate:nil];
   148     }
   149 
   150     [window removeObserver:self
   151                 forKeyPath:@"visible"];
   152 
   153     if ([window nextResponder] == self) {
   154         [window setNextResponder:nil];
   155     }
   156     if ([view nextResponder] == self) {
   157         [view setNextResponder:nil];
   158     }
   159 }
   160 
   161 - (BOOL)windowShouldClose:(id)sender
   162 {
   163     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
   164     return NO;
   165 }
   166 
   167 - (void)windowDidExpose:(NSNotification *)aNotification
   168 {
   169     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
   170 }
   171 
   172 - (void)windowDidMove:(NSNotification *)aNotification
   173 {
   174     int x, y;
   175     SDL_VideoDevice *device = SDL_GetVideoDevice();
   176     SDL_Window *window = _data->window;
   177     NSWindow *nswindow = _data->nswindow;
   178     NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   179     ConvertNSRect(&rect);
   180 
   181     if (s_moveHack) {
   182         SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
   183 
   184         s_moveHack = 0;
   185 
   186         if (blockMove) {
   187             /* Cocoa is adjusting the window in response to a mode change */
   188             rect.origin.x = window->x;
   189             rect.origin.y = window->y;
   190             ConvertNSRect(&rect);
   191             [nswindow setFrameOrigin:rect.origin];
   192             return;
   193         }
   194     }
   195 
   196     x = (int)rect.origin.x;
   197     y = (int)rect.origin.y;
   198 
   199     if (window == device->current_glwin) {
   200         [((NSOpenGLContext *) device->current_glctx) update];
   201     }
   202 
   203     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
   204 }
   205 
   206 - (void)windowDidResize:(NSNotification *)aNotification
   207 {
   208     SDL_VideoDevice *device = SDL_GetVideoDevice();
   209     int x, y, w, h;
   210     NSRect rect = [_data->nswindow contentRectForFrameRect:[_data->nswindow frame]];
   211     ConvertNSRect(&rect);
   212     x = (int)rect.origin.x;
   213     y = (int)rect.origin.y;
   214     w = (int)rect.size.width;
   215     h = (int)rect.size.height;
   216     if (SDL_IsShapedWindow(_data->window))
   217         Cocoa_ResizeWindowShape(_data->window);
   218 
   219     if (_data->window == device->current_glwin) {
   220         [((NSOpenGLContext *) device->current_glctx) update];
   221     }
   222 
   223     /* The window can move during a resize event, such as when maximizing
   224        or resizing from a corner */
   225     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MOVED, x, y);
   226     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
   227 }
   228 
   229 - (void)windowDidMiniaturize:(NSNotification *)aNotification
   230 {
   231     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   232 }
   233 
   234 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
   235 {
   236     SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   237 }
   238 
   239 - (void)windowDidBecomeKey:(NSNotification *)aNotification
   240 {
   241     SDL_Window *window = _data->window;
   242 
   243     /* We're going to get keyboard events, since we're key. */
   244     SDL_SetKeyboardFocus(window);
   245 
   246     /* If we just gained focus we need the updated mouse position */
   247     {
   248         NSPoint point;
   249         int x, y;
   250 
   251         point = [_data->nswindow mouseLocationOutsideOfEventStream];
   252         x = (int)point.x;
   253         y = (int)(window->h - point.y);
   254 
   255         if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
   256             SDL_SendMouseMotion(window, 0, 0, x, y);
   257             SDL_SetCursor(NULL);
   258         }
   259     }
   260 
   261     /* Check to see if someone updated the clipboard */
   262     Cocoa_CheckClipboardUpdate(_data->videodata);
   263 }
   264 
   265 - (void)windowDidResignKey:(NSNotification *)aNotification
   266 {
   267     /* Some other window will get mouse events, since we're not key. */
   268     if (SDL_GetMouseFocus() == _data->window) {
   269         SDL_SetMouseFocus(NULL);
   270     }
   271 
   272     /* Some other window will get keyboard events, since we're not key. */
   273     if (SDL_GetKeyboardFocus() == _data->window) {
   274         SDL_SetKeyboardFocus(NULL);
   275     }
   276 }
   277 
   278 // We'll respond to key events by doing nothing so we don't beep.
   279 // We could handle key messages here, but we lose some in the NSApp dispatch,
   280 // where they get converted to action messages, etc.
   281 - (void)flagsChanged:(NSEvent *)theEvent
   282 {
   283     //Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);
   284 }
   285 - (void)keyDown:(NSEvent *)theEvent
   286 {
   287     //Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);
   288 }
   289 - (void)keyUp:(NSEvent *)theEvent
   290 {
   291     //Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);
   292 }
   293 
   294 // We'll respond to selectors by doing nothing so we don't beep.
   295 // The escape key gets converted to a "cancel" selector, etc.
   296 - (void)doCommandBySelector:(SEL)aSelector
   297 {
   298     //NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));
   299 }
   300 
   301 - (void)mouseDown:(NSEvent *)theEvent
   302 {
   303     int button;
   304 
   305     switch ([theEvent buttonNumber]) {
   306     case 0:
   307         button = SDL_BUTTON_LEFT;
   308         break;
   309     case 1:
   310         button = SDL_BUTTON_RIGHT;
   311         break;
   312     case 2:
   313         button = SDL_BUTTON_MIDDLE;
   314         break;
   315     default:
   316         button = [theEvent buttonNumber] + 1;
   317         break;
   318     }
   319     SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
   320 }
   321 
   322 - (void)rightMouseDown:(NSEvent *)theEvent
   323 {
   324     [self mouseDown:theEvent];
   325 }
   326 
   327 - (void)otherMouseDown:(NSEvent *)theEvent
   328 {
   329     [self mouseDown:theEvent];
   330 }
   331 
   332 - (void)mouseUp:(NSEvent *)theEvent
   333 {
   334     int button;
   335 
   336     switch ([theEvent buttonNumber]) {
   337     case 0:
   338         button = SDL_BUTTON_LEFT;
   339         break;
   340     case 1:
   341         button = SDL_BUTTON_RIGHT;
   342         break;
   343     case 2:
   344         button = SDL_BUTTON_MIDDLE;
   345         break;
   346     default:
   347         button = [theEvent buttonNumber] + 1;
   348         break;
   349     }
   350     SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
   351 }
   352 
   353 - (void)rightMouseUp:(NSEvent *)theEvent
   354 {
   355     [self mouseUp:theEvent];
   356 }
   357 
   358 - (void)otherMouseUp:(NSEvent *)theEvent
   359 {
   360     [self mouseUp:theEvent];
   361 }
   362 
   363 - (void)mouseMoved:(NSEvent *)theEvent
   364 {
   365     SDL_Mouse *mouse = SDL_GetMouse();
   366     SDL_Window *window = _data->window;
   367     NSPoint point;
   368     int x, y;
   369 
   370     if (mouse->relative_mode) {
   371         return;
   372     }
   373 
   374     point = [theEvent locationInWindow];
   375     x = (int)point.x;
   376     y = (int)(window->h - point.y);
   377 
   378     if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   379         if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   380             CGPoint cgpoint;
   381 
   382             if (x < 0) {
   383                 x = 0;
   384             } else if (x >= window->w) {
   385                 x = window->w - 1;
   386             }
   387             if (y < 0) {
   388                 y = 0;
   389             } else if (y >= window->h) {
   390                 y = window->h - 1;
   391             }
   392 
   393             cgpoint.x = window->x + x;
   394             cgpoint.y = window->y + y;
   395 
   396             /* We have to disassociate the curosr & the mouse before issuing
   397              * this cursor warp, otherwise it gets limited to one update per
   398              * 250ms, and looks very choppy.
   399              */
   400             CGAssociateMouseAndMouseCursorPosition(NO);
   401             CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
   402             CGAssociateMouseAndMouseCursorPosition(YES);
   403         }
   404     }
   405     SDL_SendMouseMotion(window, 0, 0, x, y);
   406 }
   407 
   408 - (void)mouseDragged:(NSEvent *)theEvent
   409 {
   410     [self mouseMoved:theEvent];
   411 }
   412 
   413 - (void)rightMouseDragged:(NSEvent *)theEvent
   414 {
   415     [self mouseMoved:theEvent];
   416 }
   417 
   418 - (void)otherMouseDragged:(NSEvent *)theEvent
   419 {
   420     [self mouseMoved:theEvent];
   421 }
   422 
   423 - (void)scrollWheel:(NSEvent *)theEvent
   424 {
   425     Cocoa_HandleMouseWheel(_data->window, theEvent);
   426 }
   427 
   428 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
   429 {
   430     [self handleTouches:COCOA_TOUCH_DOWN withEvent:theEvent];
   431 }
   432 
   433 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
   434 {
   435     [self handleTouches:COCOA_TOUCH_MOVE withEvent:theEvent];
   436 }
   437 
   438 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
   439 {
   440     [self handleTouches:COCOA_TOUCH_UP withEvent:theEvent];
   441 }
   442 
   443 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
   444 {
   445     [self handleTouches:COCOA_TOUCH_CANCELLED withEvent:theEvent];
   446 }
   447 
   448 - (void)handleTouches:(cocoaTouchType)type withEvent:(NSEvent *)event
   449 {
   450 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   451     NSSet *touches = 0;
   452     NSEnumerator *enumerator;
   453     NSTouch *touch;
   454 
   455     switch (type) {
   456         case COCOA_TOUCH_DOWN:
   457             touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil];
   458             break;
   459         case COCOA_TOUCH_UP:
   460         case COCOA_TOUCH_CANCELLED:
   461             touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil];
   462             break;
   463         case COCOA_TOUCH_MOVE:
   464             touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil];
   465             break;
   466     }
   467 
   468     enumerator = [touches objectEnumerator];
   469     touch = (NSTouch*)[enumerator nextObject];
   470     while (touch) {
   471         const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
   472         if (!SDL_GetTouch(touchId)) {
   473             if (SDL_AddTouch(touchId, "") < 0) {
   474                 return;
   475             }
   476         } 
   477 
   478         const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
   479         float x = [touch normalizedPosition].x;
   480         float y = [touch normalizedPosition].y;
   481         /* Make the origin the upper left instead of the lower left */
   482         y = 1.0f - y;
   483 
   484         switch (type) {
   485         case COCOA_TOUCH_DOWN:
   486             SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
   487             break;
   488         case COCOA_TOUCH_UP:
   489         case COCOA_TOUCH_CANCELLED:
   490             SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
   491             break;
   492         case COCOA_TOUCH_MOVE:
   493             SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
   494             break;
   495         }
   496 
   497         touch = (NSTouch*)[enumerator nextObject];
   498     }
   499 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 */
   500 }
   501 
   502 @end
   503 
   504 @interface SDLWindow : NSWindow
   505 /* These are needed for borderless/fullscreen windows */
   506 - (BOOL)canBecomeKeyWindow;
   507 - (BOOL)canBecomeMainWindow;
   508 @end
   509 
   510 @implementation SDLWindow
   511 - (BOOL)canBecomeKeyWindow
   512 {
   513     return YES;
   514 }
   515 
   516 - (BOOL)canBecomeMainWindow
   517 {
   518     return YES;
   519 }
   520 @end
   521 
   522 @interface SDLView : NSView
   523 /* The default implementation doesn't pass rightMouseDown to responder chain */
   524 - (void)rightMouseDown:(NSEvent *)theEvent;
   525 @end
   526 
   527 @implementation SDLView
   528 - (void)rightMouseDown:(NSEvent *)theEvent
   529 {
   530     [[self nextResponder] rightMouseDown:theEvent];
   531 }
   532 @end
   533 
   534 static unsigned int
   535 GetWindowStyle(SDL_Window * window)
   536 {
   537     unsigned int style;
   538 
   539 	if (window->flags & SDL_WINDOW_FULLSCREEN) {
   540         style = NSBorderlessWindowMask;
   541 	} else {
   542 		if (window->flags & SDL_WINDOW_BORDERLESS) {
   543 			style = NSBorderlessWindowMask;
   544 		} else {
   545 			style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
   546 		}
   547 		if (window->flags & SDL_WINDOW_RESIZABLE) {
   548 			style |= NSResizableWindowMask;
   549 		}
   550 	}
   551     return style;
   552 }
   553 
   554 static int
   555 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
   556 {
   557     NSAutoreleasePool *pool;
   558     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
   559     SDL_WindowData *data;
   560 
   561     /* Allocate the window data */
   562     data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
   563     if (!data) {
   564         return SDL_OutOfMemory();
   565     }
   566     data->window = window;
   567     data->nswindow = nswindow;
   568     data->created = created;
   569     data->videodata = videodata;
   570 
   571     pool = [[NSAutoreleasePool alloc] init];
   572 
   573     /* Create an event listener for the window */
   574     data->listener = [[Cocoa_WindowListener alloc] init];
   575 
   576     /* Fill in the SDL window with the window data */
   577     {
   578         NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   579         ConvertNSRect(&rect);
   580         window->x = (int)rect.origin.x;
   581         window->y = (int)rect.origin.y;
   582         window->w = (int)rect.size.width;
   583         window->h = (int)rect.size.height;
   584     }
   585 
   586     /* Set up the listener after we create the view */
   587     [data->listener listen:data];
   588 
   589     if ([nswindow isVisible]) {
   590         window->flags |= SDL_WINDOW_SHOWN;
   591     } else {
   592         window->flags &= ~SDL_WINDOW_SHOWN;
   593     }
   594 
   595     {
   596         unsigned int style = [nswindow styleMask];
   597 
   598         if (style == NSBorderlessWindowMask) {
   599             window->flags |= SDL_WINDOW_BORDERLESS;
   600         } else {
   601             window->flags &= ~SDL_WINDOW_BORDERLESS;
   602         }
   603         if (style & NSResizableWindowMask) {
   604             window->flags |= SDL_WINDOW_RESIZABLE;
   605         } else {
   606             window->flags &= ~SDL_WINDOW_RESIZABLE;
   607         }
   608     }
   609 
   610     /* isZoomed always returns true if the window is not resizable */
   611     if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   612         window->flags |= SDL_WINDOW_MAXIMIZED;
   613     } else {
   614         window->flags &= ~SDL_WINDOW_MAXIMIZED;
   615     }
   616 
   617     if ([nswindow isMiniaturized]) {
   618         window->flags |= SDL_WINDOW_MINIMIZED;
   619     } else {
   620         window->flags &= ~SDL_WINDOW_MINIMIZED;
   621     }
   622 
   623     if ([nswindow isKeyWindow]) {
   624         window->flags |= SDL_WINDOW_INPUT_FOCUS;
   625         SDL_SetKeyboardFocus(data->window);
   626     }
   627 
   628     /* Prevents the window's "window device" from being destroyed when it is
   629      * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
   630      */
   631     [nswindow setOneShot:NO];
   632 
   633     /* All done! */
   634     [pool release];
   635     window->driverdata = data;
   636     return 0;
   637 }
   638 
   639 int
   640 Cocoa_CreateWindow(_THIS, SDL_Window * window)
   641 {
   642     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   643     NSWindow *nswindow;
   644     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
   645     NSRect rect;
   646     SDL_Rect bounds;
   647     unsigned int style;
   648 
   649     Cocoa_GetDisplayBounds(_this, display, &bounds);
   650     rect.origin.x = window->x;
   651     rect.origin.y = window->y;
   652     rect.size.width = window->w;
   653     rect.size.height = window->h;
   654     ConvertNSRect(&rect);
   655 
   656     style = GetWindowStyle(window);
   657 
   658     /* Figure out which screen to place this window */
   659     NSArray *screens = [NSScreen screens];
   660     NSScreen *screen = nil;
   661     NSScreen *candidate;
   662     int i, count = [screens count];
   663     for (i = 0; i < count; ++i) {
   664         candidate = [screens objectAtIndex:i];
   665         NSRect screenRect = [candidate frame];
   666         if (rect.origin.x >= screenRect.origin.x &&
   667             rect.origin.x < screenRect.origin.x + screenRect.size.width &&
   668             rect.origin.y >= screenRect.origin.y &&
   669             rect.origin.y < screenRect.origin.y + screenRect.size.height) {
   670             screen = candidate;
   671             rect.origin.x -= screenRect.origin.x;
   672             rect.origin.y -= screenRect.origin.y;
   673         }
   674     }
   675     nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
   676 
   677     // Create a default view for this window
   678     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
   679     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
   680     [nswindow setContentView: contentView];
   681     [contentView release];
   682 
   683     [pool release];
   684 
   685     if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
   686         [nswindow release];
   687         return -1;
   688     }
   689     return 0;
   690 }
   691 
   692 int
   693 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
   694 {
   695     NSAutoreleasePool *pool;
   696     NSWindow *nswindow = (NSWindow *) data;
   697     NSString *title;
   698 
   699     pool = [[NSAutoreleasePool alloc] init];
   700 
   701     /* Query the title from the existing window */
   702     title = [nswindow title];
   703     if (title) {
   704         window->title = SDL_strdup([title UTF8String]);
   705     }
   706 
   707     [pool release];
   708 
   709     return SetupWindowData(_this, window, nswindow, SDL_FALSE);
   710 }
   711 
   712 void
   713 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
   714 {
   715     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   716     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   717     NSString *string;
   718 
   719     if(window->title) {
   720         string = [[NSString alloc] initWithUTF8String:window->title];
   721     } else {
   722         string = [[NSString alloc] init];
   723     }
   724     [nswindow setTitle:string];
   725     [string release];
   726 
   727     [pool release];
   728 }
   729 
   730 void
   731 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
   732 {
   733     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   734     NSImage *nsimage = Cocoa_CreateImage(icon);
   735 
   736     if (nsimage) {
   737         [NSApp setApplicationIconImage:nsimage];
   738     }
   739 
   740     [pool release];
   741 }
   742 
   743 void
   744 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
   745 {
   746     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   747     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   748     NSRect rect;
   749     Uint32 moveHack;
   750 
   751     rect.origin.x = window->x;
   752     rect.origin.y = window->y;
   753     rect.size.width = window->w;
   754     rect.size.height = window->h;
   755     ConvertNSRect(&rect);
   756 
   757     moveHack = s_moveHack;
   758     s_moveHack = 0;
   759     [nswindow setFrameOrigin:rect.origin];
   760     s_moveHack = moveHack;
   761 
   762     if (window == _this->current_glwin) {
   763         [((NSOpenGLContext *) _this->current_glctx) update];
   764     }
   765 
   766     [pool release];
   767 }
   768 
   769 void
   770 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
   771 {
   772     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   773     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   774     NSWindow *nswindow = windata->nswindow;
   775     NSSize size;
   776 
   777     size.width = window->w;
   778     size.height = window->h;
   779     [nswindow setContentSize:size];
   780 
   781     if (window == _this->current_glwin) {
   782         [((NSOpenGLContext *) _this->current_glctx) update];
   783     }
   784 
   785     [pool release];
   786 }
   787 
   788 void
   789 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
   790 {
   791     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   792     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   793         
   794     NSSize minSize;
   795     minSize.width = window->min_w;
   796     minSize.height = window->min_h;
   797         
   798     [windata->nswindow setContentMinSize:minSize];
   799     
   800     [pool release];
   801 }
   802 
   803 void
   804 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
   805 {
   806     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   807     SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
   808         
   809     NSSize maxSize;
   810     maxSize.width = window->max_w;
   811     maxSize.height = window->max_h;
   812         
   813     [windata->nswindow setContentMaxSize:maxSize];
   814     
   815     [pool release];
   816 }
   817 
   818 void
   819 Cocoa_ShowWindow(_THIS, SDL_Window * window)
   820 {
   821     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   822     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
   823     NSWindow *nswindow = windowData->nswindow;
   824 
   825     if (![nswindow isMiniaturized]) {
   826         [windowData->listener pauseVisibleObservation];
   827         [nswindow makeKeyAndOrderFront:nil];
   828         [windowData->listener resumeVisibleObservation];
   829     }
   830     [pool release];
   831 }
   832 
   833 void
   834 Cocoa_HideWindow(_THIS, SDL_Window * window)
   835 {
   836     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   837     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   838 
   839     [nswindow orderOut:nil];
   840     [pool release];
   841 }
   842 
   843 void
   844 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
   845 {
   846     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   847     SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
   848     NSWindow *nswindow = windowData->nswindow;
   849 
   850     [windowData->listener pauseVisibleObservation];
   851     [nswindow makeKeyAndOrderFront:nil];
   852     [windowData->listener resumeVisibleObservation];
   853 
   854     [pool release];
   855 }
   856 
   857 void
   858 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
   859 {
   860     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   861     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   862 
   863     [nswindow zoom:nil];
   864 
   865     if (window == _this->current_glwin) {
   866         [((NSOpenGLContext *) _this->current_glctx) update];
   867     }
   868 
   869     [pool release];
   870 }
   871 
   872 void
   873 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
   874 {
   875     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   876     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   877 
   878     [nswindow miniaturize:nil];
   879     [pool release];
   880 }
   881 
   882 void
   883 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
   884 {
   885     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   886     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   887 
   888     if ([nswindow isMiniaturized]) {
   889         [nswindow deminiaturize:nil];
   890     } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
   891         [nswindow zoom:nil];
   892     }
   893     [pool release];
   894 }
   895 
   896 static NSWindow *
   897 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
   898 {
   899     if (!data->created) {
   900         /* Don't mess with other people's windows... */
   901         return nswindow;
   902     }
   903 
   904     [data->listener close];
   905     data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
   906     [data->nswindow setContentView:[nswindow contentView]];
   907     /* See comment in SetupWindowData. */
   908     [data->nswindow setOneShot:NO];
   909     [data->listener listen:data];
   910 
   911     [nswindow close];
   912 
   913     return data->nswindow;
   914 }
   915 
   916 void
   917 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
   918 {
   919     /* this message arrived in 10.6. You're out of luck on older OSes. */
   920 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
   921     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   922     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
   923     if ([nswindow respondsToSelector:@selector(setStyleMask:)]) {
   924         [nswindow setStyleMask:GetWindowStyle(window)];
   925         if (bordered) {
   926             Cocoa_SetWindowTitle(_this, window);  // this got blanked out.
   927         }
   928     }
   929     [pool release];
   930 #endif
   931 }
   932 
   933 void
   934 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
   935 {
   936     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   937     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   938     NSWindow *nswindow = data->nswindow;
   939     NSRect rect;
   940 
   941     /* The view responder chain gets messed with during setStyleMask */
   942     if ([[nswindow contentView] nextResponder] == data->listener) {
   943         [[nswindow contentView] setNextResponder:nil];
   944     }
   945 
   946     if (fullscreen) {
   947         SDL_Rect bounds;
   948 
   949         Cocoa_GetDisplayBounds(_this, display, &bounds);
   950         rect.origin.x = bounds.x;
   951         rect.origin.y = bounds.y;
   952         rect.size.width = bounds.w;
   953         rect.size.height = bounds.h;
   954         ConvertNSRect(&rect);
   955 
   956         /* Hack to fix origin on Mac OS X 10.4 */
   957         NSRect screenRect = [[nswindow screen] frame];
   958         if (screenRect.size.height >= 1.0f) {
   959             rect.origin.y += (screenRect.size.height - rect.size.height);
   960         }
   961 
   962         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
   963             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
   964         } else {
   965             nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
   966         }
   967     } else {
   968         rect.origin.x = window->windowed.x;
   969         rect.origin.y = window->windowed.y;
   970         rect.size.width = window->windowed.w;
   971         rect.size.height = window->windowed.h;
   972         ConvertNSRect(&rect);
   973 
   974         if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
   975             [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
   976         } else {
   977             nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
   978         }
   979     }
   980 
   981     /* The view responder chain gets messed with during setStyleMask */
   982     if ([[nswindow contentView] nextResponder] != data->listener) {
   983         [[nswindow contentView] setNextResponder:data->listener];
   984     }
   985 
   986     s_moveHack = 0;
   987     [nswindow setFrameOrigin:rect.origin];
   988     [nswindow setContentSize:rect.size];
   989     s_moveHack = SDL_GetTicks();
   990 
   991     /* When the window style changes the title is cleared */
   992     if (!fullscreen) {
   993         Cocoa_SetWindowTitle(_this, window);
   994     }
   995 
   996 #ifdef FULLSCREEN_TOGGLEABLE
   997     if (SDL_ShouldAllowTopmost() && fullscreen) {
   998         /* OpenGL is rendering to the window, so make it visible! */
   999         [nswindow setLevel:CGShieldingWindowLevel()];
  1000     } else {
  1001         [nswindow setLevel:kCGNormalWindowLevel];
  1002     }
  1003 #endif
  1004 
  1005     [data->listener pauseVisibleObservation];
  1006     [nswindow makeKeyAndOrderFront:nil];
  1007     [data->listener resumeVisibleObservation];
  1008 
  1009     if (window == _this->current_glwin) {
  1010         [((NSOpenGLContext *) _this->current_glctx) update];
  1011     }
  1012 
  1013     [pool release];
  1014 }
  1015 
  1016 int
  1017 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
  1018 {
  1019     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1020     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1021     const uint32_t tableSize = 256;
  1022     CGGammaValue redTable[tableSize];
  1023     CGGammaValue greenTable[tableSize];
  1024     CGGammaValue blueTable[tableSize];
  1025     uint32_t i;
  1026     float inv65535 = 1.0f / 65535.0f;
  1027 
  1028     /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
  1029     for (i = 0; i < 256; i++) {
  1030         redTable[i] = ramp[0*256+i] * inv65535;
  1031         greenTable[i] = ramp[1*256+i] * inv65535;
  1032         blueTable[i] = ramp[2*256+i] * inv65535;
  1033     }
  1034 
  1035     if (CGSetDisplayTransferByTable(display_id, tableSize,
  1036                                     redTable, greenTable, blueTable) != CGDisplayNoErr) {
  1037         return SDL_SetError("CGSetDisplayTransferByTable()");
  1038     }
  1039     return 0;
  1040 }
  1041 
  1042 int
  1043 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
  1044 {
  1045     SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
  1046     CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
  1047     const uint32_t tableSize = 256;
  1048     CGGammaValue redTable[tableSize];
  1049     CGGammaValue greenTable[tableSize];
  1050     CGGammaValue blueTable[tableSize];
  1051     uint32_t i, tableCopied;
  1052 
  1053     if (CGGetDisplayTransferByTable(display_id, tableSize,
  1054                                     redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
  1055         return SDL_SetError("CGGetDisplayTransferByTable()");
  1056     }
  1057 
  1058     for (i = 0; i < tableCopied; i++) {
  1059         ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
  1060         ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
  1061         ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
  1062     }
  1063     return 0;
  1064 }
  1065 
  1066 void
  1067 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
  1068 {
  1069     /* Move the cursor to the nearest point in the window */
  1070     if (grabbed) {
  1071         int x, y;
  1072         CGPoint cgpoint;
  1073 
  1074         SDL_GetMouseState(&x, &y);
  1075         cgpoint.x = window->x + x;
  1076         cgpoint.y = window->y + y;
  1077         CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  1078     }
  1079 	
  1080     if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
  1081 		SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1082 
  1083 		if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1084 			/* OpenGL is rendering to the window, so make it visible! */
  1085 			[data->nswindow setLevel:CGShieldingWindowLevel()];
  1086 		} else {
  1087 			[data->nswindow setLevel:kCGNormalWindowLevel];
  1088 		}
  1089 	}
  1090 }
  1091 
  1092 void
  1093 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
  1094 {
  1095     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  1096     SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
  1097 
  1098     if (data) {
  1099         [data->listener close];
  1100         [data->listener release];
  1101         if (data->created) {
  1102             [data->nswindow close];
  1103         }
  1104         SDL_free(data);
  1105     }
  1106     [pool release];
  1107 }
  1108 
  1109 SDL_bool
  1110 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
  1111 {
  1112     NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
  1113 
  1114     if (info->version.major <= SDL_MAJOR_VERSION) {
  1115         info->subsystem = SDL_SYSWM_COCOA;
  1116         info->info.cocoa.window = nswindow;
  1117         return SDL_TRUE;
  1118     } else {
  1119         SDL_SetError("Application not compiled with SDL %d.%d\n",
  1120                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
  1121         return SDL_FALSE;
  1122     }
  1123 }
  1124 
  1125 #endif /* SDL_VIDEO_DRIVER_COCOA */
  1126 
  1127 /* vi: set ts=4 sw=4 expandtab: */