vcl/inc/osx/salframeview.h   |    6 +++
 vcl/inc/osx/salinst.h        |    1 
 vcl/inc/svdata.hxx           |    1 
 vcl/osx/salframeview.mm      |   77 +++++++++++++++++++++++++++++++++++--------
 vcl/osx/salinst.cxx          |   14 ++++++-
 vcl/osx/saltimer.cxx         |    4 +-
 vcl/source/app/scheduler.cxx |    7 +++
 7 files changed, 92 insertions(+), 18 deletions(-)

New commits:
commit fed429e4f6f437997aa6a88e2d071f58aa00ee34
Author:     Patrick Luby <plub...@neooffice.org>
AuthorDate: Sun Jan 8 14:41:24 2023 -0500
Commit:     Patrick Luby <plub...@neooffice.org>
CommitDate: Fri Jan 13 14:10:00 2023 +0000

    Related: tdf#152703 Eliminate potential blocking during live resize
    
    Some events and timers call Application::Reschedule() or
    Application::Yield() so don't block and wait for events when a
    window is in live resize.
    
    Also, only native events and timers need to be dispatched to redraw
    the window so skip dispatching user events when a window is in
    live resize.
    
    Lastly, only higher priority tasks need to be fired to redraw the
    window so skip firing potentially long-running tasks, such as the
    Writer idle layout timer, when a window is in live resize.
    
    Change-Id: I5d449caa432399e836b8e59781e5cc53af718873
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145211
    Tested-by: Jenkins
    Reviewed-by: Patrick Luby <plub...@neooffice.org>

diff --git a/vcl/inc/osx/salframeview.h b/vcl/inc/osx/salframeview.h
index 7ec995b26c18..6242f3d4146a 100644
--- a/vcl/inc/osx/salframeview.h
+++ b/vcl/inc/osx/salframeview.h
@@ -29,8 +29,12 @@ enum class SalEvent;
     AquaSalFrame*       mpFrame;
     id mDraggingDestinationHandler;
     BOOL                mbInLiveResize;
+    BOOL                mbInWindowDidResize;
+    NSTimer*            mpLiveResizeTimer;
 }
 -(id)initWithSalFrame: (AquaSalFrame*)pFrame;
+-(void)clearLiveResizeTimer;
+-(void)dealloc;
 -(BOOL)canBecomeKeyWindow;
 -(void)displayIfNeeded;
 -(void)windowDidBecomeKey: (NSNotification*)pNotification;
@@ -63,6 +67,8 @@ enum class SalEvent;
 
 -(void)endExtTextInput;
 -(void)endExtTextInput:(EndExtTextInputFlags)nFlags;
+
+-(void)windowDidResizeWithTimer:(NSTimer *)pTimer;
 @end
 
 @interface SalFrameView : AquaA11yWrapper <NSTextInputClient>
diff --git a/vcl/inc/osx/salinst.h b/vcl/inc/osx/salinst.h
index 1e6fce7092fd..8811fa3c9c72 100644
--- a/vcl/inc/osx/salinst.h
+++ b/vcl/inc/osx/salinst.h
@@ -89,7 +89,6 @@ public:
     int                                     mnActivePrintJobs;
     osl::Mutex                              maUserEventListMutex;
     osl::Condition                          maWaitingYieldCond;
-    bool                                    mbIsLiveResize;
     bool                                    mbNoYieldLock;
     bool                                    mbTimerProcessed;
 
diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx
index 3651eb3bce61..06d0aeb9b9af 100644
--- a/vcl/inc/svdata.hxx
+++ b/vcl/inc/svdata.hxx
@@ -269,6 +269,7 @@ struct ImplSVWinData
     StartAutoScrollFlags    mnAutoScrollFlags = StartAutoScrollFlags::NONE; // 
auto scroll flags
     bool                    mbNoDeactivate = false;         // true: do not 
execute Deactivate
     bool                    mbNoSaveFocus = false;          // true: menus 
must not save/restore focus
+    bool                    mbIsLiveResize = false;         // true: skip 
waiting for events and low priority timers
 };
 
 typedef std::vector< std::pair< OUString, FieldUnit > > FieldUnitStringList;
diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
index 4833af8fda9e..b14d87eb7a37 100644
--- a/vcl/osx/salframeview.mm
+++ b/vcl/osx/salframeview.mm
@@ -170,6 +170,8 @@ static AquaSalFrame* getMouseContainerFrame()
 {
     mDraggingDestinationHandler = nil;
     mbInLiveResize = NO;
+    mbInWindowDidResize = NO;
+    mpLiveResizeTimer = nil;
     mpFrame = pFrame;
     NSRect aRect = { { static_cast<CGFloat>(pFrame->maGeometry.x()), 
static_cast<CGFloat>(pFrame->maGeometry.y()) },
                      { static_cast<CGFloat>(pFrame->maGeometry.width()), 
static_cast<CGFloat>(pFrame->maGeometry.height()) } };
@@ -210,6 +212,22 @@ static AquaSalFrame* getMouseContainerFrame()
     return static_cast<SalFrameWindow *>(pNSWindow);
 }
 
+-(void)clearLiveResizeTimer
+{
+    if ( mpLiveResizeTimer )
+    {
+        [mpLiveResizeTimer invalidate];
+        [mpLiveResizeTimer release];
+        mpLiveResizeTimer = nil;
+    }
+}
+
+-(void)dealloc
+{
+    [self clearLiveResizeTimer];
+    [super dealloc];
+}
+
 -(AquaSalFrame*)getSalFrame
 {
     return mpFrame;
@@ -318,12 +336,29 @@ static AquaSalFrame* getMouseContainerFrame()
     (void)pNotification;
     SolarMutexGuard aGuard;
 
+    if ( mbInWindowDidResize )
+        return;
+
+    mbInWindowDidResize = YES;
+
     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
     {
         mpFrame->UpdateFrameGeometry();
         mpFrame->CallCallback( SalEvent::Resize, nullptr );
 
         bool bInLiveResize = [self inLiveResize];
+        ImplSVData* pSVData = ImplGetSVData();
+        assert( pSVData );
+        if ( pSVData )
+        {
+            const bool bWasLiveResize = pSVData->mpWinData->mbIsLiveResize;
+            if ( bWasLiveResize != bInLiveResize )
+            {
+                pSVData->mpWinData->mbIsLiveResize = bInLiveResize;
+                Scheduler::Wakeup();
+            }
+        }
+
         if ( bInLiveResize || mbInLiveResize )
         {
             mbInLiveResize = bInLiveResize;
@@ -370,21 +405,31 @@ static AquaSalFrame* getMouseContainerFrame()
             {
                 // tdf#152703 Force repaint after live resizing ends
                 // Repost this notification so that this selector will be 
called
-                // at least once after live resizing ends. Pass nil for
-                // withObject: since it is unused and makes it easier to cancel
-                // all pending selector execution when live resizing ends.
-                [self performSelector:@selector(windowDidResize:) 
withObject:nil afterDelay:0.1f];
-            }
-            else
-            {
-                [NSObject cancelPreviousPerformRequestsWithTarget:self 
selector:@selector(windowDidResize:) object:nil];
+                // at least once after live resizing ends
+                if ( !mpLiveResizeTimer )
+                {
+                    mpLiveResizeTimer = [NSTimer 
scheduledTimerWithTimeInterval:0.1f target:self 
selector:@selector(windowDidResizeWithTimer:) userInfo:pNotification 
repeats:YES];
+                    if ( mpLiveResizeTimer )
+                    {
+                        [mpLiveResizeTimer retain];
+
+                        // The timer won't fire without a call to
+                        // Application::Reschedule() unless we copy the fix for
+                        // #i84055# from vcl/osx/saltimer.cxx and add the timer
+                        // to the NSEventTrackingRunLoopMode run loop mode
+                        [[NSRunLoop currentRunLoop] addTimer:mpLiveResizeTimer 
forMode:NSEventTrackingRunLoopMode];
+                    }
+                }
             }
         }
         else
         {
+            [self clearLiveResizeTimer];
             mpFrame->SendPaintEvent();
         }
     }
+
+    mbInWindowDidResize = NO;
 }
 
 -(void)windowDidMiniaturize: (NSNotification*)pNotification
@@ -523,6 +568,12 @@ static AquaSalFrame* getMouseContainerFrame()
         [pView endExtTextInput:nFlags];
 }
 
+-(void)windowDidResizeWithTimer:(NSTimer *)pTimer
+{
+    if ( pTimer )
+        [self windowDidResize:[pTimer userInfo]];
+}
+
 @end
 
 @implementation SalFrameView
