[Libreoffice-commits] core.git: 3 commits - include/vcl vcl/headless vcl/inc vcl/osx vcl/source vcl/unx vcl/win

Luboš Luňák l.lunak at collabora.com
Wed Feb 4 06:45:16 PST 2015


 include/vcl/timer.hxx               |    5 -
 vcl/headless/svpinst.cxx            |    5 -
 vcl/inc/saltimer.hxx                |    4 
 vcl/inc/salwtype.hxx                |    2 
 vcl/inc/unx/gtk/gtkdata.hxx         |    2 
 vcl/inc/unx/saldata.hxx             |    2 
 vcl/inc/unx/saldisp.hxx             |    1 
 vcl/inc/unx/salframe.h              |    1 
 vcl/osx/salinst.cxx                 |    3 
 vcl/osx/salnstimer.mm               |    3 
 vcl/osx/saltimer.cxx                |    3 
 vcl/source/app/svapp.cxx            |    4 
 vcl/source/app/timer.cxx            |    4 
 vcl/unx/generic/app/saldata.cxx     |   23 +++++
 vcl/unx/generic/app/saltimer.cxx    |    4 
 vcl/unx/generic/window/salframe.cxx |  147 ++++++++++++++++++++++++++++--------
 vcl/unx/gtk/app/gtkdata.cxx         |   14 ++-
 vcl/unx/kde4/KDEXLib.cxx            |    9 +-
 vcl/unx/kde4/KDEXLib.hxx            |    1 
 vcl/win/source/app/saltimer.cxx     |    3 
 20 files changed, 189 insertions(+), 51 deletions(-)

New commits:
commit fc29d34dc60dda5ddbc8f0444684bd2f4eb3668e
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Wed Feb 4 15:25:03 2015 +0100

    discard excessive (X11) autorepeat keyboard events if they overload LO
    
    If e.g. a document is slow to redraw, scrolling it by holding down a key such
    as PageDown can make LO lag behind and continue processing the queued up
    input events even (long) after the key has been released. Since with autorepeat
    keyboard events the normal user expectations are to hold it until something
    happens and the exact number of the events doesn't matter, simply discard
    excessive autorepeat keyboard events if LO can't keep up with them.
    
    Change-Id: I45acdc9aa5033647fb80760991437dddfcb6591c

diff --git a/vcl/inc/unx/salframe.h b/vcl/inc/unx/salframe.h
index d7ce0c2..af89ba8 100644
--- a/vcl/inc/unx/salframe.h
+++ b/vcl/inc/unx/salframe.h
@@ -77,7 +77,6 @@ class VCLPLUG_GEN_PUBLIC X11SalFrame : public SalFrame, public X11WindowProvider
     X11SalGraphics  *pGraphics_;            // current frame graphics
     X11SalGraphics  *pFreeGraphics_;        // first free frame graphics
 
-    Time            nReleaseTime_;      // timestamp of last key release
     sal_uInt16      nKeyCode_;          // last key code
     sal_uInt16      nKeyState_;         // last key state
     int             nCompose_;          // compose state
