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 = [[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
57 // ========================================================================
58 // wxNSApplicationObserver
59 // ========================================================================
60 @implementation wxNSApplicationObserver : NSObject
62 - (void)applicationWillBecomeActive:(NSNotification *)notification
64 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
67 - (void)applicationDidBecomeActive:(NSNotification *)notification
69 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
72 - (void)applicationWillResignActive:(NSNotification *)notification
74 wxTheApp->CocoaDelegate_applicationWillResignActive();
77 - (void)applicationDidResignActive:(NSNotification *)notification
79 wxTheApp->CocoaDelegate_applicationDidResignActive();
82 - (void)applicationWillUpdate:(NSNotification *)notification;
84 wxTheApp->CocoaDelegate_applicationWillUpdate();
87 - (void)controlTintChanged:(NSNotification *)notification
89 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
92 @end // implementation wxNSApplicationObserver : NSObject
94 // ========================================================================
96 // ========================================================================
98 // ----------------------------------------------------------------------------
99 // wxApp Static member initialization
100 // ----------------------------------------------------------------------------
101 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
102 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
103 EVT_IDLE(wxAppBase::OnIdle)
104 // EVT_END_SESSION(wxApp::OnEndSession)
105 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
108 // ----------------------------------------------------------------------------
109 // wxApp initialization/cleanup
110 // ----------------------------------------------------------------------------
111 bool wxApp::Initialize(int& argc, wxChar **argv)
113 wxAutoNSAutoreleasePool pool;
114 m_cocoaMainThread = [NSThread currentThread];
115 // Mac OS X passes a process serial number command line argument when
116 // the application is launched from the Finder. This argument must be
117 // removed from the command line arguments before being handled by the
118 // application (otherwise applications would need to handle it)
121 static const wxChar *ARG_PSN = _T("-psn_");
122 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
124 // remove this argument
126 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
130 return wxAppBase::Initialize(argc, argv);
133 void wxApp::CleanUp()
135 wxAutoNSAutoreleasePool pool;
137 wxDC::CocoaShutdownTextSystem();
138 wxMenuBarManager::DestroyInstance();
140 [[NSNotificationCenter defaultCenter] removeObserver:sg_cocoaAppObserver];
143 [m_cocoaApp setDelegate:nil];
144 [m_cocoaAppDelegate release];
145 m_cocoaAppDelegate = NULL;
148 wxAppBase::CleanUp();
151 // ----------------------------------------------------------------------------
153 // ----------------------------------------------------------------------------
159 m_isInAssert = false;
160 #endif // __WXDEBUG__
165 m_cocoaAppDelegate = NULL;
168 void wxApp::CocoaDelegate_applicationWillBecomeActive()
172 void wxApp::CocoaDelegate_applicationDidBecomeActive()
176 void wxApp::CocoaDelegate_applicationWillResignActive()
178 wxTopLevelWindowCocoa::DeactivatePendingWindow();
181 void wxApp::CocoaDelegate_applicationDidResignActive()
185 bool wxApp::OnInitGui()
187 wxAutoNSAutoreleasePool pool;
188 if(!wxAppBase::OnInitGui())
191 // Create the app using the sharedApplication method
192 m_cocoaApp = [NSApplication sharedApplication];
196 // Enable response to application delegate messages
197 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
198 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
201 // Enable response to "delegate" messages on the notification observer
202 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
203 selector:@selector(applicationWillBecomeActive:)
204 name:NSApplicationWillBecomeActiveNotification object:nil];
206 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
207 selector:@selector(applicationDidBecomeActive:)
208 name:NSApplicationDidBecomeActiveNotification object:nil];
210 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
211 selector:@selector(applicationWillResignActive:)
212 name:NSApplicationWillResignActiveNotification object:nil];
214 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
215 selector:@selector(applicationDidResignActive:)
216 name:NSApplicationDidResignActiveNotification object:nil];
218 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
219 selector:@selector(applicationWillUpdate:)
220 name:NSApplicationWillUpdateNotification object:nil];
222 // Enable response to system notifications
223 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
224 selector:@selector(controlTintChanged:)
225 name:NSControlTintDidChangeNotification object:nil];
228 wxMenuBarManager::CreateInstance();
230 wxDC::CocoaInitializeTextSystem();
236 if(m_cfRunLoopIdleObserver != NULL)
238 // Invalidate the observer which also removes it from the run loop.
239 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
240 // Release the ref as we don't need it anymore.
241 m_cfRunLoopIdleObserver.reset();
245 bool wxApp::CallOnInit()
247 // wxAutoNSAutoreleasePool pool;
253 if(!wxAppBase::OnInit())
263 wxAppConsole::Exit();
266 // Yield to other processes
267 bool wxApp::Yield(bool onlyIfNeeded)
270 static bool s_inYield = false;
273 // disable log flushing from here because a call to wxYield() shouldn't
274 // normally result in message boxes popping up &c
282 wxFAIL_MSG( wxT("wxYield called recursively" ) );
290 // Run the event loop until it is out of events
293 wxAutoNSAutoreleasePool pool;
294 NSEvent *event = [GetNSApplication()
295 nextEventMatchingMask:NSAnyEventMask
296 untilDate:[NSDate distantPast]
297 inMode:NSDefaultRunLoopMode
301 [GetNSApplication() sendEvent: event];
305 // let the logs be flashed again
314 void wxApp::WakeUpIdle()
316 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
317 location:NSZeroPoint modifierFlags:NSAnyEventMask
318 timestamp:0 windowNumber:0 context:nil
319 subtype:0 data1:0 data2:0] atStart:NO];
322 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
323 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
325 static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
329 static int sg_cApplicationWillUpdate = 0;
332 void wxApp::CocoaDelegate_applicationWillUpdate()
334 wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
336 // CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
337 CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
338 wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
340 if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
342 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
343 m_cfRunLoopIdleObserver.reset();
346 ++sg_cApplicationWillUpdate;
348 if(m_cfRunLoopIdleObserver == NULL)
350 // Enable idle event handling
351 CFRunLoopObserverContext observerContext =
358 m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
359 m_cfObservedRunLoopMode = cfRunLoopMode;
360 CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
364 static inline bool FakeNeedMoreIdle()
367 // Return true on every 10th call.
368 static int idleCount = 0;
369 return ++idleCount % 10;
375 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
377 // Ensure that the app knows we've been invalidated
378 m_cfRunLoopIdleObserver.reset();
380 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
381 sg_cApplicationWillUpdate = 0;
383 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
385 if( ProcessIdle() || FakeNeedMoreIdle() )
387 wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
388 [NSApp setWindowsNeedUpdate:YES];
392 wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
397 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
400 wxAppBase::OnAssert(file, line, cond, msg);
401 m_isInAssert = false;
403 #endif // __WXDEBUG__