@@ -602,9 +653,9 @@ static AquaSalFrame* getMouseContainerFrame()
 
 -(void)drawRect: (NSRect)aRect
 {
-    AquaSalInstance *pInstance = GetSalData()->mpInstance;
-    assert(pInstance);
-    if (!pInstance)
+    ImplSVData* pSVData = ImplGetSVData();
+    assert( pSVData );
+    if ( !pSVData )
         return;
 
     SolarMutexGuard aGuard;
@@ -612,10 +663,10 @@ static AquaSalFrame* getMouseContainerFrame()
         return;
 
     const bool bIsLiveResize = [self inLiveResize];
-    const bool bWasLiveResize = pInstance->mbIsLiveResize;
+    const bool bWasLiveResize = pSVData->mpWinData->mbIsLiveResize;
     if (bWasLiveResize != bIsLiveResize)
     {
-        pInstance->mbIsLiveResize = bIsLiveResize;
+        pSVData->mpWinData->mbIsLiveResize = bIsLiveResize;
         Scheduler::Wakeup();
     }
 
diff --git a/vcl/osx/salinst.cxx b/vcl/osx/salinst.cxx
index 48289e2a0165..f2ba3b59fb25 100644
--- a/vcl/osx/salinst.cxx
+++ b/vcl/osx/salinst.cxx
@@ -368,7 +368,6 @@ VCLPLUG_OSX_PUBLIC SalInstance* create_SalInstance()
 AquaSalInstance::AquaSalInstance()
     : SalInstance(std::make_unique<SalYieldMutex>())
     , mnActivePrintJobs( 0 )
-    , mbIsLiveResize( false )
     , mbNoYieldLock( false )
     , mbTimerProcessed( false )
 {
@@ -556,6 +555,13 @@ static bool isWakeupEvent( NSEvent *pEvent )
 
 bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
 {
+    // Related: tdf#152703 Eliminate potential blocking during live resize
+    // Some events and timers call Application::Reschedule() or
+    // Application::Yield() so don't block and wait for events when a
+    // window is in live resize
+    if ( ImplGetSVData()->mpWinData->mbIsLiveResize )
+        bWait = false;
+
     // ensure that the per thread autorelease pool is top level and
     // will therefore not be destroyed by cocoa implicitly
     SalData::ensureThreadAutoreleasePool();
@@ -565,7 +571,11 @@ bool AquaSalInstance::DoYield(bool bWait, bool 
bHandleAllCurrentEvents)
     ReleasePoolHolder aReleasePool;
 
     // first, process current user events
-    bool bHadEvent = DispatchUserEvents( bHandleAllCurrentEvents );
+    // Related: tdf#152703 Eliminate potential blocking during live resize
+    // Only native events and timers need to be dispatched to redraw
+    // the window so skip dispatching user events when a window is in
+    // live resize
+    bool bHadEvent = ( !ImplGetSVData()->mpWinData->mbIsLiveResize && 
DispatchUserEvents( bHandleAllCurrentEvents ) );
     if ( !bHandleAllCurrentEvents && bHadEvent )
         return true;
 
diff --git a/vcl/osx/saltimer.cxx b/vcl/osx/saltimer.cxx
index 4c33c321d729..8af7de217678 100644
--- a/vcl/osx/saltimer.cxx
+++ b/vcl/osx/saltimer.cxx
@@ -83,7 +83,7 @@ void AquaSalTimer::Start( sal_uInt64 nMS )
         return;
     }
 
-    m_bDirectTimeout = (0 == nMS) && !pSalData->mpInstance->mbIsLiveResize;
+    m_bDirectTimeout = (0 == nMS) && 
!ImplGetSVData()->mpWinData->mbIsLiveResize;
     if ( m_bDirectTimeout )
         Stop();
     else
@@ -142,7 +142,7 @@ void AquaSalTimer::callTimerCallback()
 
 void AquaSalTimer::handleTimerElapsed()
 {
-    if ( m_bDirectTimeout || GetSalData()->mpInstance->mbIsLiveResize )
+    if ( m_bDirectTimeout || ImplGetSVData()->mpWinData->mbIsLiveResize )
     {
         // Stop the timer, as it is just invalidated after the firing function
         Stop();
diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx
index 1f8f3034bc58..7afdfd0846e6 100644
--- a/vcl/source/app/scheduler.cxx
+++ b/vcl/source/app/scheduler.cxx
@@ -359,6 +359,13 @@ void Scheduler::CallbackTaskScheduling()
 
     for (; nTaskPriority < PRIO_COUNT; ++nTaskPriority)
     {
+        // Related: tdf#152703 Eliminate potential blocking during live resize
+        // Only higher priority tasks need to be fired to redraw the window
+        // so skip firing potentially long-running tasks, such as the Writer
+        // idle layout timer, when a window is in live resize
+        if ( ImplGetSVData()->mpWinData->mbIsLiveResize && nTaskPriority == 
static_cast<int>(TaskPriority::LOWEST) )
+            continue;
+
         pSchedulerData = rSchedCtx.mpFirstSchedulerData[nTaskPriority];
         pPrevSchedulerData = nullptr;
         while (pSchedulerData)

Reply via email to