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 <plubius@neooffice.org>
diff --git a/vcl/inc/osx/salframeview.h b/vcl/inc/osx/salframeview.h
index 7ec995b..6242f3d 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 1e6fce7..8811fa3 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 3651eb3..06d0aeb9 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 4833af8..b14d87e 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 48289e2..f2ba3b5 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 4c33c32..8af7de2 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 1f8f3034..7afdfd0 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)