]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/app.mm
make sure we are getting the gui mutex before shutting down
[wxWidgets.git] / src / cocoa / app.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: cocoa/app.mm
3 // Purpose: wxApp
4 // Author: David Elliott
5 // Modified by:
6 // Created: 2002/11/27
7 // RCS-ID: $Id$
8 // Copyright: (c) David Elliott
9 // Licence: wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifndef WX_PRECOMP
14 #include "wx/defs.h"
15 #include "wx/app.h"
16 #include "wx/dc.h"
17 #include "wx/intl.h"
18 #include "wx/log.h"
19 #endif
20
21 #include "wx/module.h"
22
23 #include "wx/cocoa/ObjcPose.h"
24 #include "wx/cocoa/autorelease.h"
25 #include "wx/cocoa/mbarman.h"
26 #include "wx/cocoa/NSApplication.h"
27
28 #if wxUSE_WX_RESOURCES
29 # include "wx/resource.h"
30 #endif
31
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
38 // ========================================================================
39 // wxPoseAsInitializer
40 // ========================================================================
41 wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
42
43 static bool sg_needIdle = true;
44
45 // ========================================================================
46 // wxPoserNSApplication
47 // ========================================================================
48 @interface wxPoserNSApplication : NSApplication
49 {
50 }
51
52 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
53 - (void)sendEvent: (NSEvent*)anEvent;
54 @end // wxPoserNSApplication
55
56 WX_IMPLEMENT_POSER(wxPoserNSApplication);
57
58 @implementation wxPoserNSApplication : NSApplication
59
60 /* NOTE: The old method of idle event handling added the handler using the
61 [NSRunLoop -performSelector:target:argument:order:modes] which caused
62 the invocation to occur at the begining of [NSApplication
63 -nextEventMatchingMask:untilDate:expiration:inMode:dequeue:]. However,
64 the code would be scheduled for invocation with every iteration of
65 the event loop. This new method simply overrides the method. The
66 same caveats apply. In particular, by the time the event loop has
67 called this method, it usually expects to receive an event. If you
68 plan on stopping the event loop, it is wise to send an event through
69 the queue to ensure this method will return.
70 See wxEventLoop::Exit() for more information.
71 */
72
73 - (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
74 {
75 // Get the same events except don't block
76 NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
77 // If we got one, simply return it
78 if(event)
79 return event;
80 // No events, try doing some idle stuff
81 if(sg_needIdle
82 #ifdef __WXDEBUG__
83 && !wxTheApp->IsInAssert()
84 #endif
85 && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
86 {
87 sg_needIdle = false;
88 wxLogTrace(wxTRACE_COCOA,wxT("Processing idle events"));
89 while(wxTheApp->ProcessIdle())
90 {
91 // Get the same events except don't block
92 NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
93 // If we got one, simply return it
94 if(event)
95 return event;
96 // we didn't get one, do some idle work
97 wxLogTrace(wxTRACE_COCOA,wxT("Looping idle events"));
98 }
99 // No more idle work requested, block
100 wxLogTrace(wxTRACE_COCOA,wxT("Finished idle processing"));
101 }
102 else
103 wxLogTrace(wxTRACE_COCOA,wxT("Avoiding idle processing sg_needIdle=%d"),sg_needIdle);
104 return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
105 }
106
107 - (void)sendEvent: (NSEvent*)anEvent
108 {
109 wxLogTrace(wxTRACE_COCOA,wxT("SendEvent"));
110 sg_needIdle = true;
111 [super sendEvent: anEvent];
112 }
113
114 @end // wxPoserNSApplication
115
116 // ========================================================================
117 // wxNSApplicationDelegate
118 // ========================================================================
119 @implementation wxNSApplicationDelegate : NSObject
120
121 // NOTE: Terminate means that the event loop does NOT return and thus
122 // cleanup code doesn't properly execute. Furthermore, wxWidgets has its
123 // own exit on frame delete mechanism.
124 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
125 {
126 return NO;
127 }
128
129 - (void)applicationWillBecomeActive:(NSNotification *)notification
130 {
131 wxTheApp->CocoaDelegate_applicationWillBecomeActive();
132 }
133
134 - (void)applicationDidBecomeActive:(NSNotification *)notification
135 {
136 wxTheApp->CocoaDelegate_applicationDidBecomeActive();
137 }
138
139 - (void)applicationWillResignActive:(NSNotification *)notification
140 {
141 wxTheApp->CocoaDelegate_applicationWillResignActive();
142 }
143
144 - (void)applicationDidResignActive:(NSNotification *)notification
145 {
146 wxTheApp->CocoaDelegate_applicationDidResignActive();
147 }
148
149 @end // implementation wxNSApplicationDelegate : NSObject
150
151 // ========================================================================
152 // wxApp
153 // ========================================================================
154
155 // ----------------------------------------------------------------------------
156 // wxApp Static member initialization
157 // ----------------------------------------------------------------------------
158 IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
159 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
160 EVT_IDLE(wxAppBase::OnIdle)
161 // EVT_END_SESSION(wxApp::OnEndSession)
162 // EVT_QUERY_END_SESSION(wxApp::OnQueryEndSession)
163 END_EVENT_TABLE()
164
165 // ----------------------------------------------------------------------------
166 // wxApp initialization/cleanup
167 // ----------------------------------------------------------------------------
168 bool wxApp::Initialize(int& argc, wxChar **argv)
169 {
170 wxAutoNSAutoreleasePool pool;
171 m_cocoaMainThread = [NSThread currentThread];
172 // Mac OS X passes a process serial number command line argument when
173 // the application is launched from the Finder. This argument must be
174 // removed from the command line arguments before being handled by the
175 // application (otherwise applications would need to handle it)
176 if ( argc > 1 )
177 {
178 static const wxChar *ARG_PSN = _T("-psn_");
179 if ( wxStrncmp(argv[1], ARG_PSN, wxStrlen(ARG_PSN)) == 0 )
180 {
181 // remove this argument
182 --argc;
183 memmove(argv + 1, argv + 2, argc * sizeof(wxChar *));
184 }
185 }
186
187 // Posing must be completed before any instances of the Objective-C
188 // classes being posed as are created.
189 wxPoseAsInitializer::InitializePosers();
190
191 return wxAppBase::Initialize(argc, argv);
192 }
193
194 void wxApp::CleanUp()
195 {
196 wxAutoNSAutoreleasePool pool;
197
198 wxDC::CocoaShutdownTextSystem();
199 wxMenuBarManager::DestroyInstance();
200
201 [m_cocoaApp setDelegate:nil];
202 [m_cocoaAppDelegate release];
203 m_cocoaAppDelegate = NULL;
204
205 wxAppBase::CleanUp();
206 }
207
208 // ----------------------------------------------------------------------------
209 // wxApp creation
210 // ----------------------------------------------------------------------------
211 wxApp::wxApp()
212 {
213 m_topWindow = NULL;
214
215 #if WXWIN_COMPATIBILITY_2_2
216 m_wantDebugOutput = TRUE;
217 #endif
218 #ifdef __WXDEBUG__
219 m_isInAssert = FALSE;
220 #endif // __WXDEBUG__
221
222 argc = 0;
223 argv = NULL;
224 m_cocoaApp = NULL;
225 m_cocoaAppDelegate = NULL;
226 }
227
228 void wxApp::CocoaDelegate_applicationWillBecomeActive()
229 {
230 }
231
232 void wxApp::CocoaDelegate_applicationDidBecomeActive()
233 {
234 }
235
236 void wxApp::CocoaDelegate_applicationWillResignActive()
237 {
238 wxTopLevelWindowCocoa::DeactivatePendingWindow();
239 }
240
241 void wxApp::CocoaDelegate_applicationDidResignActive()
242 {
243 }
244
245 bool wxApp::OnInitGui()
246 {
247 wxAutoNSAutoreleasePool pool;
248 if(!wxAppBase::OnInitGui())
249 return FALSE;
250
251 // Create the app using the sharedApplication method
252 m_cocoaApp = [NSApplication sharedApplication];
253 m_cocoaAppDelegate = [[wxNSApplicationDelegate alloc] init];
254 [m_cocoaApp setDelegate:m_cocoaAppDelegate];
255
256 wxMenuBarManager::CreateInstance();
257
258 wxDC::CocoaInitializeTextSystem();
259 // [ m_cocoaApp setDelegate:m_cocoaApp ];
260 return TRUE;
261 }
262
263 bool wxApp::CallOnInit()
264 {
265 // wxAutoNSAutoreleasePool pool;
266 return OnInit();
267 }
268
269 bool wxApp::OnInit()
270 {
271 if(!wxAppBase::OnInit())
272 return FALSE;
273
274 return TRUE;
275 }
276
277 void wxApp::Exit()
278 {
279 wxApp::CleanUp();
280
281 wxAppConsole::Exit();
282 }
283
284 // Yield to other processes
285 bool wxApp::Yield(bool onlyIfNeeded)
286 {
287 // MT-FIXME
288 static bool s_inYield = false;
289
290 #if wxUSE_LOG
291 // disable log flushing from here because a call to wxYield() shouldn't
292 // normally result in message boxes popping up &c
293 wxLog::Suspend();
294 #endif // wxUSE_LOG
295
296 if (s_inYield)
297 {
298 if ( !onlyIfNeeded )
299 {
300 wxFAIL_MSG( wxT("wxYield called recursively" ) );
301 }
302
303 return false;
304 }
305
306 s_inYield = true;
307
308 // Run the event loop until it is out of events
309 while(1)
310 {
311 wxAutoNSAutoreleasePool pool;
312 NSEvent *event = [GetNSApplication()
313 nextEventMatchingMask:NSAnyEventMask
314 untilDate:nil /* ==[NSDate distantPast] */
315 inMode:NSDefaultRunLoopMode
316 dequeue: YES];
317 if(!event)
318 break;
319 [GetNSApplication() sendEvent: event];
320 }
321
322 #if wxUSE_LOG
323 // let the logs be flashed again
324 wxLog::Resume();
325 #endif // wxUSE_LOG
326
327 s_inYield = false;
328
329 return true;
330 }
331
332 void wxApp::WakeUpIdle()
333 {
334 [m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
335 location:NSZeroPoint modifierFlags:NSAnyEventMask
336 timestamp:0 windowNumber:0 context:nil
337 subtype:0 data1:0 data2:0] atStart:NO];
338 }
339
340 #ifdef __WXDEBUG__
341 void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
342 {
343 m_isInAssert = TRUE;
344 wxAppBase::OnAssert(file, line, cond, msg);
345 m_isInAssert = FALSE;
346 }
347 #endif // __WXDEBUG__
348