diff --git a/vcl/unx/generic/window/salframe.cxx b/vcl/unx/generic/window/salframe.cxx
index 9dee672..77e70e7 100644
--- a/vcl/unx/generic/window/salframe.cxx
+++ b/vcl/unx/generic/window/salframe.cxx
@@ -778,7 +778,6 @@ X11SalFrame::X11SalFrame( SalFrame *pParent, sal_uLong nSalFrameStyle,
     hCursor_                    = None;
     nCaptured_                  = 0;
 
-     nReleaseTime_              = 0;
     nKeyCode_                   = 0;
     nKeyState_                  = 0;
     nCompose_                   = -1;
@@ -3157,6 +3156,66 @@ bool X11SalFrame::endUnicodeSequence()
 
 long X11SalFrame::HandleKeyEvent( XKeyEvent *pEvent )
 {
+    if( pEvent->type == KeyRelease )
+    {
+        // Ignore autorepeat keyrelease events. If there is a series of keypress+keyrelease+keypress events
+        // generated by holding down a key, and if these are from autorepeat (keyrelease and the following keypress
+        // have the same timestamp), drop the autorepeat keyrelease event. Not exactly sure why this is done
+        // (possibly hiding differences between platforms, or just making it more sensible, because technically
+        // the key has not been released at all).
+        bool ignore = false;
+        // Discard queued excessive autorepeat events.
+        // If the user presses and holds down a key, the autorepeating keypress events
+        // may overload LO (e.g. if the key is PageDown and the LO cannot keep up scrolling).
+        // Reduce the load by simply discarding such excessive events (so for a KeyRelease event,
+        // check if it's followed by matching KeyPress+KeyRelease pair(s) and discard those).
+        // This shouldn't have any negative effects - unlike with normal (non-autorepeat
+        // events), the user is unlikely to rely on the exact number of resulting actions
+        // (since autorepeat generates keypress events rather quickly and it's hard to estimate
+        // how many exactly) and the idea should be just keeping the key pressed until something
+        // happens (in which case more events that just lag LO shouldn't make a difference).
+        Display* dpy = pEvent->display;
+        XKeyEvent previousRelease = *pEvent;
+        while( XPending( dpy ))
+        {
+            XEvent nextEvent1;
+            bool discard1 = false;
+            XNextEvent( dpy, &nextEvent1 );
+            if( nextEvent1.type == KeyPress && nextEvent1.xkey.time == previousRelease.time
+                && !nextEvent1.xkey.send_event && nextEvent1.xkey.window == previousRelease.window
+                && nextEvent1.xkey.state == previousRelease.state && nextEvent1.xkey.keycode == previousRelease.keycode )
+            {   // This looks like another autorepeat keypress.
+                ignore = true;
+                if( XPending( dpy ))
+                {
+                    XEvent nextEvent2;
+                    XNextEvent( dpy, &nextEvent2 );
+                    if( nextEvent2.type == KeyRelease && nextEvent2.xkey.time <= ( previousRelease.time + 100 )
+                        && !nextEvent2.xkey.send_event && nextEvent2.xkey.window == previousRelease.window
+                        && nextEvent2.xkey.state == previousRelease.state && nextEvent2.xkey.keycode == previousRelease.keycode )
+                    {   // And the matching keyrelease -> drop them both.
+                        discard1 = true;
+                        previousRelease = nextEvent2.xkey;
+                        ignore = false; // There either will be another autorepeating keypress that'll lead to discarding
+                                        // the pEvent keyrelease, it this discarding makes that keyrelease the last one.
+                    }
+                    else
+                    {
+                        XPutBackEvent( dpy, &nextEvent2 );
+                        break;
+                    }
+                }
+            }
+            if( !discard1 )
+            {   // Unrelated event, put back and stop compressing.
+                XPutBackEvent( dpy, &nextEvent1 );
+                break;
+            }
+        }
+        if( ignore ) // This autorepeating keyrelease is followed by another keypress.
+            return 0;
+    }
+
     KeySym          nKeySym;
     KeySym          nUnmodifiedKeySym;
     int             nLen = 2048;
@@ -4019,24 +4078,6 @@ long X11SalFrame::HandleClientMessage( XClientMessageEvent *pEvent )
     return 0;
 }
 
-extern "C"
-{
-Bool call_checkKeyReleaseForRepeat( Display* pDisplay, XEvent* pCheck, XPointer pX11SalFrame )
-{
-    return X11SalFrame::checkKeyReleaseForRepeat( pDisplay, pCheck, pX11SalFrame );
-}
-}
-
-Bool X11SalFrame::checkKeyReleaseForRepeat( Display*, XEvent* pCheck, XPointer pX11SalFrame )
-{
-    X11SalFrame* pThis = reinterpret_cast<X11SalFrame*>(pX11SalFrame);
-    return
-        pCheck->type            == KeyPress &&
-        pCheck->xkey.state      == pThis->nKeyState_ &&
-        pCheck->xkey.keycode    == pThis->nKeyCode_ &&
-        pCheck->xkey.time       == pThis->nReleaseTime_  ? True : False;
-}
-
 long X11SalFrame::Dispatch( XEvent *pEvent )
 {
     long nRet = 0;
@@ -4062,14 +4103,7 @@ long X11SalFrame::Dispatch( XEvent *pEvent )
 
             case KeyRelease:
                 if( -1 == nCompose_ )
-                {
-                    nReleaseTime_ = pEvent->xkey.time;
-                    XEvent aEvent;
-                    if( XCheckIfEvent( pEvent->xkey.display, &aEvent, call_checkKeyReleaseForRepeat, reinterpret_cast<XPointer>(this) ) )
-                        XPutBackEvent( pEvent->xkey.display, &aEvent );
-                    else
-                        nRet        = HandleKeyEvent( &pEvent->xkey );
-                }
+                    nRet = HandleKeyEvent( &pEvent->xkey );
             break;
 
             case ButtonPress:
commit 847513d409b146400515d7796d196b8b2a142036
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Mon Feb 2 16:38:18 2015 +0100

    compress (X11) mouse wheel events
    
    There is one event per wheel step, so wheeling more can create a number
    of wheel events, and LO processed those one by one. If processing one took
    long (e.g. the repaint was complicated), the scrolling visibly lagged.
    
    This commit works only for X11 VCL backend (and by extension, KDE3/4 backends).
    
    Change-Id: I5eff7446da16167ec75925e75243314c68bc81a4

diff --git a/vcl/unx/generic/window/salframe.cxx b/vcl/unx/generic/window/salframe.cxx
index a018b5b..9dee672 100644
--- a/vcl/unx/generic/window/salframe.cxx
+++ b/vcl/unx/generic/window/salframe.cxx
@@ -2718,6 +2718,51 @@ void X11SalFrame::SimulateKeyPress( sal_uInt16 nKeyCode )
     vcl_sal::getSalDisplay(GetGenericData())->SimulateKeyPress(nKeyCode);
 }
 
+namespace
+{
+struct CompressWheelEventsData
+{
+    XEvent* firstEvent;
+    bool ignore;
+    int count; // number of compressed events
+};
+
+static Bool compressWheelEvents( Display*, XEvent* event, XPointer p )
+{
+    CompressWheelEventsData* data = reinterpret_cast< CompressWheelEventsData* >( p );
+    if( data->ignore )
+        return False; // we're already after the events to compress
+    if( event->type == ButtonPress || event->type == ButtonRelease )
+    {
+        const unsigned int mask = Button1Mask << ( event->xbutton.button - Button1 );
+        if( event->xbutton.button == data->firstEvent->xbutton.button
+            && event->xbutton.window == data->firstEvent->xbutton.window
+            && event->xbutton.x == data->firstEvent->xbutton.x
+            && event->xbutton.y == data->firstEvent->xbutton.y
+            && ( event->xbutton.state | mask ) == ( data->firstEvent->xbutton.state | mask ))
+        {
+            // Count if it's another press (i.e. wheel start event).
+            if( event->type == ButtonPress )
+                ++data->count;
+            return True; // And remove the event from the queue.
+        }
+    }
+    // Non-matching event, skip certain events that cannot possibly affect input processing,
+    // but otherwise ignore all further events.
+    switch( event->type )
+    {
+        case Expose:
+        case NoExpose:
+            break;
+        default:
+            data->ignore = true;
+            break;
+    }
+    return False;
+}
+
+} // namespace
+
 long X11SalFrame::HandleMouseEvent( XEvent *pEvent )
 {
     SalMouseEvent       aMouseEvt = {0, 0, 0, 0, 0};
@@ -2936,13 +2981,23 @@ long X11SalFrame::HandleMouseEvent( XEvent *pEvent )
                     nLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
             }
 
+            // Compress consecutive wheel events (way too fine scrolling may cause lags if one scrolling steps takes long).
+            CompressWheelEventsData data;
+            data.firstEvent = pEvent;
+            data.count = 1;
+            XEvent dummy;
+            do
+            {
+                data.ignore = false;
+            } while( XCheckIfEvent( pEvent->xany.display, &dummy, compressWheelEvents, reinterpret_cast< XPointer >( &data )));
+
             SalWheelMouseEvent  aWheelEvt;
             aWheelEvt.mnTime        = pEvent->xbutton.time;
             aWheelEvt.mnX           = pEvent->xbutton.x;
             aWheelEvt.mnY           = pEvent->xbutton.y;
-            aWheelEvt.mnDelta       = bIncrement ? 120 : -120;
+            aWheelEvt.mnDelta       = ( bIncrement ? 120 : -120 ) * data.count;
             aWheelEvt.mnNotchDelta  = bIncrement ? 1 : -1;
-            aWheelEvt.mnScrollLines = nLines;
+            aWheelEvt.mnScrollLines = nLines * data.count;
             aWheelEvt.mnCode        = sal_GetCode( pEvent->xbutton.state );
             aWheelEvt.mbHorz        = bHoriz;
 
commit 06d731428ef6cf93c7333e8228bfb6088853b52f
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Sat Jan 31 17:40:48 2015 +0100

    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

diff --git a/include/vcl/timer.hxx b/include/vcl/timer.hxx
index 8da2381..d93e7c0 100644
--- a/include/vcl/timer.hxx
+++ b/include/vcl/timer.hxx
@@ -61,8 +61,11 @@ public:
 
     Timer&          operator=( const Timer& rTimer );
 
+    /// @internal
     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.
     static void ProcessAllIdleHandlers();
diff --git a/vcl/headless/svpinst.cxx b/vcl/headless/svpinst.cxx
index 1325e3f..7523dfd 100644
--- a/vcl/headless/svpinst.cxx
+++ b/vcl/headless/svpinst.cxx
@@ -186,7 +186,10 @@ bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
                 // notify
                 ImplSVData* pSVData = ImplGetSVData();
                 if( pSVData->mpSalTimer )
-                    pSVData->mpSalTimer->CallCallback();
+                {
+                    bool idle = true; // TODO
+                    pSVData->mpSalTimer->CallCallback( idle );
+                }
             }
         }
     }
