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"
21 #include "wx/module.h"
24 #include "wx/cocoa/ObjcRef.h"
25 #include "wx/cocoa/autorelease.h"
26 #include "wx/cocoa/mbarman.h"
27 #include "wx/cocoa/NSApplication.h"
29 #import <AppKit/NSApplication.h>
30 #import <Foundation/NSRunLoop.h>
31 #import <Foundation/NSThread.h>
32 #import <AppKit/NSEvent.h>
33 #import <Foundation/NSString.h>
34 #import <Foundation/NSNotification.h>
35 #import <AppKit/NSCell.h>
37 bool wxApp::sm_isEmbedded = false; // Normally we're not a plugin
39 // wxNSApplicationObserver singleton.
40 static wxObjcAutoRefFromAlloc<wxNSApplicationObserver*> sg_cocoaAppObserver = [[WX_GET_OBJC_CLASS(wxNSApplicationObserver) alloc] init];
42 // ========================================================================
43 // wxNSApplicationDelegate
44 // ========================================================================
45 @implementation wxNSApplicationDelegate : NSObject
47 // NOTE: Terminate means that the event loop does NOT return and thus
48 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
49 // own exit on frame delete mechanism.
50 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
55 @end // implementation wxNSApplicationDelegate : NSObject
56 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSApplicationDelegate,NSObject)
58 // ========================================================================
59 // wxNSApplicationObserver
60 // ========================================================================
61 @implementation wxNSApplicationObserver : NSObject
63 - (void)applicationWillBecomeActive:(NSNotification *)notification
65 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
68 - (void)applicationDidBecomeActive:(NSNotification *)notification
70 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
73 - (void)applicationWillResignActive:(NSNotification *)notification
75 wxTheApp->CocoaDelegate_applicationWillResignActive();
78 - (void)applicationDidResignActive:(NSNotification *)notification
80 wxTheApp->CocoaDelegate_applicationDidResignActive();
83 - (void)applicationWillUpdate:(NSNotification *)notification;
85 wxTheApp->CocoaDelegate_applicationWillUpdate();
88 - (void)controlTintChanged:(NSNotification *)notification
90 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
93 @end // implementation wxNSApplicationObserver : NSObject
94 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSApplicationObserver,NSObject)
96 // ========================================================================
98 // ========================================================================
100 // ----------------------------------------------------------------------------
101 // wxApp Static member initialization
102 // ----------------------------------------------------------------------------
103 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
105 // ----------------------------------------------------------------------------
106 // wxApp initialization/cleanup
107 // ----------------------------------------------------------------------------
108 bool wxApp::Initialize(int& argc, wxChar **argv)
110 wxAutoNSAutoreleasePool pool;
111 m_cocoaMainThread = [NSThread currentThread];
112 // Mac OS X passes a process serial number command line argument when
113 // the application is launched from the Finder. This argument must be
114 // removed from the command line arguments before being handled by the
115 // application (otherwise applications would need to handle it)
118 static const wxChar *ARG_PSN = _T("-psn_");
119 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
121 // remove this argument
123 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
128 Cocoa supports -Key value options which set the user defaults key "Key"
129 to the value "value" Some of them are very handy for debugging like
130 -NSShowAllViews YES. Cocoa picks these up from the real argv so
131 our removal of them from the wx copy of it does not affect Cocoa's
134 We basically just assume that any "-NS" option and its following
135 argument needs to be removed from argv. We hope that user code does
136 not expect to see -NS options and indeed it's probably a safe bet
137 since most user code accepting options is probably using the
138 double-dash GNU-style syntax.
140 for(int i=1; i < argc; ++i)
142 static const wxChar *ARG_NS = wxT("-NS");
143 static const int ARG_NS_LEN = wxStrlen(ARG_NS);
144 if( wxStrncmp(argv[i], ARG_NS, ARG_NS_LEN) == 0 )
146 // Only eat this option if it has an argument
150 memmove(argv + i, argv + i + 2, argc * sizeof(wxChar*));
151 // drop back one position so the next run through the loop
152 // reprocesses the argument at our current index.
158 return wxAppBase::Initialize(argc, argv);
161 void wxApp::CleanUp()
163 wxAutoNSAutoreleasePool pool;
165 wxDC::CocoaShutdownTextSystem();
166 wxMenuBarManager::DestroyInstance();
168 [[NSNotificationCenter defaultCenter] removeObserver:sg_cocoaAppObserver];
171 [m_cocoaApp setDelegate:nil];
172 [m_cocoaAppDelegate release];
173 m_cocoaAppDelegate = NULL;
176 wxAppBase::CleanUp();
179 // ----------------------------------------------------------------------------
181 // ----------------------------------------------------------------------------
187 m_isInAssert = false;
188 #endif // __WXDEBUG__
195 m_cocoaAppDelegate = NULL;
198 void wxApp::CocoaDelegate_applicationWillBecomeActive()
202 void wxApp::CocoaDelegate_applicationDidBecomeActive()
206 void wxApp::CocoaDelegate_applicationWillResignActive()
208 wxTopLevelWindowCocoa::DeactivatePendingWindow();
211 void wxApp::CocoaDelegate_applicationDidResignActive()
215 bool wxApp::OnInitGui()
217 wxAutoNSAutoreleasePool pool;
218 if(!wxAppBase::OnInitGui())
221 // Create the app using the sharedApplication method
222 m_cocoaApp = [NSApplication sharedApplication];
226 // Enable response to application delegate messages
227 m_cocoaAppDelegate = [[WX_GET_OBJC_CLASS(wxNSApplicationDelegate) alloc] init];
228 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
231 // Enable response to "delegate" messages on the notification observer
232 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
233 selector:@selector(applicationWillBecomeActive:)
234 name:NSApplicationWillBecomeActiveNotification object:nil];
236 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
237 selector:@selector(applicationDidBecomeActive:)
238 name:NSApplicationDidBecomeActiveNotification object:nil];
240 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
241 selector:@selector(applicationWillResignActive:)
242 name:NSApplicationWillResignActiveNotification object:nil];
244 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
245 selector:@selector(applicationDidResignActive:)
246 name:NSApplicationDidResignActiveNotification object:nil];
248 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
249 selector:@selector(applicationWillUpdate:)
250 name:NSApplicationWillUpdateNotification object:nil];
252 // Enable response to system notifications
253 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
254 selector:@selector(controlTintChanged:)
255 name:NSControlTintDidChangeNotification object:nil];
258 wxMenuBarManager::CreateInstance();
260 wxDC::CocoaInitializeTextSystem();
266 if(m_cfRunLoopIdleObserver != NULL)
268 // Invalidate the observer which also removes it from the run loop.
269 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
270 // Release the ref as we don't need it anymore.
271 m_cfRunLoopIdleObserver.reset();
275 bool wxApp::CallOnInit()
277 // wxAutoNSAutoreleasePool pool;
283 if(!wxAppBase::OnInit())
293 wxAppConsole::Exit();
296 // Yield to other processes
297 bool wxApp::Yield(bool onlyIfNeeded)
300 static bool s_inYield = false;
303 // disable log flushing from here because a call to wxYield() shouldn't
304 // normally result in message boxes popping up &c
312 wxFAIL_MSG( wxT("wxYield called recursively" ) );
320 // Run the event loop until it is out of events
323 wxAutoNSAutoreleasePool pool;
324 /* NOTE: It may be better to use something like
325 NSEventTrackingRunLoopMode since we don't necessarily want all
326 timers/sources/observers to run, only those which would
327 run while tracking events. However, it should be noted that
328 NSEventTrackingRunLoopMode is in the common set of modes
329 so it may not effectively make much of a difference.
331 NSEvent *event = [GetNSApplication()
332 nextEventMatchingMask:NSAnyEventMask
333 untilDate:[NSDate distantPast]
334 inMode:NSDefaultRunLoopMode
338 [GetNSApplication() sendEvent: event];
342 Because we just told NSApplication to avoid blocking it will in turn
343 run the CFRunLoop with a timeout of 0 seconds. In that case, our
344 run loop observer on kCFRunLoopBeforeWaiting never fires because
345 no waiting occurs. Therefore, no idle events are sent.
347 Believe it or not, this is actually desirable because we do not want
348 to process idle from here. However, we do want to process pending
349 events because some user code expects to do work in a thread while
350 the main thread waits and then notify the main thread by posting
353 ProcessPendingEvents();
356 // let the logs be flashed again
365 void wxApp::WakeUpIdle()
367 /* When called from the main thread the NSAutoreleasePool managed by
368 the [NSApplication run] method would ordinarily be in place and so
369 one would think a pool here would be unnecessary.
371 However, when called from a different thread there is usually no
372 NSAutoreleasePool in place because wxThread has no knowledge of
373 wxCocoa. The pool here is generally only ever going to contain
374 the NSEvent we create with the factory method. As soon as we add
375 it to the main event queue with postEvent:atStart: it is retained
376 and so safe for our pool to release.
378 wxAutoNSAutoreleasePool pool;
379 /* NOTE: This is a little heavy handed. What this does is cause an
380 AppKit NSEvent to be added to NSApplication's queue (which is always
381 on the main thread). This will cause the main thread runloop to
382 exit which returns control to nextEventMatchingMask which returns
383 the event which is then sent with sendEvent: and essentially dropped
384 since it's not for a window (windowNumber 0) and NSApplication
385 certainly doesn't understand it.
387 With the exception of wxEventLoop::Exit which uses us to cause the
388 runloop to exit and return to the NSApplication event loop, most
389 callers only need wx idle to happen, or more specifically only really
390 need to ensure that ProcessPendingEvents is called which is currently
391 done without exiting the runloop.
393 Be careful if you decide to change the implementation of this method
394 as wxEventLoop::Exit depends on the current behavior.
396 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
397 location:NSZeroPoint modifierFlags:NSAnyEventMask
398 timestamp:0 windowNumber:0 context:nil
399 subtype:0 data1:0 data2:0] atStart:NO];
402 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
403 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
405 static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
409 static int sg_cApplicationWillUpdate = 0;
413 Invoked from the applicationWillUpdate notification observer. See the
414 NSApplication documentation for the official statement on when this
415 will be called. Since it can be hard to understand for a Cocoa newbie
416 I'll try to explain it here as it relates to wxCocoa.
418 Basically, we get called from within nextEventMatchingMask if and only
419 if any user code told the application to send the update notification
420 (sort of like a request for idle events). However, unlike wx idle events,
421 this notification is sent quite often, nearly every time through the loop
422 because nearly every control tells the application to send it.
424 Because wx idle events are only supposed to be sent when the event loop
425 is about to block we instead schedule a function to be called just
426 before the run loop waits and send the idle events from there.
428 It also has the desirable effect of only sending the wx idle events when
429 the event loop is actualy going to block. If the event loop is being
430 pumped manualy (e.g. like a PeekMessage) then the kCFRunLoopBeforeWaiting
431 observer never fires. Our Yield() method depends on this because sending
432 idle events from within Yield would be bad.
434 Normally you might think that we could just set the observer up once and
435 leave it attached. However, this is problematic because our run loop
436 observer calls user code (the idle handlers) which can actually display
437 modal dialogs. Displaying a modal dialog causes reentry of the event
438 loop, usually in a different run loop mode than the main loop (e.g. in
439 modal-dialog mode instead of default mode). Because we only register the
440 observer with the run loop mode at the time of this call, it won't be
441 called from a modal loop.
443 We want it to be called and thus we need a new observer.
445 void wxApp::CocoaDelegate_applicationWillUpdate()
447 wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
449 // CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
450 CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
451 wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
453 /* If we have an observer and that observer is for the wrong run loop
454 mode then invalidate it and release it.
456 if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
458 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
459 m_cfRunLoopIdleObserver.reset();
462 ++sg_cApplicationWillUpdate;
464 /* This will be true either on the first call or when the above code has
465 invalidated and released the exisiting observer.
467 if(m_cfRunLoopIdleObserver == NULL)
469 // Enable idle event handling
470 CFRunLoopObserverContext observerContext =
477 /* NOTE: I can't recall why we don't just let the observer repeat
478 instead of invalidating itself each time it fires thus requiring
479 it to be recreated for each shot but there was if I remember
480 some good (but very obscure) reason for it.
482 On the other hand, I could be wrong so don't take that as gospel.
484 m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
485 m_cfObservedRunLoopMode = cfRunLoopMode;
486 CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
490 static inline bool FakeNeedMoreIdle()
493 // Return true on every 10th call.
494 static int idleCount = 0;
495 return ++idleCount % 10;
502 Called by CFRunLoop just before waiting. This is the appropriate time to
503 send idle events. Unlike other ports, we don't peek the queue for events
504 and stop idling if there is one. Instead, if the user requests more idle
505 events we tell Cocoa to send us an applicationWillUpdate notification
506 which will cause our observer of that notification to tell CFRunLoop to
507 call us before waiting which will cause us to be fired again but only
508 after exhausting the event queue.
510 The reason we do it this way is that peeking for an event causes CFRunLoop
511 to reenter and fire off its timers, observers, and sources which we're
512 better off avoiding. Doing it this way, we basically let CFRunLoop do the
513 work of peeking for the next event which is much nicer.
515 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
517 // Ensure that CocoaDelegate_applicationWillUpdate will recreate us.
518 // We've already been invalidated by CFRunLoop because we are one-shot.
519 m_cfRunLoopIdleObserver.reset();
521 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
522 sg_cApplicationWillUpdate = 0;
524 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
526 if( ProcessIdle() || FakeNeedMoreIdle() )
528 wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
529 [NSApp setWindowsNeedUpdate:YES];
533 wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
538 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
541 wxAppBase::OnAssert(file, line, cond, msg);
542 m_isInAssert = false;
544 #endif // __WXDEBUG__
546 /* A note about Cocoa's event loops vs. run loops:
548 It's important to understand that Cocoa has a two-level event loop. The
549 outer level is run by NSApplication and can only ever happen on the main
550 thread. The nextEventMatchingMask:untilDate:inMode:dequeue: method returns
551 the next event which is then given to sendEvent: to send it. These
552 methods are defined in NSApplication and are thus part of AppKit.
554 Events (NSEvent) are only sent due to actual user actions like clicking
555 the mouse or moving the mouse or pressing a key and so on. There are no
556 paint events; there are no timer events; there are no socket events; there
559 All of those types of "events" have nothing to do with the GUI at all.
560 That is why Cocoa's AppKit doesn't implement them. Instead, they are
561 implemented in Foundation's NSRunLoop which on OS X uses CFRunLoop
562 to do the actual work.
564 How NSApplication uses NSRunLoop is rather interesting. Basically, it
565 interacts with NSRunLoop only from within the nextEventMatchingMask
566 method. It passes its inMode: argument almost directly to NSRunLoop
567 and thus CFRunLoop. The run loop then runs (e.g. loops) until it
568 is told to exit. The run loop calls the callout functions directly.
569 From within those callout functions the run loop is considered to
570 be running. Presumably, the AppKit installs a run loop source to
571 receive messages from the window server over the mach port (like a
572 socket). For some messages (e.g. need to paint) the AppKit will
573 call application code like drawRect: without exiting the run loop.
574 For other messages (ones that can be encapsulated in an NSEvent)
575 the AppKit tells the run loop to exit which returns control to
576 the nextEventMatchingMask method which then returns the NSEvent
577 object. It's important to note that once the runloop has exited
578 it is no longer considered running and thus if you ask it which
579 mode it is running in it will return nil.
581 When manually pumping the event loop care should be taken to
582 tell it to run in the correct mode. For instance, if you are
583 using it to run a modal dialog then you want to run it in
584 the modal panel run loop mode. AppKit presumably has sources
585 or timers or observers that specifically don't listen on this
586 mode. Another interesting mode is the connection reply mode.
587 This allows Cocoa to wait for a response from a distributed
588 objects message without firing off user code that may result
589 in a DO call being made thus recursing. So basically, the
590 mode is a way for Cocoa to attempt to avoid run loop recursion
591 but to allow it under certain circumstances.