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)