diff --git a/vcl/inc/saltimer.hxx b/vcl/inc/saltimer.hxx
index 3bdfbc3..1e1a941 100644
--- a/vcl/inc/saltimer.hxx
+++ b/vcl/inc/saltimer.hxx
@@ -47,10 +47,10 @@ public:
         m_pProc = pProc;
     }
 
-    void            CallCallback()
+    void            CallCallback( bool idle )
     {
         if( m_pProc )
-            m_pProc();
+            m_pProc( idle );
     }
 };
 
diff --git a/vcl/inc/salwtype.hxx b/vcl/inc/salwtype.hxx
index 8055813..7a14df0 100644
--- a/vcl/inc/salwtype.hxx
+++ b/vcl/inc/salwtype.hxx
@@ -281,7 +281,7 @@ struct SalInputContext
     sal_uLong              mnOptions;
 };
 
-typedef void (*SALTIMERPROC)();
+typedef void (*SALTIMERPROC)( bool idle );
 
 #endif // INCLUDED_VCL_INC_SALWTYPE_HXX
 
diff --git a/vcl/inc/unx/gtk/gtkdata.hxx b/vcl/inc/unx/gtk/gtkdata.hxx
index 9c2c76e..e270204 100644
--- a/vcl/inc/unx/gtk/gtkdata.hxx
+++ b/vcl/inc/unx/gtk/gtkdata.hxx
@@ -99,6 +99,7 @@ class GtkData : public SalGenericData
     GSource *m_pUserEvent;
     oslMutex m_aDispatchMutex;
     oslCondition m_aDispatchCondition;
