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 // wxNSApplicationObserver singleton.
38 static wxObjcAutoRefFromAlloc<wxNSApplicationObserver*> sg_cocoaAppObserver = [[wxNSApplicationObserver alloc] init];
40 // ========================================================================
41 // wxNSApplicationDelegate
42 // ========================================================================
43 @implementation wxNSApplicationDelegate : NSObject
45 // NOTE: Terminate means that the event loop does NOT return and thus
46 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
47 // own exit on frame delete mechanism.
48 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
53 @end // implementation wxNSApplicationDelegate : NSObject
55 // ========================================================================
56 // wxNSApplicationObserver
57 // ========================================================================
58 @implementation wxNSApplicationObserver : NSObject
60 - (void)applicationWillBecomeActive:(NSNotification *)notification
62 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
65 - (void)applicationDidBecomeActive:(NSNotification *)notification
67 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
70 - (void)applicationWillResignActive:(NSNotification *)notification
72 wxTheApp->CocoaDelegate_applicationWillResignActive();
75 - (void)applicationDidResignActive:(NSNotification *)notification
77 wxTheApp->CocoaDelegate_applicationDidResignActive();
80 - (void)applicationWillUpdate:(NSNotification *)notification;
82 wxTheApp->CocoaDelegate_applicationWillUpdate();
85 - (void)controlTintChanged:(NSNotification *)notification
87 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
90 @end // implementation wxNSApplicationObserver : NSObject
92 // ========================================================================
94 // ========================================================================
96 // ----------------------------------------------------------------------------
97 // wxApp Static member initialization
98 // ----------------------------------------------------------------------------
99 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
100 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
101 EVT_IDLE(wxAppBase::OnIdle)
102 // EVT_END_SESSION(wxApp::OnEndSession)
103 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
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 *));
128 return wxAppBase::Initialize(argc, argv);
131 void wxApp::CleanUp()
133 wxAutoNSAutoreleasePool pool;
135 wxDC::CocoaShutdownTextSystem();
136 wxMenuBarManager::DestroyInstance();
138 [m_cocoaApp setDelegate:nil];
139 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate];
140 [m_cocoaAppDelegate release];
141 m_cocoaAppDelegate = NULL;
143 wxAppBase::CleanUp();
146 // ----------------------------------------------------------------------------
148 // ----------------------------------------------------------------------------
154 m_isInAssert = false;
155 #endif // __WXDEBUG__
160 m_cocoaAppDelegate = NULL;
163 void wxApp::CocoaDelegate_applicationWillBecomeActive()
167 void wxApp::CocoaDelegate_applicationDidBecomeActive()
171 void wxApp::CocoaDelegate_applicationWillResignActive()
173 wxTopLevelWindowCocoa::DeactivatePendingWindow();
176 void wxApp::CocoaDelegate_applicationDidResignActive()
180 bool wxApp::OnInitGui()
182 wxAutoNSAutoreleasePool pool;
183 if(!wxAppBase::OnInitGui())
186 // Create the app using the sharedApplication method
187 m_cocoaApp = [NSApplication sharedApplication];
189 // Enable response to application delegate messages
190 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
191 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
193 // Enable response to "delegate" messages on the notification observer
194 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
195 selector:@selector(applicationWillBecomeActive:)
196 name:NSApplicationWillBecomeActiveNotification object:nil];
198 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
199 selector:@selector(applicationDidBecomeActive:)
200 name:NSApplicationDidBecomeActiveNotification object:nil];
202 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
203 selector:@selector(applicationWillResignActive:)
204 name:NSApplicationWillResignActiveNotification object:nil];
206 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
207 selector:@selector(applicationDidResignActive:)
208 name:NSApplicationDidResignActiveNotification object:nil];
210 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
211 selector:@selector(applicationWillUpdate:)
212 name:NSApplicationWillUpdateNotification object:nil];
214 // Enable response to system notifications
215 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
216 selector:@selector(controlTintChanged:)
217 name:NSControlTintDidChangeNotification object:nil];
219 wxMenuBarManager::CreateInstance();
221 wxDC::CocoaInitializeTextSystem();
227 if(m_cfRunLoopIdleObserver != NULL)
229 // Invalidate the observer which also removes it from the run loop.
230 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
231 // Release the ref as we don't need it anymore.
232 m_cfRunLoopIdleObserver.reset();
236 bool wxApp::CallOnInit()
238 // wxAutoNSAutoreleasePool pool;
244 if(!wxAppBase::OnInit())
254 wxAppConsole::Exit();
257 // Yield to other processes
258 bool wxApp::Yield(bool onlyIfNeeded)
261 static bool s_inYield = false;
264 // disable log flushing from here because a call to wxYield() shouldn't
265 // normally result in message boxes popping up &c
273 wxFAIL_MSG( wxT("wxYield called recursively" ) );
281 // Run the event loop until it is out of events
284 wxAutoNSAutoreleasePool pool;
285 NSEvent *event = [GetNSApplication()
286 nextEventMatchingMask:NSAnyEventMask
287 untilDate:[NSDate distantPast]
288 inMode:NSDefaultRunLoopMode
292 [GetNSApplication() sendEvent: event];
296 // let the logs be flashed again
305 void wxApp::WakeUpIdle()
307 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
308 location:NSZeroPoint modifierFlags:NSAnyEventMask
309 timestamp:0 windowNumber:0 context:nil
310 subtype:0 data1:0 data2:0] atStart:NO];
313 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
314 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
316 static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
320 static int sg_cApplicationWillUpdate = 0;
323 void wxApp::CocoaDelegate_applicationWillUpdate()
325 wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
327 // CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
328 CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
329 wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
331 if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
333 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
334 m_cfRunLoopIdleObserver.reset();
337 ++sg_cApplicationWillUpdate;
339 if(m_cfRunLoopIdleObserver == NULL)
341 // Enable idle event handling
342 CFRunLoopObserverContext observerContext =
349 m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
350 m_cfObservedRunLoopMode = cfRunLoopMode;
351 CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
355 static inline bool FakeNeedMoreIdle()
358 // Return true on every 10th call.
359 static int idleCount = 0;
360 return ++idleCount % 10;
366 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
368 // Ensure that the app knows we've been invalidated
369 m_cfRunLoopIdleObserver.reset();
371 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
372 sg_cApplicationWillUpdate = 0;
374 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
376 if( ProcessIdle() || FakeNeedMoreIdle() )
378 wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
379 [NSApp setWindowsNeedUpdate:YES];
383 wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
388 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
391 wxAppBase::OnAssert(file, line, cond, msg);
392 m_isInAssert = false;
394 #endif // __WXDEBUG__