Atlas - SDL_cocoaevents.m

Home / ext / SDL2 / src / video / cocoa Lines: 1 | Size: 15765 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#include "SDL_timer.h" 25 26#include "SDL_cocoavideo.h" 27#include "../../events/SDL_events_c.h" 28#include "SDL_assert.h" 29#include "SDL_hints.h" 30 31/* This define was added in the 10.9 SDK. */ 32#ifndef kIOPMAssertPreventUserIdleDisplaySleep 33#define kIOPMAssertPreventUserIdleDisplaySleep kIOPMAssertionTypePreventUserIdleDisplaySleep 34#endif 35 36@interface SDLApplication : NSApplication 37 38- (void)terminate:(id)sender; 39- (void)sendEvent:(NSEvent *)theEvent; 40 41+ (void)registerUserDefaults; 42 43@end 44 45@implementation SDLApplication 46 47// Override terminate to handle Quit and System Shutdown smoothly. 48- (void)terminate:(id)sender 49{ 50 SDL_SendQuit(); 51} 52 53static SDL_bool s_bShouldHandleEventsInSDLApplication = SDL_FALSE; 54 55static void Cocoa_DispatchEvent(NSEvent *theEvent) 56{ 57 SDL_VideoDevice *_this = SDL_GetVideoDevice(); 58 59 switch ([theEvent type]) { 60 case NSEventTypeLeftMouseDown: 61 case NSEventTypeOtherMouseDown: 62 case NSEventTypeRightMouseDown: 63 case NSEventTypeLeftMouseUp: 64 case NSEventTypeOtherMouseUp: 65 case NSEventTypeRightMouseUp: 66 case NSEventTypeLeftMouseDragged: 67 case NSEventTypeRightMouseDragged: 68 case NSEventTypeOtherMouseDragged: /* usually middle mouse dragged */ 69 case NSEventTypeMouseMoved: 70 case NSEventTypeScrollWheel: 71 Cocoa_HandleMouseEvent(_this, theEvent); 72 break; 73 case NSEventTypeKeyDown: 74 case NSEventTypeKeyUp: 75 case NSEventTypeFlagsChanged: 76 Cocoa_HandleKeyEvent(_this, theEvent); 77 break; 78 default: 79 break; 80 } 81} 82 83// Dispatch events here so that we can handle events caught by 84// nextEventMatchingMask in SDL, as well as events caught by other 85// processes (such as CEF) that are passed down to NSApp. 86- (void)sendEvent:(NSEvent *)theEvent 87{ 88 if (s_bShouldHandleEventsInSDLApplication) { 89 Cocoa_DispatchEvent(theEvent); 90 } 91 92 [super sendEvent:theEvent]; 93} 94 95+ (void)registerUserDefaults 96{ 97 NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys: 98 [NSNumber numberWithBool:NO], @"AppleMomentumScrollSupported", 99 [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled", 100 [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState", 101 nil]; 102 [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; 103 [appDefaults release]; 104} 105 106@end // SDLApplication 107 108/* setAppleMenu disappeared from the headers in 10.4 */ 109@interface NSApplication(NSAppleMenu) 110- (void)setAppleMenu:(NSMenu *)menu; 111@end 112 113@interface SDLAppDelegate : NSObject <NSApplicationDelegate> { 114@public 115 BOOL seenFirstActivate; 116} 117 118- (id)init; 119@end 120 121@implementation SDLAppDelegate : NSObject 122- (id)init 123{ 124 self = [super init]; 125 if (self) { 126 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 127 128 seenFirstActivate = NO; 129 130 [center addObserver:self 131 selector:@selector(windowWillClose:) 132 name:NSWindowWillCloseNotification 133 object:nil]; 134 135 [center addObserver:self 136 selector:@selector(focusSomeWindow:) 137 name:NSApplicationDidBecomeActiveNotification 138 object:nil]; 139 } 140 141 return self; 142} 143 144- (void)dealloc 145{ 146 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 147 148 [center removeObserver:self name:NSWindowWillCloseNotification object:nil]; 149 [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil]; 150 151 [super dealloc]; 152} 153 154- (void)windowWillClose:(NSNotification *)notification; 155{ 156 NSWindow *win = (NSWindow*)[notification object]; 157 158 if (![win isKeyWindow]) { 159 return; 160 } 161 162 /* HACK: Make the next window in the z-order key when the key window is 163 * closed. The custom event loop and/or windowing code we have seems to 164 * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825 165 */ 166 167 /* +[NSApp orderedWindows] never includes the 'About' window, but we still 168 * want to try its list first since the behavior in other apps is to only 169 * make the 'About' window key if no other windows are on-screen. 170 */ 171 for (NSWindow *window in [NSApp orderedWindows]) { 172 if (window != win && [window canBecomeKeyWindow]) { 173 if (![window isOnActiveSpace]) { 174 continue; 175 } 176 [window makeKeyAndOrderFront:self]; 177 return; 178 } 179 } 180 181 /* If a window wasn't found above, iterate through all visible windows in 182 * the active Space in z-order (including the 'About' window, if it's shown) 183 * and make the first one key. 184 */ 185 for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) { 186 NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]]; 187 if (window && window != win && [window canBecomeKeyWindow]) { 188 [window makeKeyAndOrderFront:self]; 189 return; 190 } 191 } 192} 193 194- (void)focusSomeWindow:(NSNotification *)aNotification 195{ 196 /* HACK: Ignore the first call. The application gets a 197 * applicationDidBecomeActive: a little bit after the first window is 198 * created, and if we don't ignore it, a window that has been created with 199 * SDL_WINDOW_MINIMIZED will ~immediately be restored. 200 */ 201 if (!seenFirstActivate) { 202 seenFirstActivate = YES; 203 return; 204 } 205 206 SDL_VideoDevice *device = SDL_GetVideoDevice(); 207 if (device && device->windows) { 208 SDL_Window *window = device->windows; 209 int i; 210 for (i = 0; i < device->num_displays; ++i) { 211 SDL_Window *fullscreen_window = device->displays[i].fullscreen_window; 212 if (fullscreen_window) { 213 if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) { 214 SDL_RestoreWindow(fullscreen_window); 215 } 216 return; 217 } 218 } 219 220 if (window->flags & SDL_WINDOW_MINIMIZED) { 221 SDL_RestoreWindow(window); 222 } else { 223 SDL_RaiseWindow(window); 224 } 225 } 226} 227 228- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename 229{ 230 return (BOOL)SDL_SendDropFile(NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL); 231} 232 233- (void)applicationDidFinishLaunching:(NSNotification *)notification 234{ 235 /* The menu bar of SDL apps which don't have the typical .app bundle 236 * structure fails to work the first time a window is created (until it's 237 * de-focused and re-focused), if this call is in Cocoa_RegisterApp instead 238 * of here. https://bugzilla.libsdl.org/show_bug.cgi?id=3051 239 */ 240 if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) { 241 [NSApp activateIgnoringOtherApps:YES]; 242 } 243 244 /* If we call this before NSApp activation, macOS might print a complaint 245 * about ApplePersistenceIgnoreState. */ 246 [SDLApplication registerUserDefaults]; 247} 248@end 249 250static SDLAppDelegate *appDelegate = nil; 251 252static NSString * 253GetApplicationName(void) 254{ 255 NSString *appName; 256 257 /* Determine the application name */ 258 appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]; 259 if (!appName) { 260 appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; 261 } 262 263 if (![appName length]) { 264 appName = [[NSProcessInfo processInfo] processName]; 265 } 266 267 return appName; 268} 269 270static void 271CreateApplicationMenus(void) 272{ 273 NSString *appName; 274 NSString *title; 275 NSMenu *appleMenu; 276 NSMenu *serviceMenu; 277 NSMenu *windowMenu; 278 NSMenu *viewMenu; 279 NSMenuItem *menuItem; 280 NSMenu *mainMenu; 281 282 if (NSApp == nil) { 283 return; 284 } 285 286 mainMenu = [[NSMenu alloc] init]; 287 288 /* Create the main menu bar */ 289 [NSApp setMainMenu:mainMenu]; 290 291 [mainMenu release]; /* we're done with it, let NSApp own it. */ 292 mainMenu = nil; 293 294 /* Create the application menu */ 295 appName = GetApplicationName(); 296 appleMenu = [[NSMenu alloc] initWithTitle:@""]; 297 298 /* Add menu items */ 299 title = [@"About " stringByAppendingString:appName]; 300 [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; 301 302 [appleMenu addItem:[NSMenuItem separatorItem]]; 303 304 [appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","]; 305 306 [appleMenu addItem:[NSMenuItem separatorItem]]; 307 308 serviceMenu = [[NSMenu alloc] initWithTitle:@""]; 309 menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; 310 [menuItem setSubmenu:serviceMenu]; 311 312 [NSApp setServicesMenu:serviceMenu]; 313 [serviceMenu release]; 314 315 [appleMenu addItem:[NSMenuItem separatorItem]]; 316 317 title = [@"Hide " stringByAppendingString:appName]; 318 [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; 319 320 menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; 321 [menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption|NSEventModifierFlagCommand)]; 322 323 [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; 324 325 [appleMenu addItem:[NSMenuItem separatorItem]]; 326 327 title = [@"Quit " stringByAppendingString:appName]; 328 [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; 329 330 /* Put menu into the menubar */ 331 menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; 332 [menuItem setSubmenu:appleMenu]; 333 [[NSApp mainMenu] addItem:menuItem]; 334 [menuItem release]; 335 336 /* Tell the application object that this is now the application menu */ 337 [NSApp setAppleMenu:appleMenu]; 338 [appleMenu release]; 339 340 341 /* Create the window menu */ 342 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; 343 344 /* Add menu items */ 345 [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; 346 347 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; 348 349 /* Put menu into the menubar */ 350 menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; 351 [menuItem setSubmenu:windowMenu]; 352 [[NSApp mainMenu] addItem:menuItem]; 353 [menuItem release]; 354 355 /* Tell the application object that this is now the window menu */ 356 [NSApp setWindowsMenu:windowMenu]; 357 [windowMenu release]; 358 359 360 /* Add the fullscreen view toggle menu option, if supported */ 361 if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) { 362 /* Create the view menu */ 363 viewMenu = [[NSMenu alloc] initWithTitle:@"View"]; 364 365 /* Add menu items */ 366 menuItem = [viewMenu addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"]; 367 [menuItem setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; 368 369 /* Put menu into the menubar */ 370 menuItem = [[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""]; 371 [menuItem setSubmenu:viewMenu]; 372 [[NSApp mainMenu] addItem:menuItem]; 373 [menuItem release]; 374 375 [viewMenu release]; 376 } 377} 378 379void 380Cocoa_RegisterApp(void) 381{ @autoreleasepool 382{ 383 /* This can get called more than once! Be careful what you initialize! */ 384 385 if (NSApp == nil) { 386 [SDLApplication sharedApplication]; 387 SDL_assert(NSApp != nil); 388 389 s_bShouldHandleEventsInSDLApplication = SDL_TRUE; 390 391 if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) { 392 [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; 393 } 394 395 if ([NSApp mainMenu] == nil) { 396 CreateApplicationMenus(); 397 } 398 [NSApp finishLaunching]; 399 if ([NSApp delegate]) { 400 /* The SDL app delegate calls this in didFinishLaunching if it's 401 * attached to the NSApp, otherwise we need to call it manually. 402 */ 403 [SDLApplication registerUserDefaults]; 404 } 405 } 406 if (NSApp && !appDelegate) { 407 appDelegate = [[SDLAppDelegate alloc] init]; 408 409 /* If someone else has an app delegate, it means we can't turn a 410 * termination into SDL_Quit, and we can't handle application:openFile: 411 */ 412 if (![NSApp delegate]) { 413 [(NSApplication *)NSApp setDelegate:appDelegate]; 414 } else { 415 appDelegate->seenFirstActivate = YES; 416 } 417 } 418}} 419 420void 421Cocoa_PumpEvents(_THIS) 422{ @autoreleasepool 423{ 424#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 425 /* Update activity every 30 seconds to prevent screensaver */ 426 SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; 427 if (_this->suspend_screensaver && !data->screensaver_use_iopm) { 428 Uint32 now = SDL_GetTicks(); 429 if (!data->screensaver_activity || 430 SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) { 431 UpdateSystemActivity(UsrActivity); 432 data->screensaver_activity = now; 433 } 434 } 435#endif 436 437 for ( ; ; ) { 438 NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ]; 439 if ( event == nil ) { 440 break; 441 } 442 443 if (!s_bShouldHandleEventsInSDLApplication) { 444 Cocoa_DispatchEvent(event); 445 } 446 447 // Pass events down to SDLApplication to be handled in sendEvent: 448 [NSApp sendEvent:event]; 449 } 450}} 451 452void 453Cocoa_SuspendScreenSaver(_THIS) 454{ @autoreleasepool 455{ 456 SDL_VideoData *data = (SDL_VideoData *)_this->driverdata; 457 458 if (!data->screensaver_use_iopm) { 459 return; 460 } 461 462 if (data->screensaver_assertion) { 463 IOPMAssertionRelease(data->screensaver_assertion); 464 data->screensaver_assertion = 0; 465 } 466 467 if (_this->suspend_screensaver) { 468 /* FIXME: this should ideally describe the real reason why the game 469 * called SDL_DisableScreenSaver. Note that the name is only meant to be 470 * seen by OS X power users. there's an additional optional human-readable 471 * (localized) reason parameter which we don't set. 472 */ 473 NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"]; 474 IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, 475 (CFStringRef) name, 476 NULL, NULL, NULL, 0, NULL, 477 &data->screensaver_assertion); 478 } 479}} 480 481#endif /* SDL_VIDEO_DRIVER_COCOA */ 482 483/* vi: set ts=4 sw=4 expandtab: */ 484
[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.