Files
wxWidgets/src/osx/core/evtloop_cf.cpp
Vadim Zeitlin 71e9885be0 Make wxEventLoop::AddSourceForFD() static.
Any event sources should be registered with all the event loops, including the
ones that will be started in the future, and not only the current (and
potentially not even existing yet) one. So make AddSourceForFD() method static.

To still allow it to do different things in console and GUI applications, as
it must, virtualize it via the new wxEventLoopSourcesManager class which has
different implementations in the two cases, returned via wxAppTraits as usual.

Notice that this required moving the implementation of this method from
src/osx/core/evtloop_cf.cpp to src/osx/core/utilsexc_cf.cpp as the former file
is base-only and didn't have access to wxGUIAppTraits.

See #10258.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74341 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-07-03 00:28:42 +00:00

530 lines
14 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/osx/core/evtloop_cf.cpp
// Purpose: wxEventLoop implementation common to both Carbon and Cocoa
// Author: Vadim Zeitlin
// Created: 2009-10-18
// RCS-ID: $Id$
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/evtloop.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/app.h"
#endif
#include "wx/evtloopsrc.h"
#include "wx/scopedptr.h"
#include "wx/osx/private.h"
#include "wx/osx/core/cfref.h"
#include "wx/thread.h"
#if wxUSE_GUI
#include "wx/nonownedwnd.h"
#endif
// ============================================================================
// wxCFEventLoopSource and wxCFEventLoop implementation
// ============================================================================
#if wxUSE_EVENTLOOP_SOURCE
void wxCFEventLoopSource::SetFileDescriptor(CFFileDescriptorRef cffd)
{
wxASSERT_MSG( !m_cffd, "shouldn't be called more than once" );
m_cffd = cffd;
}
wxCFEventLoopSource::~wxCFEventLoopSource()
{
if ( m_cffd )
CFRelease(m_cffd);
}
#endif // wxUSE_EVENTLOOP_SOURCE
void wxCFEventLoop::OSXCommonModeObserverCallBack(CFRunLoopObserverRef observer, int activity, void *info)
{
wxCFEventLoop * eventloop = static_cast<wxCFEventLoop *>(info);
if ( eventloop )
eventloop->CommonModeObserverCallBack(observer, activity);
}
void wxCFEventLoop::OSXDefaultModeObserverCallBack(CFRunLoopObserverRef observer, int activity, void *info)
{
wxCFEventLoop * eventloop = static_cast<wxCFEventLoop *>(info);
if ( eventloop )
eventloop->DefaultModeObserverCallBack(observer, activity);
}
void wxCFEventLoop::CommonModeObserverCallBack(CFRunLoopObserverRef WXUNUSED(observer), int activity)
{
if ( activity & kCFRunLoopBeforeTimers )
{
// process pending wx events first as they correspond to low-level events
// which happened before, i.e. typically pending events were queued by a
// previous call to Dispatch() and if we didn't process them now the next
// call to it might enqueue them again (as happens with e.g. socket events
// which would be generated as long as there is input available on socket
// and this input is only removed from it when pending event handlers are
// executed)
if ( wxTheApp && ShouldProcessIdleEvents() )
wxTheApp->ProcessPendingEvents();
}
if ( activity & kCFRunLoopBeforeWaiting )
{
if ( ShouldProcessIdleEvents() && ProcessIdle() )
{
WakeUp();
}
else
{
#if wxUSE_THREADS
wxMutexGuiLeaveOrEnter();
#endif
}
}
}
void
wxCFEventLoop::DefaultModeObserverCallBack(CFRunLoopObserverRef WXUNUSED(observer),
int WXUNUSED(activity))
{
/*
if ( activity & kCFRunLoopBeforeTimers )
{
}
if ( activity & kCFRunLoopBeforeWaiting )
{
}
*/
}
wxCFEventLoop::wxCFEventLoop()
{
m_shouldExit = false;
m_processIdleEvents = true;
#if wxUSE_UIACTIONSIMULATOR
m_shouldWaitForEvent = false;
#endif
m_runLoop = CFGetCurrentRunLoop();
CFRunLoopObserverContext ctxt;
bzero( &ctxt, sizeof(ctxt) );
ctxt.info = this;
m_commonModeRunLoopObserver = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopBeforeTimers | kCFRunLoopBeforeWaiting , true /* repeats */, 0,
(CFRunLoopObserverCallBack) wxCFEventLoop::OSXCommonModeObserverCallBack, &ctxt );
CFRunLoopAddObserver(m_runLoop, m_commonModeRunLoopObserver, kCFRunLoopCommonModes);
m_defaultModeRunLoopObserver = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopBeforeTimers | kCFRunLoopBeforeWaiting , true /* repeats */, 0,
(CFRunLoopObserverCallBack) wxCFEventLoop::OSXDefaultModeObserverCallBack, &ctxt );
CFRunLoopAddObserver(m_runLoop, m_defaultModeRunLoopObserver, kCFRunLoopDefaultMode);
}
wxCFEventLoop::~wxCFEventLoop()
{
CFRunLoopRemoveObserver(m_runLoop, m_commonModeRunLoopObserver, kCFRunLoopCommonModes);
CFRunLoopRemoveObserver(m_runLoop, m_defaultModeRunLoopObserver, kCFRunLoopDefaultMode);
CFRelease(m_defaultModeRunLoopObserver);
CFRelease(m_commonModeRunLoopObserver);
}
CFRunLoopRef wxCFEventLoop::CFGetCurrentRunLoop() const
{
return CFRunLoopGetCurrent();
}
void wxCFEventLoop::WakeUp()
{
CFRunLoopWakeUp(m_runLoop);
}
#if wxUSE_BASE
void wxMacWakeUp()
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
if ( loop )
loop->WakeUp();
}
#endif
bool wxCFEventLoop::YieldFor(long eventsToProcess)
{
#if wxUSE_THREADS
// Yielding from a non-gui thread needs to bail out, otherwise we end up
// possibly sending events in the thread too.
if ( !wxThread::IsMain() )
{
return true;
}
#endif // wxUSE_THREADS
m_isInsideYield = true;
m_eventsToProcessInsideYield = eventsToProcess;
#if wxUSE_LOG
// disable log flushing from here because a call to wxYield() shouldn't
// normally result in message boxes popping up &c
wxLog::Suspend();
#endif // wxUSE_LOG
// process all pending events:
while ( DoProcessEvents() == 1 )
;
// it's necessary to call ProcessIdle() to update the frames sizes which
// might have been changed (it also will update other things set from
// OnUpdateUI() which is a nice (and desired) side effect)
while ( ProcessIdle() ) {}
// if there are pending events, we must process them.
if (wxTheApp)
wxTheApp->ProcessPendingEvents();
#if wxUSE_LOG
wxLog::Resume();
#endif // wxUSE_LOG
m_isInsideYield = false;
return true;
}
// implement/override base class pure virtual
bool wxCFEventLoop::Pending() const
{
return true;
}
int wxCFEventLoop::DoProcessEvents()
{
#if wxUSE_UIACTIONSIMULATOR
if ( m_shouldWaitForEvent )
{
int handled = DispatchTimeout( 1000 );
wxASSERT_MSG( handled == 1, "No Event Available");
m_shouldWaitForEvent = false;
return handled;
}
else
#endif
return DispatchTimeout( 0 );
}
bool wxCFEventLoop::Dispatch()
{
return DoProcessEvents() != 0;
}
int wxCFEventLoop::DispatchTimeout(unsigned long timeout)
{
if ( !wxTheApp )
return 0;
int status = DoDispatchTimeout(timeout);
switch( status )
{
case 0:
break;
case -1:
if ( m_shouldExit )
return 0;
break;
case 1:
break;
}
return status;
}
int wxCFEventLoop::DoDispatchTimeout(unsigned long timeout)
{
SInt32 status = CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout / 1000.0 , true);
switch( status )
{
case kCFRunLoopRunFinished:
wxFAIL_MSG( "incorrect run loop state" );
break;
case kCFRunLoopRunStopped:
return 0;
break;
case kCFRunLoopRunTimedOut:
return -1;
break;
case kCFRunLoopRunHandledSource:
default:
break;
}
return 1;
}
void wxCFEventLoop::OSXDoRun()
{
for ( ;; )
{
// generate and process idle events for as long as we don't
// have anything else to do
DoProcessEvents();
// if the "should exit" flag is set, the loop should terminate
// but not before processing any remaining messages so while
// Pending() returns true, do process them
if ( m_shouldExit )
{
while ( DoProcessEvents() == 1 )
;
break;
}
}
}
void wxCFEventLoop::OSXDoStop()
{
CFRunLoopStop(CFGetCurrentRunLoop());
}
// enters a loop calling OnNextIteration(), Pending() and Dispatch() and
// terminating when Exit() is called
int wxCFEventLoop::DoRun()
{
// we must ensure that OnExit() is called even if an exception is thrown
// from inside ProcessEvents() but we must call it from Exit() in normal
// situations because it is supposed to be called synchronously,
// wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or
// something similar here)
#if wxUSE_EXCEPTIONS
for ( ;; )
{
try
{
#endif // wxUSE_EXCEPTIONS
OSXDoRun();
#if wxUSE_EXCEPTIONS
// exit the outer loop as well
break;
}
catch ( ... )
{
try
{
if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() )
{
OnExit();
break;
}
//else: continue running the event loop
}
catch ( ... )
{
// OnException() throwed, possibly rethrowing the same
// exception again: very good, but we still need OnExit() to
// be called
OnExit();
throw;
}
}
}
#endif // wxUSE_EXCEPTIONS
return m_exitcode;
}
// sets the "should exit" flag and wakes up the loop so that it terminates
// soon
void wxCFEventLoop::ScheduleExit(int rc)
{
m_exitcode = rc;
m_shouldExit = true;
OSXDoStop();
}
wxCFEventLoopPauseIdleEvents::wxCFEventLoopPauseIdleEvents()
{
wxCFEventLoop* cfl = dynamic_cast<wxCFEventLoop*>(wxEventLoopBase::GetActive());
if ( cfl )
{
m_formerState = cfl->ShouldProcessIdleEvents();
cfl->SetProcessIdleEvents(false);
}
else
m_formerState = true;
}
wxCFEventLoopPauseIdleEvents::~wxCFEventLoopPauseIdleEvents()
{
wxCFEventLoop* cfl = dynamic_cast<wxCFEventLoop*>(wxEventLoopBase::GetActive());
if ( cfl )
cfl->SetProcessIdleEvents(m_formerState);
}
// TODO Move to thread_osx.cpp
#if wxUSE_THREADS
// ----------------------------------------------------------------------------
// GUI Serialization copied from MSW implementation
// ----------------------------------------------------------------------------
// if it's false, some secondary thread is holding the GUI lock
static bool gs_bGuiOwnedByMainThread = true;
// critical section which controls access to all GUI functions: any secondary
// thread (i.e. except the main one) must enter this crit section before doing
// any GUI calls
static wxCriticalSection *gs_critsectGui = NULL;
// critical section which protects gs_nWaitingForGui variable
static wxCriticalSection *gs_critsectWaitingForGui = NULL;
// number of threads waiting for GUI in wxMutexGuiEnter()
static size_t gs_nWaitingForGui = 0;
void wxOSXThreadModuleOnInit()
{
gs_critsectWaitingForGui = new wxCriticalSection();
gs_critsectGui = new wxCriticalSection();
gs_critsectGui->Enter();
}
void wxOSXThreadModuleOnExit()
{
if ( gs_critsectGui )
{
if ( !wxGuiOwnedByMainThread() )
{
gs_critsectGui->Enter();
gs_bGuiOwnedByMainThread = true;
}
gs_critsectGui->Leave();
wxDELETE(gs_critsectGui);
}
wxDELETE(gs_critsectWaitingForGui);
}
// wake up the main thread
void WXDLLIMPEXP_BASE wxWakeUpMainThread()
{
wxMacWakeUp();
}
void wxMutexGuiEnterImpl()
{
// this would dead lock everything...
wxASSERT_MSG( !wxThread::IsMain(),
wxT("main thread doesn't want to block in wxMutexGuiEnter()!") );
// the order in which we enter the critical sections here is crucial!!
// set the flag telling to the main thread that we want to do some GUI
{
wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
gs_nWaitingForGui++;
}
wxWakeUpMainThread();
// now we may block here because the main thread will soon let us in
// (during the next iteration of OnIdle())
gs_critsectGui->Enter();
}
void wxMutexGuiLeaveImpl()
{
wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
if ( wxThread::IsMain() )
{
gs_bGuiOwnedByMainThread = false;
}
else
{
// decrement the number of threads waiting for GUI access now
wxASSERT_MSG( gs_nWaitingForGui > 0,
wxT("calling wxMutexGuiLeave() without entering it first?") );
gs_nWaitingForGui--;
wxWakeUpMainThread();
}
gs_critsectGui->Leave();
}
void WXDLLIMPEXP_BASE wxMutexGuiLeaveOrEnter()
{
wxASSERT_MSG( wxThread::IsMain(),
wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
if ( !gs_critsectWaitingForGui )
return;
wxCriticalSectionLocker enter(*gs_critsectWaitingForGui);
if ( gs_nWaitingForGui == 0 )
{
// no threads are waiting for GUI - so we may acquire the lock without
// any danger (but only if we don't already have it)
if ( !wxGuiOwnedByMainThread() )
{
gs_critsectGui->Enter();
gs_bGuiOwnedByMainThread = true;
}
//else: already have it, nothing to do
}
else
{
// some threads are waiting, release the GUI lock if we have it
if ( wxGuiOwnedByMainThread() )
wxMutexGuiLeave();
//else: some other worker thread is doing GUI
}
}
bool WXDLLIMPEXP_BASE wxGuiOwnedByMainThread()
{
return gs_bGuiOwnedByMainThread;
}
#endif