+    bool blockIdleTimeout;
 
 public:
     GtkData( SalInstance *pInstance );
@@ -120,6 +121,7 @@ public:
     virtual bool ErrorTrapPop( bool bIgnoreError ) SAL_OVERRIDE;
 
     inline GtkSalDisplay *GetGtkDisplay() const;
+    bool BlockIdleTimeout() const { return blockIdleTimeout; }
 };
 
 class GtkSalFrame;
diff --git a/vcl/inc/unx/saldata.hxx b/vcl/inc/unx/saldata.hxx
index 2c58231..0dba4c9 100644
--- a/vcl/inc/unx/saldata.hxx
+++ b/vcl/inc/unx/saldata.hxx
@@ -77,7 +77,7 @@ public:
 
     void                    StartTimer( sal_uLong nMS );
     inline  void            StopTimer();
-    void                    Timeout() const;
+    void                    Timeout( bool idle ) const;
 
     // X errors
     virtual void            ErrorTrapPush() SAL_OVERRIDE;
diff --git a/vcl/inc/unx/saldisp.hxx b/vcl/inc/unx/saldisp.hxx
index 63b8bae..aa63565 100644
--- a/vcl/inc/unx/saldisp.hxx
+++ b/vcl/inc/unx/saldisp.hxx
@@ -157,6 +157,7 @@ protected:
     timeval         m_aTimeout;
     sal_uLong       m_nTimeoutMS;
     int             m_pTimeoutFDS[2];
