1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/app.mm
4 // Author: David Elliott
8 // Copyright: (c) David Elliott
10 // Licence: wxWidgets licence
11 /////////////////////////////////////////////////////////////////////////////
13 #include "wx/wxprec.h"
20 #include "wx/module.h"
23 #include "wx/cocoa/ObjcRef.h"
24 #include "wx/cocoa/autorelease.h"
25 #include "wx/cocoa/mbarman.h"
26 #include "wx/cocoa/NSApplication.h"
28 #include "wx/cocoa/dc.h"
30 #import <AppKit/NSApplication.h>
31 #import <Foundation/NSRunLoop.h>
32 #import <Foundation/NSThread.h>
33 #import <AppKit/NSEvent.h>
34 #import <Foundation/NSString.h>
35 #import <Foundation/NSNotification.h>
36 #import <AppKit/NSCell.h>
38 bool wxApp::sm_isEmbedded = false; // Normally we're not a plugin
40 // wxNSApplicationObserver singleton.
41 static wxObjcAutoRefFromAlloc<wxNSApplicationObserver*> sg_cocoaAppObserver = [[WX_GET_OBJC_CLASS(wxNSApplicationObserver) alloc] init];
43 // ========================================================================
44 // wxNSApplicationDelegate
45 // ========================================================================
46 @implementation wxNSApplicationDelegate : NSObject
48 // NOTE: Terminate means that the event loop does NOT return and thus
49 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
50 // own exit on frame delete mechanism.
51 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
56 @end // implementation wxNSApplicationDelegate : NSObject
57 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSApplicationDelegate,NSObject)
59 // ========================================================================
60 // wxNSApplicationObserver
61 // ========================================================================
62 @implementation wxNSApplicationObserver : NSObject
64 - (void)applicationWillBecomeActive:(NSNotification *)notification
66 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
69 - (void)applicationDidBecomeActive:(NSNotification *)notification
71 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
74 - (void)applicationWillResignActive:(NSNotification *)notification
76 wxTheApp->CocoaDelegate_applicationWillResignActive();
79 - (void)applicationDidResignActive:(NSNotification *)notification
81 wxTheApp->CocoaDelegate_applicationDidResignActive();
84 - (void)applicationWillUpdate:(NSNotification *)notification;
86 wxTheApp->CocoaDelegate_applicationWillUpdate();
89 - (void)controlTintChanged:(NSNotification *)notification
91 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
94 @end // implementation wxNSApplicationObserver : NSObject
95 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSApplicationObserver,NSObject)
97 // ========================================================================
99 // ========================================================================
101 // ----------------------------------------------------------------------------
102 // wxApp Static member initialization
103 // ----------------------------------------------------------------------------
104 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
106 // ----------------------------------------------------------------------------
107 // wxApp initialization/cleanup
108 // ----------------------------------------------------------------------------
109 bool wxApp::Initialize(int& argc, wxChar **argv)
111 wxAutoNSAutoreleasePool pool;
112 m_cocoaMainThread = [NSThread currentThread];
113 // Mac OS X passes a process serial number command line argument when
114 // the application is launched from the Finder. This argument must be
115 // removed from the command line arguments before being handled by the
116 // application (otherwise applications would need to handle it)
119 static const wxChar *ARG_PSN = _T("-psn_");
120 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
122 // remove this argument
124 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
129 Cocoa supports -Key value options which set the user defaults key "Key"
130 to the value "value" Some of them are very handy for debugging like
131 -NSShowAllViews YES. Cocoa picks these up from the real argv so
132 our removal of them from the wx copy of it does not affect Cocoa's
135 We basically just assume that any "-NS" option and its following
136 argument needs to be removed from argv. We hope that user code does
137 not expect to see -NS options and indeed it's probably a safe bet
138 since most user code accepting options is probably using the
139 double-dash GNU-style syntax.
141 for(int i=1; i < argc; ++i)
143 static const wxChar *ARG_NS = wxT("-NS");
144 static const int ARG_NS_LEN = wxStrlen(ARG_NS);
145 if( wxStrncmp(argv[i], ARG_NS, ARG_NS_LEN) == 0 )
147 // Only eat this option if it has an argument
151 memmove(argv + i, argv + i + 2, argc * sizeof(wxChar*));
152 // drop back one position so the next run through the loop
153 // reprocesses the argument at our current index.
159 return wxAppBase::Initialize(argc, argv);
162 void wxApp::CleanUp()
164 wxAutoNSAutoreleasePool pool;
166 wxCocoaDCImpl::CocoaShutdownTextSystem();
167 wxMenuBarManager::DestroyInstance();
169 [[NSNotificationCenter defaultCenter] removeObserver:sg_cocoaAppObserver];
172 [m_cocoaApp setDelegate:nil];
173 [m_cocoaAppDelegate release];
174 m_cocoaAppDelegate = NULL;
177 wxAppBase::CleanUp();
180 // ----------------------------------------------------------------------------
182 // ----------------------------------------------------------------------------
188 m_isInAssert = false;
189 #endif // __WXDEBUG__
196 m_cocoaAppDelegate = NULL;
199 void wxApp::CocoaDelegate_applicationWillBecomeActive()
203 void wxApp::CocoaDelegate_applicationDidBecomeActive()
207 void wxApp::CocoaDelegate_applicationWillResignActive()
209 wxTopLevelWindowCocoa::DeactivatePendingWindow();
212 void wxApp::CocoaDelegate_applicationDidResignActive()
216 bool wxApp::OnInitGui()
218 wxAutoNSAutoreleasePool pool;
219 if(!wxAppBase::OnInitGui())
222 // Create the app using the sharedApplication method
223 m_cocoaApp = [NSApplication sharedApplication];
227 // Enable response to application delegate messages
228 m_cocoaAppDelegate = [[WX_GET_OBJC_CLASS(wxNSApplicationDelegate) alloc] init];
229 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
232 // Enable response to "delegate" messages on the notification observer
233 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
234 selector:@selector(applicationWillBecomeActive:)
235 name:NSApplicationWillBecomeActiveNotification object:nil];
237 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
238 selector:@selector(applicationDidBecomeActive:)
239 name:NSApplicationDidBecomeActiveNotification object:nil];
241 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
242 selector:@selector(applicationWillResignActive:)
243 name:NSApplicationWillResignActiveNotification object:nil];
245 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
246 selector:@selector(applicationDidResignActive:)
247 name:NSApplicationDidResignActiveNotification object:nil];
249 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
250 selector:@selector(applicationWillUpdate:)
251 name:NSApplicationWillUpdateNotification object:nil];
253 // Enable response to system notifications
254 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
255 selector:@selector(controlTintChanged:)
256 name:NSControlTintDidChangeNotification object:nil];
259 wxMenuBarManager::CreateInstance();
261 wxCocoaDCImpl::CocoaInitializeTextSystem();
267 if(m_cfRunLoopIdleObserver != NULL)
269 // Invalidate the observer which also removes it from the run loop.
270 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
271 // Release the ref as we don't need it anymore.
272 m_cfRunLoopIdleObserver.reset();
276 bool wxApp::CallOnInit()
278 // wxAutoNSAutoreleasePool pool;
284 if(!wxAppBase::OnInit())
294 wxAppConsole::Exit();
297 // Yield to other processes
298 bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess)
301 // disable log flushing from here because a call to wxYield() shouldn't
302 // normally result in message boxes popping up &c
310 wxFAIL_MSG( wxT("wxYield called recursively" ) );
316 m_isInsideYield = true;
317 m_eventsToProcessInsideYield = eventsToProcess;
319 // Run the event loop until it is out of events
322 // TODO: implement event filtering using the eventsToProcess mask
324 wxAutoNSAutoreleasePool pool;
325 /* NOTE: It may be better to use something like
326 NSEventTrackingRunLoopMode since we don't necessarily want all
327 timers/sources/observers to run, only those which would
328 run while tracking events. However, it should be noted that
329 NSEventTrackingRunLoopMode is in the common set of modes
330 so it may not effectively make much of a difference.
332 NSEvent *event = [GetNSApplication()
333 nextEventMatchingMask:NSAnyEventMask
334 untilDate:[NSDate distantPast]
335 inMode:NSDefaultRunLoopMode
339 [GetNSApplication() sendEvent: event];
343 Because we just told NSApplication to avoid blocking it will in turn
344 run the CFRunLoop with a timeout of 0 seconds. In that case, our
345 run loop observer on kCFRunLoopBeforeWaiting never fires because
346 no waiting occurs. Therefore, no idle events are sent.
348 Believe it or not, this is actually desirable because we do not want
349 to process idle from here. However, we do want to process pending
350 events because some user code expects to do work in a thread while
351 the main thread waits and then notify the main thread by posting
354 ProcessPendingEvents();
357 // let the logs be flashed again
361 m_isInsideYield = false;
366 void wxApp::WakeUpIdle()
368 /* When called from the main thread the NSAutoreleasePool managed by
369 the [NSApplication run] method would ordinarily be in place and so
370 one would think a pool here would be unnecessary.
372 However, when called from a different thread there is usually no
373 NSAutoreleasePool in place because wxThread has no knowledge of
374 wxCocoa. The pool here is generally only ever going to contain
375 the NSEvent we create with the factory method. As soon as we add
376 it to the main event queue with postEvent:atStart: it is retained
377 and so safe for our pool to release.
379 wxAutoNSAutoreleasePool pool;
380 /* NOTE: This is a little heavy handed. What this does is cause an
381 AppKit NSEvent to be added to NSApplication's queue (which is always
382 on the main thread). This will cause the main thread runloop to
383 exit which returns control to nextEventMatchingMask which returns
384 the event which is then sent with sendEvent: and essentially dropped
385 since it's not for a window (windowNumber 0) and NSApplication
386 certainly doesn't understand it.
388 With the exception of wxEventLoop::Exit which uses us to cause the
389 runloop to exit and return to the NSApplication event loop, most
390 callers only need wx idle to happen, or more specifically only really
391 need to ensure that ProcessPendingEvents is called which is currently
392 done without exiting the runloop.
394 Be careful if you decide to change the implementation of this method
395 as wxEventLoop::Exit depends on the current behavior.
397 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
398 location:NSZeroPoint modifierFlags:NSAnyEventMask
399 timestamp:0 windowNumber:0 context:nil
400 subtype:0 data1:0 data2:0] atStart:NO];
403 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
404 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
406 static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
410 static int sg_cApplicationWillUpdate = 0;
414 Invoked from the applicationWillUpdate notification observer. See the
415 NSApplication documentation for the official statement on when this
416 will be called. Since it can be hard to understand for a Cocoa newbie
417 I'll try to explain it here as it relates to wxCocoa.
419 Basically, we get called from within nextEventMatchingMask if and only
420 if any user code told the application to send the update notification
421 (sort of like a request for idle events). However, unlike wx idle events,
422 this notification is sent quite often, nearly every time through the loop
423 because nearly every control tells the application to send it.
425 Because wx idle events are only supposed to be sent when the event loop
426 is about to block we instead schedule a function to be called just
427 before the run loop waits and send the idle events from there.
429 It also has the desirable effect of only sending the wx idle events when
430 the event loop is actualy going to block. If the event loop is being
431 pumped manualy (e.g. like a PeekMessage) then the kCFRunLoopBeforeWaiting
432 observer never fires. Our Yield() method depends on this because sending
433 idle events from within Yield would be bad.
435 Normally you might think that we could just set the observer up once and
436 leave it attached. However, this is problematic because our run loop
437 observer calls user code (the idle handlers) which can actually display
438 modal dialogs. Displaying a modal dialog causes reentry of the event
439 loop, usually in a different run loop mode than the main loop (e.g. in
440 modal-dialog mode instead of default mode). Because we only register the
441 observer with the run loop mode at the time of this call, it won't be
442 called from a modal loop.
444 We want it to be called and thus we need a new observer.
446 void wxApp::CocoaDelegate_applicationWillUpdate()
448 wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
450 // CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
451 CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
452 wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
454 /* If we have an observer and that observer is for the wrong run loop
455 mode then invalidate it and release it.
457 if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
459 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
460 m_cfRunLoopIdleObserver.reset();
463 ++sg_cApplicationWillUpdate;
465 /* This will be true either on the first call or when the above code has
466 invalidated and released the exisiting observer.
468 if(m_cfRunLoopIdleObserver == NULL)
470 // Enable idle event handling
471 CFRunLoopObserverContext observerContext =
478 /* NOTE: I can't recall why we don't just let the observer repeat
479 instead of invalidating itself each time it fires thus requiring
480 it to be recreated for each shot but there was if I remember
481 some good (but very obscure) reason for it.
483 On the other hand, I could be wrong so don't take that as gospel.
485 m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
486 m_cfObservedRunLoopMode = cfRunLoopMode;
487 CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
491 static inline bool FakeNeedMoreIdle()
494 // Return true on every 10th call.
495 static int idleCount = 0;
496 return ++idleCount % 10;
503 Called by CFRunLoop just before waiting. This is the appropriate time to
504 send idle events. Unlike other ports, we don't peek the queue for events
505 and stop idling if there is one. Instead, if the user requests more idle
506 events we tell Cocoa to send us an applicationWillUpdate notification
507 which will cause our observer of that notification to tell CFRunLoop to
508 call us before waiting which will cause us to be fired again but only
509 after exhausting the event queue.
511 The reason we do it this way is that peeking for an event causes CFRunLoop
512 to reenter and fire off its timers, observers, and sources which we're
513 better off avoiding. Doing it this way, we basically let CFRunLoop do the
514 work of peeking for the next event which is much nicer.
516 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
518 // Ensure that CocoaDelegate_applicationWillUpdate will recreate us.
519 // We've already been invalidated by CFRunLoop because we are one-shot.
520 m_cfRunLoopIdleObserver.reset();
522 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
523 sg_cApplicationWillUpdate = 0;
525 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
527 if( ProcessIdle() || FakeNeedMoreIdle() )
529 wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
530 [NSApp setWindowsNeedUpdate:YES];
534 wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
539 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
542 wxAppBase::OnAssert(file, line, cond, msg);
543 m_isInAssert = false;
545 #endif // __WXDEBUG__
547 /* A note about Cocoa's event loops vs. run loops:
549 It's important to understand that Cocoa has a two-level event loop. The
550 outer level is run by NSApplication and can only ever happen on the main
551 thread. The nextEventMatchingMask:untilDate:inMode:dequeue: method returns
552 the next event which is then given to sendEvent: to send it. These
553 methods are defined in NSApplication and are thus part of AppKit.
555 Events (NSEvent) are only sent due to actual user actions like clicking
556 the mouse or moving the mouse or pressing a key and so on. There are no
557 paint events; there are no timer events; there are no socket events; there
560 All of those types of "events" have nothing to do with the GUI at all.
561 That is why Cocoa's AppKit doesn't implement them. Instead, they are
562 implemented in Foundation's NSRunLoop which on OS X uses CFRunLoop
563 to do the actual work.
565 How NSApplication uses NSRunLoop is rather interesting. Basically, it
566 interacts with NSRunLoop only from within the nextEventMatchingMask
567 method. It passes its inMode: argument almost directly to NSRunLoop
568 and thus CFRunLoop. The run loop then runs (e.g. loops) until it
569 is told to exit. The run loop calls the callout functions directly.
570 From within those callout functions the run loop is considered to
571 be running. Presumably, the AppKit installs a run loop source to
572 receive messages from the window server over the mach port (like a
573 socket). For some messages (e.g. need to paint) the AppKit will
574 call application code like drawRect: without exiting the run loop.
575 For other messages (ones that can be encapsulated in an NSEvent)
576 the AppKit tells the run loop to exit which returns control to
577 the nextEventMatchingMask method which then returns the NSEvent
578 object. It's important to note that once the runloop has exited
579 it is no longer considered running and thus if you ask it which
580 mode it is running in it will return nil.
582 When manually pumping the event loop care should be taken to
583 tell it to run in the correct mode. For instance, if you are
584 using it to run a modal dialog then you want to run it in
585 the modal panel run loop mode. AppKit presumably has sources
586 or timers or observers that specifically don't listen on this
587 mode. Another interesting mode is the connection reply mode.
588 This allows Cocoa to wait for a response from a distributed
589 objects message without firing off user code that may result
590 in a DO call being made thus recursing. So basically, the
591 mode is a way for Cocoa to attempt to avoid run loop recursion
592 but to allow it under certain circumstances.