[Libreoffice-commits] core.git: vcl/inc vcl/README.scheduler vcl/win

Jan-Marek Glogowski glogow at fbihome.de
Fri Oct 13 14:49:47 UTC 2017


 vcl/README.scheduler        |    9 +++++++++
 vcl/inc/win/saltimer.h      |   20 ++++++++++++++++++++
 vcl/win/app/salinst.cxx     |   11 +++++++++++
 vcl/win/app/saltimer.cxx    |   34 ++++++++++++++++++++++++++++++++--
 vcl/win/window/salframe.cxx |   11 +++++++++++
 5 files changed, 83 insertions(+), 2 deletions(-)

New commits:
commit abe3af8e9871907aa75b913f2b8094cac651bc6d
Author: Jan-Marek Glogowski <glogow at fbihome.de>
Date:   Thu Oct 12 18:19:12 2017 +0200

    WIN fix redraw during window move and resize
    
    During window move and resize, Windows spawns a nested message
    loop, blocking our direct processing. In this case we switch to
    timer messages for all timeouts.
    
    But if LO is busy with background jobs, the posted 0ms timer
    messages will block any system event processing, halting any
    updates until we're idle again. So for these cases we switch
    to the WM_TIMER based SetTimer timer.
    
    Change-Id: I854f4984d7c75d6829f82cda5cb4479967edce48
    Reviewed-on: https://gerrit.libreoffice.org/43350
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Jan-Marek Glogowski <glogow at fbihome.de>

diff --git a/vcl/README.scheduler b/vcl/README.scheduler
index a052dd420c74..566a88629301 100644
--- a/vcl/README.scheduler
+++ b/vcl/README.scheduler
@@ -187,6 +187,15 @@ the system message wasn't already our timer. As a result we can also skip the
 polling. All this is one more reason to drop the single message processing
 in favour of always processing all pending (system) events.
 
+There is an other special case, we have to handle: window updates during move
+and resize of windows. These system actions run in their own nested message
+loop. So we have to completely switch to timers, even for 0ms. But these
+posted events prevent any event processing, while we're busy. The only viable
+solution seems to be to switch to WM_TIMER based timers, as these generate
+messages with the lowest system priority (but they don't allow 0ms timeouts).
+So processing slows down during resize and move, but we gain working painting,
+even when busy.
+
 An additional workaround is implemented for the delayed queuing of posted
 messages, where PeekMessage in WinSalTimer::Stop() won't be able remove the
 just posted timer callback message. See "General: invalidation of elapsed
diff --git a/vcl/inc/win/saltimer.h b/vcl/inc/win/saltimer.h
index 68973e1cadc3..d762b51b6716 100644
--- a/vcl/inc/win/saltimer.h
+++ b/vcl/inc/win/saltimer.h
@@ -31,13 +31,20 @@ class WinSalTimer final : public SalTimer, protected VersionedEvent
     // for access to ImplHandleElapsedTimer
     friend bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents );
 
+    /**
+     * Identifier for our SetTimer based timer
+     */
+    static constexpr UINT_PTR m_aWmTimerId = 0xdeadbeef;
+
     HANDLE       m_nTimerId;          ///< Windows timer id
     bool         m_bDirectTimeout;    ///< timeout can be processed directly
+    bool         m_bForceRealTimer;   ///< enforce using a real timer for 0ms
 
     void ImplStart( sal_uIntPtr nMS );
     void ImplStop();
     void ImplHandleTimerEvent( WPARAM aWPARAM );
     void ImplHandleElapsedTimer();
+    void ImplHandle_WM_TIMER( WPARAM aWPARAM );
 
 public:
     WinSalTimer();
@@ -48,6 +55,14 @@ public:
 
     inline bool IsDirectTimeout() const;
     inline bool HasTimerElapsed() const;
+
+    /**
+     * Enforces the usage of a real timer instead of the message queue
+     *
+     * Needed for Window resize processing, as this starts a modal event loop.
+     */
+    void SetForceRealTimer( bool bVal );
+    inline bool GetForceRealTimer() const;
 };
 
 inline bool WinSalTimer::IsDirectTimeout() const
@@ -60,6 +75,11 @@ inline bool WinSalTimer::HasTimerElapsed() const
     return m_bDirectTimeout || ExistsValidEvent();
 }
 
+inline bool WinSalTimer::GetForceRealTimer() const
+{
+    return m_bForceRealTimer;
+}
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index ade332297fc6..54598a420040 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -530,6 +530,10 @@ bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
         }
     }
 
