Kaydet (Commit) 06d73142 authored tarafından Luboš Luňák's avatar Luboš Luňák

make idle timers actually activate only when idle

Without this, they can activate after any call to the event processing,
so they may activate in cases such as when updating progressbar while
loading a document, or on repeated user input (so things like showing
spellchecking get updated when the app is busy redrawing). This change
makes idle timers activate only when there's nothing more for the event
loop to process. It's a bit of a question if this doesn't break something
that happens to expect idle timers to be not-really-idle timers, but oh well.

No change for non-X11 platforms, as there's I don't know how to check
the event queues.

Change-Id: I074a88f2f5eeb4b456a11916a0ec2ad6f54dfbab
üst e6a58b5e
...@@ -61,8 +61,11 @@ public: ...@@ -61,8 +61,11 @@ public:
Timer& operator=( const Timer& rTimer ); Timer& operator=( const Timer& rTimer );
/// @internal
static void ImplDeInitTimer(); static void ImplDeInitTimer();
static void ImplTimerCallbackProc(); /// @internal
/// @p idle - allow also idle timers
static void ImplTimerCallbackProc( bool idle );
/// Process all pending idle tasks ahead of time in priority order. /// Process all pending idle tasks ahead of time in priority order.
static void ProcessAllIdleHandlers(); static void ProcessAllIdleHandlers();
......
...@@ -186,7 +186,10 @@ bool SvpSalInstance::CheckTimeout( bool bExecuteTimers ) ...@@ -186,7 +186,10 @@ bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
// notify // notify
ImplSVData* pSVData = ImplGetSVData(); ImplSVData* pSVData = ImplGetSVData();
if( pSVData->mpSalTimer ) if( pSVData->mpSalTimer )
pSVData->mpSalTimer->CallCallback(); {
bool idle = true; // TODO
pSVData->mpSalTimer->CallCallback( idle );
}
} }
} }
} }
......
...@@ -47,10 +47,10 @@ public: ...@@ -47,10 +47,10 @@ public:
m_pProc = pProc; m_pProc = pProc;
} }
void CallCallback() void CallCallback( bool idle )
{ {
if( m_pProc ) if( m_pProc )
m_pProc(); m_pProc( idle );
} }
}; };
......
...@@ -281,7 +281,7 @@ struct SalInputContext ...@@ -281,7 +281,7 @@ struct SalInputContext
sal_uLong mnOptions; sal_uLong mnOptions;
}; };
typedef void (*SALTIMERPROC)(); typedef void (*SALTIMERPROC)( bool idle );
#endif // INCLUDED_VCL_INC_SALWTYPE_HXX #endif // INCLUDED_VCL_INC_SALWTYPE_HXX
......
...@@ -99,6 +99,7 @@ class GtkData : public SalGenericData ...@@ -99,6 +99,7 @@ class GtkData : public SalGenericData
GSource *m_pUserEvent; GSource *m_pUserEvent;
oslMutex m_aDispatchMutex; oslMutex m_aDispatchMutex;
oslCondition m_aDispatchCondition; oslCondition m_aDispatchCondition;
bool blockIdleTimeout;
public: public:
GtkData( SalInstance *pInstance ); GtkData( SalInstance *pInstance );
...@@ -120,6 +121,7 @@ public: ...@@ -120,6 +121,7 @@ public:
virtual bool ErrorTrapPop( bool bIgnoreError ) SAL_OVERRIDE; virtual bool ErrorTrapPop( bool bIgnoreError ) SAL_OVERRIDE;
inline GtkSalDisplay *GetGtkDisplay() const; inline GtkSalDisplay *GetGtkDisplay() const;
bool BlockIdleTimeout() const { return blockIdleTimeout; }
}; };
class GtkSalFrame; class GtkSalFrame;
......
...@@ -77,7 +77,7 @@ public: ...@@ -77,7 +77,7 @@ public:
void StartTimer( sal_uLong nMS ); void StartTimer( sal_uLong nMS );
inline void StopTimer(); inline void StopTimer();
void Timeout() const; void Timeout( bool idle ) const;
// X errors // X errors
virtual void ErrorTrapPush() SAL_OVERRIDE; virtual void ErrorTrapPush() SAL_OVERRIDE;
......
...@@ -157,6 +157,7 @@ protected: ...@@ -157,6 +157,7 @@ protected:
timeval m_aTimeout; timeval m_aTimeout;
sal_uLong m_nTimeoutMS; sal_uLong m_nTimeoutMS;
int m_pTimeoutFDS[2]; int m_pTimeoutFDS[2];
bool blockIdleTimeout;
int nFDs_; int nFDs_;
fd_set aReadFDS_; fd_set aReadFDS_;
......
...@@ -646,7 +646,8 @@ void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -646,7 +646,8 @@ void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
{ {
// this cause crashes on MacOSX 10.4 // this cause crashes on MacOSX 10.4
// [AquaSalTimer::pRunningTimer fire]; // [AquaSalTimer::pRunningTimer fire];
ImplGetSVData()->mpSalTimer->CallCallback(); bool idle = true; // TODO
ImplGetSVData()->mpSalTimer->CallCallback( idle );
} }
} }
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
if( pSVData->mpSalTimer ) if( pSVData->mpSalTimer )
{ {
YIELD_GUARD; YIELD_GUARD;
pSVData->mpSalTimer->CallCallback(); bool idle = true; // TODO
pSVData->mpSalTimer->CallCallback( idle );
// NSTimer does not end nextEventMatchingMask of NSApplication // NSTimer does not end nextEventMatchingMask of NSApplication
// so we need to wakeup a waiting Yield to inform it something happened // so we need to wakeup a waiting Yield to inform it something happened
......
...@@ -91,7 +91,8 @@ void AquaSalTimer::handleStartTimerEvent( NSEvent* pEvent ) ...@@ -91,7 +91,8 @@ void AquaSalTimer::handleStartTimerEvent( NSEvent* pEvent )
{ {
YIELD_GUARD; YIELD_GUARD;
// timer already elapsed since event posted // timer already elapsed since event posted
pSVData->mpSalTimer->CallCallback(); bool idle = true; // TODO
pSVData->mpSalTimer->CallCallback( idle );
} }
ImplSalStartTimer( sal_uLong( [pEvent data1] ) ); ImplSalStartTimer( sal_uLong( [pEvent data1] ) );
} }
......
...@@ -344,7 +344,7 @@ inline void ImplYield( bool i_bWait, bool i_bAllEvents ) ...@@ -344,7 +344,7 @@ inline void ImplYield( bool i_bWait, bool i_bAllEvents )
// run timers that have timed out // run timers that have timed out
if ( !pSVData->mbNoCallTimer ) if ( !pSVData->mbNoCallTimer )
while ( pSVData->mbNotAllTimerCalled ) while ( pSVData->mbNotAllTimerCalled )
Timer::ImplTimerCallbackProc(); Timer::ImplTimerCallbackProc( !i_bWait );
pSVData->maAppData.mnDispatchLevel++; pSVData->maAppData.mnDispatchLevel++;
// do not wait for events if application was already quit; in that // do not wait for events if application was already quit; in that
...@@ -366,7 +366,7 @@ inline void ImplYield( bool i_bWait, bool i_bAllEvents ) ...@@ -366,7 +366,7 @@ inline void ImplYield( bool i_bWait, bool i_bAllEvents )
{ {
do do
{ {
Timer::ImplTimerCallbackProc(); Timer::ImplTimerCallbackProc( !i_bWait );
} }
while( pSVData->mbNotAllTimerCalled ); while( pSVData->mbNotAllTimerCalled );
} }
......
...@@ -129,7 +129,7 @@ static void ImplStartTimer( ImplSVData* pSVData, sal_uLong nMS ) ...@@ -129,7 +129,7 @@ static void ImplStartTimer( ImplSVData* pSVData, sal_uLong nMS )
} }
} }
void Timer::ImplTimerCallbackProc() void Timer::ImplTimerCallbackProc( bool idle )
{ {
ImplSVData* pSVData = ImplGetSVData(); ImplSVData* pSVData = ImplGetSVData();
ImplTimerData* pTimerData; ImplTimerData* pTimerData;
...@@ -151,7 +151,7 @@ void Timer::ImplTimerCallbackProc() ...@@ -151,7 +151,7 @@ void Timer::ImplTimerCallbackProc()
// If the timer is not new, was not deleted, and if it is not in the timeout handler, then // If the timer is not new, was not deleted, and if it is not in the timeout handler, then
// call the handler as soon as the time is up. // call the handler as soon as the time is up.
if ( (pTimerData->mnTimerUpdate < pSVData->mnTimerUpdate) && if ( (pTimerData->mnTimerUpdate < pSVData->mnTimerUpdate) &&
!pTimerData->mbDelete && !pTimerData->mbInTimeout ) !pTimerData->mbDelete && !pTimerData->mbInTimeout && (!pTimerData->mpTimer->mbIdle || idle) )
{ {
// time has expired // time has expired
if ( pTimerData->GetDeadline() <= nTime ) if ( pTimerData->GetDeadline() <= nTime )
......
...@@ -336,6 +336,7 @@ void X11SalData::PopXErrorLevel() ...@@ -336,6 +336,7 @@ void X11SalData::PopXErrorLevel()
} }
SalXLib::SalXLib() SalXLib::SalXLib()
: blockIdleTimeout( false )
{ {
m_aTimeout.tv_sec = 0; m_aTimeout.tv_sec = 0;
m_aTimeout.tv_usec = 0; m_aTimeout.tv_usec = 0;
...@@ -646,8 +647,20 @@ bool SalXLib::CheckTimeout( bool bExecuteTimers ) ...@@ -646,8 +647,20 @@ bool SalXLib::CheckTimeout( bool bExecuteTimers )
* timers are being dispatched. * timers are being dispatched.
*/ */
m_aTimeout += m_nTimeoutMS; m_aTimeout += m_nTimeoutMS;
// Determine if the app is idle (for idle timers). If there's user input pending,
// if there's IO pending or if we're called inside a temporary yield (=blockIdleTimeout),
// then the app is not idle.
bool idle = true;
if( blockIdleTimeout || XPending( vcl_sal::getSalDisplay(GetGenericData())->GetDisplay()))
idle = false;
for ( int nFD = 0; idle && nFD < nFDs_; nFD++ )
{
YieldEntry* pEntry = &(yieldTable[nFD]);
if ( pEntry->fd && pEntry->HasPendingEvent())
idle = false;
}
// notify // notify
GetX11SalData()->Timeout(); GetX11SalData()->Timeout( idle );
} }
} }
} }
...@@ -656,6 +669,7 @@ bool SalXLib::CheckTimeout( bool bExecuteTimers ) ...@@ -656,6 +669,7 @@ bool SalXLib::CheckTimeout( bool bExecuteTimers )
void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
{ {
blockIdleTimeout = !bWait;
// check for timeouts here if you want to make screenshots // check for timeouts here if you want to make screenshots
static char* p_prioritize_timer = getenv ("SAL_HIGHPRIORITY_REPAINT"); static char* p_prioritize_timer = getenv ("SAL_HIGHPRIORITY_REPAINT");
if (p_prioritize_timer != NULL) if (p_prioritize_timer != NULL)
...@@ -674,7 +688,10 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -674,7 +688,10 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
{ {
pEntry->HandleNextEvent(); pEntry->HandleNextEvent();
if( ! bHandleAllCurrentEvents ) if( ! bHandleAllCurrentEvents )
{
blockIdleTimeout = false;
return; return;
}
} }
} }
} }
...@@ -746,7 +763,10 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -746,7 +763,10 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
// someone-else has done the job for us // someone-else has done the job for us
if (nFound == 0) if (nFound == 0)
{
blockIdleTimeout = false;
return; return;
}
for ( int nFD = 0; nFD < nFDs_; nFD++ ) for ( int nFD = 0; nFD < nFDs_; nFD++ )
{ {
...@@ -772,6 +792,7 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -772,6 +792,7 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
} }
} }
} }
blockIdleTimeout = false;
} }
void SalXLib::Wakeup() void SalXLib::Wakeup()
......
...@@ -29,11 +29,11 @@ ...@@ -29,11 +29,11 @@
#include <unx/saltimer.h> #include <unx/saltimer.h>
#include <unx/salinst.h> #include <unx/salinst.h>
void X11SalData::Timeout() const void X11SalData::Timeout( bool idle ) const
{ {
ImplSVData* pSVData = ImplGetSVData(); ImplSVData* pSVData = ImplGetSVData();
if( pSVData->mpSalTimer ) if( pSVData->mpSalTimer )
pSVData->mpSalTimer->CallCallback(); pSVData->mpSalTimer->CallCallback( idle );
} }
void SalXLib::StopTimer() void SalXLib::StopTimer()
......
...@@ -502,6 +502,7 @@ GtkData::GtkData( SalInstance *pInstance ) ...@@ -502,6 +502,7 @@ GtkData::GtkData( SalInstance *pInstance )
#else #else
: SalGenericData( SAL_DATA_GTK, pInstance ) : SalGenericData( SAL_DATA_GTK, pInstance )
#endif #endif
, blockIdleTimeout( false )
{ {
m_pUserEvent = NULL; m_pUserEvent = NULL;
m_aDispatchMutex = osl_createMutex(); m_aDispatchMutex = osl_createMutex();
...@@ -551,6 +552,7 @@ void GtkData::Dispose() ...@@ -551,6 +552,7 @@ void GtkData::Dispose()
void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents ) void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
{ {
blockIdleTimeout = !bWait;
/* #i33212# only enter g_main_context_iteration in one thread at any one /* #i33212# only enter g_main_context_iteration in one thread at any one
* time, else one of them potentially will never end as long as there is * time, else one of them potentially will never end as long as there is
* another thread in there. Having only one yieldin thread actually dispatch * another thread in there. Having only one yieldin thread actually dispatch
...@@ -564,7 +566,10 @@ void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -564,7 +566,10 @@ void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
if( osl_tryToAcquireMutex( m_aDispatchMutex ) ) if( osl_tryToAcquireMutex( m_aDispatchMutex ) )
bDispatchThread = true; bDispatchThread = true;
else if( ! bWait ) else if( ! bWait )
{
blockIdleTimeout = false;
return; // someone else is waiting already, return return; // someone else is waiting already, return
}
if( bDispatchThread ) if( bDispatchThread )
{ {
...@@ -596,6 +601,7 @@ void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -596,6 +601,7 @@ void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
if( bWasEvent ) if( bWasEvent )
osl_setCondition( m_aDispatchCondition ); // trigger non dispatch thread yields osl_setCondition( m_aDispatchCondition ); // trigger non dispatch thread yields
} }
blockIdleTimeout = false;
} }
void GtkData::Init() void GtkData::Init()
...@@ -822,7 +828,7 @@ extern "C" { ...@@ -822,7 +828,7 @@ extern "C" {
if( !pTSource->pInstance ) if( !pTSource->pInstance )
return FALSE; return FALSE;
SalData *pSalData = GetSalData(); GtkData *pSalData = static_cast< GtkData* >( GetSalData());
osl::Guard< comphelper::SolarMutex > aGuard( pSalData->m_pInstance->GetYieldMutex() ); osl::Guard< comphelper::SolarMutex > aGuard( pSalData->m_pInstance->GetYieldMutex() );
...@@ -830,7 +836,11 @@ extern "C" { ...@@ -830,7 +836,11 @@ extern "C" {
ImplSVData* pSVData = ImplGetSVData(); ImplSVData* pSVData = ImplGetSVData();
if( pSVData->mpSalTimer ) if( pSVData->mpSalTimer )
pSVData->mpSalTimer->CallCallback(); {
// TODO: context_pending should be probably checked too, but it causes locking assertion failures
bool idle = !pSalData->BlockIdleTimeout() && /*!g_main_context_pending( NULL ) &&*/ !gdk_events_pending();
pSVData->mpSalTimer->CallCallback( idle );
}
return TRUE; return TRUE;
} }
......
...@@ -54,7 +54,7 @@ KDEXLib::KDEXLib() : ...@@ -54,7 +54,7 @@ KDEXLib::KDEXLib() :
SalXLib(), m_bStartupDone(false), m_pApplication(0), SalXLib(), m_bStartupDone(false), m_pApplication(0),
m_pFreeCmdLineArgs(0), m_pAppCmdLineArgs(0), m_nFakeCmdLineArgs( 0 ), m_pFreeCmdLineArgs(0), m_pAppCmdLineArgs(0), m_nFakeCmdLineArgs( 0 ),
m_frameWidth( -1 ), m_isGlibEventLoopType(false), m_frameWidth( -1 ), m_isGlibEventLoopType(false),
m_allowKdeDialogs(false) m_allowKdeDialogs(false), blockIdleTimeout(false)
{ {
// the timers created here means they belong to the main thread. // the timers created here means they belong to the main thread.
// As the timeoutTimer runs the LO event queue, which may block on a dialog, // As the timeoutTimer runs the LO event queue, which may block on a dialog,
...@@ -309,6 +309,7 @@ void KDEXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -309,6 +309,7 @@ void KDEXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
void KDEXLib::processYield( bool bWait, bool bHandleAllCurrentEvents ) void KDEXLib::processYield( bool bWait, bool bHandleAllCurrentEvents )
{ {
blockIdleTimeout = !bWait;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance( qApp->thread()); QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance( qApp->thread());
bool wasEvent = false; bool wasEvent = false;
for( int cnt = bHandleAllCurrentEvents ? 100 : 1; for( int cnt = bHandleAllCurrentEvents ? 100 : 1;
...@@ -321,6 +322,7 @@ void KDEXLib::processYield( bool bWait, bool bHandleAllCurrentEvents ) ...@@ -321,6 +322,7 @@ void KDEXLib::processYield( bool bWait, bool bHandleAllCurrentEvents )
} }
if( bWait && !wasEvent ) if( bWait && !wasEvent )
dispatcher->processEvents( QEventLoop::WaitForMoreEvents ); dispatcher->processEvents( QEventLoop::WaitForMoreEvents );
blockIdleTimeout = false;
} }
void KDEXLib::StartTimer( sal_uLong nMS ) void KDEXLib::StartTimer( sal_uLong nMS )
...@@ -360,7 +362,10 @@ void KDEXLib::timeoutActivated() ...@@ -360,7 +362,10 @@ void KDEXLib::timeoutActivated()
SalKDEDisplay::self()->DispatchInternalEvent(); SalKDEDisplay::self()->DispatchInternalEvent();
X11SalData *pData = static_cast<X11SalData*>(ImplGetSVData()->mpSalData); X11SalData *pData = static_cast<X11SalData*>(ImplGetSVData()->mpSalData);
pData->Timeout(); // QGuiEventDispatcherGlib makes glib watch also X11 fd, but its hasPendingEvents()
// doesn't check X11, so explicitly check XPending() here.
bool idle = QApplication::hasPendingEvents() && !blockIdleTimeout && !XPending( QX11Info::display());
pData->Timeout( idle );
// QTimer is not single shot, so will be restarted immediatelly // QTimer is not single shot, so will be restarted immediatelly
} }
......
...@@ -54,6 +54,7 @@ class KDEXLib : public QObject, public SalXLib ...@@ -54,6 +54,7 @@ class KDEXLib : public QObject, public SalXLib
int m_frameWidth; int m_frameWidth;
bool m_isGlibEventLoopType; bool m_isGlibEventLoopType;
bool m_allowKdeDialogs; bool m_allowKdeDialogs;
bool blockIdleTimeout;
private: private:
void setupEventLoop(); void setupEventLoop();
......
...@@ -147,7 +147,8 @@ void EmitTimerCallback() ...@@ -147,7 +147,8 @@ void EmitTimerCallback()
// try this a short time later again. // try this a short time later again.
if (pSVData->mpSalTimer && ImplSalYieldMutexTryToAcquire()) if (pSVData->mpSalTimer && ImplSalYieldMutexTryToAcquire())
{ {
pSVData->mpSalTimer->CallCallback(); bool idle = true; // TODO
pSVData->mpSalTimer->CallCallback( idle );
ImplSalYieldMutexRelease(); ImplSalYieldMutexRelease();
// Run the timer in the correct time, if we started this // Run the timer in the correct time, if we started this
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment