1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/osx/core/evtloop_cf.cpp 
   3 // Purpose:     wxEventLoop implementation common to both Carbon and Cocoa 
   4 // Author:      Vadim Zeitlin 
   6 // Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org> 
   7 //              (c) 2013 Rob Bresalier 
   8 // Licence:     wxWindows licence 
   9 /////////////////////////////////////////////////////////////////////////////// 
  11 // ============================================================================ 
  13 // ============================================================================ 
  15 // ---------------------------------------------------------------------------- 
  17 // ---------------------------------------------------------------------------- 
  19 // for compilers that support precompilation, includes "wx.h". 
  20 #include "wx/wxprec.h" 
  26 #include "wx/evtloop.h" 
  33 #include "wx/evtloopsrc.h" 
  35 #include "wx/scopedptr.h" 
  37 #include "wx/osx/private.h" 
  38 #include "wx/osx/core/cfref.h" 
  39 #include "wx/thread.h" 
  42     #include "wx/nonownedwnd.h" 
  45 #include <CoreFoundation/CFSocket.h> 
  47 // ============================================================================ 
  48 // wxCFEventLoopSource and wxCFEventLoop implementation 
  49 // ============================================================================ 
  51 #if wxUSE_EVENTLOOP_SOURCE 
  53 void wxCFEventLoopSource::InitSourceSocket(CFSocketRef cfSocket
) 
  55     wxASSERT_MSG( !m_cfSocket
, "shouldn't be called more than once" ); 
  57     m_cfSocket 
= cfSocket
; 
  60 wxCFEventLoopSource::~wxCFEventLoopSource() 
  64         CFSocketInvalidate(m_cfSocket
); 
  65         CFRelease(m_cfSocket
); 
  69 #endif // wxUSE_EVENTLOOP_SOURCE 
  71 void wxCFEventLoop::OSXCommonModeObserverCallBack(CFRunLoopObserverRef observer
, int activity
, void *info
) 
  73     wxCFEventLoop 
* eventloop 
= static_cast<wxCFEventLoop 
*>(info
); 
  75         eventloop
->CommonModeObserverCallBack(observer
, activity
); 
  78 void wxCFEventLoop::OSXDefaultModeObserverCallBack(CFRunLoopObserverRef observer
, int activity
, void *info
) 
  80     wxCFEventLoop 
* eventloop 
= static_cast<wxCFEventLoop 
*>(info
); 
  82         eventloop
->DefaultModeObserverCallBack(observer
, activity
); 
  85 void wxCFEventLoop::CommonModeObserverCallBack(CFRunLoopObserverRef 
WXUNUSED(observer
), int activity
) 
  87     if ( activity 
& kCFRunLoopBeforeTimers 
) 
  89         // process pending wx events first as they correspond to low-level events 
  90         // which happened before, i.e. typically pending events were queued by a 
  91         // previous call to Dispatch() and if we didn't process them now the next 
  92         // call to it might enqueue them again (as happens with e.g. socket events 
  93         // which would be generated as long as there is input available on socket 
  94         // and this input is only removed from it when pending event handlers are 
  97         if ( wxTheApp 
&& ShouldProcessIdleEvents() ) 
  98             wxTheApp
->ProcessPendingEvents(); 
 101     if ( activity 
& kCFRunLoopBeforeWaiting 
) 
 103         if ( ShouldProcessIdleEvents() && ProcessIdle() ) 
 110             wxMutexGuiLeaveOrEnter(); 
 117 wxCFEventLoop::DefaultModeObserverCallBack(CFRunLoopObserverRef 
WXUNUSED(observer
), 
 118                                            int WXUNUSED(activity
)) 
 121     if ( activity & kCFRunLoopBeforeTimers ) 
 125     if ( activity & kCFRunLoopBeforeWaiting ) 
 132 wxCFEventLoop::wxCFEventLoop() 
 134     m_shouldExit 
= false; 
 135     m_processIdleEvents 
= true; 
 137 #if wxUSE_UIACTIONSIMULATOR 
 138     m_shouldWaitForEvent 
= false; 
 141     m_runLoop 
= CFGetCurrentRunLoop(); 
 143     CFRunLoopObserverContext ctxt
; 
 144     bzero( &ctxt
, sizeof(ctxt
) ); 
 146     m_commonModeRunLoopObserver 
= CFRunLoopObserverCreate( kCFAllocatorDefault
, kCFRunLoopBeforeTimers 
| kCFRunLoopBeforeWaiting 
, true /* repeats */, 0, 
 147                                                           (CFRunLoopObserverCallBack
) wxCFEventLoop::OSXCommonModeObserverCallBack
, &ctxt 
); 
 148     CFRunLoopAddObserver(m_runLoop
, m_commonModeRunLoopObserver
, kCFRunLoopCommonModes
); 
 150     m_defaultModeRunLoopObserver 
