Atlas - SDL_cocoaevents.m

Home / ext / SDL / src / video / cocoa Lines: 1 | Size: 24347 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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#ifdef SDL_VIDEO_DRIVER_COCOA 24 25#include "SDL_cocoavideo.h" 26#include "../../events/SDL_events_c.h" 27 28static SDL_Window *FindSDLWindowForNSWindow(NSWindow *win) 29{ 30 SDL_Window *sdlwindow = NULL; 31 SDL_VideoDevice *device = SDL_GetVideoDevice(); 32 if (device && device->windows) { 33 for (sdlwindow = device->windows; sdlwindow; sdlwindow = sdlwindow->next) { 34 NSWindow *nswindow = ((__bridge SDL_CocoaWindowData *)sdlwindow->internal).nswindow; 35 if (win == nswindow) { 36 return sdlwindow; 37 } 38 } 39 } 40 41 return sdlwindow; 42} 43 44@interface SDL3Application : NSApplication 45 46- (void)terminate:(id)sender; 47- (void)sendEvent:(NSEvent *)theEvent; 48 49+ (void)registerUserDefaults; 50 51@end 52 53@implementation SDL3Application 54 55// Override terminate to handle Quit and System Shutdown smoothly. 56- (void)terminate:(id)sender 57{ 58 SDL_SendQuit(); 59} 60 61static bool s_bShouldHandleEventsInSDLApplication = false; 62 63static void Cocoa_DispatchEvent(NSEvent *theEvent) 64{ 65 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 66 67 switch ([theEvent type]) { 68 case NSEventTypeLeftMouseDown: 69 case NSEventTypeOtherMouseDown: 70 case NSEventTypeRightMouseDown: 71 case NSEventTypeLeftMouseUp: 72 case NSEventTypeOtherMouseUp: 73 case NSEventTypeRightMouseUp: 74 case NSEventTypeLeftMouseDragged: 75 case NSEventTypeRightMouseDragged: 76 case NSEventTypeOtherMouseDragged: // usually middle mouse dragged 77 case NSEventTypeMouseMoved: 78 case NSEventTypeScrollWheel: 79 case NSEventTypeMouseEntered: 80 case NSEventTypeMouseExited: 81 Cocoa_HandleMouseEvent(_this, theEvent); 82 break; 83 case NSEventTypeKeyDown: 84 case NSEventTypeKeyUp: 85 case NSEventTypeFlagsChanged: 86 Cocoa_HandleKeyEvent(_this, theEvent); 87 break; 88 default: 89 break; 90 } 91} 92 93// Dispatch events here so that we can handle events caught by 94// nextEventMatchingMask in SDL, as well as events caught by other 95// processes (such as CEF) that are passed down to NSApp. 96- (void)sendEvent:(NSEvent *)theEvent 97{ 98 if (s_bShouldHandleEventsInSDLApplication) { 99 Cocoa_DispatchEvent(theEvent); 100 } 101 102 [super sendEvent:theEvent]; 103} 104 105+ (void)registerUserDefaults 106{ 107 BOOL momentumScrollSupported = (BOOL)SDL_GetHintBoolean(SDL_HINT_MAC_SCROLL_MOMENTUM, false); 108 BOOL pressAndHoldEnabled = (BOOL)SDL_GetHintBoolean(SDL_HINT_MAC_PRESS_AND_HOLD, true); 109 110 NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: 111 [NSNumber numberWithBool:momentumScrollSupported], @"AppleMomentumScrollSupported", 112 [NSNumber numberWithBool:pressAndHoldEnabled], @"ApplePressAndHoldEnabled", 113 [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState", 114 nil]; 115 [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; 116} 117 118@end // SDL3Application 119 120// setAppleMenu disappeared from the headers in 10.4 121@interface NSApplication (NSAppleMenu) 122- (void)setAppleMenu:(NSMenu *)menu; 123@end 124 125@interface SDL3AppDelegate : NSObject <NSApplicationDelegate> 126{ 127 @public 128 BOOL seenFirstActivate; 129} 130 131- (id)init; 132- (void)localeDidChange:(NSNotification *)notification; 133- (void)observeValueForKeyPath:(NSString *)keyPath 134 ofObject:(id)object 135 change:(NSDictionary *)change 136 context:(void *)context; 137- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app; 138- (IBAction)menu:(id)sender; 139@end 140 141@implementation SDL3AppDelegate : NSObject 142- (id)init 143{ 144 self = [super init]; 145 if (self) { 146 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 147 bool registerActivationHandlers = SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true); 148 149 seenFirstActivate = NO; 150 151 if (registerActivationHandlers) { 152 [center addObserver:self 153 selector:@selector(windowWillClose:) 154 name:NSWindowWillCloseNotification 155 object:nil]; 156 157 [center addObserver:self 158 selector:@selector(focusSomeWindow:) 159 name:NSApplicationDidBecomeActiveNotification 160 object:nil]; 161 162 [center addObserver:self 163 selector:@selector(screenParametersChanged:) 164 name:NSApplicationDidChangeScreenParametersNotification 165 object:nil]; 166 } 167 168 [center addObserver:self 169 selector:@selector(localeDidChange:) 170 name:NSCurrentLocaleDidChangeNotification 171 object:nil]; 172 173 [NSApp addObserver:self 174 forKeyPath:@"effectiveAppearance" 175 options:NSKeyValueObservingOptionInitial 176 context:nil]; 177 } 178 179 return self; 180} 181 182- (void)dealloc 183{ 184 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 185 186 [center removeObserver:self name:NSWindowWillCloseNotification object:nil]; 187 [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil]; 188 [center removeObserver:self name:NSApplicationDidChangeScreenParametersNotification object:nil]; 189 [center removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil]; 190 [NSApp removeObserver:self forKeyPath:@"effectiveAppearance"]; 191 192 // Remove our URL event handler only if we set it 193 if ([NSApp delegate] == self) { 194 [[NSAppleEventManager sharedAppleEventManager] 195 removeEventHandlerForEventClass:kInternetEventClass 196 andEventID:kAEGetURL]; 197 } 198} 199 200- (void)windowWillClose:(NSNotification *)notification 201{ 202 NSWindow *win = (NSWindow *)[notification object]; 203 204 if (![win isKeyWindow]) { 205 return; 206 } 207 208 // Don't do anything if this was not an SDL window that was closed 209 if (FindSDLWindowForNSWindow(win) == NULL) { 210 return; 211 } 212 213 /* HACK: Make the next window in the z-order key when the key window is 214 * closed. The custom event loop and/or windowing code we have seems to 215 * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825 216 */ 217 218 /* +[NSApp orderedWindows] never includes the 'About' window, but we still 219 * want to try its list first since the behavior in other apps is to only 220 * make the 'About' window key if no other windows are on-screen. 221 */ 222 for (NSWindow *window in [NSApp orderedWindows]) { 223 if (window != win && [window canBecomeKeyWindow]) { 224 if (![window isOnActiveSpace]) { 225 continue; 226 } 227 [window makeKeyAndOrderFront:self]; 228 return; 229 } 230 } 231 232 /* If a window wasn't found above, iterate through all visible windows in 233 * the active Space in z-order (including the 'About' window, if it's shown) 234 * and make the first one key. 235 */ 236 for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) { 237 NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]]; 238 if (window && window != win && [window canBecomeKeyWindow]) { 239 [window makeKeyAndOrderFront:self]; 240 return; 241 } 242 } 243} 244 245- (void)focusSomeWindow:(NSNotification *)aNotification 246{ 247 SDL_VideoDevice *device; 248 /* HACK: Ignore the first call. The application gets a 249 * applicationDidBecomeActive: a little bit after the first window is 250 * created, and if we don't ignore it, a window that has been created with 251 * SDL_WINDOW_MINIMIZED will ~immediately be restored. 252 */ 253 if (!seenFirstActivate) { 254 seenFirstActivate = YES; 255 return; 256 } 257 258 /* Don't do anything if the application already has a key window 259 * that is not an SDL window. 260 */ 261 if ([NSApp keyWindow] && FindSDLWindowForNSWindow([NSApp keyWindow]) == NULL) { 262 return; 263 } 264 265 // Restore any fullscreen window 266 device = SDL_GetVideoDevice(); 267 if (device && device->windows) { 268 for (int i = 0; i < device->num_displays; ++i) { 269 SDL_Window *fullscreen_window = device->displays[i]->fullscreen_window; 270 if (fullscreen_window) { 271 if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) { 272 SDL_RestoreWindow(fullscreen_window); 273 } 274 return; 275 } 276 } 277 } 278} 279 280- (void)screenParametersChanged:(NSNotification *)aNotification 281{ 282 SDL_VideoDevice *device = SDL_GetVideoDevice(); 283 if (device) { 284 Cocoa_UpdateDisplays(device); 285 } 286} 287 288- (void)localeDidChange:(NSNotification *)notification 289{ 290 SDL_SendLocaleChangedEvent(); 291} 292 293- (void)observeValueForKeyPath:(NSString *)keyPath 294 ofObject:(id)object 295 change:(NSDictionary *)change 296 context:(void *)context 297{ 298 SDL_SetSystemTheme(Cocoa_GetSystemTheme()); 299} 300 301- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename 302{ 303 return (BOOL)SDL_SendDropFile(NULL, NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL); 304} 305 306- (void)applicationDidFinishLaunching:(NSNotification *)notification 307{ 308 if (!SDL_GetHintBoolean("SDL_MAC_REGISTER_ACTIVATION_HANDLERS", true)) { 309 return; 310 } 311 312 /* The menu bar of SDL apps which don't have the typical .app bundle 313 * structure fails to work the first time a window is created (until it's 314 * de-focused and re-focused), if this call is in Cocoa_RegisterApp instead 315 * of here. https://github.com/libsdl-org/SDL/issues/1913 316 */ 317 318 /* this apparently became unnecessary on macOS 14.0, and will addition pop up a 319 hidden dock if you're moving the mouse during launch, so change the default 320 behaviour there. https://github.com/libsdl-org/SDL/issues/10340 321 (13.6 still needs it, presumably 13.7 does, too.) */ 322 bool background_app_default = false; 323 if (@available(macOS 14.0, *)) { 324 background_app_default = true; /* by default, don't explicitly activate the dock and then us again to force to foreground */ 325 } 326 327 if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, background_app_default)) { 328 // Get more aggressive for Catalina: activate the Dock first so we definitely reset all activation state. 329 for (NSRunningApplication *i in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) { 330 [i activateWithOptions:NSApplicationActivateIgnoringOtherApps]; 331 break; 332 } 333 SDL_Delay(300); // !!! FIXME: this isn't right. 334 [NSApp activateIgnoringOtherApps:YES]; 335 } 336 337 /* If we call this before NSApp activation, macOS might print a complaint 338 * about ApplePersistenceIgnoreState. */ 339 [SDL3Application registerUserDefaults]; 340} 341 342- (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent 343{ 344 NSString *path = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; 345 SDL_SendDropFile(NULL, NULL, [path UTF8String]); 346 SDL_SendDropComplete(NULL); 347} 348 349- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app 350{ 351 // This just tells Cocoa that we didn't do any custom save state magic for the app, 352 // so the system is safe to use NSSecureCoding internally, instead of using unencrypted 353 // save states for backwards compatibility. If we don't return YES here, we'll get a 354 // warning on the console at startup: 355 // 356 // ``` 357 // WARNING: Secure coding is not enabled for restorable state! Enable secure coding by implementing NSApplicationDelegate.applicationSupportsSecureRestorableState: and returning YES. 358 // ``` 359 // 360 // More-detailed explanation: 361 // https://stackoverflow.com/questions/77283578/sonoma-and-nsapplicationdelegate-applicationsupportssecurerestorablestate/77320845#77320845 362 return YES; 363} 364 365- (IBAction)menu:(id)sender 366{ 367 SDL_TrayEntry *entry = [[sender representedObject] pointerValue]; 368 369 SDL_ClickTrayEntry(entry); 370} 371 372@end 373 374static SDL3AppDelegate *appDelegate = nil; 375 376static NSString *GetApplicationName(void) 377{ 378 NSString *appName = nil; 379 380 const char *metaname = SDL_GetStringProperty(SDL_GetGlobalProperties(), SDL_PROP_APP_METADATA_NAME_STRING, NULL); 381 if (metaname && *metaname) { 382 appName = [NSString stringWithUTF8String:metaname]; 383 } 384 385 // Determine the application name 386 if (!appName) { 387 appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; 388 if (!appName) { 389 appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; 390 } 391 } 392 393 if (![appName length]) { 394 appName = [[NSProcessInfo processInfo] processName]; 395 } 396 397 return appName; 398} 399 400static bool LoadMainMenuNibIfAvailable(void) 401{ 402 NSDictionary *infoDict; 403 NSString *mainNibFileName; 404 bool success = false; 405 406 infoDict = [[NSBundle mainBundle] infoDictionary]; 407 if (infoDict) { 408 mainNibFileName = [infoDict valueForKey:@"NSMainNibFile"]; 409 410 if (mainNibFileName) { 411 success = [[NSBundle mainBundle] loadNibNamed:mainNibFileName owner:[NSApplication sharedApplication] topLevelObjects:nil]; 412 } 413 } 414 415 return success; 416} 417 418static void CreateApplicationMenus(void) 419{ 420 NSString *appName; 421 NSString *title; 422 NSMenu *appleMenu; 423 NSMenu *serviceMenu; 424 NSMenu *windowMenu; 425 NSMenuItem *menuItem; 426 NSMenu *mainMenu; 427 428 if (NSApp == nil) { 429 return; 430 } 431 432 mainMenu = [[NSMenu alloc] init]; 433 434 // Create the main menu bar 435 [NSApp setMainMenu:mainMenu]; 436 437 // Create the application menu 438 appName = GetApplicationName(); 439 appleMenu = [[NSMenu alloc] initWithTitle:@""]; 440 441 // Add menu items 442 title = [@"About " stringByAppendingString:appName]; 443 444 // !!! FIXME: Menu items can't take parameters, just a basic selector, so this should instead call a selector 445 // !!! FIXME: that itself calls -[NSApplication orderFrontStandardAboutPanelWithOptions:optionsDictionary], 446 // !!! FIXME: filling in that NSDictionary with SDL_GetAppMetadataProperty() 447 [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; 448 449 [appleMenu addItem:[NSMenuItem separatorItem]]; 450 451 [appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","]; 452 453 [appleMenu addItem:[NSMenuItem separatorItem]]; 454 455 serviceMenu = [[NSMenu alloc] initWithTitle:@""]; 456 menuItem = [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; 457 [menuItem setSubmenu:serviceMenu]; 458 459 [NSApp setServicesMenu:serviceMenu]; 460 461 [appleMenu addItem:[NSMenuItem separatorItem]]; 462 463 title = [@"Hide " stringByAppendingString:appName]; 464 [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; 465 466 menuItem = [appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; 467 [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)]; 468 469 [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; 470 471 [appleMenu addItem:[NSMenuItem separatorItem]]; 472 473 title = [@"Quit " stringByAppendingString:appName]; 474 [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; 475 476 // Put menu into the menubar 477 menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; 478 [menuItem setSubmenu:appleMenu]; 479 [[NSApp mainMenu] addItem:menuItem]; 480 481 // Tell the application object that this is now the application menu 482 [NSApp setAppleMenu:appleMenu]; 483 484 // Create the window menu 485 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; 486 487 // Add menu items 488 [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"]; 489 490 [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; 491 492 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; 493 494 // Add the fullscreen toggle menu option. 495 /* Cocoa should update the title to Enter or Exit Full Screen automatically. 496 * But if not, then just fallback to Toggle Full Screen. 497 */ 498 menuItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; 499 [menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; 500 [windowMenu addItem:menuItem]; 501 502 // Put menu into the menubar 503 menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; 504 [menuItem setSubmenu:windowMenu]; 505 [[NSApp mainMenu] addItem:menuItem]; 506 507 // Tell the application object that this is now the window menu 508 [NSApp setWindowsMenu:windowMenu]; 509} 510 511void Cocoa_RegisterApp(void) 512{ 513 @autoreleasepool { 514 // This can get called more than once! Be careful what you initialize! 515 516 if (NSApp == nil) { 517 [SDL3Application sharedApplication]; 518 SDL_assert(NSApp != nil); 519 520 s_bShouldHandleEventsInSDLApplication = true; 521 522 if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, false)) { 523 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 524 } 525 526 /* If there aren't already menus in place, look to see if there's 527 * a nib we should use. If not, then manually create the basic 528 * menus we meed. 529 */ 530 if ([NSApp mainMenu] == nil) { 531 bool nibLoaded; 532 533 nibLoaded = LoadMainMenuNibIfAvailable(); 534 if (!nibLoaded) { 535 CreateApplicationMenus(); 536 } 537 } 538 [NSApp finishLaunching]; 539 if ([NSApp delegate]) { 540 /* The SDL app delegate calls this in didFinishLaunching if it's 541 * attached to the NSApp, otherwise we need to call it manually. 542 */ 543 [SDL3Application registerUserDefaults]; 544 } 545 } 546 if (NSApp && !appDelegate) { 547 appDelegate = [[SDL3AppDelegate alloc] init]; 548 549 /* If someone else has an app delegate, it means we can't turn a 550 * termination into SDL_Quit, and we can't handle application:openFile: 551 */ 552 if (![NSApp delegate]) { 553 /* Only register the URL event handler if we are being set as the 554 * app delegate to avoid replacing any existing event handler. 555 */ 556 [[NSAppleEventManager sharedAppleEventManager] 557 setEventHandler:appDelegate 558 andSelector:@selector(handleURLEvent:withReplyEvent:) 559 forEventClass:kInternetEventClass 560 andEventID:kAEGetURL]; 561 562 [(NSApplication *)NSApp setDelegate:appDelegate]; 563 } else { 564 appDelegate->seenFirstActivate = YES; 565 } 566 } 567 } 568} 569 570Uint64 Cocoa_GetEventTimestamp(NSTimeInterval nsTimestamp) 571{ 572 static Uint64 timestamp_offset; 573 Uint64 timestamp = (Uint64)(nsTimestamp * SDL_NS_PER_SECOND); 574 Uint64 now = SDL_GetTicksNS(); 575 576 if (!timestamp_offset) { 577 timestamp_offset = (now - timestamp); 578 } 579 timestamp += timestamp_offset; 580 581 if (timestamp > now) { 582 timestamp_offset -= (timestamp - now); 583 timestamp = now; 584 } 585 return timestamp; 586} 587 588int Cocoa_PumpEventsUntilDate(SDL_VideoDevice *_this, NSDate *expiration, bool accumulate) 589{ 590 // Run any existing modal sessions. 591 for (SDL_Window *w = _this->windows; w; w = w->next) { 592 SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)w->internal; 593 if (data.modal_session) { 594 [NSApp runModalSession:data.modal_session]; 595 } 596 } 597 598 for (;;) { 599 NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:expiration inMode:NSDefaultRunLoopMode dequeue:YES]; 600 if (event == nil) { 601 return 0; 602 } 603 604 if (!s_bShouldHandleEventsInSDLApplication) { 605 Cocoa_DispatchEvent(event); 606 } 607 608 // Pass events down to SDL3Application to be handled in sendEvent: 609 [NSApp sendEvent:event]; 610 if (!accumulate) { 611 break; 612 } 613 } 614 return 1; 615} 616 617int Cocoa_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) 618{ 619 @autoreleasepool { 620 if (timeoutNS > 0) { 621 NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow:(double)timeoutNS / SDL_NS_PER_SECOND]; 622 return Cocoa_PumpEventsUntilDate(_this, limitDate, false); 623 } else if (timeoutNS == 0) { 624 return Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], false); 625 } else { 626 while (Cocoa_PumpEventsUntilDate(_this, [NSDate distantFuture], false) == 0) { 627 } 628 } 629 return 1; 630 } 631} 632 633void Cocoa_PumpEvents(SDL_VideoDevice *_this) 634{ 635 @autoreleasepool { 636 Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], true); 637 } 638} 639 640void Cocoa_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window) 641{ 642 @autoreleasepool { 643 NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined 644 location:NSMakePoint(0, 0) 645 modifierFlags:0 646 timestamp:0.0 647 windowNumber:((__bridge SDL_CocoaWindowData *)window->internal).window_number 648 context:nil 649 subtype:0 650 data1:0 651 data2:0]; 652 653 [NSApp postEvent:event atStart:YES]; 654 } 655} 656 657bool Cocoa_SuspendScreenSaver(SDL_VideoDevice *_this) 658{ 659 @autoreleasepool { 660 SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->internal; 661 662 if (data.screensaver_assertion) { 663 IOPMAssertionRelease(data.screensaver_assertion); 664 data.screensaver_assertion = kIOPMNullAssertionID; 665 } 666 667 if (_this->suspend_screensaver) { 668 /* FIXME: this should ideally describe the real reason why the game 669 * called SDL_DisableScreenSaver. Note that the name is only meant to be 670 * seen by macOS power users. there's an additional optional human-readable 671 * (localized) reason parameter which we don't set. 672 */ 673 IOPMAssertionID assertion = kIOPMNullAssertionID; 674 NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"]; 675 IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, 676 (__bridge CFStringRef)name, 677 NULL, NULL, NULL, 0, NULL, 678 &assertion); 679 data.screensaver_assertion = assertion; 680 } 681 } 682 return true; 683} 684 685#endif // SDL_VIDEO_DRIVER_COCOA 686
[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.