1 /////////////////////////////////////////////////////////////////////////////
4 // Author: David Elliott
8 // Copyright: (c) David Elliott
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
21 #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 #if wxUSE_WX_RESOURCES
29 # include "wx/resource.h"
32 #import <AppKit/NSApplication.h>
33 #import <Foundation/NSRunLoop.h>
34 #import <Foundation/NSThread.h>
35 #import <AppKit/NSEvent.h>
36 #import <Foundation/NSString.h>
37 #import <Foundation/NSNotification.h>
38 #import <AppKit/NSCell.h>
40 // ========================================================================
41 // wxPoseAsInitializer
42 // ========================================================================
43 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
45 static bool sg_needIdle = true;
47 // ========================================================================
48 // wxPoserNSApplication
49 // ========================================================================
50 @interface wxPoserNSApplication : NSApplication
54 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
55 - (void)sendEvent: (NSEvent*)anEvent;
56 @end // wxPoserNSApplication
58 WX_IMPLEMENT_POSER(wxPoserNSApplication);
60 @implementation wxPoserNSApplication : NSApplication
62 /* NOTE: The old method of idle event handling added the handler using the
63 [NSRunLoop -performSelector:target:argument:order:modes] which caused
64 the invocation to occur at the begining of [NSApplication
65 -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:]. However,
66 the code would be scheduled for invocation with every iteration of
67 the event loop. This new method simply overrides the method. The
68 same caveats apply. In particular, by the time the event loop has
69 called this method, it usually expects to receive an event. If you
70 plan on stopping the event loop, it is wise to send an event through
71 the queue to ensure this method will return.
72 See wxEventLoop::Exit() for more information.
74 This overridden method calls the superclass method with an untilDate
75 parameter that indicates nil should be returned if there are no pending
76 events. That is, nextEventMatchingMask: should not wait for an event.
77 If nil is returned then idle event processing occurs until the user
78 does not request anymore idle events or until a real event comes through.
80 Apple documentation states that nil can be passed in place of
81 [NSDate distantPast] to the untilDate parameter. However, according
82 to Ryan Norton this crashes on Jaguar (10.2).
85 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
87 // Get the same events except don't block
88 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
89 // If we got one, simply return it
92 // No events, try doing some idle stuff
95 && !wxTheApp->IsInAssert()
97 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
100 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
101 while(wxTheApp->ProcessIdle())
103 // Get the same events except don't block
104 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
105 // If we got one, simply return it
108 // we didn't get one, do some idle work
109 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
111 // No more idle work requested, block
112 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
115 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
116 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
119 - (void)sendEvent: (NSEvent*)anEvent
121 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
123 [super sendEvent: anEvent];
126 @end // wxPoserNSApplication
128 // ========================================================================
129 // wxNSApplicationDelegate
130 // ========================================================================
131 @implementation wxNSApplicationDelegate : NSObject
133 // NOTE: Terminate means that the event loop does NOT return and thus
134 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
135 // own exit on frame delete mechanism.
136 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
141 - (void)applicationWillBecomeActive:(NSNotification *)notification
143 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
146 - (void)applicationDidBecomeActive:(NSNotification *)notification
148 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
151 - (void)applicationWillResignActive:(NSNotification *)notification
153 wxTheApp->CocoaDelegate_applicationWillResignActive();
156 - (void)applicationDidResignActive:(NSNotification *)notification
158 wxTheApp->CocoaDelegate_applicationDidResignActive();
161 - (void)controlTintChanged:(NSNotification *)notification
163 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
166 @end // implementation wxNSApplicationDelegate : NSObject
168 // ========================================================================
170 // ========================================================================
172 // ----------------------------------------------------------------------------
173 // wxApp Static member initialization
174 // ----------------------------------------------------------------------------
175 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
176 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
177 EVT_IDLE(wxAppBase::OnIdle)
178 // EVT_END_SESSION(wxApp::OnEndSession)
179 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
182 // ----------------------------------------------------------------------------
183 // wxApp initialization/cleanup
184 // ----------------------------------------------------------------------------
185 bool wxApp::Initialize(int& argc, wxChar **argv)
187 wxAutoNSAutoreleasePool pool;
188 m_cocoaMainThread = [NSThread currentThread];
189 // Mac OS X passes a process serial number command line argument when
190 // the application is launched from the Finder. This argument must be
191 // removed from the command line arguments before being handled by the
192 // application (otherwise applications would need to handle it)
195 static const wxChar *ARG_PSN = _T("-psn_");
196 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
198 // remove this argument
200 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
204 // Posing must be completed before any instances of the Objective-C
205 // classes being posed as are created.
206 wxPoseAsInitializer::InitializePosers();
208 return wxAppBase::Initialize(argc, argv);
211 void wxApp::CleanUp()
213 wxAutoNSAutoreleasePool pool;
215 wxDC::CocoaShutdownTextSystem();
216 wxMenuBarManager::DestroyInstance();
218 [m_cocoaApp setDelegate:nil];
219 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
220 name:NSControlTintDidChangeNotification object:nil];
221 [m_cocoaAppDelegate release];
222 m_cocoaAppDelegate = NULL;
224 wxAppBase::CleanUp();
227 // ----------------------------------------------------------------------------
229 // ----------------------------------------------------------------------------
234 #if WXWIN_COMPATIBILITY_2_2
235 m_wantDebugOutput = TRUE;
238 m_isInAssert = FALSE;
239 #endif // __WXDEBUG__
244 m_cocoaAppDelegate = NULL;
247 void wxApp::CocoaDelegate_applicationWillBecomeActive()
251 void wxApp::CocoaDelegate_applicationDidBecomeActive()
255 void wxApp::CocoaDelegate_applicationWillResignActive()
257 wxTopLevelWindowCocoa::DeactivatePendingWindow();
260 void wxApp::CocoaDelegate_applicationDidResignActive()
264 bool wxApp::OnInitGui()
266 wxAutoNSAutoreleasePool pool;
267 if(!wxAppBase::OnInitGui())
270 // Create the app using the sharedApplication method
271 m_cocoaApp = [NSApplication sharedApplication];
272 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
273 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
274 [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
275 selector:@selector(controlTintChanged:)
276 name:NSControlTintDidChangeNotification object:nil];
278 wxMenuBarManager::CreateInstance();
280 wxDC::CocoaInitializeTextSystem();
284 bool wxApp::CallOnInit()
286 // wxAutoNSAutoreleasePool pool;
292 if(!wxAppBase::OnInit())
302 wxAppConsole::Exit();
305 // Yield to other processes
306 bool wxApp::Yield(bool onlyIfNeeded)
309 static bool s_inYield = false;
312 // disable log flushing from here because a call to wxYield() shouldn't
313 // normally result in message boxes popping up &c
321 wxFAIL_MSG( wxT("wxYield called recursively" ) );
329 // Run the event loop until it is out of events
332 wxAutoNSAutoreleasePool pool;
333 NSEvent *event = [GetNSApplication()
334 nextEventMatchingMask:NSAnyEventMask
335 untilDate:[NSDate distantPast]
336 inMode:NSDefaultRunLoopMode
340 [GetNSApplication() sendEvent: event];
344 // let the logs be flashed again
353 void wxApp::WakeUpIdle()
355 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
356 location:NSZeroPoint modifierFlags:NSAnyEventMask
357 timestamp:0 windowNumber:0 context:nil
358 subtype:0 data1:0 data2:0] atStart:NO];
362 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
365 wxAppBase::OnAssert(file, line, cond, msg);
366 m_isInAssert = FALSE;
368 #endif // __WXDEBUG__