SDL  2.0
SDL_cocoaevents.m
Go to the documentation of this file.
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 {
56 
57  switch ([theEvent type]) {
58  case NSEventTypeLeftMouseDown:
59  case NSEventTypeOtherMouseDown:
60  case NSEventTypeRightMouseDown:
61  case NSEventTypeLeftMouseUp:
62  case NSEventTypeOtherMouseUp:
63  case NSEventTypeRightMouseUp:
64  case NSEventTypeLeftMouseDragged:
65  case NSEventTypeRightMouseDragged:
66  case NSEventTypeOtherMouseDragged: /* usually middle mouse dragged */
67  case NSEventTypeMouseMoved:
68  case NSEventTypeScrollWheel:
69  Cocoa_HandleMouseEvent(_this, theEvent);
70  break;
71  case NSEventTypeKeyDown:
72  case NSEventTypeKeyUp:
73  case NSEventTypeFlagsChanged:
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 
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:(NSEventModifierFlagOption|NSEventModifierFlagCommand)];
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:NSEventModifierFlagControl | NSEventModifierFlagCommand];
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 
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
395 { @autoreleasepool
396 {
397 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
398  /* Update activity every 30 seconds to prevent screensaver */
401  Uint32 now = SDL_GetTicks();
402  if (!data->screensaver_activity ||
403  SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
404  UpdateSystemActivity(UsrActivity);
405  data->screensaver_activity = now;
406  }
407  }
408 #endif
409 
410  for ( ; ; ) {
411  NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ];
412  if ( event == nil ) {
413  break;
414  }
415 
416  if (!s_bShouldHandleEventsInSDLApplication) {
417  Cocoa_DispatchEvent(event);
418  }
419 
420  // Pass events down to SDLApplication to be handled in sendEvent:
421  [NSApp sendEvent:event];
422  }
423 }}
424 
425 void
427 { @autoreleasepool
428 {
430 
431  if (!data->screensaver_use_iopm) {
432  return;
433  }
434 
435  if (data->screensaver_assertion) {
436  IOPMAssertionRelease(data->screensaver_assertion);
437  data->screensaver_assertion = 0;
438  }
439 
440  if (_this->suspend_screensaver) {
441  /* FIXME: this should ideally describe the real reason why the game
442  * called SDL_DisableScreenSaver. Note that the name is only meant to be
443  * seen by OS X power users. there's an additional optional human-readable
444  * (localized) reason parameter which we don't set.
445  */
446  NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"];
447  IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep,
448  (CFStringRef) name,
449  NULL, NULL, NULL, 0, NULL,
450  &data->screensaver_assertion);
451  }
452 }}
453 
454 #endif /* SDL_VIDEO_DRIVER_COCOA */
455 
456 /* vi: set ts=4 sw=4 expandtab: */
void Cocoa_RegisterApp(void)
GLuint id
void Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
GLuint num
int SDL_SendDropFile(SDL_Window *window, const char *file)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
void Cocoa_SuspendScreenSaver(_THIS)
IOPMAssertionID screensaver_assertion
uint32_t Uint32
Definition: SDL_stdinc.h:181
int SDL_SendDropComplete(SDL_Window *window)
GLuint const GLchar * name
#define SDL_GetHintBoolean
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
static SDL_AudioDeviceID device
Definition: loopwave.c:37
#define SDL_HINT_MAC_BACKGROUND_APP
When set don&#39;t force the SDL app to become a foreground process.
Definition: SDL_hints.h:672
Uint32 screensaver_activity
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define _THIS
struct _cl_event * event
void Cocoa_PumpEvents(_THIS)
BOOL screensaver_use_iopm
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:312
SDL_Window * windows
Definition: SDL_sysvideo.h:313
void Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
SDL_Window * fullscreen_window
Definition: SDL_sysvideo.h:134
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define NULL
Definition: begin_code.h:164
SDL_bool
Definition: SDL_stdinc.h:139
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
The type used to identify a window.
Definition: SDL_sysvideo.h:73
void terminate(int sig)
Definition: testlock.c:45
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:586
SDL_bool suspend_screensaver
Definition: SDL_sysvideo.h:310
GLuint GLsizei GLsizei * length
Uint32 flags
Definition: SDL_sysvideo.h:83
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
GLuint in
#define floor
Definition: math_private.h:37
int SDL_SendQuit(void)
Definition: SDL_quit.c:137