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 *));
127 return wxAppBase::Initialize(argc, argv);
130 void wxApp::CleanUp()
132 wxAutoNSAutoreleasePool pool;
134 wxDC::CocoaShutdownTextSystem();
135 wxMenuBarManager::DestroyInstance();
137 [[NSNotificationCenter defaultCenter] removeObserver:sg_cocoaAppObserver];
140 [m_cocoaApp setDelegate:nil];
141 [m_cocoaAppDelegate release];
142 m_cocoaAppDelegate = NULL;
145 wxAppBase::CleanUp();
148 // ----------------------------------------------------------------------------
150 // ----------------------------------------------------------------------------
156 m_isInAssert = false;
157 #endif // __WXDEBUG__
162 m_cocoaAppDelegate = NULL;
165 void wxApp::CocoaDelegate_applicationWillBecomeActive()
169 void wxApp::CocoaDelegate_applicationDidBecomeActive()
173 void wxApp::CocoaDelegate_applicationWillResignActive()
175 wxTopLevelWindowCocoa::DeactivatePendingWindow();
178 void wxApp::CocoaDelegate_applicationDidResignActive()
182 bool wxApp::OnInitGui()
184 wxAutoNSAutoreleasePool pool;
185 if(!wxAppBase::OnInitGui())
188 // Create the app using the sharedApplication method
189 m_cocoaApp = [NSApplication sharedApplication];
193 // Enable response to application delegate messages
194 m_cocoaAppDelegate = [[WX_GET_OBJC_CLASS(wxNSApplicationDelegate) alloc] init];
195 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
198 // Enable response to "delegate" messages on the notification observer
199 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
200 selector:@selector(applicationWillBecomeActive:)
201 name:NSApplicationWillBecomeActiveNotification object:nil];
203 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
204 selector:@selector(applicationDidBecomeActive:)
205 name:NSApplicationDidBecomeActiveNotification object:nil];
207 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
208 selector:@selector(applicationWillResignActive:)
209 name:NSApplicationWillResignActiveNotification object:nil];
211 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
212 selector:@selector(applicationDidResignActive:)
213 name:NSApplicationDidResignActiveNotification object:nil];
215 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
216 selector:@selector(applicationWillUpdate:)
217 name:NSApplicationWillUpdateNotification object:nil];
219 // Enable response to system notifications
220 [[NSNotificationCenter defaultCenter] addObserver:sg_cocoaAppObserver
221 selector:@selector(controlTintChanged:)
222 name:NSControlTintDidChangeNotification object:nil];
225 wxMenuBarManager::CreateInstance();
227 wxDC::CocoaInitializeTextSystem();
233 if(m_cfRunLoopIdleObserver != NULL)
235 // Invalidate the observer which also removes it from the run loop.
236 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
237 // Release the ref as we don't need it anymore.
238 m_cfRunLoopIdleObserver.reset();
242 bool wxApp::CallOnInit()
244 // wxAutoNSAutoreleasePool pool;
250 if(!wxAppBase::OnInit())
260 wxAppConsole::Exit();
263 // Yield to other processes
264 bool wxApp::Yield(bool onlyIfNeeded)
267 static bool s_inYield = false;
270 // disable log flushing from here because a call to wxYield() shouldn't
271 // normally result in message boxes popping up &c
279 wxFAIL_MSG( wxT("wxYield called recursively" ) );
287 // Run the event loop until it is out of events
290 wxAutoNSAutoreleasePool pool;
291 NSEvent *event = [GetNSApplication()
292 nextEventMatchingMask:NSAnyEventMask
293 untilDate:[NSDate distantPast]
294 inMode:NSDefaultRunLoopMode
298 [GetNSApplication() sendEvent: event];
302 // let the logs be flashed again
311 void wxApp::WakeUpIdle()
313 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
314 location:NSZeroPoint modifierFlags:NSAnyEventMask
315 timestamp:0 windowNumber:0 context:nil
316 subtype:0 data1:0 data2:0] atStart:NO];
319 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
320 extern "C" static void ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
322 static_cast<wxApp*>(info)->CF_ObserveMainRunLoopBeforeWaiting(observer, activity);
326 static int sg_cApplicationWillUpdate = 0;
329 void wxApp::CocoaDelegate_applicationWillUpdate()
331 wxLogTrace(wxTRACE_COCOA,wxT("applicationWillUpdate"));
333 // CFRunLoopRef cfRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
334 CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
335 wxCFRef<CFStringRef> cfRunLoopMode(CFRunLoopCopyCurrentMode(cfRunLoop));
337 if(m_cfRunLoopIdleObserver != NULL && m_cfObservedRunLoopMode != cfRunLoopMode)
339 CFRunLoopObserverInvalidate(m_cfRunLoopIdleObserver);
340 m_cfRunLoopIdleObserver.reset();
343 ++sg_cApplicationWillUpdate;
345 if(m_cfRunLoopIdleObserver == NULL)
347 // Enable idle event handling
348 CFRunLoopObserverContext observerContext =
355 m_cfRunLoopIdleObserver.reset(CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, /*repeats*/FALSE, /*priority*/0, ObserveMainRunLoopBeforeWaiting, &observerContext));
356 m_cfObservedRunLoopMode = cfRunLoopMode;
357 CFRunLoopAddObserver(cfRunLoop, m_cfRunLoopIdleObserver, m_cfObservedRunLoopMode);
361 static inline bool FakeNeedMoreIdle()
364 // Return true on every 10th call.
365 static int idleCount = 0;
366 return ++idleCount % 10;
372 void wxApp::CF_ObserveMainRunLoopBeforeWaiting(CFRunLoopObserverRef observer, int activity)
374 // Ensure that the app knows we've been invalidated
375 m_cfRunLoopIdleObserver.reset();
377 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN (%d)"), sg_cApplicationWillUpdate);
378 sg_cApplicationWillUpdate = 0;
380 wxLogTrace(wxTRACE_COCOA,wxT("Idle BEGIN"));
382 if( ProcessIdle() || FakeNeedMoreIdle() )
384 wxLogTrace(wxTRACE_COCOA, wxT("Idle REQUEST MORE"));
385 [NSApp setWindowsNeedUpdate:YES];
389 wxLogTrace(wxTRACE_COCOA, wxT("Idle END"));
394 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
397 wxAppBase::OnAssert(file, line, cond, msg);
398 m_isInAssert = false;
400 #endif // __WXDEBUG__