2 Simple DirectMedia Layer
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "SDL_config.h"
23 #if SDL_VIDEO_DRIVER_COCOA
25 #include "SDL_syswm.h"
26 #include "SDL_timer.h" /* For SDL_GetTicks() */
27 #include "SDL_hints.h"
28 #include "../SDL_sysvideo.h"
29 #include "../../events/SDL_keyboard_c.h"
30 #include "../../events/SDL_mouse_c.h"
31 #include "../../events/SDL_touch_c.h"
32 #include "../../events/SDL_windowevents_c.h"
33 #include "SDL_cocoavideo.h"
34 #include "SDL_cocoashape.h"
35 #include "SDL_cocoamouse.h"
36 #include "SDL_cocoaopengl.h"
38 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
39 /* Taken from AppKit/NSOpenGLView.h in 10.8 SDK. */
40 @interface NSView (NSOpenGLSurfaceResolution)
41 - (BOOL)wantsBestResolutionOpenGLSurface;
42 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
46 static Uint32 s_moveHack;
48 static void ConvertNSRect(NSRect *r)
50 r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
54 ScheduleContextUpdates(SDL_WindowData *data)
56 NSMutableArray *contexts = data->nscontexts;
57 @synchronized (contexts) {
58 for (SDLOpenGLContext *context in contexts) {
59 [context scheduleUpdate];
65 GetHintCtrlClickEmulateRightClick()
67 const char *hint = SDL_GetHint( SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK );
68 return hint != NULL && *hint != '0';
72 GetWindowStyle(SDL_Window * window)
76 if (window->flags & SDL_WINDOW_FULLSCREEN) {
77 style = NSBorderlessWindowMask;
79 if (window->flags & SDL_WINDOW_BORDERLESS) {
80 style = NSBorderlessWindowMask;
82 style = (NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask);
84 if (window->flags & SDL_WINDOW_RESIZABLE) {
85 style |= NSResizableWindowMask;
92 @implementation Cocoa_WindowListener
94 - (void)listen:(SDL_WindowData *)data
96 NSNotificationCenter *center;
97 NSWindow *window = data->nswindow;
98 NSView *view = [window contentView];
101 observingVisible = YES;
103 wasVisible = [window isVisible];
105 inFullscreenTransition = NO;
107 center = [NSNotificationCenter defaultCenter];
109 if ([window delegate] != nil) {
110 [center addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:window];
111 [center addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:window];
112 [center addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:window];
113 [center addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:window];
114 [center addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:window];
115 [center addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:window];
116 [center addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:window];
117 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
118 [center addObserver:self selector:@selector(windowWillEnterFullScreen:) name:NSWindowWillEnterFullScreenNotification object:window];
119 [center addObserver:self selector:@selector(windowDidEnterFullScreen:) name:NSWindowDidEnterFullScreenNotification object:window];
120 [center addObserver:self selector:@selector(windowWillExitFullScreen:) name:NSWindowWillExitFullScreenNotification object:window];
121 [center addObserver:self selector:@selector(windowDidExitFullScreen:) name:NSWindowDidExitFullScreenNotification object:window];
122 #endif /* Mac OS X 10.7+ */
124 [window setDelegate:self];
127 /* Haven't found a delegate / notification that triggers when the window is
128 * ordered out (is not visible any more). You can be ordered out without
129 * minimizing, so DidMiniaturize doesn't work. (e.g. -[NSWindow orderOut:])
131 [window addObserver:self
132 forKeyPath:@"visible"
133 options:NSKeyValueObservingOptionNew
136 [window setNextResponder:self];
137 [window setAcceptsMouseMovedEvents:YES];
139 [view setNextResponder:self];
141 if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) {
142 [view setAcceptsTouchEvents:YES];
146 - (void)observeValueForKeyPath:(NSString *)keyPath
148 change:(NSDictionary *)change
149 context:(void *)context
151 if (!observingVisible) {
155 if (object == _data->nswindow && [keyPath isEqualToString:@"visible"]) {
156 int newVisibility = [[change objectForKey:@"new"] intValue];
158 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
160 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
165 -(void) pauseVisibleObservation
167 observingVisible = NO;
168 wasVisible = [_data->nswindow isVisible];
171 -(void) resumeVisibleObservation
173 BOOL isVisible = [_data->nswindow isVisible];
174 observingVisible = YES;
175 if (wasVisible != isVisible) {
177 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_SHOWN, 0, 0);
179 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
182 wasVisible = isVisible;
186 - (BOOL) isToggledFullscreen
193 NSNotificationCenter *center;
194 NSWindow *window = _data->nswindow;
195 NSView *view = [window contentView];
196 NSArray *windows = nil;
198 center = [NSNotificationCenter defaultCenter];
200 if ([window delegate] != self) {
201 [center removeObserver:self name:NSWindowDidExposeNotification object:window];
202 [center removeObserver:self name:NSWindowDidMoveNotification object:window];
203 [center removeObserver:self name:NSWindowDidResizeNotification object:window];
204 [center removeObserver:self name:NSWindowDidMiniaturizeNotification object:window];
205 [center removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window];
206 [center removeObserver:self name:NSWindowDidBecomeKeyNotification object:window];
207 [center removeObserver:self name:NSWindowDidResignKeyNotification object:window];
208 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
209 [center removeObserver:self name:NSWindowWillEnterFullScreenNotification object:window];
210 [center removeObserver:self name:NSWindowDidEnterFullScreenNotification object:window];
211 [center removeObserver:self name:NSWindowWillExitFullScreenNotification object:window];
212 [center removeObserver:self name:NSWindowDidExitFullScreenNotification object:window];
213 #endif /* Mac OS X 10.7+ */
215 [window setDelegate:nil];
218 [window removeObserver:self
219 forKeyPath:@"visible"];
221 if ([window nextResponder] == self) {
222 [window setNextResponder:nil];
224 if ([view nextResponder] == self) {
225 [view setNextResponder:nil];
228 /* Make the next window in the z-order Key. If we weren't the foreground
229 when closed, this is a no-op.
230 !!! FIXME: Note that this is a hack, and there are corner cases where
231 !!! FIXME: this fails (such as the About box). The typical nib+RunLoop
232 !!! FIXME: handles this for Cocoa apps, but we bypass all that in SDL.
233 !!! FIXME: We should remove this code when we find a better way to
234 !!! FIXME: have the system do this for us. See discussion in
235 !!! FIXME: http://bugzilla.libsdl.org/show_bug.cgi?id=1825
237 windows = [NSApp orderedWindows];
238 if ([windows count] > 0) {
239 NSWindow *win = (NSWindow *) [windows objectAtIndex:0];
240 [win makeKeyAndOrderFront:self];
244 - (BOOL)windowShouldClose:(id)sender
246 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
250 - (void)windowDidExpose:(NSNotification *)aNotification
252 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
255 - (void)windowDidMove:(NSNotification *)aNotification
258 SDL_Window *window = _data->window;
259 NSWindow *nswindow = _data->nswindow;
260 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
261 ConvertNSRect(&rect);
264 SDL_bool blockMove = ((SDL_GetTicks() - s_moveHack) < 500);
269 /* Cocoa is adjusting the window in response to a mode change */
270 rect.origin.x = window->x;
271 rect.origin.y = window->y;
272 ConvertNSRect(&rect);
273 [nswindow setFrameOrigin:rect.origin];
278 x = (int)rect.origin.x;
279 y = (int)rect.origin.y;
281 ScheduleContextUpdates(_data);
283 SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MOVED, x, y);
286 - (void)windowDidResize:(NSNotification *)aNotification
289 NSRect rect = [_data->nswindow contentRectForFrameRect:[_data->nswindow frame]];
290 ConvertNSRect(&rect);
291 x = (int)rect.origin.x;
292 y = (int)rect.origin.y;
293 w = (int)rect.size.width;
294 h = (int)rect.size.height;
296 if (inFullscreenTransition) {
297 /* We'll take care of this at the end of the transition */
301 if (SDL_IsShapedWindow(_data->window)) {
302 Cocoa_ResizeWindowShape(_data->window);
305 ScheduleContextUpdates(_data);
307 /* The window can move during a resize event, such as when maximizing
308 or resizing from a corner */
309 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MOVED, x, y);
310 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESIZED, w, h);
312 const BOOL zoomed = [_data->nswindow isZoomed];
314 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
316 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MAXIMIZED, 0, 0);
320 - (void)windowDidMiniaturize:(NSNotification *)aNotification
322 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
325 - (void)windowDidDeminiaturize:(NSNotification *)aNotification
327 SDL_SendWindowEvent(_data->window, SDL_WINDOWEVENT_RESTORED, 0, 0);
330 - (void)windowDidBecomeKey:(NSNotification *)aNotification
332 SDL_Window *window = _data->window;
333 SDL_Mouse *mouse = SDL_GetMouse();
335 /* We're going to get keyboard events, since we're key. */
336 SDL_SetKeyboardFocus(window);
338 /* If we just gained focus we need the updated mouse position */
339 if (!mouse->relative_mode) {
343 point = [_data->nswindow mouseLocationOutsideOfEventStream];
345 y = (int)(window->h - point.y);
347 if (x >= 0 && x < window->w && y >= 0 && y < window->h) {
348 SDL_SendMouseMotion(window, 0, 0, x, y);
352 /* Check to see if someone updated the clipboard */
353 Cocoa_CheckClipboardUpdate(_data->videodata);
356 - (void)windowDidResignKey:(NSNotification *)aNotification
358 /* Some other window will get mouse events, since we're not key. */
359 if (SDL_GetMouseFocus() == _data->window) {
360 SDL_SetMouseFocus(NULL);
363 /* Some other window will get keyboard events, since we're not key. */
364 if (SDL_GetKeyboardFocus() == _data->window) {
365 SDL_SetKeyboardFocus(NULL);
369 - (void)windowWillEnterFullScreen:(NSNotification *)aNotification
371 SDL_Window *window = _data->window;
372 NSWindow *nswindow = _data->nswindow;
374 if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
375 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
376 [nswindow setStyleMask:(NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask)];
378 [nswindow setStyleMask:NSBorderlessWindowMask];
382 inFullscreenTransition = YES;
385 - (void)windowDidEnterFullScreen:(NSNotification *)aNotification
387 inFullscreenTransition = NO;
388 [self windowDidResize:aNotification];
391 - (void)windowWillExitFullScreen:(NSNotification *)aNotification
393 inFullscreenTransition = YES;
396 - (void)windowDidExitFullScreen:(NSNotification *)aNotification
398 SDL_Window *window = _data->window;
399 NSWindow *nswindow = _data->nswindow;
401 if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
402 [nswindow setStyleMask:GetWindowStyle(window)];
405 inFullscreenTransition = NO;
406 [self windowDidResize:aNotification];
409 /* We'll respond to key events by doing nothing so we don't beep.
410 * We could handle key messages here, but we lose some in the NSApp dispatch,
411 * where they get converted to action messages, etc.
413 - (void)flagsChanged:(NSEvent *)theEvent
415 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
417 - (void)keyDown:(NSEvent *)theEvent
419 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
421 - (void)keyUp:(NSEvent *)theEvent
423 /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
426 /* We'll respond to selectors by doing nothing so we don't beep.
427 * The escape key gets converted to a "cancel" selector, etc.
429 - (void)doCommandBySelector:(SEL)aSelector
431 /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
434 - (void)mouseDown:(NSEvent *)theEvent
438 switch ([theEvent buttonNumber]) {
440 if (([theEvent modifierFlags] & NSControlKeyMask) &&
441 GetHintCtrlClickEmulateRightClick()) {
443 button = SDL_BUTTON_RIGHT;
446 button = SDL_BUTTON_LEFT;
450 button = SDL_BUTTON_RIGHT;
453 button = SDL_BUTTON_MIDDLE;
456 button = [theEvent buttonNumber] + 1;
459 SDL_SendMouseButton(_data->window, 0, SDL_PRESSED, button);
462 - (void)rightMouseDown:(NSEvent *)theEvent
464 [self mouseDown:theEvent];
467 - (void)otherMouseDown:(NSEvent *)theEvent
469 [self mouseDown:theEvent];
472 - (void)mouseUp:(NSEvent *)theEvent
476 switch ([theEvent buttonNumber]) {
479 button = SDL_BUTTON_RIGHT;
482 button = SDL_BUTTON_LEFT;
486 button = SDL_BUTTON_RIGHT;
489 button = SDL_BUTTON_MIDDLE;
492 button = [theEvent buttonNumber] + 1;
495 SDL_SendMouseButton(_data->window, 0, SDL_RELEASED, button);
498 - (void)rightMouseUp:(NSEvent *)theEvent
500 [self mouseUp:theEvent];
503 - (void)otherMouseUp:(NSEvent *)theEvent
505 [self mouseUp:theEvent];
508 - (void)mouseMoved:(NSEvent *)theEvent
510 SDL_Mouse *mouse = SDL_GetMouse();
511 SDL_Window *window = _data->window;
515 if (mouse->relative_mode) {
519 point = [theEvent locationInWindow];
521 y = (int)(window->h - point.y);
523 if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
524 if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
529 } else if (x >= window->w) {
534 } else if (y >= window->h) {
538 #if !SDL_MAC_NO_SANDBOX
539 /* When SDL_MAC_NO_SANDBOX is set, this is handled by
540 * SDL_cocoamousetap.m.
543 cgpoint.x = window->x + x;
544 cgpoint.y = window->y + y;
546 /* According to the docs, this was deprecated in 10.6, but it's still
547 * around. The substitute requires a CGEventSource, but I'm not entirely
548 * sure how we'd procure the right one for this event.
550 CGSetLocalEventsSuppressionInterval(0.0);
551 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
552 CGSetLocalEventsSuppressionInterval(0.25);
556 SDL_SendMouseMotion(window, 0, 0, x, y);
559 - (void)mouseDragged:(NSEvent *)theEvent
561 [self mouseMoved:theEvent];
564 - (void)rightMouseDragged:(NSEvent *)theEvent
566 [self mouseMoved:theEvent];
569 - (void)otherMouseDragged:(NSEvent *)theEvent
571 [self mouseMoved:theEvent];
574 - (void)scrollWheel:(NSEvent *)theEvent
576 Cocoa_HandleMouseWheel(_data->window, theEvent);
579 - (void)touchesBeganWithEvent:(NSEvent *) theEvent
581 [self handleTouches:COCOA_TOUCH_DOWN withEvent:theEvent];
584 - (void)touchesMovedWithEvent:(NSEvent *) theEvent
586 [self handleTouches:COCOA_TOUCH_MOVE withEvent:theEvent];
589 - (void)touchesEndedWithEvent:(NSEvent *) theEvent
591 [self handleTouches:COCOA_TOUCH_UP withEvent:theEvent];
594 - (void)touchesCancelledWithEvent:(NSEvent *) theEvent
596 [self handleTouches:COCOA_TOUCH_CANCELLED withEvent:theEvent];
599 - (void)handleTouches:(cocoaTouchType)type withEvent:(NSEvent *)event
602 NSEnumerator *enumerator;
606 case COCOA_TOUCH_DOWN:
607 touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil];
610 case COCOA_TOUCH_CANCELLED:
611 touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil];
613 case COCOA_TOUCH_MOVE:
614 touches = [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil];
618 enumerator = [touches objectEnumerator];
619 touch = (NSTouch*)[enumerator nextObject];
621 const SDL_TouchID touchId = (SDL_TouchID)(intptr_t)[touch device];
622 if (!SDL_GetTouch(touchId)) {
623 if (SDL_AddTouch(touchId, "") < 0) {
628 const SDL_FingerID fingerId = (SDL_FingerID)(intptr_t)[touch identity];
629 float x = [touch normalizedPosition].x;
630 float y = [touch normalizedPosition].y;
631 /* Make the origin the upper left instead of the lower left */
635 case COCOA_TOUCH_DOWN:
636 SDL_SendTouch(touchId, fingerId, SDL_TRUE, x, y, 1.0f);
639 case COCOA_TOUCH_CANCELLED:
640 SDL_SendTouch(touchId, fingerId, SDL_FALSE, x, y, 1.0f);
642 case COCOA_TOUCH_MOVE:
643 SDL_SendTouchMotion(touchId, fingerId, x, y, 1.0f);
647 touch = (NSTouch*)[enumerator nextObject];
653 @interface SDLWindow : NSWindow
654 /* These are needed for borderless/fullscreen windows */
655 - (BOOL)canBecomeKeyWindow;
656 - (BOOL)canBecomeMainWindow;
659 @implementation SDLWindow
660 - (BOOL)canBecomeKeyWindow
665 - (BOOL)canBecomeMainWindow
671 @interface SDLView : NSView
673 /* The default implementation doesn't pass rightMouseDown to responder chain */
674 - (void)rightMouseDown:(NSEvent *)theEvent;
677 @implementation SDLView
678 - (void)rightMouseDown:(NSEvent *)theEvent
680 [[self nextResponder] rightMouseDown:theEvent];
683 - (void)resetCursorRects
685 [super resetCursorRects];
686 SDL_Mouse *mouse = SDL_GetMouse();
688 if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
689 [self addCursorRect:[self bounds]
690 cursor:mouse->cur_cursor->driverdata];
692 [self addCursorRect:[self bounds]
693 cursor:[NSCursor invisibleCursor]];
699 SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created)
701 NSAutoreleasePool *pool;
702 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
703 SDL_WindowData *data;
705 /* Allocate the window data */
706 data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
708 return SDL_OutOfMemory();
710 data->window = window;
711 data->nswindow = nswindow;
712 data->created = created;
713 data->videodata = videodata;
714 data->nscontexts = [[NSMutableArray alloc] init];
716 pool = [[NSAutoreleasePool alloc] init];
718 /* Create an event listener for the window */
719 data->listener = [[Cocoa_WindowListener alloc] init];
721 /* Fill in the SDL window with the window data */
723 NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
724 ConvertNSRect(&rect);
725 window->x = (int)rect.origin.x;
726 window->y = (int)rect.origin.y;
727 window->w = (int)rect.size.width;
728 window->h = (int)rect.size.height;
731 /* Set up the listener after we create the view */
732 [data->listener listen:data];
734 if ([nswindow isVisible]) {
735 window->flags |= SDL_WINDOW_SHOWN;
737 window->flags &= ~SDL_WINDOW_SHOWN;
741 unsigned int style = [nswindow styleMask];
743 if (style == NSBorderlessWindowMask) {
744 window->flags |= SDL_WINDOW_BORDERLESS;
746 window->flags &= ~SDL_WINDOW_BORDERLESS;
748 if (style & NSResizableWindowMask) {
749 window->flags |= SDL_WINDOW_RESIZABLE;
751 window->flags &= ~SDL_WINDOW_RESIZABLE;
755 /* isZoomed always returns true if the window is not resizable */
756 if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
757 window->flags |= SDL_WINDOW_MAXIMIZED;
759 window->flags &= ~SDL_WINDOW_MAXIMIZED;
762 if ([nswindow isMiniaturized]) {
763 window->flags |= SDL_WINDOW_MINIMIZED;
765 window->flags &= ~SDL_WINDOW_MINIMIZED;
768 if ([nswindow isKeyWindow]) {
769 window->flags |= SDL_WINDOW_INPUT_FOCUS;
770 SDL_SetKeyboardFocus(data->window);
773 /* Prevents the window's "window device" from being destroyed when it is
774 * hidden. See http://www.mikeash.com/pyblog/nsopenglcontext-and-one-shot.html
776 [nswindow setOneShot:NO];
780 window->driverdata = data;
785 Cocoa_CreateWindow(_THIS, SDL_Window * window)
787 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
789 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
794 Cocoa_GetDisplayBounds(_this, display, &bounds);
795 rect.origin.x = window->x;
796 rect.origin.y = window->y;
797 rect.size.width = window->w;
798 rect.size.height = window->h;
799 ConvertNSRect(&rect);
801 style = GetWindowStyle(window);
803 /* Figure out which screen to place this window */
804 NSArray *screens = [NSScreen screens];
805 NSScreen *screen = nil;
807 int i, count = [screens count];
808 for (i = 0; i < count; ++i) {
809 candidate = [screens objectAtIndex:i];
810 NSRect screenRect = [candidate frame];
811 if (rect.origin.x >= screenRect.origin.x &&
812 rect.origin.x < screenRect.origin.x + screenRect.size.width &&
813 rect.origin.y >= screenRect.origin.y &&
814 rect.origin.y < screenRect.origin.y + screenRect.size.height) {
816 rect.origin.x -= screenRect.origin.x;
817 rect.origin.y -= screenRect.origin.y;
822 nswindow = [[SDLWindow alloc] initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO screen:screen];
824 @catch (NSException *e) {
825 SDL_SetError("%s", [[e reason] UTF8String]);
829 [nswindow setBackgroundColor:[NSColor blackColor]];
830 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
831 if ([nswindow respondsToSelector:@selector(setCollectionBehavior:)]) {
832 [nswindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
836 /* Create a default view for this window */
837 rect = [nswindow contentRectForFrameRect:[nswindow frame]];
838 NSView *contentView = [[SDLView alloc] initWithFrame:rect];
840 if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
841 if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
842 [contentView setWantsBestResolutionOpenGLSurface:YES];
846 [nswindow setContentView: contentView];
847 [contentView release];
851 if (SetupWindowData(_this, window, nswindow, SDL_TRUE) < 0) {
859 Cocoa_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
861 NSAutoreleasePool *pool;
862 NSWindow *nswindow = (NSWindow *) data;
865 pool = [[NSAutoreleasePool alloc] init];
867 /* Query the title from the existing window */
868 title = [nswindow title];
870 window->title = SDL_strdup([title UTF8String]);
875 return SetupWindowData(_this, window, nswindow, SDL_FALSE);
879 Cocoa_SetWindowTitle(_THIS, SDL_Window * window)
881 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
882 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
886 string = [[NSString alloc] initWithUTF8String:window->title];
888 string = [[NSString alloc] init];
890 [nswindow setTitle:string];
897 Cocoa_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
899 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
900 NSImage *nsimage = Cocoa_CreateImage(icon);
903 [NSApp setApplicationIconImage:nsimage];
910 Cocoa_SetWindowPosition(_THIS, SDL_Window * window)
912 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
913 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
914 NSWindow *nswindow = windata->nswindow;
918 rect.origin.x = window->x;
919 rect.origin.y = window->y;
920 rect.size.width = window->w;
921 rect.size.height = window->h;
922 ConvertNSRect(&rect);
924 moveHack = s_moveHack;
926 [nswindow setFrameOrigin:rect.origin];
927 s_moveHack = moveHack;
929 ScheduleContextUpdates(windata);
935 Cocoa_SetWindowSize(_THIS, SDL_Window * window)
937 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
938 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
939 NSWindow *nswindow = windata->nswindow;
942 size.width = window->w;
943 size.height = window->h;
944 [nswindow setContentSize:size];
946 ScheduleContextUpdates(windata);
952 Cocoa_SetWindowMinimumSize(_THIS, SDL_Window * window)
954 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
955 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
958 minSize.width = window->min_w;
959 minSize.height = window->min_h;
961 [windata->nswindow setContentMinSize:minSize];
967 Cocoa_SetWindowMaximumSize(_THIS, SDL_Window * window)
969 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
970 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
973 maxSize.width = window->max_w;
974 maxSize.height = window->max_h;
976 [windata->nswindow setContentMaxSize:maxSize];
982 Cocoa_ShowWindow(_THIS, SDL_Window * window)
984 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
985 SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
986 NSWindow *nswindow = windowData->nswindow;
988 if (![nswindow isMiniaturized]) {
989 [windowData->listener pauseVisibleObservation];
990 [nswindow makeKeyAndOrderFront:nil];
991 [windowData->listener resumeVisibleObservation];
997 Cocoa_HideWindow(_THIS, SDL_Window * window)
999 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1000 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1002 [nswindow orderOut:nil];
1007 Cocoa_RaiseWindow(_THIS, SDL_Window * window)
1009 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1010 SDL_WindowData *windowData = ((SDL_WindowData *) window->driverdata);
1011 NSWindow *nswindow = windowData->nswindow;
1013 /* makeKeyAndOrderFront: has the side-effect of deminiaturizing and showing
1014 a minimized or hidden window, so check for that before showing it.
1016 [windowData->listener pauseVisibleObservation];
1017 if (![nswindow isMiniaturized] && [nswindow isVisible]) {
1018 [nswindow makeKeyAndOrderFront:nil];
1020 [windowData->listener resumeVisibleObservation];
1026 Cocoa_MaximizeWindow(_THIS, SDL_Window * window)
1028 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1029 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
1030 NSWindow *nswindow = windata->nswindow;
1032 [nswindow zoom:nil];
1034 ScheduleContextUpdates(windata);
1040 Cocoa_MinimizeWindow(_THIS, SDL_Window * window)
1042 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1043 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1045 [nswindow miniaturize:nil];
1050 Cocoa_RestoreWindow(_THIS, SDL_Window * window)
1052 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1053 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1055 if ([nswindow isMiniaturized]) {
1056 [nswindow deminiaturize:nil];
1057 } else if ((window->flags & SDL_WINDOW_RESIZABLE) && [nswindow isZoomed]) {
1058 [nswindow zoom:nil];
1064 Cocoa_RebuildWindow(SDL_WindowData * data, NSWindow * nswindow, unsigned style)
1066 if (!data->created) {
1067 /* Don't mess with other people's windows... */
1071 [data->listener close];
1072 data->nswindow = [[SDLWindow alloc] initWithContentRect:[[nswindow contentView] frame] styleMask:style backing:NSBackingStoreBuffered defer:NO screen:[nswindow screen]];
1073 [data->nswindow setContentView:[nswindow contentView]];
1074 /* See comment in SetupWindowData. */
1075 [data->nswindow setOneShot:NO];
1076 [data->listener listen:data];
1080 return data->nswindow;
1084 Cocoa_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
1086 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1087 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1088 if ([nswindow respondsToSelector:@selector(setStyleMask:)]) {
1089 [nswindow setStyleMask:GetWindowStyle(window)];
1091 Cocoa_SetWindowTitle(_this, window); /* this got blanked out. */
1098 Cocoa_CanToggleFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
1100 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1101 NSWindow *nswindow = data->nswindow;
1103 if (![nswindow respondsToSelector: @selector(toggleFullScreen:)]) {
1107 /* We can enter new style fullscreen mode for "fullscreen desktop" */
1108 if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
1112 /* We can always leave new style fullscreen mode */
1113 if (!fullscreen && [data->listener isToggledFullscreen]) {
1117 /* Requesting a mode switched fullscreen mode */
1122 Cocoa_SetWindowFullscreen_NewStyle(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
1124 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1125 NSWindow *nswindow = data->nswindow;
1127 if (fullscreen != [data->listener isToggledFullscreen]) {
1128 [nswindow performSelector: @selector(toggleFullScreen:) withObject:nswindow];
1130 ScheduleContextUpdates(data);
1134 Cocoa_SetWindowFullscreen_OldStyle(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
1136 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1137 NSWindow *nswindow = data->nswindow;
1140 /* The view responder chain gets messed with during setStyleMask */
1141 if ([[nswindow contentView] nextResponder] == data->listener) {
1142 [[nswindow contentView] setNextResponder:nil];
1148 Cocoa_GetDisplayBounds(_this, display, &bounds);
1149 rect.origin.x = bounds.x;
1150 rect.origin.y = bounds.y;
1151 rect.size.width = bounds.w;
1152 rect.size.height = bounds.h;
1153 ConvertNSRect(&rect);
1155 /* Hack to fix origin on Mac OS X 10.4 */
1156 NSRect screenRect = [[nswindow screen] frame];
1157 if (screenRect.size.height >= 1.0f) {
1158 rect.origin.y += (screenRect.size.height - rect.size.height);
1161 if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
1162 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)NSBorderlessWindowMask];
1164 nswindow = Cocoa_RebuildWindow(data, nswindow, NSBorderlessWindowMask);
1167 rect.origin.x = window->windowed.x;
1168 rect.origin.y = window->windowed.y;
1169 rect.size.width = window->windowed.w;
1170 rect.size.height = window->windowed.h;
1171 ConvertNSRect(&rect);
1173 if ([nswindow respondsToSelector: @selector(setStyleMask:)]) {
1174 [nswindow performSelector: @selector(setStyleMask:) withObject: (id)(uintptr_t)GetWindowStyle(window)];
1176 nswindow = Cocoa_RebuildWindow(data, nswindow, GetWindowStyle(window));
1180 /* The view responder chain gets messed with during setStyleMask */
1181 if ([[nswindow contentView] nextResponder] != data->listener) {
1182 [[nswindow contentView] setNextResponder:data->listener];
1186 [nswindow setContentSize:rect.size];
1187 [nswindow setFrameOrigin:rect.origin];
1188 s_moveHack = SDL_GetTicks();
1190 /* When the window style changes the title is cleared */
1192 Cocoa_SetWindowTitle(_this, window);
1195 if (SDL_ShouldAllowTopmost() && fullscreen) {
1196 /* OpenGL is rendering to the window, so make it visible! */
1197 [nswindow setLevel:CGShieldingWindowLevel()];
1199 [nswindow setLevel:kCGNormalWindowLevel];
1202 if ([nswindow isVisible] || fullscreen) {
1203 [data->listener pauseVisibleObservation];
1204 [nswindow makeKeyAndOrderFront:nil];
1205 [data->listener resumeVisibleObservation];
1208 ScheduleContextUpdates(data);
1212 Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
1214 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1216 if (Cocoa_CanToggleFullscreen(_this, window, display, fullscreen)) {
1217 Cocoa_SetWindowFullscreen_NewStyle(_this, window, display, fullscreen);
1219 Cocoa_SetWindowFullscreen_OldStyle(_this, window, display, fullscreen);
1226 Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
1228 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1229 CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
1230 const uint32_t tableSize = 256;
1231 CGGammaValue redTable[tableSize];
1232 CGGammaValue greenTable[tableSize];
1233 CGGammaValue blueTable[tableSize];
1235 float inv65535 = 1.0f / 65535.0f;
1237 /* Extract gamma values into separate tables, convert to floats between 0.0 and 1.0 */
1238 for (i = 0; i < 256; i++) {
1239 redTable[i] = ramp[0*256+i] * inv65535;
1240 greenTable[i] = ramp[1*256+i] * inv65535;
1241 blueTable[i] = ramp[2*256+i] * inv65535;
1244 if (CGSetDisplayTransferByTable(display_id, tableSize,
1245 redTable, greenTable, blueTable) != CGDisplayNoErr) {
1246 return SDL_SetError("CGSetDisplayTransferByTable()");
1252 Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
1254 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
1255 CGDirectDisplayID display_id = ((SDL_DisplayData *)display->driverdata)->display;
1256 const uint32_t tableSize = 256;
1257 CGGammaValue redTable[tableSize];
1258 CGGammaValue greenTable[tableSize];
1259 CGGammaValue blueTable[tableSize];
1260 uint32_t i, tableCopied;
1262 if (CGGetDisplayTransferByTable(display_id, tableSize,
1263 redTable, greenTable, blueTable, &tableCopied) != CGDisplayNoErr) {
1264 return SDL_SetError("CGGetDisplayTransferByTable()");
1267 for (i = 0; i < tableCopied; i++) {
1268 ramp[0*256+i] = (Uint16)(redTable[i] * 65535.0f);
1269 ramp[1*256+i] = (Uint16)(greenTable[i] * 65535.0f);
1270 ramp[2*256+i] = (Uint16)(blueTable[i] * 65535.0f);
1276 Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
1278 /* Move the cursor to the nearest point in the window */
1283 SDL_GetMouseState(&x, &y);
1284 cgpoint.x = window->x + x;
1285 cgpoint.y = window->y + y;
1286 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
1289 if ( window->flags & SDL_WINDOW_FULLSCREEN ) {
1290 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1292 if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
1293 /* OpenGL is rendering to the window, so make it visible! */
1294 [data->nswindow setLevel:CGShieldingWindowLevel()];
1296 [data->nswindow setLevel:kCGNormalWindowLevel];
1302 Cocoa_DestroyWindow(_THIS, SDL_Window * window)
1304 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1305 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1308 [data->listener close];
1309 [data->listener release];
1310 if (data->created) {
1311 [data->nswindow close];
1314 NSArray *contexts = [[data->nscontexts copy] autorelease];
1315 for (SDLOpenGLContext *context in contexts) {
1316 /* Calling setWindow:NULL causes the context to remove itself from the context list. */
1317 [context setWindow:NULL];
1319 [data->nscontexts release];
1327 Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
1329 NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow;
1331 if (info->version.major <= SDL_MAJOR_VERSION) {
1332 info->subsystem = SDL_SYSWM_COCOA;
1333 info->info.cocoa.window = nswindow;
1336 SDL_SetError("Application not compiled with SDL %d.%d\n",
1337 SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
1342 #endif /* SDL_VIDEO_DRIVER_COCOA */
1344 /* vi: set ts=4 sw=4 expandtab: */