src/video/cocoa/SDL_cocoaevents.m
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
child 11144 cccd37a9c28b
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_COCOA
    24 #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 @end
    42 
    43 @implementation SDLApplication
    44 
    45 // Override terminate to handle Quit and System Shutdown smoothly.
    46 - (void)terminate:(id)sender
    47 {
    48     SDL_SendQuit();
    49 }
    50 
    51 static SDL_bool s_bShouldHandleEventsInSDLApplication = SDL_FALSE;
    52 
    53 static void Cocoa_DispatchEvent(NSEvent *theEvent)
    54 {
    55     SDL_VideoDevice *_this = SDL_GetVideoDevice();
    56 
    57     switch ([theEvent type]) {
    58         case NSLeftMouseDown:
    59         case NSOtherMouseDown:
    60         case NSRightMouseDown:
    61         case NSLeftMouseUp:
    62         case NSOtherMouseUp:
    63         case NSRightMouseUp:
    64         case NSLeftMouseDragged:
    65         case NSRightMouseDragged:
    66         case NSOtherMouseDragged: /* usually middle mouse dragged */
    67         case NSMouseMoved:
    68         case NSScrollWheel:
    69             Cocoa_HandleMouseEvent(_this, theEvent);
    70             break;
    71         case NSKeyDown:
    72         case NSKeyUp:
    73         case NSFlagsChanged:
    74             Cocoa_HandleKeyEvent(_this, theEvent);
    75             break;
    76         default:
    77             break;
    78     }
    79 }
    80 
    81 // Dispatch events here so that we can handle events caught by
    82 // nextEventMatchingMask in SDL, as well as events caught by other
    83 // processes (such as CEF) that are passed down to NSApp.
    84 - (void)sendEvent:(NSEvent *)theEvent
    85 {
    86     if (s_bShouldHandleEventsInSDLApplication) {
    87         Cocoa_DispatchEvent(theEvent);
    88     }
    89 
    90     [super sendEvent:theEvent];
    91 }
    92 
    93 @end // SDLApplication
    94 
    95 /* setAppleMenu disappeared from the headers in 10.4 */
    96 @interface NSApplication(NSAppleMenu)
    97 - (void)setAppleMenu:(NSMenu *)menu;
    98 @end
    99 
   100 @interface SDLAppDelegate : NSObject <NSApplicationDelegate> {
   101 @public
   102     BOOL seenFirstActivate;
   103 }
   104 
   105 - (id)init;
   106 @end
   107 
   108 @implementation SDLAppDelegate : NSObject
   109 - (id)init
   110 {
   111     self = [super init];
   112     if (self) {
   113         NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   114 
   115         seenFirstActivate = NO;
   116 
   117         [center addObserver:self
   118                    selector:@selector(windowWillClose:)
   119                        name:NSWindowWillCloseNotification
   120                      object:nil];
   121 
   122         [center addObserver:self
   123                    selector:@selector(focusSomeWindow:)
   124                        name:NSApplicationDidBecomeActiveNotification
   125                      object:nil];
   126     }
   127 
   128     return self;
   129 }
   130 
   131 - (void)dealloc
   132 {
   133     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   134 
   135     [center removeObserver:self name:NSWindowWillCloseNotification object:nil];
   136     [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
   137 
   138     [super dealloc];
   139 }
   140 
   141 - (void)windowWillClose:(NSNotification *)notification;
   142 {
   143     NSWindow *win = (NSWindow*)[notification object];
   144 
   145     if (![win isKeyWindow]) {
   146         return;
   147     }
   148 
   149     /* HACK: Make the next window in the z-order key when the key window is
   150      * closed. The custom event loop and/or windowing code we have seems to
   151      * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825
   152      */
   153 
   154     /* +[NSApp orderedWindows] never includes the 'About' window, but we still
   155      * want to try its list first since the behavior in other apps is to only
   156      * make the 'About' window key if no other windows are on-screen.
   157      */
   158     for (NSWindow *window in [NSApp orderedWindows]) {
   159         if (window != win && [window canBecomeKeyWindow]) {
   160             if (![window isOnActiveSpace]) {
   161                 continue;
   162             }
   163             [window makeKeyAndOrderFront:self];
   164             return;
   165         }
   166     }
   167 
   168     /* If a window wasn't found above, iterate through all visible windows in
   169      * the active Space in z-order (including the 'About' window, if it's shown)
   170      * and make the first one key.
   171      */
   172     for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) {
   173         NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]];
   174         if (window && window != win && [window canBecomeKeyWindow]) {
   175             [window makeKeyAndOrderFront:self];
   176             return;
   177         }
   178     }
   179 }
   180 
   181 - (void)focusSomeWindow:(NSNotification *)aNotification
   182 {
   183     /* HACK: Ignore the first call. The application gets a
   184      * applicationDidBecomeActive: a little bit after the first window is
   185      * created, and if we don't ignore it, a window that has been created with
   186      * SDL_WINDOW_MINIMIZED will ~immediately be restored.
   187      */
   188     if (!seenFirstActivate) {
   189         seenFirstActivate = YES;
   190         return;
   191     }
   192 
   193     SDL_VideoDevice *device = SDL_GetVideoDevice();
   194     if (device && device->windows) {
   195         SDL_Window *window = device->windows;
   196         int i;
   197         for (i = 0; i < device->num_displays; ++i) {
   198             SDL_Window *fullscreen_window = device->displays[i].fullscreen_window;
   199             if (fullscreen_window) {
   200                 if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) {
   201                     SDL_RestoreWindow(fullscreen_window);
   202                 }
   203                 return;
   204             }
   205         }
   206 
   207         if (window->flags & SDL_WINDOW_MINIMIZED) {
   208             SDL_RestoreWindow(window);
   209         } else {
   210             SDL_RaiseWindow(window);
   211         }
   212     }
   213 }
   214 
   215 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
   216 {
   217     return (BOOL)SDL_SendDropFile(NULL, [filename UTF8String]) && SDL_SendDropComplete(NULL);
   218 }
   219 @end
   220 
   221 static SDLAppDelegate *appDelegate = nil;
   222 
   223 static NSString *
   224 GetApplicationName(void)
   225 {
   226     NSString *appName;
   227 
   228     /* Determine the application name */
   229     appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
   230     if (!appName) {
   231         appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
   232     }
   233 
   234     if (![appName length]) {
   235         appName = [[NSProcessInfo processInfo] processName];
   236     }
   237 
   238     return appName;
   239 }
   240 
   241 static void
   242 CreateApplicationMenus(void)
   243 {
   244     NSString *appName;
   245     NSString *title;
   246     NSMenu *appleMenu;
   247     NSMenu *serviceMenu;
   248     NSMenu *windowMenu;
   249     NSMenu *viewMenu;
   250     NSMenuItem *menuItem;
   251     NSMenu *mainMenu;
   252 
   253     if (NSApp == nil) {
   254         return;
   255     }
   256 
   257     mainMenu = [[NSMenu alloc] init];
   258 
   259     /* Create the main menu bar */
   260     [NSApp setMainMenu:mainMenu];
   261 
   262     [mainMenu release];  /* we're done with it, let NSApp own it. */
   263     mainMenu = nil;
   264 
   265     /* Create the application menu */
   266     appName = GetApplicationName();
   267     appleMenu = [[NSMenu alloc] initWithTitle:@""];
   268 
   269     /* Add menu items */
   270     title = [@"About " stringByAppendingString:appName];
   271     [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
   272 
   273     [appleMenu addItem:[NSMenuItem separatorItem]];
   274 
   275     [appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","];
   276 
   277     [appleMenu addItem:[NSMenuItem separatorItem]];
   278 
   279     serviceMenu = [[NSMenu alloc] initWithTitle:@""];
   280     menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""];
   281     [menuItem setSubmenu:serviceMenu];
   282 
   283     [NSApp setServicesMenu:serviceMenu];
   284     [serviceMenu release];
   285 
   286     [appleMenu addItem:[NSMenuItem separatorItem]];
   287 
   288     title = [@"Hide " stringByAppendingString:appName];
   289     [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
   290 
   291     menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
   292     [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
   293 
   294     [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
   295 
   296     [appleMenu addItem:[NSMenuItem separatorItem]];
   297 
   298     title = [@"Quit " stringByAppendingString:appName];
   299     [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
   300 
   301     /* Put menu into the menubar */
   302     menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
   303     [menuItem setSubmenu:appleMenu];
   304     [[NSApp mainMenu] addItem:menuItem];
   305     [menuItem release];
   306 
   307     /* Tell the application object that this is now the application menu */
   308     [NSApp setAppleMenu:appleMenu];
   309     [appleMenu release];
   310 
   311 
   312     /* Create the window menu */
   313     windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
   314 
   315     /* Add menu items */
   316     [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
   317 
   318     [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
   319 
   320     /* Put menu into the menubar */
   321     menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
   322     [menuItem setSubmenu:windowMenu];
   323     [[NSApp mainMenu] addItem:menuItem];
   324     [menuItem release];
   325 
   326     /* Tell the application object that this is now the window menu */
   327     [NSApp setWindowsMenu:windowMenu];
   328     [windowMenu release];
   329 
   330 
   331     /* Add the fullscreen view toggle menu option, if supported */
   332     if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) {
   333         /* Create the view menu */
   334         viewMenu = [[NSMenu alloc] initWithTitle:@"View"];
   335 
   336         /* Add menu items */
   337         menuItem = [viewMenu addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
   338         [menuItem setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
   339 
   340         /* Put menu into the menubar */
   341         menuItem = [[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""];
   342         [menuItem setSubmenu:viewMenu];
   343         [[NSApp mainMenu] addItem:menuItem];
   344         [menuItem release];
   345 
   346         [viewMenu release];
   347     }
   348 }
   349 
   350 void
   351 Cocoa_RegisterApp(void)
   352 { @autoreleasepool
   353 {
   354     /* This can get called more than once! Be careful what you initialize! */
   355 
   356     if (NSApp == nil) {
   357         [SDLApplication sharedApplication];
   358         SDL_assert(NSApp != nil);
   359 
   360         s_bShouldHandleEventsInSDLApplication = SDL_TRUE;
   361 
   362         if (!SDL_GetHintBoolean(SDL_HINT_MAC_BACKGROUND_APP, SDL_FALSE)) {
   363             [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
   364             [NSApp activateIgnoringOtherApps:YES];
   365 		}
   366 		
   367         if ([NSApp mainMenu] == nil) {
   368             CreateApplicationMenus();
   369         }
   370         [NSApp finishLaunching];
   371         NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:
   372             [NSNumber numberWithBool:NO], @"AppleMomentumScrollSupported",
   373             [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled",
   374             [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState",
   375             nil];
   376         [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
   377         [appDefaults release];
   378     }
   379     if (NSApp && !appDelegate) {
   380         appDelegate = [[SDLAppDelegate alloc] init];
   381 
   382         /* If someone else has an app delegate, it means we can't turn a
   383          * termination into SDL_Quit, and we can't handle application:openFile:
   384          */
   385         if (![NSApp delegate]) {
   386             [(NSApplication *)NSApp setDelegate:appDelegate];
   387         } else {
   388             appDelegate->seenFirstActivate = YES;
   389         }
   390     }
   391 }}
   392 
   393 void
   394 Cocoa_PumpEvents(_THIS)
   395 { @autoreleasepool
   396 {
   397     /* Update activity every 30 seconds to prevent screensaver */
   398     SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
   399     if (_this->suspend_screensaver && !data->screensaver_use_iopm) {
   400         Uint32 now = SDL_GetTicks();
   401         if (!data->screensaver_activity ||
   402             SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
   403             UpdateSystemActivity(UsrActivity);
   404             data->screensaver_activity = now;
   405         }
   406     }
   407 
   408     for ( ; ; ) {
   409         NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ];
   410         if ( event == nil ) {
   411             break;
   412         }
   413 
   414         if (!s_bShouldHandleEventsInSDLApplication) {
   415             Cocoa_DispatchEvent(event);
   416         }
   417 
   418         // Pass events down to SDLApplication to be handled in sendEvent:
   419         [NSApp sendEvent:event];
   420     }
   421 }}
   422 
   423 void
   424 Cocoa_SuspendScreenSaver(_THIS)
   425 { @autoreleasepool
   426 {
   427     SDL_VideoData *data = (SDL_VideoData *)_this->driverdata;
   428 
   429     if (!data->screensaver_use_iopm) {
   430         return;
   431     }
   432 
   433     if (data->screensaver_assertion) {
   434         IOPMAssertionRelease(data->screensaver_assertion);
   435         data->screensaver_assertion = 0;
   436     }
   437 
   438     if (_this->suspend_screensaver) {
   439         /* FIXME: this should ideally describe the real reason why the game
   440          * called SDL_DisableScreenSaver. Note that the name is only meant to be
   441          * seen by OS X power users. there's an additional optional human-readable
   442          * (localized) reason parameter which we don't set.
   443          */
   444         NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"];
   445         IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep,
   446                                            (CFStringRef) name,
   447                                            NULL, NULL, NULL, 0, NULL,
   448                                            &data->screensaver_assertion);
   449     }
   450 }}
   451 
   452 #endif /* SDL_VIDEO_DRIVER_COCOA */
   453 
   454 /* vi: set ts=4 sw=4 expandtab: */