Only drain all pending events when exiting outermost wxEventLoop
This is especially important under MSW, where the modality of the nested event loops actually ends as soon as wxModalEventLoop::Exit() is called, and so we must avoid dispatching any events in the current loop after it happens or we risk reentering the same loop again, which could result in e.g. parent modal dialog being closed before the child event loop returns (because the event closing the former was dispatched from the latter) and other unexpected sequences of events. To prevent this from happening, only dispatch pending events after the loop exit if it's the outermost loop, as there should be no danger in doing it in this case. Conversely, we don't lose anything by not doing this in nested event loops as the outer loop will take care of any remaining pending events anyhow. To make this work in an ABI-compatible way, add a global counter of the currently existing event loops which is used to check if there is more than one event loop currently running. Closes #11273, #11573, #11269.
This commit is contained in:
@@ -63,11 +63,8 @@
|
|||||||
class WXDLLIMPEXP_BASE wxEventLoopBase
|
class WXDLLIMPEXP_BASE wxEventLoopBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// trivial, but needed (because of wxEventLoopBase) ctor
|
|
||||||
wxEventLoopBase();
|
wxEventLoopBase();
|
||||||
|
virtual ~wxEventLoopBase();
|
||||||
// dtor
|
|
||||||
virtual ~wxEventLoopBase() { }
|
|
||||||
|
|
||||||
// use this to check whether the event loop was successfully created before
|
// use this to check whether the event loop was successfully created before
|
||||||
// using it
|
// using it
|
||||||
|
@@ -24,6 +24,13 @@
|
|||||||
#include "wx/scopeguard.h"
|
#include "wx/scopeguard.h"
|
||||||
#include "wx/apptrait.h"
|
#include "wx/apptrait.h"
|
||||||
#include "wx/private/eventloopsourcesmanager.h"
|
#include "wx/private/eventloopsourcesmanager.h"
|
||||||
|
|
||||||
|
// Counts currently existing event loops.
|
||||||
|
//
|
||||||
|
// As wxEventLoop can be only used from the main thread, there is no need to
|
||||||
|
// protect accesses to this variable.
|
||||||
|
static int gs_eventLoopCount = 0;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// wxEventLoopBase
|
// wxEventLoopBase
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -32,12 +39,19 @@ wxEventLoopBase *wxEventLoopBase::ms_activeLoop = NULL;
|
|||||||
|
|
||||||
wxEventLoopBase::wxEventLoopBase()
|
wxEventLoopBase::wxEventLoopBase()
|
||||||
{
|
{
|
||||||
|
gs_eventLoopCount++;
|
||||||
|
|
||||||
m_isInsideRun = false;
|
m_isInsideRun = false;
|
||||||
m_shouldExit = false;
|
m_shouldExit = false;
|
||||||
m_yieldLevel = 0;
|
m_yieldLevel = 0;
|
||||||
m_eventsToProcessInsideYield = wxEVT_CATEGORY_ALL;
|
m_eventsToProcessInsideYield = wxEVT_CATEGORY_ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxEventLoopBase::~wxEventLoopBase()
|
||||||
|
{
|
||||||
|
gs_eventLoopCount--;
|
||||||
|
}
|
||||||
|
|
||||||
bool wxEventLoopBase::IsMain() const
|
bool wxEventLoopBase::IsMain() const
|
||||||
{
|
{
|
||||||
if (wxTheApp)
|
if (wxTheApp)
|
||||||
@@ -254,32 +268,42 @@ int wxEventLoopManual::DoRun()
|
|||||||
OnNextIteration();
|
OnNextIteration();
|
||||||
|
|
||||||
// generate and process idle events for as long as we don't
|
// generate and process idle events for as long as we don't
|
||||||
// have anything else to do
|
// have anything else to do, but stop doing this if Exit() is
|
||||||
|
// called by one of the idle handlers
|
||||||
while ( !m_shouldExit && !Pending() && ProcessIdle() )
|
while ( !m_shouldExit && !Pending() && ProcessIdle() )
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// if Exit() was called, don't dispatch any more events here
|
||||||
if ( m_shouldExit )
|
if ( m_shouldExit )
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// a message came or no more idle processing to do, dispatch
|
// a message came or no more idle processing to do, dispatch
|
||||||
// all the pending events and call Dispatch() to wait for the
|
// all the pending events and call Dispatch() to wait for the
|
||||||
// next message
|
// next message
|
||||||
if ( !ProcessEvents() )
|
if ( !ProcessEvents() || m_shouldExit )
|
||||||
{
|
|
||||||
// we got WM_QUIT
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Process the remaining queued messages, both at the level of the
|
// If we exit the outermost loop, process the remaining queued
|
||||||
// underlying toolkit level (Pending/Dispatch()) and wx level
|
// messages, both at the level of the underlying toolkit level
|
||||||
// (Has/ProcessPendingEvents()).
|
// (Pending/Dispatch()) and wx level (Has/ProcessPendingEvents()).
|
||||||
//
|
//
|
||||||
// We do run the risk of never exiting this loop if pending event
|
// Note that we must not do this for nested modal event loops as,
|
||||||
|
// at least in wxMSW, the modality has already been undone when
|
||||||
|
// Exit() was called, and so we could end up with reentrancies such
|
||||||
|
// as starting another modal event loop before exiting this one.
|
||||||
|
// To avoid this we must not dispatch any events after calling
|
||||||
|
// Exit() and, for the nested loops, there is no real reason to do
|
||||||
|
// it anyhow, as the outer loop will still dispatch them.
|
||||||
|
//
|
||||||
|
// Finally, even when doing this in the outermost loop, there is
|
||||||
|
// still the risk of never exiting this loop if pending event
|
||||||
// handlers endlessly generate new events but they shouldn't do
|
// handlers endlessly generate new events but they shouldn't do
|
||||||
// this in a well-behaved program and we shouldn't just discard the
|
// this in a well-behaved program and we shouldn't just discard the
|
||||||
// events we already have, they might be important.
|
// events we already have, they might be important.
|
||||||
|
if ( gs_eventLoopCount != 1 )
|
||||||
|
break;
|
||||||
|
|
||||||
for ( ;; )
|
for ( ;; )
|
||||||
{
|
{
|
||||||
bool hasMoreEvents = false;
|
bool hasMoreEvents = false;
|
||||||
|
Reference in New Issue
Block a user