= CFRunLoopObserverCreate( kCFAllocatorDefault
, kCFRunLoopBeforeTimers 
| kCFRunLoopBeforeWaiting 
, true /* repeats */, 0, 
 151                                                            (CFRunLoopObserverCallBack
) wxCFEventLoop::OSXDefaultModeObserverCallBack
, &ctxt 
); 
 152     CFRunLoopAddObserver(m_runLoop
, m_defaultModeRunLoopObserver
, kCFRunLoopDefaultMode
); 
 155 wxCFEventLoop::~wxCFEventLoop() 
 157     CFRunLoopRemoveObserver(m_runLoop
, m_commonModeRunLoopObserver
, kCFRunLoopCommonModes
); 
 158     CFRunLoopRemoveObserver(m_runLoop
, m_defaultModeRunLoopObserver
, kCFRunLoopDefaultMode
); 
 160     CFRelease(m_defaultModeRunLoopObserver
); 
 161     CFRelease(m_commonModeRunLoopObserver
); 
 165 CFRunLoopRef 
wxCFEventLoop::CFGetCurrentRunLoop() const 
 167     return CFRunLoopGetCurrent(); 
 170 void wxCFEventLoop::WakeUp() 
 172     CFRunLoopWakeUp(m_runLoop
); 
 179     wxEventLoopBase 
* const loop 
= wxEventLoopBase::GetActive(); 
 187 bool wxCFEventLoop::YieldFor(long eventsToProcess
) 
 190     // Yielding from a non-gui thread needs to bail out, otherwise we end up 
 191     // possibly sending events in the thread too. 
 192     if ( !wxThread::IsMain() ) 
 196 #endif // wxUSE_THREADS 
 198     m_isInsideYield 
= true; 
 199     m_eventsToProcessInsideYield 
= eventsToProcess
; 
 202     // disable log flushing from here because a call to wxYield() shouldn't 
 203     // normally result in message boxes popping up &c 
 207     // process all pending events: 
 208     while ( DoProcessEvents() == 1 ) 
 211     // it's necessary to call ProcessIdle() to update the frames sizes which 
 212     // might have been changed (it also will update other things set from 
 213     // OnUpdateUI() which is a nice (and desired) side effect) 
 214     while ( ProcessIdle() ) {} 
 216     // if there are pending events, we must process them. 
 218         wxTheApp
->ProcessPendingEvents(); 
 223     m_isInsideYield 
= false; 
 228 // implement/override base class pure virtual 
 229 bool wxCFEventLoop::Pending() const 
 234 int wxCFEventLoop::DoProcessEvents() 
 236 #if wxUSE_UIACTIONSIMULATOR 
 237     if ( m_shouldWaitForEvent 
) 
 239         int  handled 
= DispatchTimeout( 1000 ); 
 240         wxASSERT_MSG( handled 
== 1, "No Event Available"); 
 241         m_shouldWaitForEvent 
= false; 
 246         return DispatchTimeout( 0 ); 
 249 bool wxCFEventLoop::Dispatch() 
 251     return DoProcessEvents() != 0; 
 254 int wxCFEventLoop::DispatchTimeout(unsigned long timeout
) 
 259     int status 
= DoDispatchTimeout(timeout
); 
 277 int wxCFEventLoop::DoDispatchTimeout(unsigned long timeout
) 
 279     SInt32 status 
= CFRunLoopRunInMode(kCFRunLoopDefaultMode
, timeout 
/ 1000.0 , true); 
 282         case kCFRunLoopRunFinished
: 
 283             wxFAIL_MSG( "incorrect run loop state" ); 
 285         case kCFRunLoopRunStopped
: 
 288         case kCFRunLoopRunTimedOut
: 
 291         case kCFRunLoopRunHandledSource
: 
 298 void wxCFEventLoop::OSXDoRun() 
 302         // generate and process idle events for as long as we don't 
 303         // have anything else to do 
 306         // if the "should exit" flag is set, the loop should terminate 
 307         // but not before processing any remaining messages so while 
 308         // Pending() returns true, do process them 
 311             while ( DoProcessEvents() == 1 ) 
 319 void wxCFEventLoop::OSXDoStop() 
 321     CFRunLoopStop(CFGetCurrentRunLoop()); 
 324 // enters a loop calling OnNextIteration(), Pending() and Dispatch() and 
 325 // terminating when Exit() is called 
 326 int wxCFEventLoop::DoRun() 
 328     // we must ensure that OnExit() is called even if an exception is thrown 
 329     // from inside ProcessEvents() but we must call it from Exit() in normal 
 330     // situations because it is supposed to be called synchronously, 
 331     // wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or 
 332     // something similar here) 
 338 #endif // wxUSE_EXCEPTIONS 
 343             // exit the outer loop as well 
 350                 if ( !wxTheApp 
|| !wxTheApp
->OnExceptionInMainLoop() ) 
 355                 //else: continue running the event loop 
 359                 // OnException() throwed, possibly rethrowing the same 
 360                 // exception again: very good, but we still need OnExit() to 
 367 #endif // wxUSE_EXCEPTIONS 
 372 // sets the "should exit" flag and wakes up the loop so that it terminates 
 374 void wxCFEventLoop::ScheduleExit(int rc
) 
 381 wxCFEventLoopPauseIdleEvents::wxCFEventLoopPauseIdleEvents() 
 383     wxCFEventLoop
