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