+    bool            blockIdleTimeout;
 
     int             nFDs_;
     fd_set          aReadFDS_;
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index a531dbd..91bfa14 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -646,7 +646,8 @@ void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
             {
                 // this cause crashes on MacOSX 10.4
                 // [AquaSalTimer::pRunningTimer fire];
-                ImplGetSVData()->mpSalTimer->CallCallback();
+                bool idle = true; // TODO
+                ImplGetSVData()->mpSalTimer->CallCallback( idle );
             }
         }
 
diff --git a/vcl/osx/salnstimer.mm b/vcl/osx/salnstimer.mm
index bad2f4e..fe0e907 100644
--- a/vcl/osx/salnstimer.mm
+++ b/vcl/osx/salnstimer.mm
@@ -35,7 +35,8 @@
         if( pSVData->mpSalTimer )
         {
             YIELD_GUARD;
-            pSVData->mpSalTimer->CallCallback();
+            bool idle = true; // TODO
+            pSVData->mpSalTimer->CallCallback( idle );
             
             // NSTimer does not end nextEventMatchingMask of NSApplication
             // so we need to wakeup a waiting Yield to inform it something happened
diff --git a/vcl/osx/saltimer.cxx b/vcl/osx/saltimer.cxx
index 17c4d80..6c83af7 100644
--- a/vcl/osx/saltimer.cxx
+++ b/vcl/osx/saltimer.cxx
@@ -91,7 +91,8 @@ void AquaSalTimer::handleStartTimerEvent( NSEvent* pEvent )
         {
             YIELD_GUARD;
             // timer already elapsed since event posted
-            pSVData->mpSalTimer->CallCallback();
+            bool idle = true; // TODO
+            pSVData->mpSalTimer->CallCallback( idle );
         }
         ImplSalStartTimer( sal_uLong( [pEvent data1] ) );
     }
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index 19890e7..e393978 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -344,7 +344,7 @@ inline void ImplYield( bool i_bWait, bool i_bAllEvents )
     // run timers that have timed out
     if ( !pSVData->mbNoCallTimer )
         while ( pSVData->mbNotAllTimerCalled )
-            Timer::ImplTimerCallbackProc();
+            Timer::ImplTimerCallbackProc( !i_bWait );
 
     pSVData->maAppData.mnDispatchLevel++;
     // 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 )
     {
         do
         {
-            Timer::ImplTimerCallbackProc();
+            Timer::ImplTimerCallbackProc( !i_bWait );
         }
         while( pSVData->mbNotAllTimerCalled );
     }
diff --git a/vcl/source/app/timer.cxx b/vcl/source/app/timer.cxx
index 529c8b5..5742191 100644
--- a/vcl/source/app/timer.cxx
+++ b/vcl/source/app/timer.cxx
@@ -129,7 +129,7 @@ static void ImplStartTimer( ImplSVData* pSVData, sal_uLong nMS )
     }
 }
 
