]> git.saurik.com Git - wxWidgets.git/blob - src/osx/core/evtloop_cf.cpp
704a8334134328e52694ad5caef368b2eb417440
[wxWidgets.git] / src / osx / core / evtloop_cf.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/core/evtloop_cf.cpp
3 // Purpose: wxEventLoop implementation common to both Carbon and Cocoa
4 // Author: Vadim Zeitlin
5 // Created: 2009-10-18
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #include "wx/evtloop.h"
27
28 #ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/app.h"
31 #endif
32
33 #include "wx/evtloopsrc.h"
34
35 #include "wx/scopedptr.h"
36
37 #include "wx/osx/private.h"
38 #include "wx/osx/core/cfref.h"
39 #include "wx/thread.h"
40
41 #if wxUSE_GUI
42 #include "wx/nonownedwnd.h"
43 #endif
44
45 // ============================================================================
46 // wxCFEventLoopSource and wxCFEventLoop implementation
47 // ============================================================================
48
49 #if wxUSE_EVENTLOOP_SOURCE
50
51 void wxCFEventLoopSource::SetFileDescriptor(CFFileDescriptorRef cffd)
52 {
53 wxASSERT_MSG( !m_cffd, "shouldn't be called more than once" );
54
55 m_cffd = cffd;
56 }
57
58 wxCFEventLoopSource::~wxCFEventLoopSource()
59 {
60 if ( m_cffd )
61 CFRelease(m_cffd);
62 }
63
64 #endif // wxUSE_EVENTLOOP_SOURCE
65
66 void wxCFEventLoop::OSXCommonModeObserverCallBack(CFRunLoopObserverRef observer, int activity, void *info)
67 {
68 wxCFEventLoop * eventloop = static_cast<wxCFEventLoop *>(info);
69 if ( eventloop )
70 eventloop->CommonModeObserverCallBack(observer, activity);
71 }
72
73 void wxCFEventLoop::OSXDefaultModeObserverCallBack(CFRunLoopObserverRef observer, int activity, void *info)
74 {
75 wxCFEventLoop * eventloop = static_cast<wxCFEventLoop *>(info);
76 if ( eventloop )
77 eventloop->DefaultModeObserverCallBack(observer, activity);
78 }
79
80 void wxCFEventLoop::CommonModeObserverCallBack(CFRunLoopObserverRef WXUNUSED(observer), int activity)
81 {
82 if ( activity & kCFRunLoopBeforeTimers )
83 {
84 // process pending wx events first as they correspond to low-level events
85 // which happened before, i.e. typically pending events were queued by a
86 // previous call to Dispatch() and if we didn't process them now the next
87 // call to it might enqueue them again (as happens with e.g. socket events
88 // which would be generated as long as there is input available on socket
89 // and this input is only removed from it when pending event handlers are
90 // executed)
91
92 if ( wxTheApp && ShouldProcessIdleEvents() )
93 wxTheApp->ProcessPendingEvents();
94 }
95
96 if ( activity & kCFRunLoopBeforeWaiting )
97 {
98 if ( ShouldProcessIdleEvents() && ProcessIdle() )
99 {
100 WakeUp();
101 }
102 else
103 {
104 #if wxUSE_THREADS
105 wxMutexGuiLeaveOrEnter();
106 #endif
107 }
108 }
109 }
110
111 void
112 wxCFEventLoop::DefaultModeObserverCallBack(CFRunLoopObserverRef WXUNUSED(observer),
113 int WXUNUSED(activity))
114 {
115 /*
116 if ( activity & kCFRunLoopBeforeTimers )
117 {
118 }
119
120 if ( activity & kCFRunLoopBeforeWaiting )
121 {
122 }
123 */
124 }
125
126
127 wxCFEventLoop::wxCFEventLoop()
128 {
129 m_shouldExit = false;
130 m_processIdleEvents = true;
131
132 #if wxUSE_UIACTIONSIMULATOR
133 m_shouldWaitForEvent = false;
134 #endif
135
136 m_runLoop = CFGetCurrentRunLoop();
137
138 CFRunLoopObserverContext ctxt;
139 bzero( &ctxt, sizeof(ctxt) );
140 ctxt.info = this;
141 m_commonModeRunLoopObserver = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopBeforeTimers | kCFRunLoopBeforeWaiting , true /* repeats */, 0,
142 (CFRunLoopObserverCallBack) wxCFEventLoop::OSXCommonModeObserverCallBack, &ctxt );
143 CFRunLoopAddObserver(m_runLoop, m_commonModeRunLoopObserver, kCFRunLoopCommonModes);
144
145 m_defaultModeRunLoopObserver = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopBeforeTimers | kCFRunLoopBeforeWaiting , true /* repeats */, 0,
146 (CFRunLoopObserverCallBack) wxCFEventLoop::OSXDefaultModeObserverCallBack, &ctxt );
147 CFRunLoopAddObserver(m_runLoop, m_defaultModeRunLoopObserver, kCFRunLoopDefaultMode);
148 }
149
150 wxCFEventLoop::~wxCFEventLoop()
151 {
152 CFRunLoopRemoveObserver(m_runLoop, m_commonModeRunLoopObserver, kCFRunLoopCommonModes);
153 CFRunLoopRemoveObserver(m_runLoop, m_defaultModeRunLoopObserver, kCFRunLoopDefaultMode);
154
155 CFRelease(m_defaultModeRunLoopObserver);
156 CFRelease(m_commonModeRunLoopObserver);
157 }
158
159
160 CFRunLoopRef wxCFEventLoop::CFGetCurrentRunLoop() const
161 {
162 return CFRunLoopGetCurrent();
163 }
164
165 void wxCFEventLoop::WakeUp()
166 {
167 CFRunLoopWakeUp(m_runLoop);
168 }
169
170 #if wxUSE_BASE
171
172 void wxMacWakeUp()
173 {
174 wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
175
176 if ( loop )
177 loop->WakeUp();
178 }
179
180 #endif
181
182 bool wxCFEventLoop::YieldFor(long eventsToProcess)
183 {
184 #if wxUSE_THREADS
185 // Yielding from a non-gui thread needs to bail out, otherwise we end up
186 // possibly sending events in the thread too.
187 if ( !wxThread::IsMain() )
188 {
189 return true;
190 }
191 #endif // wxUSE_THREADS
192
193 m_isInsideYield = true;
194 m_eventsToProcessInsideYield = eventsToProcess;
195
196 #if wxUSE_LOG
197 // disable log flushing from here because a call to wxYield() shouldn't
198 // normally result in message boxes popping up &c
199 wxLog::Suspend();
200 #endif // wxUSE_LOG
201
202 // process all pending events:
203 while ( DoProcessEvents() == 1 )
204 ;
205
206 // it's necessary to call ProcessIdle() to update the frames sizes which
207 // might have been changed (it also will update other things set from
208 // OnUpdateUI() which is a nice (and desired) side effect)
209 while ( ProcessIdle() ) {}
210
211 // if there are pending events, we must process them.
212 if (wxTheApp)
213 wxTheApp->ProcessPendingEvents();
214
215 #if wxUSE_LOG
216 wxLog::Resume();
217 #endif // wxUSE_LOG
218 m_isInsideYield = false;
219
220 return true;
221 }
222
223 // implement/override base class pure virtual
224 bool wxCFEventLoop::Pending() const
225 {
226 return true;
227 }
228
229 int wxCFEventLoop::DoProcessEvents()
230 {
231 #if wxUSE_UIACTIONSIMULATOR
232 if ( m_shouldWaitForEvent )
233 {
234 int handled = DispatchTimeout( 1000 );
235 wxASSERT_MSG( handled == 1, "No Event Available");
236 m_shouldWaitForEvent = false;
237 return handled;
238 }
239 else
240 #endif
241 return DispatchTimeout( 0 );
242 }
243
244 bool wxCFEventLoop::Dispatch()
245 {
246 return DoProcessEvents() != 0;
247 }
248
249 int wxCFEventLoop::DispatchTimeout(unsigned long timeout)
250 {
251 if ( !wxTheApp )
252 return 0;
253
254 int status = DoDispatchTimeout(timeout);
255
256 switch( status )
257 {
258 case 0:
259 break;
260 case -1:
261 if ( m_shouldExit )
262 return 0;
263
264 break;
265 case 1:
266 break;
267 }
268
269 return status;
270 }
271
272 int wxCFEventLoop::DoDispatchTimeout(unsigned long timeout)
273 {
274 SInt32 status = CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout / 1000.0 , true);
275 switch( status )
276 {
277 case kCFRunLoopRunFinished:
278 wxFAIL_MSG( "incorrect run loop state" );
279 break;
280 case kCFRunLoopRunStopped:
281 return 0;
282 break;
283 case kCFRunLoopRunTimedOut:
284 return -1;
285 break;
286 case kCFRunLoopRunHandledSource:
287 default:
288 break;
289 }
290 return 1;
291 }
292
293 void wxCFEventLoop::OSXDoRun()
294 {
295 for ( ;; )
296 {
297 // generate and process idle events for as long as we don't
298 // have anything else to do
299 DoProcessEvents();
300
301 // if the "should exit" flag is set, the loop should terminate
302 // but not before processing any remaining messages so while
303 // Pending() returns true, do process them
304 if ( m_shouldExit )
305 {
306 while ( DoProcessEvents() == 1 )
307 ;
308
309 break;
310 }
311 }
312 }
313
314 void wxCFEventLoop::OSXDoStop()
315 {
316 CFRunLoopStop(CFGetCurrentRunLoop());
317 }
318
319 // enters a loop calling OnNextIteration(), Pending() and Dispatch() and
320 // terminating when Exit() is called
321 int wxCFEventLoop::DoRun()
322 {
323 // we must ensure that OnExit() is called even if an exception is thrown
324 // from inside ProcessEvents() but we must call it from Exit() in normal
325 // situations because it is supposed to be called synchronously,
326 // wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or
327 // something similar here)
328 #if wxUSE_EXCEPTIONS
329 for ( ;; )
330 {
331 try
332 {
333 #endif // wxUSE_EXCEPTIONS
334
335 OSXDoRun();
336
337 #if wxUSE_EXCEPTIONS
338 // exit the outer loop as well
339 break;
340 }
341 catch ( ... )
342 {
343 try
344 {
345 if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() )
346 {
347 OnExit();
348 break;
349 }
350 //else: continue running the event loop
351 }
352 catch ( ... )
353 {
354 // OnException() throwed, possibly rethrowing the same
355 // exception again: very good, but we still need OnExit() to
356 // be called
357 OnExit();
358 throw;
359 }
360 }
361 }
362 #endif // wxUSE_EXCEPTIONS
363
364 return m_exitcode;
365 }
366
367 // sets the "should exit" flag and wakes up the loop so that it terminates
368 // soon
369 void wxCFEventLoop::ScheduleExit(int rc)
370 {
371 m_exitcode = rc;
372 m_shouldExit = true;
373 OSXDoStop();
374 }
375
376 wxCFEventLoopPauseIdleEvents::wxCFEventLoopPauseIdleEvents()
377 {
378 wxCFEventLoop* cfl = dynamic_cast<wxCFEventLoop*>(wxEventLoopBase::GetActive());
379 if ( cfl )
380 {
381 m_formerState = cfl->ShouldProcessIdleEvents();
382 cfl->SetProcessIdleEvents(false);
383 }
384 else
385 m_formerState = true;
386 }
387
388 wxCFEventLoopPauseIdleEvents::~wxCFEventLoopPauseIdleEvents()
389 {
390 wxCFEventLoop* cfl = dynamic_cast<wxCFEventLoop*>(wxEventLoopBase::GetActive());
391 if ( cfl )
392 cfl->SetProcessIdleEvents(m_formerState);
393 }
394
395 // TODO Move to thread_osx.cpp
396
397 #if wxUSE_THREADS
398
399 // ----------------------------------------------------------------------------
400 // GUI Serialization copied from MSW implementation
401 // ----------------------------------------------------------------------------
402
403 // if it's false, some secondary thread is holding the GUI lock
404 static bool gs_bGuiOwnedByMainThread = true;
405
406 // critical section which controls access to all GUI functions: any secondary
407 // thread (i.e. except the main one) must enter this crit section before doing
408 // any GUI calls
409 static wxCriticalSection *gs_critsectGui = NULL;
410
411 // critical section which protects gs_nWaitingForGui variable
412 static wxCriticalSection *gs_critsectWaitingForGui = NULL;
413
414 // number of threads waiting for GUI in wxMutexGuiEnter()
415 static size_t gs_nWaitingForGui = 0;
416
417 void wxOSXThreadModuleOnInit()
418 {
419 gs_critsectWaitingForGui = new wxCriticalSection();
420 gs_critsectGui = new wxCriticalSection();
421 gs_critsectGui->Enter();
422 }
423
424
425 void wxOSXThreadModuleOnExit()
426 {
427 if ( gs_critsectGui )
428 {
429 if ( !wxGuiOwnedByMainThread() )
430 {
431 gs_critsectGui->Enter();
432 gs_bGuiOwnedByMainThread = true;
433 }
434
435 gs_critsectGui->Leave();
436 wxDELETE(gs_critsectGui);
437 }
438
439 wxDELETE(gs_critsectWaitingForGui);
440 }
441
442
443 // wake up the main thread
444 void WXDLLIMPEXP_BASE wxWakeUpMainThread()
445 {
446 wxMacWakeUp();
447 }
448
449 void wxMutexGuiEnterImpl()
450 {
451 // this would dead lock everything...
452 wxASSERT_MSG( !wxThread::IsMain(),
453 wxT("main thread doesn't want to block in wxMutexGuiEnter()!") );
454
455 // the order in which we enter the critical sections here is crucial!!
456
457 // set the flag telling to the main thread that we want to do some GUI
458 {
459 wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
460
461 gs_nWaitingForGui++;
462 }
463
464 wxWakeUpMainThread();
465
466 // now we may block here because the main thread will soon let us in
467 // (during the next iteration of OnIdle())
468 gs_critsectGui->Enter();
469 }
470
471 void wxMutexGuiLeaveImpl()
472 {
473 wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
474
475 if ( wxThread::IsMain() )
476 {
477 gs_bGuiOwnedByMainThread = false;
478 }
479 else
480 {
481 // decrement the number of threads waiting for GUI access now
482 wxASSERT_MSG( gs_nWaitingForGui > 0,
483 wxT("calling wxMutexGuiLeave() without entering it first?") );
484
485 gs_nWaitingForGui--;
486
487 wxWakeUpMainThread();
488 }
489
490 gs_critsectGui->Leave();
491 }
492
493 void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter()
494 {
495 wxASSERT_MSG( wxThread::IsMain(),
496 wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
497
498 if ( !gs_critsectWaitingForGui )
499 return;
500
501 wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
502
503 if ( gs_nWaitingForGui == 0 )
504 {
505 // no threads are waiting for GUI - so we may acquire the lock without
506 // any danger (but only if we don't already have it)
507 if ( !wxGuiOwnedByMainThread() )
508 {
509 gs_critsectGui->Enter();
510
511 gs_bGuiOwnedByMainThread = true;
512 }
513 //else: already have it, nothing to do
514 }
515 else
516 {
517 // some threads are waiting, release the GUI lock if we have it
518 if ( wxGuiOwnedByMainThread() )
519 wxMutexGuiLeave();
520 //else: some other worker thread is doing GUI
521 }
522 }
523
524 bool WXDLLIMPEXP_BASE wxGuiOwnedByMainThread()
525 {
526 return gs_bGuiOwnedByMainThread;
527 }
528
529 #endif