1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/cocoa/app.mm
4 // Author: David Elliott
8 // Copyright: (c) David Elliott
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
20 #include "wx/module.h"
23 #include "wx/cocoa/ObjcPose.h"
24 #include "wx/cocoa/autorelease.h"
25 #include "wx/cocoa/mbarman.h"
26 #include "wx/cocoa/NSApplication.h"
28 #import <AppKit/NSApplication.h>
29 #import <Foundation/NSRunLoop.h>
30 #import <Foundation/NSThread.h>
31 #import <AppKit/NSEvent.h>
32 #import <Foundation/NSString.h>
33 #import <Foundation/NSNotification.h>
34 #import <AppKit/NSCell.h>
36 // ========================================================================
37 // wxPoseAsInitializer
38 // ========================================================================
39 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
41 static bool sg_needIdle = true;
43 // ========================================================================
44 // wxPoserNSApplication
45 // ========================================================================
46 @interface wxPoserNSApplication : NSApplication
50 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
51 - (void)sendEvent: (NSEvent*)anEvent;
52 @end // wxPoserNSApplication
54 WX_IMPLEMENT_POSER(wxPoserNSApplication);
56 @implementation wxPoserNSApplication : NSApplication
58 /* NOTE: The old method of idle event handling added the handler using the
59 [NSRunLoop -performSelector:target:argument:order:modes] which caused
60 the invocation to occur at the begining of [NSApplication
61 -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:]. However,
62 the code would be scheduled for invocation with every iteration of
63 the event loop. This new method simply overrides the method. The
64 same caveats apply. In particular, by the time the event loop has
65 called this method, it usually expects to receive an event. If you
66 plan on stopping the event loop, it is wise to send an event through
67 the queue to ensure this method will return.
68 See wxEventLoop::Exit() for more information.
70 This overridden method calls the superclass method with an untilDate
71 parameter that indicates nil should be returned if there are no pending
72 events. That is, nextEventMatchingMask: should not wait for an event.
73 If nil is returned then idle event processing occurs until the user
74 does not request anymore idle events or until a real event comes through.
76 RN: Even though Apple documentation states that nil can be passed in place
77 of [NSDate distantPast] in the untilDate parameter, this causes Jaguar (10.2)
78 to get stuck in some kind of loop deep within nextEventMatchingMask:, thus we
79 need to explicitly pass [NSDate distantPast] instead.
82 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
84 // Get the same events except don't block
85 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
86 // If we got one, simply return it
89 // No events, try doing some idle stuff
92 && !wxTheApp->IsInAssert()
94 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
97 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
98 while(wxTheApp->ProcessIdle())
100 // Get the same events except don't block
101 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
102 // If we got one, simply return it
105 // we didn't get one, do some idle work
106 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
108 // No more idle work requested, block
109 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
112 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
113 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
116 - (void)sendEvent: (NSEvent*)anEvent
118 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
120 [super sendEvent: anEvent];
123 @end // wxPoserNSApplication
125 // ========================================================================
126 // wxNSApplicationDelegate
127 // ========================================================================
128 @implementation wxNSApplicationDelegate : NSObject
130 // NOTE: Terminate means that the event loop does NOT return and thus
131 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
132 // own exit on frame delete mechanism.
133 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
138 - (void)applicationWillBecomeActive:(NSNotification *)notification
140 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
143 - (void)applicationDidBecomeActive:(NSNotification *)notification
145 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
148 - (void)applicationWillResignActive:(NSNotification *)notification
150 wxTheApp->CocoaDelegate_applicationWillResignActive();
153 - (void)applicationDidResignActive:(NSNotification *)notification
155 wxTheApp->CocoaDelegate_applicationDidResignActive();
158 - (void)controlTintChanged:(NSNotification *)notification
160 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
163 @end // implementation wxNSApplicationDelegate : NSObject
165 // ========================================================================
167 // ========================================================================
169 // ----------------------------------------------------------------------------
170 // wxApp Static member initialization
171 // ----------------------------------------------------------------------------
172 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
173 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
174 EVT_IDLE(wxAppBase::OnIdle)
175 // EVT_END_SESSION(wxApp::OnEndSession)
176 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
179 // ----------------------------------------------------------------------------
180 // wxApp initialization/cleanup
181 // ----------------------------------------------------------------------------
182 bool wxApp::Initialize(int& argc, wxChar **argv)
184 wxAutoNSAutoreleasePool pool;
185 m_cocoaMainThread = [NSThread currentThread];
186 // Mac OS X passes a process serial number command line argument when
187 // the application is launched from the Finder. This argument must be
188 // removed from the command line arguments before being handled by the
189 // application (otherwise applications would need to handle it)
192 static const wxChar *ARG_PSN = _T("-psn_");
193 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
195 // remove this argument
197 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
201 // Posing must be completed before any instances of the Objective-C
202 // classes being posed as are created.
203 wxPoseAsInitializer::InitializePosers();
205 return wxAppBase::Initialize(argc, argv);
208 void wxApp::CleanUp()
210 wxAutoNSAutoreleasePool pool;
212 wxDC::CocoaShutdownTextSystem();
213 wxMenuBarManager::DestroyInstance();
215 [m_cocoaApp setDelegate:nil];
216 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
217 name:NSControlTintDidChangeNotification object:nil];
218 [m_cocoaAppDelegate release];
219 m_cocoaAppDelegate = NULL;
221 wxAppBase::CleanUp();
224 // ----------------------------------------------------------------------------
226 // ----------------------------------------------------------------------------
232 m_isInAssert = false;
233 #endif // __WXDEBUG__
238 m_cocoaAppDelegate = NULL;
241 void wxApp::CocoaDelegate_applicationWillBecomeActive()
245 void wxApp::CocoaDelegate_applicationDidBecomeActive()
249 void wxApp::CocoaDelegate_applicationWillResignActive()
251 wxTopLevelWindowCocoa::DeactivatePendingWindow();
254 void wxApp::CocoaDelegate_applicationDidResignActive()
258 bool wxApp::OnInitGui()
260 wxAutoNSAutoreleasePool pool;
261 if(!wxAppBase::OnInitGui())
264 // Create the app using the sharedApplication method
265 m_cocoaApp = [NSApplication sharedApplication];
266 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
267 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
268 [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
269 selector:@selector(controlTintChanged:)
270 name:NSControlTintDidChangeNotification object:nil];
272 wxMenuBarManager::CreateInstance();
274 wxDC::CocoaInitializeTextSystem();
278 bool wxApp::CallOnInit()
280 // wxAutoNSAutoreleasePool pool;
286 if(!wxAppBase::OnInit())
296 wxAppConsole::Exit();
299 // Yield to other processes
300 bool wxApp::Yield(bool onlyIfNeeded)
303 static bool s_inYield = false;
306 // disable log flushing from here because a call to wxYield() shouldn't
307 // normally result in message boxes popping up &c
315 wxFAIL_MSG( wxT("wxYield called recursively" ) );
323 // Run the event loop until it is out of events
326 wxAutoNSAutoreleasePool pool;
327 NSEvent *event = [GetNSApplication()
328 nextEventMatchingMask:NSAnyEventMask
329 untilDate:[NSDate distantPast]
330 inMode:NSDefaultRunLoopMode
334 [GetNSApplication() sendEvent: event];
338 // let the logs be flashed again
347 void wxApp::WakeUpIdle()
349 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
350 location:NSZeroPoint modifierFlags:NSAnyEventMask
351 timestamp:0 windowNumber:0 context:nil
352 subtype:0 data1:0 data2:0] atStart:NO];
356 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
359 wxAppBase::OnAssert(file, line, cond, msg);
360 m_isInAssert = false;
362 #endif // __WXDEBUG__