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 Ê ÊRN: We used to use nil as the untilDate in previous versions since nil
75 Ê Êis a shorter and more concise way of specifying an infinite amount of
76 ÊÊ Êtime than [NSDate distantPast]. ÊHowever, Apple neglects to mention in
77 Ê Êtheir documentation that nil is not handled correctly in OSX 10.2
78 Ê Ê(and possibly lower) and when the call is reached the system comes to
79 Ê Êa screeching halt, therefore we need to specify [NSDate distantPast]
80 Ê Êexplicitly so that wxCocoa will work correctly in OSX 10.2.
83 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
85 // Get the same events except don't block
86 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
87 // If we got one, simply return it
90 // No events, try doing some idle stuff
93 && !wxTheApp->IsInAssert()
95 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
98 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
99 while(wxTheApp->ProcessIdle())
101 // Get the same events except don't block
102 NSEvent *event = [super nextEventMatchingMask:mask untilDate:[NSDate distantPast] inMode:mode dequeue:flag];
103 // If we got one, simply return it
106 // we didn't get one, do some idle work
107 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
109 // No more idle work requested, block
110 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
113 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
114 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
117 - (void)sendEvent: (NSEvent*)anEvent
119 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
121 [super sendEvent: anEvent];
124 @end // wxPoserNSApplication
126 // ========================================================================
127 // wxNSApplicationDelegate
128 // ========================================================================
129 @implementation wxNSApplicationDelegate : NSObject
131 // NOTE: Terminate means that the event loop does NOT return and thus
132 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
133 // own exit on frame delete mechanism.
134 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
139 - (void)applicationWillBecomeActive:(NSNotification *)notification
141 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
144 - (void)applicationDidBecomeActive:(NSNotification *)notification
146 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
149 - (void)applicationWillResignActive:(NSNotification *)notification
151 wxTheApp->CocoaDelegate_applicationWillResignActive();
154 - (void)applicationDidResignActive:(NSNotification *)notification
156 wxTheApp->CocoaDelegate_applicationDidResignActive();
159 - (void)controlTintChanged:(NSNotification *)notification
161 wxLogDebug(wxT("TODO: send EVT_SYS_COLOUR_CHANGED as appropriate"));
164 @end // implementation wxNSApplicationDelegate : NSObject
166 // ========================================================================
168 // ========================================================================
170 // ----------------------------------------------------------------------------
171 // wxApp Static member initialization
172 // ----------------------------------------------------------------------------
173 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
174 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
175 EVT_IDLE(wxAppBase::OnIdle)
176 // EVT_END_SESSION(wxApp::OnEndSession)
177 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
180 // ----------------------------------------------------------------------------
181 // wxApp initialization/cleanup
182 // ----------------------------------------------------------------------------
183 bool wxApp::Initialize(int& argc, wxChar **argv)
185 wxAutoNSAutoreleasePool pool;
186 m_cocoaMainThread = [NSThread currentThread];
187 // Mac OS X passes a process serial number command line argument when
188 // the application is launched from the Finder. This argument must be
189 // removed from the command line arguments before being handled by the
190 // application (otherwise applications would need to handle it)
193 static const wxChar *ARG_PSN = _T("-psn_");
194 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
196 // remove this argument
198 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
202 // Posing must be completed before any instances of the Objective-C
203 // classes being posed as are created.
204 wxPoseAsInitializer::InitializePosers();
206 return wxAppBase::Initialize(argc, argv);
209 void wxApp::CleanUp()
211 wxAutoNSAutoreleasePool pool;
213 wxDC::CocoaShutdownTextSystem();
214 wxMenuBarManager::DestroyInstance();
216 [m_cocoaApp setDelegate:nil];
217 [[NSNotificationCenter defaultCenter] removeObserver:m_cocoaAppDelegate
218 name:NSControlTintDidChangeNotification object:nil];
219 [m_cocoaAppDelegate release];
220 m_cocoaAppDelegate = NULL;
222 wxAppBase::CleanUp();
225 // ----------------------------------------------------------------------------
227 // ----------------------------------------------------------------------------
232 #if WXWIN_COMPATIBILITY_2_2
233 m_wantDebugOutput = TRUE;
236 m_isInAssert = FALSE;
237 #endif // __WXDEBUG__
242 m_cocoaAppDelegate = NULL;
245 void wxApp::CocoaDelegate_applicationWillBecomeActive()
249 void wxApp::CocoaDelegate_applicationDidBecomeActive()
253 void wxApp::CocoaDelegate_applicationWillResignActive()
255 wxTopLevelWindowCocoa::DeactivatePendingWindow();
258 void wxApp::CocoaDelegate_applicationDidResignActive()
262 bool wxApp::OnInitGui()
264 wxAutoNSAutoreleasePool pool;
265 if(!wxAppBase::OnInitGui())
268 // Create the app using the sharedApplication method
269 m_cocoaApp = [NSApplication sharedApplication];
270 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
271 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
272 [[NSNotificationCenter defaultCenter] addObserver:m_cocoaAppDelegate
273 selector:@selector(controlTintChanged:)
274 name:NSControlTintDidChangeNotification object:nil];
276 wxMenuBarManager::CreateInstance();
278 wxDC::CocoaInitializeTextSystem();
282 bool wxApp::CallOnInit()
284 // wxAutoNSAutoreleasePool pool;
290 if(!wxAppBase::OnInit())
300 wxAppConsole::Exit();
303 // Yield to other processes
304 bool wxApp::Yield(bool onlyIfNeeded)
307 static bool s_inYield = false;
310 // disable log flushing from here because a call to wxYield() shouldn't
311 // normally result in message boxes popping up &c
319 wxFAIL_MSG( wxT("wxYield called recursively" ) );
327 // Run the event loop until it is out of events
330 wxAutoNSAutoreleasePool pool;
331 NSEvent *event = [GetNSApplication()
332 nextEventMatchingMask:NSAnyEventMask
333 untilDate:[NSDate distantPast]
334 inMode:NSDefaultRunLoopMode
338 [GetNSApplication() sendEvent: event];
342 // let the logs be flashed again
351 void wxApp::WakeUpIdle()
353 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
354 location:NSZeroPoint modifierFlags:NSAnyEventMask
355 timestamp:0 windowNumber:0 context:nil
356 subtype:0 data1:0 data2:0] atStart:NO];
360 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
363 wxAppBase::OnAssert(file, line, cond, msg);
364 m_isInAssert = FALSE;
366 #endif // __WXDEBUG__