-void Timer::ImplTimerCallbackProc()
+void Timer::ImplTimerCallbackProc( bool idle )
 {
     ImplSVData*     pSVData = ImplGetSVData();
     ImplTimerData*  pTimerData;
@@ -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
         // call the handler as soon as the time is up.
         if ( (pTimerData->mnTimerUpdate < pSVData->mnTimerUpdate) &&
-             !pTimerData->mbDelete && !pTimerData->mbInTimeout )
+             !pTimerData->mbDelete && !pTimerData->mbInTimeout && (!pTimerData->mpTimer->mbIdle || idle) )
         {
             // time has expired
             if ( pTimerData->GetDeadline() <= nTime )
diff --git a/vcl/unx/generic/app/saldata.cxx b/vcl/unx/generic/app/saldata.cxx
index 317c16e..2a90eb7 100644
--- a/vcl/unx/generic/app/saldata.cxx
+++ b/vcl/unx/generic/app/saldata.cxx
@@ -336,6 +336,7 @@ void X11SalData::PopXErrorLevel()
 }
 
 SalXLib::SalXLib()
+    : blockIdleTimeout( false )
 {
     m_aTimeout.tv_sec       = 0;
     m_aTimeout.tv_usec      = 0;
@@ -646,8 +647,20 @@ bool SalXLib::CheckTimeout( bool bExecuteTimers )
                 *  timers are being dispatched.
                 */
                 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
-                GetX11SalData()->Timeout();
+                GetX11SalData()->Timeout( idle );
             }
         }
     }
@@ -656,6 +669,7 @@ bool SalXLib::CheckTimeout( bool bExecuteTimers )
 
 void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
 {
+    blockIdleTimeout = !bWait;
     // check for timeouts here if you want to make screenshots
     static char* p_prioritize_timer = getenv ("SAL_HIGHPRIORITY_REPAINT");
     if (p_prioritize_timer != NULL)
@@ -674,7 +688,10 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
             {
                 pEntry->HandleNextEvent();
                 if( ! bHandleAllCurrentEvents )
+                {
+                    blockIdleTimeout = false;
                     return;
+                }
             }
         }
     }
@@ -746,7 +763,10 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
 
         // someone-else has done the job for us
         if (nFound == 0)
+        {
+            blockIdleTimeout = false;
             return;
+        }
 
         for ( int nFD = 0; nFD < nFDs_; nFD++ )
         {
@@ -772,6 +792,7 @@ void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
             }
         }
     }
+    blockIdleTimeout = false;
 }
 
 void SalXLib::Wakeup()
diff --git a/vcl/unx/generic/app/saltimer.cxx b/vcl/unx/generic/app/saltimer.cxx
index 19b4495..fc55afb 100644
--- a/vcl/unx/generic/app/saltimer.cxx
+++ b/vcl/unx/generic/app/saltimer.cxx
@@ -29,11 +29,11 @@
 #include <unx/saltimer.h>
 #include <unx/salinst.h>
 
-void X11SalData::Timeout() const
+void X11SalData::Timeout( bool idle ) const
 {
     ImplSVData* pSVData = ImplGetSVData();
     if( pSVData->mpSalTimer )
-        pSVData->mpSalTimer->CallCallback();
+        pSVData->mpSalTimer->CallCallback( idle );
 }
 
 void SalXLib::StopTimer()
diff --git a/vcl/unx/gtk/app/gtkdata.cxx b/vcl/unx/gtk/app/gtkdata.cxx
index 8e20f62..cdbfd0a 100644
--- a/vcl/unx/gtk/app/gtkdata.cxx
+++ b/vcl/unx/gtk/app/gtkdata.cxx
@@ -502,6 +502,7 @@ GtkData::GtkData( SalInstance *pInstance )
 #else
     : SalGenericData( SAL_DATA_GTK, pInstance )
 #endif
