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