* cfl 
= dynamic_cast<wxCFEventLoop
*>(wxEventLoopBase::GetActive()); 
 386         m_formerState 
= cfl
->ShouldProcessIdleEvents(); 
 387         cfl
->SetProcessIdleEvents(false); 
 390         m_formerState 
= true; 
 393 wxCFEventLoopPauseIdleEvents::~wxCFEventLoopPauseIdleEvents() 
 395     wxCFEventLoop
* cfl 
= dynamic_cast<wxCFEventLoop
*>(wxEventLoopBase::GetActive()); 
 397         cfl
->SetProcessIdleEvents(m_formerState
); 
 400 // TODO Move to thread_osx.cpp 
 404 // ---------------------------------------------------------------------------- 
 405 // GUI Serialization copied from MSW implementation 
 406 // ---------------------------------------------------------------------------- 
 408 // if it's false, some secondary thread is holding the GUI lock 
 409 static bool gs_bGuiOwnedByMainThread 
= true; 
 411 // critical section which controls access to all GUI functions: any secondary 
 412 // thread (i.e. except the main one) must enter this crit section before doing 
 414 static wxCriticalSection 
*gs_critsectGui 
= NULL
; 
 416 // critical section which protects gs_nWaitingForGui variable 
 417 static wxCriticalSection 
*gs_critsectWaitingForGui 
= NULL
; 
 419 // number of threads waiting for GUI in wxMutexGuiEnter() 
 420 static size_t gs_nWaitingForGui 
= 0; 
 422 void wxOSXThreadModuleOnInit() 
 424     gs_critsectWaitingForGui 
= new wxCriticalSection(); 
 425     gs_critsectGui 
= new wxCriticalSection(); 
 426     gs_critsectGui
->Enter(); 
 430 void wxOSXThreadModuleOnExit() 
 432     if ( gs_critsectGui 
) 
 434         if ( !wxGuiOwnedByMainThread() ) 
 436             gs_critsectGui
->Enter(); 
 437             gs_bGuiOwnedByMainThread 
= true; 
 440         gs_critsectGui
->Leave(); 
 441         wxDELETE(gs_critsectGui
); 
 444     wxDELETE(gs_critsectWaitingForGui
); 
 448 // wake up the main thread 
 449 void WXDLLIMPEXP_BASE 
wxWakeUpMainThread() 
 454 void wxMutexGuiEnterImpl() 
 456     // this would dead lock everything... 
 457     wxASSERT_MSG( !wxThread::IsMain(), 
 458                  wxT("main thread doesn't want to block in wxMutexGuiEnter()!") ); 
 460     // the order in which we enter the critical sections here is crucial!! 
 462     // set the flag telling to the main thread that we want to do some GUI 
 464         wxCriticalSectionLocker 
enter(*gs_critsectWaitingForGui
); 
 469     wxWakeUpMainThread(); 
 471     // now we may block here because the main thread will soon let us in 
 472     // (during the next iteration of OnIdle()) 
 473     gs_critsectGui
->Enter(); 
 476 void wxMutexGuiLeaveImpl() 
 478     wxCriticalSectionLocker 
enter(*gs_critsectWaitingForGui
); 
 480     if ( wxThread::IsMain() ) 
 482         gs_bGuiOwnedByMainThread 
= false; 
 486         // decrement the number of threads waiting for GUI access now 
 487         wxASSERT_MSG( gs_nWaitingForGui 
> 0, 
 488                      wxT("calling wxMutexGuiLeave() without entering it first?") ); 
 492         wxWakeUpMainThread(); 
 495     gs_critsectGui
->Leave(); 
 498 void WXDLLIMPEXP_BASE 
wxMutexGuiLeaveOrEnter() 
 500     wxASSERT_MSG( wxThread::IsMain(), 
 501                  wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") ); 
 503     if ( !gs_critsectWaitingForGui 
) 
 506     wxCriticalSectionLocker 
enter(*gs_critsectWaitingForGui
); 
 508     if ( gs_nWaitingForGui 
== 0 ) 
 510         // no threads are waiting for GUI - so we may acquire the lock without 
 511         // any danger (but only if we don't already have it) 
 512         if ( !wxGuiOwnedByMainThread() ) 
 514             gs_critsectGui
->Enter(); 
 516             gs_bGuiOwnedByMainThread 
= true; 
 518         //else: already have it, nothing to do 
 522         // some threads are waiting, release the GUI lock if we have it 
 523         if ( wxGuiOwnedByMainThread() ) 
 525         //else: some other worker thread is doing GUI 
 529 bool WXDLLIMPEXP_BASE 
wxGuiOwnedByMainThread() 
 531     return gs_bGuiOwnedByMainThread
;