move pending event processing back to wxApp (these methods were moved into wxEventLoopBase during YieldFor() refactoring - see #10320): we need to be able to queue events even when there's no event loop running (e.g. wxApp::OnInit)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59284 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Francesco Montorsi
2009-03-02 20:45:22 +00:00
parent 805c8d92b9
commit 8e40ed8535
8 changed files with 250 additions and 267 deletions

View File

@@ -277,14 +277,46 @@ public:
#endif // wxUSE_EXCEPTIONS
// pending events
// --------------
// IMPORTANT: all these methods conceptually belong to wxEventLoopBase
// but for many reasons we need to allow queuing of events
// even when there's no event loop (e.g. in wxApp::OnInit);
// this feature is used e.g. to queue events on secondary threads
// or in wxPython to use wx.CallAfter before the GUI is initialized
// process all events in the m_handlersWithPendingEvents list -- it is necessary
// to call this function to process posted events. This happens during each
// event loop iteration in GUI mode but if there is no main loop, it may be
// also called directly.
virtual void ProcessPendingEvents();
// check if there are pending events on global pending event list
bool HasPendingEvents() const;
// temporary suspends processing of the pending events
void SuspendProcessingOfPendingEvents();
// resume processing of the pending events previously stopped because of a
// call to SuspendProcessingOfPendingEvents()
void ResumeProcessingOfPendingEvents();
// called by ~wxEvtHandler to (eventually) remove the handler from the list of
// the handlers with pending events
void RemovePendingEventHandler(wxEvtHandler* toRemove);
// adds an event handler to the list of the handlers with pending events
void AppendPendingEventHandler(wxEvtHandler* toAppend);
// moves the event handler from the list of the handlers with pending events
//to the list of the handlers with _delayed_ pending events
void DelayPendingEventHandler(wxEvtHandler* toDelay);
// wxEventLoop redirections
// ------------------------
virtual void SuspendProcessingOfPendingEvents();
virtual void ResumeProcessingOfPendingEvents();
virtual void ProcessPendingEvents();
bool HasPendingEvents() const;
virtual bool Pending();
virtual bool Dispatch();
@@ -388,6 +420,18 @@ protected:
// been started yet or has already terminated)
wxEventLoopBase *m_mainLoop;
// the array of the handlers with pending events which needs to be processed
// inside ProcessPendingEvents()
wxEvtHandlerArray m_handlersWithPendingEvents;
// helper array used by ProcessPendingEvents()
wxEvtHandlerArray m_handlersWithPendingDelayedEvents;
#if wxUSE_THREADS
// this critical section protects both the lists above
wxCriticalSection m_handlersWithPendingEventsLocker;
#endif
friend class WXDLLIMPEXP_FWD_BASE wxEvtHandler;
// the application object is a singleton anyhow, there is no sense in

View File

@@ -96,37 +96,6 @@ public:
virtual void WakeUp() = 0;
// pending events
// --------------
// process all events in the wxHandlersWithPendingEvents list -- it is necessary
// to call this function to process posted events. This happens during each
// event loop iteration in GUI mode but if there is no main loop, it may be
// also called directly.
virtual void ProcessPendingEvents();
// check if there are pending events on global pending event list
bool HasPendingEvents() const;
// temporary suspends processing of the pending events
void SuspendProcessingOfPendingEvents();
// resume processing of the pending events previously stopped because of a
// call to SuspendProcessingOfPendingEvents()
void ResumeProcessingOfPendingEvents();
// called by ~wxEvtHandler to (eventually) remove the handler from the list of
// the handlers with pending events
void RemovePendingEventHandler(wxEvtHandler* toRemove);
// adds an event handler to the list of the handlers with pending events
void AppendPendingEventHandler(wxEvtHandler* toAppend);
// moves the event handler from the list of the handlers with pending events
//to the list of the handlers with _delayed_ pending events
void DelayPendingEventHandler(wxEvtHandler* toDelay);
// idle handling
// -------------
@@ -188,19 +157,7 @@ protected:
// the pointer to currently active loop
static wxEventLoopBase *ms_activeLoop;
// the array of the handlers with pending events which needs to be processed
// inside ProcessPendingEvents()
wxEvtHandlerArray m_handlersWithPendingEvents;
// helper array used by ProcessPendingEvents()
wxEvtHandlerArray m_handlersWithPendingDelayedEvents;
#if wxUSE_THREADS
// this critical section protects both the lists above
wxCriticalSection m_handlersWithPendingEventsLocker;
#endif
// Yield() helpers:
// YieldFor() helpers:
bool m_isInsideYield;
long m_eventsToProcessInsideYield;

View File

@@ -114,6 +114,45 @@ public:
//@}
/**
@name Pending events
Pending events are handled by wxAppConsole rather than wxEventLoopBase
to allow queuing of events even when there's no event loop
(e.g. in wxAppConsole::OnInit).
*/
//@{
/**
Process all pending events; it is necessary to call this function to
process posted events.
This happens during each event loop iteration in GUI mode but
it may be also called directly.
*/
virtual void ProcessPendingEvents();
/**
Returns @true if there are pending events on the internal pending event list.
*/
bool HasPendingEvents() const;
/**
Temporary suspends processing of the pending events.
@see ResumeProcessingOfPendingEvents()
*/
void SuspendProcessingOfPendingEvents();
/**
Resume processing of the pending events previously stopped because of a
call to SuspendProcessingOfPendingEvents().
*/
void ResumeProcessingOfPendingEvents();
//@}
/**
Allows external code to modify global ::wxTheApp, but you should really
know what you're doing if you call it.

View File

@@ -333,7 +333,7 @@ public:
@library{wxbase}
@category{events}
@see @ref overview_events_processing
@see @ref overview_events_processing, wxEventBlocker, wxEventLoopBase
*/
class wxEvtHandler : public wxObject
{

View File

@@ -146,41 +146,6 @@ public:
//@}
/**
@name Pending events
*/
//@{
/**
Process all pending events; it is necessary to call this function to
process posted events.
This happens during each event loop iteration in GUI mode but
it may be also called directly.
*/
virtual void ProcessPendingEvents();
/**
Returns @true if there are pending events on the internal pending event list.
*/
bool HasPendingEvents() const;
/**
Temporary suspends processing of the pending events.
@see ResumeProcessingOfPendingEvents()
*/
void SuspendProcessingOfPendingEvents();
/**
Resume processing of the pending events previously stopped because of a
call to SuspendProcessingOfPendingEvents().
*/
void ResumeProcessingOfPendingEvents();
//@}
/**
@name Idle handling
*/

View File

@@ -313,34 +313,6 @@ bool wxAppConsoleBase::Dispatch()
return loop && loop->Dispatch();
}
bool wxAppConsoleBase::HasPendingEvents() const
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
return loop && loop->HasPendingEvents();
}
void wxAppConsoleBase::SuspendProcessingOfPendingEvents()
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
if (loop) loop->SuspendProcessingOfPendingEvents();
}
void wxAppConsoleBase::ResumeProcessingOfPendingEvents()
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
if (loop) loop->ResumeProcessingOfPendingEvents();
}
void wxAppConsoleBase::ProcessPendingEvents()
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
if (loop) loop->ProcessPendingEvents();
}
bool wxAppConsoleBase::Yield(bool onlyIfNeeded)
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
@@ -379,6 +351,117 @@ int wxAppConsoleBase::FilterEvent(wxEvent& WXUNUSED(event))
return -1;
}
void wxAppConsoleBase::DelayPendingEventHandler(wxEvtHandler* toDelay)
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
// move the handler from the list of handlers with processable pending events
// to the list of handlers with pending events which needs to be processed later
m_handlersWithPendingEvents.Remove(toDelay);
if (m_handlersWithPendingDelayedEvents.Index(toDelay) == wxNOT_FOUND)
m_handlersWithPendingDelayedEvents.Add(toDelay);
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxAppConsoleBase::RemovePendingEventHandler(wxEvtHandler* toRemove)
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
if (m_handlersWithPendingEvents.Index(toRemove) != wxNOT_FOUND)
{
m_handlersWithPendingEvents.Remove(toRemove);
// check that the handler was present only once in the list
wxASSERT_MSG( m_handlersWithPendingEvents.Index(toRemove) == wxNOT_FOUND,
"Handler occurs twice in the m_handlersWithPendingEvents list!" );
}
//else: it wasn't in this list at all, it's ok
if (m_handlersWithPendingDelayedEvents.Index(toRemove) != wxNOT_FOUND)
{
m_handlersWithPendingDelayedEvents.Remove(toRemove);
// check that the handler was present only once in the list
wxASSERT_MSG( m_handlersWithPendingDelayedEvents.Index(toRemove) == wxNOT_FOUND,
"Handler occurs twice in m_handlersWithPendingDelayedEvents list!" );
}
//else: it wasn't in this list at all, it's ok
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxAppConsoleBase::AppendPendingEventHandler(wxEvtHandler* toAppend)
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
if ( m_handlersWithPendingEvents.Index(toAppend) == wxNOT_FOUND )
m_handlersWithPendingEvents.Add(toAppend);
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
bool wxAppConsoleBase::HasPendingEvents() const
{
wxENTER_CRIT_SECT(const_cast<wxAppConsoleBase*>(this)->m_handlersWithPendingEventsLocker);
bool has = !m_handlersWithPendingEvents.IsEmpty();
wxLEAVE_CRIT_SECT(const_cast<wxAppConsoleBase*>(this)->m_handlersWithPendingEventsLocker);
return has;
}
void wxAppConsoleBase::SuspendProcessingOfPendingEvents()
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
// entering the critical section locks blocks calls to ProcessPendingEvents()
}
void wxAppConsoleBase::ResumeProcessingOfPendingEvents()
{
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxAppConsoleBase::ProcessPendingEvents()
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
wxCHECK_RET( m_handlersWithPendingDelayedEvents.IsEmpty(),
"this helper list should be empty" );
// iterate until the list becomes empty: the handlers remove themselves
// from it when they don't have any more pending events
while (!m_handlersWithPendingEvents.IsEmpty())
{
// In ProcessPendingEvents(), new handlers might be added
// and we can safely leave the critical section here.
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
// NOTE: we always call ProcessPendingEvents() on the first event handler
// with pending events because handlers auto-remove themselves
// from this list (see RemovePendingEventHandler) if they have no
// more pending events.
m_handlersWithPendingEvents[0]->ProcessPendingEvents();
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
// now the wxHandlersWithPendingEvents is surely empty; however some event
// handlers may have moved themselves into wxHandlersWithPendingDelayedEvents
// because of a selective wxYield call in progress.
// Now we need to move them back to wxHandlersWithPendingEvents so the next
// call to this function has the chance of processing them:
if (!m_handlersWithPendingDelayedEvents.IsEmpty())
{
WX_APPEND_ARRAY(m_handlersWithPendingEvents, m_handlersWithPendingDelayedEvents);
m_handlersWithPendingDelayedEvents.Clear();
}
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
// ----------------------------------------------------------------------------
// exception handling
// ----------------------------------------------------------------------------

View File

@@ -402,10 +402,9 @@ wxEvent& wxEvent::operator=(const wxEvent& src)
#if wxUSE_GUI
/*
* Command events
*
*/
// ----------------------------------------------------------------------------
// wxCommandEvent
// ----------------------------------------------------------------------------
#ifdef __VISUALC__
// 'this' : used in base member initializer list (for m_commandString)
@@ -447,9 +446,9 @@ wxString wxCommandEvent::GetString() const
}
}
/*
* UI update events
*/
// ----------------------------------------------------------------------------
// wxUpdateUIEvent
// ----------------------------------------------------------------------------
#if wxUSE_LONGLONG
wxLongLong wxUpdateUIEvent::sm_lastUpdate = 0;
@@ -506,9 +505,9 @@ void wxUpdateUIEvent::ResetUpdateTime()
#endif
}
/*
* Scroll events
*/
// ----------------------------------------------------------------------------
// wxScrollEvent
// ----------------------------------------------------------------------------
wxScrollEvent::wxScrollEvent(wxEventType commandType,
int id,
@@ -520,9 +519,9 @@ wxScrollEvent::wxScrollEvent(wxEventType commandType,
m_commandInt = pos;
}
/*
* ScrollWin events
*/
// ----------------------------------------------------------------------------
// wxScrollWinEvent
// ----------------------------------------------------------------------------
wxScrollWinEvent::wxScrollWinEvent(wxEventType commandType,
int pos,
@@ -533,10 +532,9 @@ wxScrollWinEvent::wxScrollWinEvent(wxEventType commandType,
m_commandInt = pos;
}
/*
* Mouse events
*
*/
// ----------------------------------------------------------------------------
// wxMouseEvent
// ----------------------------------------------------------------------------
wxMouseEvent::wxMouseEvent(wxEventType commandType)
{
@@ -751,11 +749,9 @@ wxPoint wxMouseEvent::GetLogicalPosition(const wxDC& dc) const
return pt;
}
/*
* Keyboard event
*
*/
// ----------------------------------------------------------------------------
// wxKeyEvent
// ----------------------------------------------------------------------------
wxKeyEvent::wxKeyEvent(wxEventType type)
{
@@ -782,18 +778,30 @@ wxKeyEvent::wxKeyEvent(const wxKeyEvent& evt)
#endif
}
// ----------------------------------------------------------------------------
// wxWindowCreateEvent
// ----------------------------------------------------------------------------
wxWindowCreateEvent::wxWindowCreateEvent(wxWindow *win)
{
SetEventType(wxEVT_CREATE);
SetEventObject(win);
}
// ----------------------------------------------------------------------------
// wxWindowDestroyEvent
// ----------------------------------------------------------------------------
wxWindowDestroyEvent::wxWindowDestroyEvent(wxWindow *win)
{
SetEventType(wxEVT_DESTROY);
SetEventObject(win);
}
// ----------------------------------------------------------------------------
// wxChildFocusEvent
// ----------------------------------------------------------------------------
wxChildFocusEvent::wxChildFocusEvent(wxWindow *win)
: wxCommandEvent(wxEVT_CHILD_FOCUS)
{
@@ -1076,9 +1084,8 @@ wxEvtHandler::~wxEvtHandler()
delete m_pendingEvents;
// Remove us from the list of the pending events if necessary.
wxEventLoopBase *loop = wxEventLoopBase::GetActive();
if (loop)
loop->RemovePendingEventHandler(this);
if (wxTheApp)
wxTheApp->RemovePendingEventHandler(this);
// we only delete object data, not untyped
if ( m_clientDataType == wxClientData_Object )
@@ -1124,12 +1131,11 @@ void wxEvtHandler::QueueEvent(wxEvent *event)
{
wxCHECK_RET( event, "NULL event can't be posted" );
wxEventLoopBase* loop = wxEventLoopBase::GetActive();
if (!loop)
if (!wxTheApp)
{
// we need an event loop which manages the list of event handlers with
// pending events... cannot proceed without it!
wxLogDebug("No event loop is running! Cannot queue this event!");
wxLogDebug("No application object! Cannot queue this event!");
// anyway delete the given event to avoid memory leaks
delete event;
@@ -1148,7 +1154,7 @@ void wxEvtHandler::QueueEvent(wxEvent *event)
// 2) Add this event handler to list of event handlers that
// have pending events.
loop->AppendPendingEventHandler(this);
wxTheApp->AppendPendingEventHandler(this);
// only release m_pendingEventsLock now because otherwise there is a race
// condition as described in the ticket #9093: we could process the event
@@ -1165,12 +1171,11 @@ void wxEvtHandler::QueueEvent(wxEvent *event)
void wxEvtHandler::ProcessPendingEvents()
{
wxEventLoopBase* loop = wxEventLoopBase::GetActive();
if (!loop)
if (!wxTheApp)
{
// we need an event loop which manages the list of event handlers with
// pending events... cannot proceed without it!
wxLogDebug("No event loop is running! Cannot process pending events!");
wxLogDebug("No application object! Cannot process pending events!");
return;
}
@@ -1201,7 +1206,7 @@ void wxEvtHandler::ProcessPendingEvents()
if (!node)
{
// all our events are NOT processable now... signal this:
loop->DelayPendingEventHandler(this);
wxTheApp->DelayPendingEventHandler(this);
// see the comment at the beginning of evtloop.h header for the
// logic behind YieldFor() and behind DelayPendingEventHandler()
@@ -1223,7 +1228,7 @@ void wxEvtHandler::ProcessPendingEvents()
{
// if there are no more pending events left, we don't need to
// stay in this list
loop->RemovePendingEventHandler(this);
wxTheApp->RemovePendingEventHandler(this);
}
wxLEAVE_CRIT_SECT( m_pendingEventsLock );
@@ -1235,11 +1240,8 @@ void wxEvtHandler::ProcessPendingEvents()
// of this object any more
}
/*
* Event table stuff
*/
/* static */ bool
wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry,
/* static */
bool wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry,
wxEvtHandler *handler,
wxEvent& event)
{
@@ -1412,7 +1414,6 @@ bool wxEvtHandler::SafelyProcessEvent(wxEvent& event)
#endif // wxUSE_EXCEPTIONS
}
bool wxEvtHandler::SearchEventTable(wxEventTable& table, wxEvent& event)
{
const wxEventType eventType = event.GetEventType();

View File

@@ -56,116 +56,6 @@ void wxEventLoopBase::OnExit()
wxTheApp->OnEventLoopExit(this);
}
void wxEventLoopBase::DelayPendingEventHandler(wxEvtHandler* toDelay)
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
// move the handler from the list of handlers with processable pending events
// to the list of handlers with pending events which needs to be processed later
m_handlersWithPendingEvents.Remove(toDelay);
if (m_handlersWithPendingDelayedEvents.Index(toDelay) == wxNOT_FOUND)
m_handlersWithPendingDelayedEvents.Add(toDelay);
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxEventLoopBase::RemovePendingEventHandler(wxEvtHandler* toRemove)
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
if (m_handlersWithPendingEvents.Index(toRemove) != wxNOT_FOUND)
{
m_handlersWithPendingEvents.Remove(toRemove);
// check that the handler was present only once in the list
wxASSERT_MSG( m_handlersWithPendingEvents.Index(toRemove) == wxNOT_FOUND,
"Handler occurs twice in the m_handlersWithPendingEvents list!" );
}
//else: it wasn't in this list at all, it's ok
if (m_handlersWithPendingDelayedEvents.Index(toRemove) != wxNOT_FOUND)
{
m_handlersWithPendingDelayedEvents.Remove(toRemove);
// check that the handler was present only once in the list
wxASSERT_MSG( m_handlersWithPendingDelayedEvents.Index(toRemove) == wxNOT_FOUND,
"Handler occurs twice in m_handlersWithPendingDelayedEvents list!" );
}
//else: it wasn't in this list at all, it's ok
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxEventLoopBase::AppendPendingEventHandler(wxEvtHandler* toAppend)
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
if ( m_handlersWithPendingEvents.Index(toAppend) == wxNOT_FOUND )
m_handlersWithPendingEvents.Add(toAppend);
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
bool wxEventLoopBase::HasPendingEvents() const
{
wxENTER_CRIT_SECT(const_cast<wxEventLoopBase*>(this)->m_handlersWithPendingEventsLocker);
bool has = !m_handlersWithPendingEvents.IsEmpty();
wxLEAVE_CRIT_SECT(const_cast<wxEventLoopBase*>(this)->m_handlersWithPendingEventsLocker);
return has;
}
void wxEventLoopBase::SuspendProcessingOfPendingEvents()
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxEventLoopBase::ResumeProcessingOfPendingEvents()
{
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxEventLoopBase::ProcessPendingEvents()
{
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
wxCHECK_RET( m_handlersWithPendingDelayedEvents.IsEmpty(),
"this helper list should be empty" );
// iterate until the list becomes empty: the handlers remove themselves
// from it when they don't have any more pending events
while (!m_handlersWithPendingEvents.IsEmpty())
{
// In ProcessPendingEvents(), new handlers might be added
// and we can safely leave the critical section here.
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
// NOTE: we always call ProcessPendingEvents() on the first event handler
// with pending events because handlers auto-remove themselves
// from this list (see RemovePendingEventHandler) if they have no
// more pending events.
m_handlersWithPendingEvents[0]->ProcessPendingEvents();
wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
// now the wxHandlersWithPendingEvents is surely empty; however some event
// handlers may have moved themselves into wxHandlersWithPendingDelayedEvents
// because of a selective wxYield call in progress.
// Now we need to move them back to wxHandlersWithPendingEvents so the next
// call to this function has the chance of processing them:
if (!m_handlersWithPendingDelayedEvents.IsEmpty())
{
WX_APPEND_ARRAY(m_handlersWithPendingEvents, m_handlersWithPendingDelayedEvents);
m_handlersWithPendingDelayedEvents.Clear();
}
wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker);
}
void wxEventLoopBase::WakeUpIdle()
{
WakeUp();
@@ -173,13 +63,17 @@ void wxEventLoopBase::WakeUpIdle()
bool wxEventLoopBase::ProcessIdle()
{
if (!wxTheApp)
return false;
// process pending wx events before sending idle events
ProcessPendingEvents();
wxTheApp->ProcessPendingEvents();
// synthetize an idle event and send it to wxApp
wxIdleEvent event;
event.SetEventObject(wxTheApp);
wxTheApp->ProcessEvent(event);
return event.MoreRequested();
}