diff --git a/docs/changes.txt b/docs/changes.txt index 27d14a2650..47a8b6e3ec 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -110,6 +110,7 @@ wxMSW: - Fix wxScreenDC::GetSize() with multiple monitors (iwbnwif). - Fix background colour returned by wxTextCtrl::GetStyle() (Andreas Falkenhahn). - Revert to using equally-sized buttons in wxToolBar by default. +- Restore dispatching wxThreadEvent while resizing the window broken in 3.1.0. wxOSX: diff --git a/include/wx/msw/evtloop.h b/include/wx/msw/evtloop.h index be8f597726..4ccb2a610b 100644 --- a/include/wx/msw/evtloop.h +++ b/include/wx/msw/evtloop.h @@ -53,6 +53,7 @@ public: // override/implement base class virtuals virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); + virtual void WakeUp(); protected: virtual void OnNextIteration(); diff --git a/include/wx/msw/evtloopconsole.h b/include/wx/msw/evtloopconsole.h index a4eb6d54ac..01d77805f7 100644 --- a/include/wx/msw/evtloopconsole.h +++ b/include/wx/msw/evtloopconsole.h @@ -15,20 +15,9 @@ class WXDLLIMPEXP_BASE wxMSWEventLoopBase : public wxEventLoopManual { public: wxMSWEventLoopBase(); - virtual ~wxMSWEventLoopBase(); // implement base class pure virtuals virtual bool Pending() const; - virtual void WakeUp(); - -#if wxUSE_THREADS - // MSW-specific method to wait for the termination of the specified (by its - // native handle) thread or any input message arriving (in GUI case). - // - // Return value is WAIT_OBJECT_0 if the thread terminated, WAIT_OBJECT_0+1 - // if a message arrived with anything else indicating an error. - WXDWORD MSWWaitForThread(WXHANDLE hThread); -#endif // wxUSE_THREADS protected: // get the next message from queue and return true or return false if we @@ -36,13 +25,8 @@ protected: bool GetNextMessage(WXMSG *msg); // same as above but with a timeout and return value can be -1 meaning that - // time out expired in addition to true/false + // time out expired in addition to int GetNextMessageTimeout(WXMSG *msg, unsigned long timeout); - -private: - // An auto-reset Win32 event which is signalled when we need to wake up the - // main thread waiting in GetNextMessage[Timeout](). - WXHANDLE m_heventWake; }; #if wxUSE_CONSOLE_EVENTLOOP @@ -55,6 +39,7 @@ public: // override/implement base class virtuals virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); + virtual void WakeUp(); // Windows-specific function to process a single message virtual void ProcessMessage(WXMSG *msg); diff --git a/src/msw/app.cpp b/src/msw/app.cpp index e808d98e97..26c4d634f3 100644 --- a/src/msw/app.cpp +++ b/src/msw/app.cpp @@ -225,16 +225,23 @@ WXDWORD wxGUIAppTraits::WaitForThread(WXHANDLE hThread, int flags) // have a running event loop as we would never remove them from the message // queue then and so we would enter an infinite loop as // MsgWaitForMultipleObjects() keeps returning WAIT_OBJECT_0 + 1. - if ( flags == wxTHREAD_WAIT_YIELD && wxIsMainThread() ) + if ( flags == wxTHREAD_WAIT_BLOCK || + !wxIsMainThread() || + !wxEventLoop::GetActive() ) { - wxMSWEventLoopBase* const - evtLoop = static_cast(wxEventLoop::GetActive()); - if ( evtLoop ) - return evtLoop->MSWWaitForThread(hThread); + // Simple blocking wait. + return DoSimpleWaitForThread(hThread); } - // Simple blocking wait. - return DoSimpleWaitForThread(hThread); + return ::MsgWaitForMultipleObjects + ( + 1, // number of objects to wait for + (HANDLE *)&hThread, // the objects + false, // wait for any objects, not all + INFINITE, // no timeout + QS_ALLINPUT | // return as soon as there are any events + QS_ALLPOSTMESSAGE + ); } #endif // wxUSE_THREADS @@ -747,16 +754,42 @@ void wxApp::OnIdle(wxIdleEvent& WXUNUSED(event)) void wxApp::WakeUpIdle() { - wxEventLoopBase * const evtLoop = wxEventLoop::GetActive(); - if ( !evtLoop ) + // Send the top window a dummy message so idle handler processing will + // start up again. Doing it this way ensures that the idle handler + // wakes up in the right thread (see also wxWakeUpMainThread() which does + // the same for the main app thread only) + wxWindow * const topWindow = wxTheApp->GetTopWindow(); + if ( topWindow ) { - // We can't wake up the event loop if there is none and there is just - // no need to do anything in this case, any pending events will be - // handled when the event loop starts. - return; - } + HWND hwndTop = GetHwndOf(topWindow); - evtLoop->WakeUp(); + // Do not post WM_NULL if there's already a pending WM_NULL to avoid + // overflowing the message queue. + // + // Notice that due to a limitation of PeekMessage() API (which handles + // 0,0 range specially), we have to check the range from 0-1 instead. + // This still makes it possible to overflow the queue with WM_NULLs by + // interspersing the calles to WakeUpIdle() with windows creation but + // it should be rather hard to do it accidentally. + MSG msg; + if ( !::PeekMessage(&msg, hwndTop, 0, 1, PM_NOREMOVE) || + ::PeekMessage(&msg, hwndTop, 1, 1, PM_NOREMOVE) ) + { + // If this fails too, there is really not much we can do, but then + // neither do we need to, as it normally indicates that the window + // queue is full to the brim with the messages and so the main loop + // is running and doesn't need to be woken up. + // + // Notice that we especially should not try use wxLogLastError() + // here as this would lead to another call to wxWakeUpIdle() from + // inside wxLog and stack overflow due to the resulting recursion. + ::PostMessage(hwndTop, WM_NULL, 0, 0); + } + } +#if wxUSE_THREADS + else + wxWakeUpMainThread(); +#endif // wxUSE_THREADS } // ---------------------------------------------------------------------------- diff --git a/src/msw/evtloop.cpp b/src/msw/evtloop.cpp index eb053cd33d..b0ab5be33c 100644 --- a/src/msw/evtloop.cpp +++ b/src/msw/evtloop.cpp @@ -248,6 +248,11 @@ void wxGUIEventLoop::OnNextIteration() #endif // wxUSE_THREADS } +void wxGUIEventLoop::WakeUp() +{ + ::PostMessage(NULL, WM_NULL, 0, 0); +} + // ---------------------------------------------------------------------------- // Yield to incoming messages diff --git a/src/msw/evtloopconsole.cpp b/src/msw/evtloopconsole.cpp index c0ba2716fa..a746343609 100644 --- a/src/msw/evtloopconsole.cpp +++ b/src/msw/evtloopconsole.cpp @@ -42,17 +42,6 @@ wxMSWEventLoopBase::wxMSWEventLoopBase() { m_shouldExit = false; m_exitcode = 0; - - // Create initially not signalled auto-reset event object. - m_heventWake = ::CreateEvent(NULL, FALSE, FALSE, NULL); - if ( !m_heventWake ) - wxLogLastError(wxS("CreateEvent(wake)")); -} - -wxMSWEventLoopBase::~wxMSWEventLoopBase() -{ - if ( m_heventWake && !::CloseHandle(m_heventWake) ) - wxLogLastError(wxS("CloseHandle(wake)")); } // ---------------------------------------------------------------------------- @@ -65,36 +54,26 @@ bool wxMSWEventLoopBase::Pending() const return ::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE) != 0; } -void wxMSWEventLoopBase::WakeUp() -{ - if ( !::SetEvent(m_heventWake) ) - wxLogLastError(wxS("SetEvent(wake)")); -} - -#if wxUSE_THREADS - -WXDWORD wxMSWEventLoopBase::MSWWaitForThread(WXHANDLE hThread) -{ - // The order is important here, the code using this function assumes that - // WAIT_OBJECT_0 indicates the thread termination and anything else -- the - // availability of an input event. So the thread handle must come first. - HANDLE handles[2] = { hThread, m_heventWake }; - return ::MsgWaitForMultipleObjects - ( - WXSIZEOF(handles), // number of objects to wait for - handles, // the objects - false, // wait for any objects, not all - INFINITE, // no timeout - QS_ALLINPUT | // return as soon as there are any events - QS_ALLPOSTMESSAGE - ); -} - -#endif // wxUSE_THREADS - bool wxMSWEventLoopBase::GetNextMessage(WXMSG* msg) { - return GetNextMessageTimeout(msg, INFINITE) == TRUE; + const BOOL rc = ::GetMessage(msg, NULL, 0, 0); + + if ( rc == 0 ) + { + // got WM_QUIT + return false; + } + + if ( rc == -1 ) + { + // should never happen, but let's test for it nevertheless + wxLogLastError(wxT("GetMessage")); + + // still break from the loop + return false; + } + + return true; } int wxMSWEventLoopBase::GetNextMessageTimeout(WXMSG *msg, unsigned long timeout) @@ -102,14 +81,16 @@ int wxMSWEventLoopBase::GetNextMessageTimeout(WXMSG *msg, unsigned long timeout) // MsgWaitForMultipleObjects() won't notice any input which was already // examined (e.g. using PeekMessage()) but not yet removed from the queue // so we need to remove any immediately messages manually - while ( !::PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) + if ( !::PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) { + // we use this function just in order to not block longer than the + // given timeout, so we don't pass any handles to it at all DWORD rc = ::MsgWaitForMultipleObjects ( - 1, &m_heventWake, + 0, NULL, FALSE, timeout, - QS_ALLINPUT | QS_ALLPOSTMESSAGE + QS_ALLINPUT ); switch ( rc ) @@ -123,17 +104,13 @@ int wxMSWEventLoopBase::GetNextMessageTimeout(WXMSG *msg, unsigned long timeout) return -1; case WAIT_OBJECT_0: - // We were woken up by a background thread, which means there - // is no actual input message available, but we should still - // return to the event loop, so pretend there was WM_NULL in - // the queue. - wxZeroMemory(*msg); - return TRUE; - - case WAIT_OBJECT_0 + 1: - // Some message is supposed to be available, but spurious - // wake ups are also possible, so just return to the loop: - // either we'll get the message or start waiting again. + if ( !::PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) + { + // somehow it may happen that MsgWaitForMultipleObjects() + // returns true but there are no messages -- just treat it + // the same as timeout then + return -1; + } break; } } @@ -147,6 +124,13 @@ int wxMSWEventLoopBase::GetNextMessageTimeout(WXMSG *msg, unsigned long timeout) #if wxUSE_CONSOLE_EVENTLOOP +void wxConsoleEventLoop::WakeUp() +{ +#if wxUSE_THREADS + wxWakeUpMainThread(); +#endif +} + void wxConsoleEventLoop::ProcessMessage(WXMSG *msg) { ::DispatchMessage(msg);