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)