+    // we're back in the main loop after resize or move
+    if ( pTimer )
+        pTimer->SetForceRealTimer( false );
+
     return bWasMsg;
 }
 
@@ -665,6 +669,13 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, i
             pTimer->ImplHandleTimerEvent( wParam );
             break;
         }
+        case WM_TIMER:
+        {
+            WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+            assert( pTimer != nullptr );
+            pTimer->ImplHandle_WM_TIMER( wParam );
+            break;
+        }
     }
 
     return nRet;
diff --git a/vcl/win/app/saltimer.cxx b/vcl/win/app/saltimer.cxx
index fe22d53db8c8..9d20c70bdb5a 100644
--- a/vcl/win/app/saltimer.cxx
+++ b/vcl/win/app/saltimer.cxx
@@ -41,12 +41,15 @@ void WinSalTimer::ImplStop()
     const WinSalInstance *pInst = pSalData->mpInstance;
     assert( !pInst || pSalData->mnAppThreadId == GetCurrentThreadId() );
 
+    if ( m_bForceRealTimer && m_bDirectTimeout )
+        KillTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId );
+    m_bDirectTimeout = false;
+
     const HANDLE hTimer = m_nTimerId;
     if ( nullptr == hTimer )
         return;
 
     m_nTimerId = nullptr;
-    m_bDirectTimeout = false;
     DeleteTimerQueueTimer( nullptr, hTimer, INVALID_HANDLE_VALUE );
     // Keep InvalidateEvent after DeleteTimerQueueTimer, because the event id
     // is set in SalTimerProc, which DeleteTimerQueueTimer will finish or cancel.
@@ -73,13 +76,21 @@ void WinSalTimer::ImplStart( sal_uLong nMS )
     if ( !m_bDirectTimeout )
         CreateTimerQueueTimer(&m_nTimerId, nullptr, SalTimerProc, this,
                               nMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
-    // We don't need any wakeup message, as this code can just run in the
+    else if ( m_bForceRealTimer )
+    {
+        // so we don't block the nested message queue in move and resize
+        // with posted 0ms SAL_MSG_TIMER_CALLBACK messages
+        SetTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId,
+                  USER_TIMER_MINIMUM, nullptr );
+    }
+    // we don't need any wakeup message, as this code can just run in the
     // main thread!
 }
 
 WinSalTimer::WinSalTimer()
     : m_nTimerId( nullptr )
     , m_bDirectTimeout( false )
+    , m_bForceRealTimer( false )
 {
 }
 
@@ -156,4 +167,23 @@ void WinSalTimer::ImplHandleTimerEvent( const WPARAM aWPARAM )
     ImplHandleElapsedTimer();
 }
 
+void WinSalTimer::SetForceRealTimer( const bool bVal )
+{
+    if ( m_bForceRealTimer == bVal )
+        return;
+
+    m_bForceRealTimer = bVal;
+
+    // we need a real timer, as m_bDirectTimeout won't be processed
+    if ( bVal && m_bDirectTimeout )
+        Start( 0 );
+}
+
+void WinSalTimer::ImplHandle_WM_TIMER( const WPARAM aWPARAM )
+{
+    assert( m_aWmTimerId == aWPARAM );
+    if ( m_aWmTimerId == aWPARAM && m_bDirectTimeout && m_bForceRealTimer )
+        ImplHandleElapsedTimer();
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx
index 622fa7bba2b2..dcbfda8163a8 100644
--- a/vcl/win/window/salframe.cxx
+++ b/vcl/win/window/salframe.cxx
@@ -3977,6 +3977,10 @@ static void ImplHandleSizeMsg( HWND hWnd, WPARAM wParam, LPARAM lParam )
             ImplSaveFrameState( pFrame );
             // Call Hdl
             ImplCallSizeHdl( hWnd );
+
+            WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+            if ( pTimer )
+                pTimer->SetForceRealTimer( true );
         }
     }
 }
@@ -4782,6 +4786,13 @@ static bool ImplHandleSysCommand( HWND hWnd, WPARAM wParam, LPARAM lParam )
         }
     }
 
+    if ( nCommand == SC_MOVE )
+    {
+        WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+        if ( pTimer )
+            pTimer->SetForceRealTimer( true );
+    }
+
     if ( nCommand == SC_KEYMENU )
     {
         // do not process SC_KEYMENU if we have a native menu


More information about the Libreoffice-commits mailing list