+    , blockIdleTimeout( false )
 {
     m_pUserEvent = NULL;
     m_aDispatchMutex = osl_createMutex();
@@ -551,6 +552,7 @@ void GtkData::Dispose()
 
 void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
 {
+    blockIdleTimeout = !bWait;
     /* #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
      * another thread in there. Having only one yieldin thread actually dispatch
@@ -564,7 +566,10 @@ void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
         if( osl_tryToAcquireMutex( m_aDispatchMutex ) )
             bDispatchThread = true;
         else if( ! bWait )
+        {
+            blockIdleTimeout = false;
             return; // someone else is waiting already, return
+        }
 
         if( bDispatchThread )
         {
@@ -596,6 +601,7 @@ void GtkData::Yield( bool bWait, bool bHandleAllCurrentEvents )
         if( bWasEvent )
             osl_setCondition( m_aDispatchCondition ); // trigger non dispatch thread yields
     }
+    blockIdleTimeout = false;
 }
 
 void GtkData::Init()
@@ -822,7 +828,7 @@ extern "C" {
         if( !pTSource->pInstance )
             return FALSE;
 
-        SalData *pSalData = GetSalData();
+        GtkData *pSalData = static_cast< GtkData* >( GetSalData());
 
         osl::Guard< comphelper::SolarMutex > aGuard( pSalData->m_pInstance->GetYieldMutex() );
 
@@ -830,7 +836,11 @@ extern "C" {
 
         ImplSVData* pSVData = ImplGetSVData();
         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;
     }
diff --git a/vcl/unx/kde4/KDEXLib.cxx b/vcl/unx/kde4/KDEXLib.cxx
index dc357dd..0b15fbe 100644
--- a/vcl/unx/kde4/KDEXLib.cxx
+++ b/vcl/unx/kde4/KDEXLib.cxx
@@ -54,7 +54,7 @@ KDEXLib::KDEXLib() :
     SalXLib(),  m_bStartupDone(false), m_pApplication(0),
     m_pFreeCmdLineArgs(0), m_pAppCmdLineArgs(0), m_nFakeCmdLineArgs( 0 ),
     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.
     // 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 )
 
 void KDEXLib::processYield( bool bWait, bool bHandleAllCurrentEvents )
 {
+    blockIdleTimeout = !bWait;
     QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance( qApp->thread());
     bool wasEvent = false;
     for( int cnt = bHandleAllCurrentEvents ? 100 : 1;
@@ -321,6 +322,7 @@ void KDEXLib::processYield( bool bWait, bool bHandleAllCurrentEvents )
     }
     if( bWait && !wasEvent )
         dispatcher->processEvents( QEventLoop::WaitForMoreEvents );
+    blockIdleTimeout = false;
 }
 
 void KDEXLib::StartTimer( sal_uLong nMS )
@@ -360,7 +362,10 @@ void KDEXLib::timeoutActivated()
         SalKDEDisplay::self()->DispatchInternalEvent();
 
     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
 }
 
diff --git a/vcl/unx/kde4/KDEXLib.hxx b/vcl/unx/kde4/KDEXLib.hxx
index 1f2a2dd..0a1aec3 100644
--- a/vcl/unx/kde4/KDEXLib.hxx
+++ b/vcl/unx/kde4/KDEXLib.hxx
@@ -54,6 +54,7 @@ class KDEXLib : public QObject, public SalXLib
         int m_frameWidth;
         bool m_isGlibEventLoopType;
         bool m_allowKdeDialogs;
+        bool blockIdleTimeout;
 
     private:
         void setupEventLoop();
diff --git a/vcl/win/source/app/saltimer.cxx b/vcl/win/source/app/saltimer.cxx
index 9b545a1..18f666c 100644
--- a/vcl/win/source/app/saltimer.cxx
+++ b/vcl/win/source/app/saltimer.cxx
@@ -147,7 +147,8 @@ void EmitTimerCallback()
     // try this a short time later again.
     if (pSVData->mpSalTimer && ImplSalYieldMutexTryToAcquire())
     {
-        pSVData->mpSalTimer->CallCallback();
+        bool idle = true; // TODO
+        pSVData->mpSalTimer->CallCallback( idle );
         ImplSalYieldMutexRelease();
 
         // Run the timer in the correct time, if we started this


More information about the Libreoffice-commits mailing list