SDL  2.0
SDL_cocoaevents.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 
40 @end
41 
42 @implementation SDLApplication
43 
44 // Override terminate to handle Quit and System Shutdown smoothly.
45 - (void)terminate:(id)sender
46 {
47  SDL_SendQuit();
48 }
49 
50 @end // SDLApplication
51 
52 /* setAppleMenu disappeared from the headers in 10.4 */
53 @interface NSApplication(NSAppleMenu)
54 - (void)setAppleMenu:(NSMenu *)menu;
55 @end
56 
57 @interface SDLAppDelegate : NSObject <NSApplicationDelegate> {
58 @public
59  BOOL seenFirstActivate;
60 }
61 
62 - (id)init;
63 @end
64 
65 @implementation SDLAppDelegate : NSObject
66 - (id)init
67 {
68  self = [super init];
69  if (self) {
70  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
71 
72  seenFirstActivate = NO;
73 
74  [center addObserver:self
75  selector:@selector(windowWillClose:)
76  name:NSWindowWillCloseNotification
77  object:nil];
78 
79  [center addObserver:self
80  selector:@selector(focusSomeWindow:)
81  name:NSApplicationDidBecomeActiveNotification
82  object:nil];
83  }
84 
85  return self;
86 }
87 
88 - (void)dealloc
89 {
90  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
91 
92  [center removeObserver:self name:NSWindowWillCloseNotification object:nil];
93  [center removeObserver:self name:NSApplicationDidBecomeActiveNotification object:nil];
94 
95  [super dealloc];
96 }
97 
98 - (void)windowWillClose:(NSNotification *)notification;
99 {
100  NSWindow *win = (NSWindow*)[notification object];
101 
102  if (![win isKeyWindow]) {
103  return;
104  }
105 
106  /* HACK: Make the next window in the z-order key when the key window is
107  * closed. The custom event loop and/or windowing code we have seems to
108  * prevent the normal behavior: https://bugzilla.libsdl.org/show_bug.cgi?id=1825
109  */
110 
111  /* +[NSApp orderedWindows] never includes the 'About' window, but we still
112  * want to try its list first since the behavior in other apps is to only
113  * make the 'About' window key if no other windows are on-screen.
114  */
115  for (NSWindow *window in [NSApp orderedWindows]) {
116  if (window != win && [window canBecomeKeyWindow]) {
117  if ([window respondsToSelector:@selector(isOnActiveSpace)]) {
118  if (![window isOnActiveSpace]) {
119  continue;
120  }
121  }
122  [window makeKeyAndOrderFront:self];
123  return;
124  }
125  }
126 
127  /* If a window wasn't found above, iterate through all visible windows
128  * (including the 'About' window, if it's shown) and make the first one key.
129  * Note that +[NSWindow windowNumbersWithOptions:] was added in 10.6.
130  */
131  if ([NSWindow respondsToSelector:@selector(windowNumbersWithOptions:)]) {
132  /* Get all visible windows in the active Space, in z-order. */
133  for (NSNumber *num in [NSWindow windowNumbersWithOptions:0]) {
134  NSWindow *window = [NSApp windowWithWindowNumber:[num integerValue]];
135  if (window && window != win && [window canBecomeKeyWindow]) {
136  [window makeKeyAndOrderFront:self];
137  return;
138  }
139  }
140  }
141 }
142 
143 - (void)focusSomeWindow:(NSNotification *)aNotification
144 {
145  /* HACK: Ignore the first call. The application gets a
146  * applicationDidBecomeActive: a little bit after the first window is
147  * created, and if we don't ignore it, a window that has been created with
148  * SDL_WINDOW_MINIMIZED will ~immediately be restored.
149  */
150  if (!seenFirstActivate) {
151  seenFirstActivate = YES;
152  return;
153  }
154 
156  if (device && device->windows) {
157  SDL_Window *window = device->windows;
158  int i;
159  for (i = 0; i < device->num_displays; ++i) {
160  SDL_Window *fullscreen_window = device->displays[i].fullscreen_window;
161  if (fullscreen_window) {
162  if (fullscreen_window->flags & SDL_WINDOW_MINIMIZED) {
163  SDL_RestoreWindow(fullscreen_window);
164  }
165  return;
166  }
167  }
168 
169  if (window->flags & SDL_WINDOW_MINIMIZED) {
170  SDL_RestoreWindow(window);
171  } else {
172  SDL_RaiseWindow(window);
173  }
174  }
175 }
176 
177 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
178 {
179  return (BOOL)SDL_SendDropFile([filename UTF8String]);
180 }
181 @end
182 
183 static SDLAppDelegate *appDelegate = nil;
184 
185 static NSString *
186 GetApplicationName(void)
187 {
188  NSString *appName;
189 
190  /* Determine the application name */
191  appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
192  if (!appName) {
193  appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
194  }
195 
196  if (![appName length]) {
197  appName = [[NSProcessInfo processInfo] processName];
198  }
199 
200  return appName;
201 }
202 
203 static void
204 CreateApplicationMenus(void)
205 {
206  NSString *appName;
207  NSString *title;
208  NSMenu *appleMenu;
209  NSMenu *serviceMenu;
210  NSMenu *windowMenu;
211  NSMenu *viewMenu;
212  NSMenuItem *menuItem;
213  NSMenu *mainMenu;
214 
215  if (NSApp == nil) {
216  return;
217  }
218 
219  mainMenu = [[NSMenu alloc] init];
220 
221  /* Create the main menu bar */
222  [NSApp setMainMenu:mainMenu];
223 
224  [mainMenu release]; /* we're done with it, let NSApp own it. */
225  mainMenu = nil;
226 
227  /* Create the application menu */
228  appName = GetApplicationName();
229  appleMenu = [[NSMenu alloc] initWithTitle:@""];
230 
231  /* Add menu items */
232  title = [@"About " stringByAppendingString:appName];
233  [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
234 
235  [appleMenu addItem:[NSMenuItem separatorItem]];
236 
237  [appleMenu addItemWithTitle:@"Preferences…" action:nil keyEquivalent:@","];
238 
239  [appleMenu addItem:[NSMenuItem separatorItem]];
240 
241  serviceMenu = [[NSMenu alloc] initWithTitle:@""];
242  menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""];
243  [menuItem setSubmenu:serviceMenu];
244 
245  [NSApp setServicesMenu:serviceMenu];
246  [serviceMenu release];
247 
248  [appleMenu addItem:[NSMenuItem separatorItem]];
249 
250  title = [@"Hide " stringByAppendingString:appName];
251  [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
252 
253  menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
254  [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
255 
256  [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
257 
258  [appleMenu addItem:[NSMenuItem separatorItem]];
259 
260  title = [@"Quit " stringByAppendingString:appName];
261  [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
262 
263  /* Put menu into the menubar */
264  menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
265  [menuItem setSubmenu:appleMenu];
266  [[NSApp mainMenu] addItem:menuItem];
267  [menuItem release];
268 
269  /* Tell the application object that this is now the application menu */
270  [NSApp setAppleMenu:appleMenu];
271  [appleMenu release];
272 
273 
274  /* Create the window menu */
275  windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
276 
277  /* Add menu items */
278  [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
279 
280  [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
281 
282  /* Put menu into the menubar */
283  menuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
284  [menuItem setSubmenu:windowMenu];
285  [[NSApp mainMenu] addItem:menuItem];
286  [menuItem release];
287 
288  /* Tell the application object that this is now the window menu */
289  [NSApp setWindowsMenu:windowMenu];
290  [windowMenu release];
291 
292 
293  /* Add the fullscreen view toggle menu option, if supported */
294  if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) {
295  /* Create the view menu */
296  viewMenu = [[NSMenu alloc] initWithTitle:@"View"];
297 
298  /* Add menu items */
299  menuItem = [viewMenu addItemWithTitle:@"Toggle Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"];
300  [menuItem setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask];
301 
302  /* Put menu into the menubar */
303  menuItem = [[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""];
304  [menuItem setSubmenu:viewMenu];
305  [[NSApp mainMenu] addItem:menuItem];
306  [menuItem release];
307 
308  [viewMenu release];
309  }
310 }
311 
312 void
313 Cocoa_RegisterApp(void)
314 { @autoreleasepool
315 {
316  /* This can get called more than once! Be careful what you initialize! */
317 
318  if (NSApp == nil) {
319  [SDLApplication sharedApplication];
320  SDL_assert(NSApp != nil);
321 
322  const char *hint = SDL_GetHint(SDL_HINT_MAC_BACKGROUND_APP);
323  if (!hint || *hint == '0') {
324 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
325  if ([NSApp respondsToSelector:@selector(setActivationPolicy:)]) {
326 #endif
327  [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
328 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
329  } else {
330  ProcessSerialNumber psn = {0, kCurrentProcess};
331  TransformProcessType(&psn, kProcessTransformToForegroundApplication);
332  }
333 #endif
334  [NSApp activateIgnoringOtherApps:YES];
335  }
336 
337  if ([NSApp mainMenu] == nil) {
338  CreateApplicationMenus();
339  }
340  [NSApp finishLaunching];
341  NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:
342  [NSNumber numberWithBool:NO], @"AppleMomentumScrollSupported",
343  [NSNumber numberWithBool:NO], @"ApplePressAndHoldEnabled",
344  [NSNumber numberWithBool:YES], @"ApplePersistenceIgnoreState",
345  nil];
346  [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
347  [appDefaults release];
348  }
349  if (NSApp && !appDelegate) {
350  appDelegate = [[SDLAppDelegate alloc] init];
351 
352  /* If someone else has an app delegate, it means we can't turn a
353  * termination into SDL_Quit, and we can't handle application:openFile:
354  */
355  if (![NSApp delegate]) {
356  [(NSApplication *)NSApp setDelegate:appDelegate];
357  } else {
358  appDelegate->seenFirstActivate = YES;
359  }
360  }
361 }}
362 
363 void
365 { @autoreleasepool
366 {
367  /* Update activity every 30 seconds to prevent screensaver */
370  Uint32 now = SDL_GetTicks();
371  if (!data->screensaver_activity ||
372  SDL_TICKS_PASSED(now, data->screensaver_activity + 30000)) {
373  UpdateSystemActivity(UsrActivity);
374  data->screensaver_activity = now;
375  }
376  }
377 
378  for ( ; ; ) {
379  NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ];
380  if ( event == nil ) {
381  break;
382  }
383 
384  switch ([event type]) {
385  case NSLeftMouseDown:
386  case NSOtherMouseDown:
387  case NSRightMouseDown:
388  case NSLeftMouseUp:
389  case NSOtherMouseUp:
390  case NSRightMouseUp:
391  case NSLeftMouseDragged:
392  case NSRightMouseDragged:
393  case NSOtherMouseDragged: /* usually middle mouse dragged */
394  case NSMouseMoved:
395  case NSScrollWheel:
397  break;
398  case NSKeyDown:
399  case NSKeyUp:
400  case NSFlagsChanged:
402  break;
403  default:
404  break;
405  }
406  /* Pass through to NSApp to make sure everything stays in sync */
407  [NSApp sendEvent:event];
408  }
409 }}
410 
411 void
413 { @autoreleasepool
414 {
416 
417  if (!data->screensaver_use_iopm) {
418  return;
419  }
420 
421  if (data->screensaver_assertion) {
422  IOPMAssertionRelease(data->screensaver_assertion);
423  data->screensaver_assertion = 0;
424  }
425 
426  if (_this->suspend_screensaver) {
427  /* FIXME: this should ideally describe the real reason why the game
428  * called SDL_DisableScreenSaver. Note that the name is only meant to be
429  * seen by OS X power users. there's an additional optional human-readable
430  * (localized) reason parameter which we don't set.
431  */
432  NSString *name = [GetApplicationName() stringByAppendingString:@" using SDL_DisableScreenSaver"];
433  IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep,
434  (CFStringRef) name,
435  NULL, NULL, NULL, 0, NULL,
436  &data->screensaver_assertion);
437  }
438 }}
439 
440 #endif /* SDL_VIDEO_DRIVER_COCOA */
441 
442 /* vi: set ts=4 sw=4 expandtab: */
void Cocoa_RegisterApp(void)
GLuint id
void Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
GLuint num
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:155
SDL_Window * window
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
#define SDL_GetHint
void Cocoa_SuspendScreenSaver(_THIS)
GLuint const GLchar * name
IOPMAssertionID screensaver_assertion
static SDL_VideoDevice * _this
Definition: SDL_video.c:114
#define SDL_HINT_MAC_BACKGROUND_APP
When set don&#39;t force the SDL app to become a foreground process.
Definition: SDL_hints.h:548
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1564
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:280
SDL_Window * windows
Definition: SDL_sysvideo.h:281
void Cocoa_HandleMouseEvent(_THIS, NSEvent *event)
SDL_Window * fullscreen_window
Definition: SDL_sysvideo.h:129
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:42
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
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
int SDL_SendDropFile(const char *file)
The type used to identify a window.
Definition: SDL_sysvideo.h:71
void terminate(int sig)
Definition: testlock.c:45
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:573
SDL_bool suspend_screensaver
Definition: SDL_sysvideo.h:278
GLuint GLsizei GLsizei * length
Uint32 flags
Definition: SDL_sysvideo.h:81
#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
int SDL_SendQuit(void)
Definition: SDL_quit.c:139