From: Vadim Zeitlin Date: Fri, 26 Dec 2008 22:28:34 +0000 (+0000) Subject: added wxEventLoop::DispatchTimeout() X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/9af42efda6c78093872a67180d43d5eeba261fee added wxEventLoop::DispatchTimeout() git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@57571 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/docs/changes.txt b/docs/changes.txt index f08a7c08c3..0941ac08bd 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -291,6 +291,7 @@ All: - Added wxMemoryInputStream(wxInputStream&) ctor (Stas Sergeev). - Implemented wxMemoryInputStream::CanRead(). - Implemented wxMemoryFSHandler::FindFirst/Next(). +- Added wxEventLoop::DispatchTimeout(). - Added wxEXEC_BLOCK flag (Hank Schultz). - Add support for wxStream-derived classes to wxRTTI (Stas Sergeev). - Added wxStreamBuffer::Truncate() (Stas Sergeev). diff --git a/include/wx/dfb/evtloop.h b/include/wx/dfb/evtloop.h index 80c0b1f069..4c3380819a 100644 --- a/include/wx/dfb/evtloop.h +++ b/include/wx/dfb/evtloop.h @@ -27,6 +27,7 @@ public: virtual bool Pending() const; virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); // returns DirectFB event buffer used by wx static wxIDirectFBEventBufferPtr GetDirectFBEventBuffer(); diff --git a/include/wx/evtloop.h b/include/wx/evtloop.h index bb22bb84db..5d78a3bf56 100644 --- a/include/wx/evtloop.h +++ b/include/wx/evtloop.h @@ -31,6 +31,7 @@ public: // using it virtual bool IsOk() const { return true; } + // start the event loop, return the exit code when it is finished virtual int Run() = 0; @@ -43,6 +44,12 @@ public: // dispatch a single event, return false if we should exit from the loop virtual bool Dispatch() = 0; + // same as Dispatch() but doesn't wait for longer than the specified (in + // ms) timeout, return true if an event was processed, false if we should + // exit the loop or -1 if timeout expired + virtual int DispatchTimeout(unsigned long timeout) = 0; + + // return currently active (running) event loop, may be NULL static wxEventLoopBase *GetActive() { return ms_activeLoop; } @@ -121,6 +128,8 @@ protected: #include "wx/dfb/evtloop.h" #else // other platform +#define wxNEEDS_GENERIC_DISPATCH_TIMEOUT + class WXDLLIMPEXP_FWD_CORE wxEventLoopImpl; class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxEventLoopBase @@ -133,6 +142,7 @@ public: virtual void Exit(int rc = 0); virtual bool Pending() const; virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp() { } protected: diff --git a/include/wx/msw/evtloop.h b/include/wx/msw/evtloop.h index fd548b87f2..88ab13492b 100644 --- a/include/wx/msw/evtloop.h +++ b/include/wx/msw/evtloop.h @@ -32,6 +32,10 @@ protected: // get the next message from queue and return true or return false if we // got WM_QUIT or an error occurred 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 + int GetNextMessageTimeout(WXMSG *msg, unsigned long timeout); }; #if wxUSE_GUI @@ -66,6 +70,7 @@ public: // override/implement base class virtuals virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); protected: @@ -92,8 +97,12 @@ public: // override/implement base class virtuals virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); + // MSW-specific function to process a single message + virtual void ProcessMessage(WXMSG *msg); + protected: virtual void OnNextIteration(); }; diff --git a/include/wx/osx/carbon/evtloop.h b/include/wx/osx/carbon/evtloop.h index 60f5704574..b55112994f 100644 --- a/include/wx/osx/carbon/evtloop.h +++ b/include/wx/osx/carbon/evtloop.h @@ -12,17 +12,25 @@ #ifndef _WX_MAC_CARBON_EVTLOOP_H_ #define _WX_MAC_CARBON_EVTLOOP_H_ +class OpaqueEventRef; +typedef OpaqueEventRef *EventRef; + class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxEventLoopManual { public: wxGUIEventLoop(); + // implement/override base class pure virtual virtual bool Pending() const; virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); - // implement base class pure virtual virtual void WakeUp(); + private: + // dispatch an event and release it + void DispatchAndReleaseEvent(EventRef event); + double m_sleepTime; }; diff --git a/include/wx/palmos/evtloop.h b/include/wx/palmos/evtloop.h index 6cd10d6245..36341946e8 100644 --- a/include/wx/palmos/evtloop.h +++ b/include/wx/palmos/evtloop.h @@ -26,6 +26,7 @@ public: virtual void Exit(int rc = 0); virtual bool Pending() const; virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); virtual bool IsRunning() const; // MSW-specific methods diff --git a/include/wx/unix/evtloop.h b/include/wx/unix/evtloop.h index 3f5b54913c..ddb101516f 100644 --- a/include/wx/unix/evtloop.h +++ b/include/wx/unix/evtloop.h @@ -29,6 +29,7 @@ public: // implement base class pure virtuals virtual bool Pending() const; virtual bool Dispatch(); + virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); virtual bool IsOk() const { return m_dispatcher != NULL; } diff --git a/interface/wx/evtloop.h b/interface/wx/evtloop.h index d4a7966a95..be246731b5 100644 --- a/interface/wx/evtloop.h +++ b/interface/wx/evtloop.h @@ -82,6 +82,24 @@ public: */ virtual bool Dispatch() = 0; + /** + Dispatch an event but not wait longer than the specified timeout for + it. + + If an event is received before the specified @a timeout expires, it is + processed and the function returns 1 normally or 0 if the event loop + should quite. Otherwise, i.e. if the timeout expires, the functions + returns -1 without processing any events. + + @param timeout + The maximal time to wait for the events in milliseconds. + + @return + 1 if an event was processed, 0 if the event loop should quit or -1 + if the timeout expired. + */ + virtual int DispatchTimeout(unsigned long timeout) = 0; + /** Return true if this event loop is currently running. diff --git a/src/common/evtloopcmn.cpp b/src/common/evtloopcmn.cpp index 3db4ddacf6..997718b44a 100644 --- a/src/common/evtloopcmn.cpp +++ b/src/common/evtloopcmn.cpp @@ -153,3 +153,23 @@ void wxEventLoopManual::Exit(int rc) } #endif // __WXMSW__ || __WXMAC__ || __WXDFB__ + +#ifdef wxNEEDS_GENERIC_DISPATCH_TIMEOUT + +int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) +{ + // TODO: this is, of course, horribly inefficient and a proper wait with + // timeout should be implemented for all ports natively... + const wxMilliClock_t timeEnd = wxGetLocalTimeMillis() + timeout; + for ( ;; ) + { + if ( Pending() ) + return Dispatch(); + + if ( wxGetLocalTimeMillis() >= timeEnd ) + return -1; + } +} + +#endif // wxNEEDS_GENERIC_DISPATCH_TIMEOUT + diff --git a/src/dfb/evtloop.cpp b/src/dfb/evtloop.cpp index 6c6bef402d..7f7807ac72 100644 --- a/src/dfb/evtloop.cpp +++ b/src/dfb/evtloop.cpp @@ -83,18 +83,28 @@ bool wxGUIEventLoop::Pending() const bool wxGUIEventLoop::Dispatch() { - wxCHECK_MSG( ms_buffer, false, "invalid event buffer" ); - // NB: we don't block indefinitely waiting for an event, but instead // time out after a brief period in order to make sure that // OnNextIteration() will be called frequently enough + // + // TODO: remove this hack, instead use CreateFileDescriptor() to properly + // multiplex GUI and socket input const int TIMEOUT = 100; + // treat time out (-1 return value) as normal successful return so that + // OnNextIteration() is called + return !!DispatchTimeout(TIMEOUT); +} + +int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) +{ + wxCHECK_MSG( ms_buffer, 0, "invalid event buffer" ); + // release the GUI mutex so that other threads have a chance to post // events: wxMutexGuiLeave(); - bool rv = ms_buffer->WaitForEventWithTimeout(0, TIMEOUT); + bool rv = ms_buffer->WaitForEventWithTimeout(0, timeout); // and acquire it back before calling any event handlers: wxMutexGuiEnter(); @@ -112,9 +122,7 @@ bool wxGUIEventLoop::Dispatch() } case DFB_TIMEOUT: - // timed out, pretend we processed an event so that - // OnNextIteration is called - break; + return -1; default: // don't terminate the loop due to errors (they were reported @@ -123,7 +131,7 @@ bool wxGUIEventLoop::Dispatch() } } - return true; + return 1; } void wxGUIEventLoop::WakeUp() diff --git a/src/msw/evtloop.cpp b/src/msw/evtloop.cpp index 601b8b870b..7c1ab86c19 100644 --- a/src/msw/evtloop.cpp +++ b/src/msw/evtloop.cpp @@ -99,6 +99,41 @@ bool wxMSWEventLoopBase::GetNextMessage(WXMSG* msg) return true; } +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 + // + // NB: using MsgWaitForMultipleObjectsEx() could simplify the code here but + // it is not available in very old Windows versions + 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 + if ( ::MsgWaitForMultipleObjects + ( + 0, NULL, + FALSE, + timeout, + QS_ALLINPUT + ) == WAIT_TIMEOUT ) + { + return -1; + } + + if ( !::PeekMessage(msg, 0, 0, 0, PM_REMOVE) ) + { + wxFAIL_MSG( _T("PeekMessage() should have succeeded") ); + + return -1; + } + } + + return msg->message != WM_QUIT; +} + + #endif // wxUSE_BASE #if wxUSE_GUI @@ -289,6 +324,18 @@ bool wxGUIEventLoop::Dispatch() return true; } +int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) +{ + MSG msg; + int rc = GetNextMessageTimeout(&msg, timeout); + if ( rc != 1 ) + return rc; + + ProcessMessage(&msg); + + return 1; +} + void wxGUIEventLoop::OnNextIteration() { #if wxUSE_THREADS @@ -318,22 +365,39 @@ void wxConsoleEventLoop::WakeUp() #endif } -bool wxConsoleEventLoop::Dispatch() +void wxConsoleEventLoop::ProcessMessage(WXMSG *msg) { - MSG msg; - if ( !GetNextMessage(&msg) ) - return false; - - if ( msg.message == WM_TIMER ) + if ( msg->message == WM_TIMER ) { - TIMERPROC proc = (TIMERPROC)msg.lParam; + TIMERPROC proc = (TIMERPROC)msg->lParam; if ( proc ) - (*proc)(NULL, 0, msg.wParam, 0); + (*proc)(NULL, 0, msg->wParam, 0); } else { - ::DispatchMessage(&msg); + ::DispatchMessage(msg); } +} + +bool wxConsoleEventLoop::Dispatch() +{ + MSG msg; + if ( !GetNextMessage(&msg) ) + return false; + + ProcessMessage(&msg); + + return !m_shouldExit; +} + +int wxConsoleEventLoop::DispatchTimeout(unsigned long timeout) +{ + MSG msg; + int rc = GetNextMessageTimeout(&msg, timeout); + if ( rc != 1 ) + return rc; + + ProcessMessage(&msg); return !m_shouldExit; } diff --git a/src/osx/carbon/evtloop.cpp b/src/osx/carbon/evtloop.cpp index ec51ee972b..45aad41e25 100644 --- a/src/osx/carbon/evtloop.cpp +++ b/src/osx/carbon/evtloop.cpp @@ -48,6 +48,18 @@ void wxGUIEventLoop::WakeUp() wxMacWakeUp(); } +void wxGUIEventLoop::DispatchAndReleaseEvent(EventRef theEvent) +{ + if ( wxTheApp ) + wxTheApp->MacSetCurrentEvent( theEvent, NULL ); + + OSStatus status = SendEventToEventTarget(theEvent, GetEventDispatcherTarget()); + if (status == eventNotHandledErr && wxTheApp) + wxTheApp->MacHandleUnhandledEvent(theEvent); + + ReleaseEvent( theEvent ); +} + bool wxGUIEventLoop::Pending() const { EventRef theEvent; @@ -95,17 +107,33 @@ bool wxGUIEventLoop::Dispatch() break; default: - if ( wxTheApp ) - wxTheApp->MacSetCurrentEvent( theEvent, NULL ); - - OSStatus status = SendEventToEventTarget(theEvent, GetEventDispatcherTarget()); - if (status == eventNotHandledErr && wxTheApp) - wxTheApp->MacHandleUnhandledEvent(theEvent); - - ReleaseEvent( theEvent ); + DispatchAndReleaseEvent(theEvent); m_sleepTime = kEventDurationNoWait ; break; } return true; } + +int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) +{ + EventRef event; + OSStatus status = ReceiveNextEvent(0, NULL, timeout/1000, true, &event); + switch ( status ) + { + default: + wxFAIL_MSG( "unexpected ReceiveNextEvent() error" ); + // fall through + + case eventLoopTimedOutErr: + return -1; + + case eventLoopQuitErr: + return 0; + + case noErr: + DispatchAndReleaseEvent(event); + return 1; + } +} + diff --git a/src/palmos/evtloop.cpp b/src/palmos/evtloop.cpp index 01bd21e982..5dc164d157 100644 --- a/src/palmos/evtloop.cpp +++ b/src/palmos/evtloop.cpp @@ -135,6 +135,11 @@ bool wxGUIEventLoop::Dispatch() return false; } +int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) +{ + return -1; +} + void wxGUIEventLoop::WakeUp() { return; diff --git a/src/unix/evtloopunix.cpp b/src/unix/evtloopunix.cpp index fc1f469ae4..7f27c1c2a2 100644 --- a/src/unix/evtloopunix.cpp +++ b/src/unix/evtloopunix.cpp @@ -149,30 +149,34 @@ bool wxConsoleEventLoop::Pending() const bool wxConsoleEventLoop::Dispatch() { - // calculate the timeout until the next timer expiration - int timeout; + DispatchTimeout(wxFDIODispatcher::TIMEOUT_INFINITE); + return true; +} + +int wxConsoleEventLoop::DispatchTimeout(unsigned long timeout) +{ #if wxUSE_TIMER + // check if we need to decrease the timeout to account for a timer wxUsecClock_t nextTimer; if ( wxTimerScheduler::Get().GetNext(&nextTimer) ) { - // timeout is in ms - timeout = (nextTimer / 1000).ToLong(); + unsigned long timeUntilNextTimer = wxMilliClockToLong(nextTimer / 1000); + if ( timeUntilNextTimer < timeout ) + timeout = timeUntilNextTimer; } - else // no timers, we can block forever #endif // wxUSE_TIMER - { - timeout = wxFDIODispatcher::TIMEOUT_INFINITE; - } - m_dispatcher->Dispatch(timeout); + bool hadEvent = m_dispatcher->Dispatch(timeout); #if wxUSE_TIMER - wxTimerScheduler::Get().NotifyExpired(); -#endif + if ( wxTimerScheduler::Get().NotifyExpired() ) + hadEvent = true; +#endif // wxUSE_TIMER wxTheApp->ProcessPendingEvents(); - return true; + + return hadEvent ? 1 : -1; } void wxConsoleEventLoop::WakeUp()