summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorPatrick Luby <plubius@neooffice.org>2023-01-08 14:41:24 -0500
committerPatrick Luby <plubius@neooffice.org>2023-01-13 14:10:00 +0000
commitfed429e4f6f437997aa6a88e2d071f58aa00ee34 (patch)
tree0723aaf6f5e4698fbab4ce4bd368ad00e997c070 /vcl
parent1a79594a27f41ad369e7c387c51e00afb1352872 (diff)
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>
Diffstat (limited to 'vcl')
-rw-r--r--vcl/inc/osx/salframeview.h6
-rw-r--r--vcl/inc/osx/salinst.h1
-rw-r--r--vcl/inc/svdata.hxx1
-rw-r--r--vcl/osx/salframeview.mm77
-rw-r--r--vcl/osx/salinst.cxx14
-rw-r--r--vcl/osx/saltimer.cxx4
-rw-r--r--vcl/source/app/scheduler.cxx7
7 files changed, 92 insertions, 18 deletions
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)