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