You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openoffice.apache.org by ji...@apache.org on 2026/10/22 12:16:15 UTC
(openoffice) branch AOO50X updated (1cf2fd7641 -> 35e60117af)
This is an automated email from the ASF dual-hosted git repository.
jimjag pushed a change to branch AOO50X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
from 1cf2fd7641 junk temp AOO5 images
new 33d22be1ab macOS: run the AppKit menu and window calls from UNO threads on AOO's main thread
new fb856822bb unopkg: treat end of input at the license prompt as a "no"
new 35e60117af Skip the first-start wizard in headless mode
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
main/desktop/source/app/app.cxx | 4 +-
main/desktop/source/deployment/inc/dp_misc.h | 3 +-
main/desktop/source/deployment/misc/dp_misc.cxx | 13 +-
.../desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx | 9 +-
main/vcl/aqua/source/app/salinst.cxx | 225 +++++++++++++++++++--
main/vcl/aqua/source/window/salframe.cxx | 68 +++++++
main/vcl/aqua/source/window/salmenu.cxx | 74 ++++++-
main/vcl/inc/aqua/salinst.h | 46 +++++
8 files changed, 413 insertions(+), 29 deletions(-)
(openoffice) 02/03: unopkg: treat end of input at the license prompt as a "no"
Posted by ji...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jimjag pushed a commit to branch AOO50X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit fb856822bb3ed7d2b9c47013cb7b6df49fb56560
Author: Jim Jagielski <ji...@gmail.com>
AuthorDate: Thu Oct 22 08:09:49 2026 -0400
unopkg: treat end of input at the license prompt as a "no"
With stdin at EOF it re-prompted forever at full CPU.
(cherry picked from commit f8a0ffbbd0d7df0e0952e0568544022aac776eee)
---
main/desktop/source/deployment/inc/dp_misc.h | 3 ++-
main/desktop/source/deployment/misc/dp_misc.cxx | 13 ++++++++-----
main/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx | 9 ++++++++-
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/main/desktop/source/deployment/inc/dp_misc.h b/main/desktop/source/deployment/inc/dp_misc.h
index 1627363183..e50425be5a 100644
--- a/main/desktop/source/deployment/inc/dp_misc.h
+++ b/main/desktop/source/deployment/inc/dp_misc.h
@@ -150,9 +150,10 @@ void writeConsoleError(::rtl::OString const & sText);
On Linux/Unix/etc. it uses fgets to read char values and converts them to OUString
using osl_getThreadTextEncoding as target encoding. The returned string has a maximum
size of 1024 and does NOT include leading and trailing white space(applied OUString::trim())
+ Returns false at end of input or on a read error.
*/
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC
-::rtl::OUString readConsole();
+bool readConsole( ::rtl::OUString & rLine );
/** print the text to the console in a debug build.
The argument is forwarded to writeConsole. The function does not add new line.
diff --git a/main/desktop/source/deployment/misc/dp_misc.cxx b/main/desktop/source/deployment/misc/dp_misc.cxx
index a03c692323..921a46c34c 100644
--- a/main/desktop/source/deployment/misc/dp_misc.cxx
+++ b/main/desktop/source/deployment/misc/dp_misc.cxx
@@ -542,17 +542,18 @@ void writeConsoleError(::rtl::OString const & sText)
-OUString readConsole()
+bool readConsole( OUString & rLine )
{
#ifdef WNT
sal_Unicode aBuffer[1024];
DWORD dwRead = 0;
//unopkg.com feeds unopkg.exe with wchar_t|s
- if (ReadFile( GetStdHandle(STD_INPUT_HANDLE), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) )
+ if (ReadFile( GetStdHandle(STD_INPUT_HANDLE), &aBuffer, sizeof(aBuffer), &dwRead, NULL ) && dwRead > 0)
{
OSL_ASSERT((dwRead % 2) == 0);
OUString value( aBuffer, dwRead / 2);
- return value.trim();
+ rLine = value.trim();
+ return true;
}
#else
char buf[1024];
@@ -561,10 +562,12 @@ OUString readConsole()
if (fgets(buf, 1024, stdin) != NULL)
{
OUString value = ::rtl::OStringToOUString(::rtl::OString(buf), osl_getThreadTextEncoding());
- return value.trim();
+ rLine = value.trim();
+ return true;
}
#endif
- return OUString();
+ rLine = OUString();
+ return false;
}
void TRACE(::rtl::OUString const & sText)
diff --git a/main/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx b/main/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
index d6b91fb6d5..552ecf2d5a 100644
--- a/main/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
+++ b/main/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
@@ -183,7 +183,14 @@ void CommandEnvironmentImpl::printLicense(
do
{
- OUString sAnswer = dp_misc::readConsole();
+ OUString sAnswer;
+ if (! dp_misc::readConsole( sAnswer ))
+ {
+ // end of input: answer "no" rather than asking forever
+ dp_misc::writeConsole(sNewLine);
+ decline = true;
+ break;
+ }
if (xCollator->compareString(sAnswer, sYES) == 0
|| xCollator->compareString(sAnswer, sY) == 0)
{
(openoffice) 03/03: Skip the first-start wizard in headless mode
Posted by ji...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jimjag pushed a commit to branch AOO50X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 35e60117af8cb100cc66346013600830bb41c9d3
Author: Jim Jagielski <ji...@gmail.com>
AuthorDate: Thu Oct 22 08:14:45 2026 -0400
Skip the first-start wizard in headless mode
Headless mode cancels every dialog, so the wizard ended the office silently on every start.
(cherry picked from commit ba0201429312936c43cd0b8fae745f1e3fb580fd)
---
main/desktop/source/app/app.cxx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/main/desktop/source/app/app.cxx b/main/desktop/source/app/app.cxx
index 4368ea1426..21ddd44770 100644
--- a/main/desktop/source/app/app.cxx
+++ b/main/desktop/source/app/app.cxx
@@ -2007,8 +2007,8 @@ void Desktop::Main()
// check whether the shutdown is caused by restart
pExecGlobals->bRestartRequested = ( xRestartManager.is() && xRestartManager->isRestartRequested( sal_True ) );
- // First Start Wizard allowed ?
- if ( ! pCmdLineArgs->IsNoFirstStartWizard() && !pExecGlobals->bRestartRequested )
+ // First Start Wizard allowed ? Headless mode cancels every dialog, so the wizard would end the office.
+ if ( ! pCmdLineArgs->IsNoFirstStartWizard() && ! pCmdLineArgs->IsHeadless() && !pExecGlobals->bRestartRequested )
{
RTL_LOGFILE_CONTEXT_TRACE( aLog, "{ FirstStartWizard" );
(openoffice) 01/03: macOS: run the AppKit menu and window calls from UNO threads on AOO's main thread
Posted by ji...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jimjag pushed a commit to branch AOO50X
in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 33d22be1ab634eb5ed0cbab2d3104438b847f927
Author: Jim Jagielski <ji...@gmail.com>
AuthorDate: Thu Oct 22 08:01:51 2026 -0400
macOS: run the AppKit menu and window calls from UNO threads on AOO's main thread
macOS AppKit throws or blocks on menu and window changes made off the main thread, so
remote UNO clients crashed soffice on load and close, and failed on the first edit.
(cherry picked from commit 2a4643a67e92c4338a0f7b947e78b04714015a0d)
---
main/vcl/aqua/source/app/salinst.cxx | 225 ++++++++++++++++++++++++++++---
main/vcl/aqua/source/window/salframe.cxx | 68 ++++++++++
main/vcl/aqua/source/window/salmenu.cxx | 74 +++++++++-
main/vcl/inc/aqua/salinst.h | 46 +++++++
4 files changed, 393 insertions(+), 20 deletions(-)
diff --git a/main/vcl/aqua/source/app/salinst.cxx b/main/vcl/aqua/source/app/salinst.cxx
index 0cc37adeb7..1d2288e3f3 100644
--- a/main/vcl/aqua/source/app/salinst.cxx
+++ b/main/vcl/aqua/source/app/salinst.cxx
@@ -25,6 +25,7 @@
#include "precompiled_vcl.hxx"
#include <stdio.h>
+#include <utility>
#include "tools/fsys.hxx"
#include "tools/getprocessworkingdir.hxx"
@@ -339,21 +340,159 @@ void DeInitSalMain()
// =======================================================================
+static void postYieldWakeupEvent()
+{
+ if( ! NSApp )
+ return;
+ SalData::ensureThreadAutoreleasePool();
+ NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
+ location: NSZeroPoint
+ modifierFlags: 0
+ timestamp: 0
+ windowNumber: 0
+ context: nil
+ subtype: AquaSalInstance::YieldWakeupEvent
+ data1: 0
+ data2: 0 ];
+ if( pEvent )
+ [NSApp postEvent: pEvent atStart: NO];
+}
+
+@interface SalMainThreadRunner : NSObject
+{
+}
+-(void)runPending: (id)pUnused;
+@end
+
+@implementation SalMainThreadRunner
+-(void)runPending: (id)pUnused
+{
+ (void)pUnused;
+ GetSalData()->mpFirstInstance->mpSalYieldMutex->runPendingOnMainThread();
+}
+@end
+
SalYieldMutex::SalYieldMutex()
{
mnCount = 0;
mnThreadId = 0;
+ mnMainThreadId = 0;
+ pthread_mutex_init( &maMainThreadMutex, NULL );
+ pthread_cond_init( &maMainThreadCond, NULL );
+ pthread_cond_init( &maResultCond, NULL );
+ mbWakeUpMain = false;
+ mbMainWaiting = false;
+ mbResultReady = false;
+ mpMainThreadFunc = NULL;
+ mbRunningForOwner = false;
+}
+
+SalYieldMutex::~SalYieldMutex()
+{
+ pthread_cond_destroy( &maResultCond );
+ pthread_cond_destroy( &maMainThreadCond );
+ pthread_mutex_destroy( &maMainThreadMutex );
+}
+
+bool SalYieldMutex::isMainThread() const
+{
+ return mnMainThreadId != 0 && vos::OThread::getCurrentIdentifier() == mnMainThreadId;
}
void SalYieldMutex::acquire()
{
- OMutex::acquire();
+ if( isMainThread() )
+ {
+ if( mbRunningForOwner )
+ return;
+ acquireOnMainThread();
+ }
+ else
+ OMutex::acquire();
mnThreadId = vos::OThread::getCurrentIdentifier();
mnCount++;
}
+void SalYieldMutex::acquireOnMainThread()
+{
+ // while another thread owns the mutex, run what it hands over via runOnMainThread()
+ for(;;)
+ {
+ pthread_mutex_lock( &maMainThreadMutex );
+ if( OMutex::tryToAcquire() )
+ {
+ mbWakeUpMain = false;
+ pthread_mutex_unlock( &maMainThreadMutex );
+ return;
+ }
+ mbMainWaiting = true;
+ while( ! mbWakeUpMain )
+ pthread_cond_wait( &maMainThreadCond, &maMainThreadMutex );
+ mbMainWaiting = false;
+ mbWakeUpMain = false;
+ const boost::function0<void>* pFunc = mpMainThreadFunc;
+ mpMainThreadFunc = NULL;
+ pthread_mutex_unlock( &maMainThreadMutex );
+
+ if( pFunc )
+ runForOwner( pFunc );
+ }
+}
+
+void SalYieldMutex::runPendingOnMainThread()
+{
+ if( ! isMainThread() || mbRunningForOwner )
+ return;
+
+ pthread_mutex_lock( &maMainThreadMutex );
+ const boost::function0<void>* pFunc = mpMainThreadFunc;
+ mpMainThreadFunc = NULL;
+ pthread_mutex_unlock( &maMainThreadMutex );
+
+ if( pFunc )
+ runForOwner( pFunc );
+}
+
+void SalYieldMutex::runForOwner( const boost::function0<void>* pFunc )
+{
+ std::exception_ptr aException;
+ mbRunningForOwner = true;
+ try
+ {
+ (*pFunc)();
+ }
+ catch( ... )
+ {
+ aException = std::current_exception();
+ }
+ mbRunningForOwner = false;
+
+ pthread_mutex_lock( &maMainThreadMutex );
+ maMainThreadException = aException;
+ mbResultReady = true;
+ pthread_cond_signal( &maResultCond );
+ pthread_mutex_unlock( &maMainThreadMutex );
+}
+
void SalYieldMutex::release()
{
+ const bool bMainThread = isMainThread();
+ if( bMainThread && mbRunningForOwner )
+ return;
+
+ // the main thread may be waiting for this release in acquireOnMainThread()
+ if( ! bMainThread && mnCount == 1 && mnThreadId == vos::OThread::getCurrentIdentifier() )
+ {
+ pthread_mutex_lock( &maMainThreadMutex );
+ mnThreadId = 0;
+ mnCount--;
+ OMutex::release();
+ mbWakeUpMain = true;
+ pthread_cond_signal( &maMainThreadCond );
+ pthread_mutex_unlock( &maMainThreadMutex );
+ return;
+ }
+
if ( mnThreadId == vos::OThread::getCurrentIdentifier() )
{
if ( mnCount == 1 )
@@ -363,8 +502,56 @@ void SalYieldMutex::release()
OMutex::release();
}
+void SalYieldMutex::runOnMainThread( const boost::function0<void>& rFunc )
+{
+ if( isMainThread() )
+ {
+ rFunc();
+ return;
+ }
+
+ // the main thread only runs rFunc for the owner of the mutex
+ vos::OGuard aGuard( *this );
+
+ bool bMainWaiting;
+ pthread_mutex_lock( &maMainThreadMutex );
+ mpMainThreadFunc = &rFunc;
+ mbWakeUpMain = true;
+ bMainWaiting = mbMainWaiting;
+ pthread_cond_signal( &maMainThreadCond );
+ pthread_mutex_unlock( &maMainThreadMutex );
+
+ // common modes also reach a main thread inside AppKit's menu tracking or live resize loops
+ if( ! bMainWaiting )
+ {
+ static SalMainThreadRunner* pRunner = [[SalMainThreadRunner alloc] init];
+ [pRunner performSelectorOnMainThread: @selector(runPending:)
+ withObject: nil
+ waitUntilDone: NO
+ modes: [NSArray arrayWithObject: NSRunLoopCommonModes]];
+ }
+
+ std::exception_ptr aException;
+ pthread_mutex_lock( &maMainThreadMutex );
+ while( ! mbResultReady )
+ pthread_cond_wait( &maResultCond, &maMainThreadMutex );
+ mbResultReady = false;
+ std::swap( aException, maMainThreadException );
+ pthread_mutex_unlock( &maMainThreadMutex );
+
+ if( aException )
+ std::rethrow_exception( aException );
+}
+
+bool SalYieldMutex::isRunningForOwner() const
+{
+ return isMainThread() && mbRunningForOwner;
+}
+
sal_Bool SalYieldMutex::tryToAcquire()
{
+ if( isMainThread() && mbRunningForOwner )
+ return sal_True;
if ( OMutex::tryToAcquire() )
{
mnThreadId = vos::OThread::getCurrentIdentifier();
@@ -444,10 +631,11 @@ void DestroySalInstance( SalInstance* pInst )
AquaSalInstance::AquaSalInstance()
{
+ maMainThread = vos::OThread::getCurrentIdentifier();
mpSalYieldMutex = new SalYieldMutex;
+ mpSalYieldMutex->setMainThread( maMainThread );
mpSalYieldMutex->acquire();
::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex );
- maMainThread = vos::OThread::getCurrentIdentifier();
mbWaitingYield = false;
maUserEventListMutex = osl_createMutex();
mnActivePrintJobs = 0;
@@ -471,20 +659,7 @@ void AquaSalInstance::wakeupYield()
{
// wakeup :Yield
if( mbWaitingYield )
- {
- SalData::ensureThreadAutoreleasePool();
- NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
- location: NSZeroPoint
- modifierFlags: 0
- timestamp: 0
- windowNumber: 0
- context: nil
- subtype: AquaSalInstance::YieldWakeupEvent
- data1: 0
- data2: 0 ];
- if( pEvent )
- [NSApp postEvent: pEvent atStart: NO];
- }
+ postYieldWakeupEvent();
}
// -----------------------------------------------------------------------
@@ -546,6 +721,9 @@ bool AquaSalInstance::CheckYieldMutex()
{
bool bRet = true;
+ if( mpSalYieldMutex->isRunningForOwner() )
+ return bRet;
+
SalYieldMutex* pYieldMutex = mpSalYieldMutex;
if ( pYieldMutex->GetThreadId() !=
vos::OThread::getCurrentIdentifier() )
@@ -565,6 +743,16 @@ bool AquaSalInstance::isNSAppThread() const
// -----------------------------------------------------------------------
+bool AquaSalInstance::forwardToMainThread( const boost::function0<void>& rFunc )
+{
+ if( isNSAppThread() )
+ return false;
+ mpSalYieldMutex->runOnMainThread( rFunc );
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
{
switch( [pEvent subtype] )
@@ -820,6 +1008,9 @@ void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
bool AquaSalInstance::AnyInput( sal_uInt16 nType )
{
+ if( ! isNSAppThread() )
+ return callOnMainThread< bool >( boost::bind( &AquaSalInstance::AnyInput, this, nType ) );
+
if( nType & INPUT_APPEVENT )
{
if( ! aAppEventList.empty() )
@@ -883,6 +1074,8 @@ SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameSt
void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
{
+ if( forwardToMainThread( boost::bind( &AquaSalInstance::DestroyFrame, this, pFrame ) ) )
+ return;
delete pFrame;
}
diff --git a/main/vcl/aqua/source/window/salframe.cxx b/main/vcl/aqua/source/window/salframe.cxx
index 8638e68e8b..af99c4c153 100644
--- a/main/vcl/aqua/source/window/salframe.cxx
+++ b/main/vcl/aqua/source/window/salframe.cxx
@@ -301,6 +301,9 @@ sal_Bool AquaSalFrame::PostEvent( void *pData )
// -----------------------------------------------------------------------
void AquaSalFrame::SetTitle(const XubString& rTitle)
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetTitle, this, boost::cref( rTitle ) ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -345,6 +348,9 @@ void AquaSalFrame::SetIcon( sal_uInt16 )
void AquaSalFrame::SetRepresentedURL( const rtl::OUString& i_rDocURL )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetRepresentedURL, this, boost::cref( i_rDocURL ) ) ) )
+ return;
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -418,6 +424,9 @@ void AquaSalFrame::SendPaintEvent( const Rectangle* pRect )
void AquaSalFrame::Show(sal_Bool bVisible, sal_Bool bNoActivate)
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::Show, this, bVisible, bNoActivate ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -487,6 +496,9 @@ void AquaSalFrame::Enable( sal_Bool )
void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetMinClientSize, this, nWidth, nHeight ) ) )
+ return;
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -512,6 +524,9 @@ void AquaSalFrame::SetMinClientSize( long nWidth, long nHeight )
void AquaSalFrame::SetMaxClientSize( long nWidth, long nHeight )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetMaxClientSize, this, nWidth, nHeight ) ) )
+ return;
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -541,6 +556,9 @@ void AquaSalFrame::SetMaxClientSize( long nWidth, long nHeight )
void AquaSalFrame::SetClientSize( long nWidth, long nHeight )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetClientSize, this, nWidth, nHeight ) ) )
+ return;
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -576,6 +594,9 @@ void AquaSalFrame::GetClientSize( long& rWidth, long& rHeight )
void AquaSalFrame::SetWindowState( const SalFrameState* pState )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetWindowState, this, pState ) ) )
+ return;
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -652,6 +673,10 @@ void AquaSalFrame::SetWindowState( const SalFrameState* pState )
sal_Bool AquaSalFrame::GetWindowState( SalFrameState* pState )
{
+ AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if( ! pInst->isNSAppThread() )
+ return pInst->callOnMainThread< sal_Bool >( boost::bind( &AquaSalFrame::GetWindowState, this, pState ) );
+
if ( !mpNSWindow )
return FALSE;
@@ -692,6 +717,9 @@ sal_Bool AquaSalFrame::GetWindowState( SalFrameState* pState )
void AquaSalFrame::SetScreenNumber(unsigned int nScreen)
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetScreenNumber, this, nScreen ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -729,6 +757,9 @@ void AquaSalFrame::SetScreenNumber(unsigned int nScreen)
void AquaSalFrame::ShowFullScreen( sal_Bool bFullScreen, sal_Int32 nDisplay )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::ShowFullScreen, this, bFullScreen, nDisplay ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -842,6 +873,9 @@ public:
void AquaSalFrame::StartPresentation( sal_Bool bStart )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::StartPresentation, this, bStart ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -874,6 +908,9 @@ void AquaSalFrame::SetAlwaysOnTop( sal_Bool )
void AquaSalFrame::ToTop(sal_uInt16 nFlags)
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::ToTop, this, nFlags ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -936,6 +973,9 @@ NSCursor* AquaSalFrame::getCurrentCursor() const
void AquaSalFrame::SetPointer( PointerStyle ePointerStyle )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetPointer, this, ePointerStyle ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -1237,6 +1277,9 @@ void AquaSalFrame::getResolution( sal_Int32& o_rDPIX, sal_Int32& o_rDPIY )
// doesn't make the anything cleaner for now
void AquaSalFrame::UpdateSettings( AllSettings& rSettings )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::UpdateSettings, this, boost::ref( rSettings ) ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -1354,6 +1397,9 @@ void AquaSalFrame::Beep( SoundType eSoundType )
void AquaSalFrame::SetPosSize(long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags)
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetPosSize, this, nX, nY, nWidth, nHeight, nFlags ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -1444,6 +1490,9 @@ void AquaSalFrame::SetPosSize(long nX, long nY, long nWidth, long nHeight, sal_u
void AquaSalFrame::GetWorkArea( Rectangle& rRect )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::GetWorkArea, this, boost::ref( rRect ) ) ) )
+ return;
+
if ( !mpNSWindow )
return;
@@ -1463,6 +1512,10 @@ void AquaSalFrame::GetWorkArea( Rectangle& rRect )
SalPointerState AquaSalFrame::GetPointerState()
{
+ AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if( ! pInst->isNSAppThread() )
+ return pInst->callOnMainThread< SalPointerState >( boost::bind( &AquaSalFrame::GetPointerState, this ) );
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -1573,6 +1626,9 @@ void AquaSalFrame::DrawMenuBar()
void AquaSalFrame::SetMenu( SalMenu* pSalMenu )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetMenu, this, pSalMenu ) ) )
+ return;
+
// #i113170# may not be the main thread if called from UNO API
SalData::ensureThreadAutoreleasePool();
@@ -1585,6 +1641,9 @@ void AquaSalFrame::SetMenu( SalMenu* pSalMenu )
void AquaSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::SetExtendedFrameStyle, this, nStyle ) ) )
+ return;
+
if ( mpNSWindow )
{
// #i113170# may not be the main thread if called from UNO API
@@ -1701,6 +1760,9 @@ void AquaSalFrame::CaptureMouse( sal_Bool bCapture )
void AquaSalFrame::ResetClipRegion()
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::ResetClipRegion, this ) ) )
+ return;
+
if ( !mpNSWindow )
{
return;
@@ -1724,6 +1786,9 @@ void AquaSalFrame::ResetClipRegion()
void AquaSalFrame::BeginSetClipRegion( sal_uLong nRects )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::BeginSetClipRegion, this, nRects ) ) )
+ return;
+
if ( !mpNSWindow )
{
return;
@@ -1763,6 +1828,9 @@ void AquaSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight
void AquaSalFrame::EndSetClipRegion()
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalFrame::EndSetClipRegion, this ) ) )
+ return;
+
if ( !mpNSWindow )
{
return;
diff --git a/main/vcl/aqua/source/window/salmenu.cxx b/main/vcl/aqua/source/window/salmenu.cxx
index 5f66d7e460..a33db1f963 100644
--- a/main/vcl/aqua/source/window/salmenu.cxx
+++ b/main/vcl/aqua/source/window/salmenu.cxx
@@ -98,6 +98,8 @@ static MainMenuSelector* pMainMenuSelector = nil;
static void initAppMenu()
{
static bool bOnce = true;
+ if( bOnce && GetSalData()->mpFirstInstance->forwardToMainThread( &initAppMenu ) )
+ return;
if( bOnce )
{
bOnce = false;
@@ -209,6 +211,9 @@ static void initAppMenu()
SalMenu* AquaSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* pVCLMenu )
{
+ if( ! isNSAppThread() )
+ return callOnMainThread< SalMenu* >( boost::bind( &AquaSalInstance::CreateMenu, this, bMenuBar, pVCLMenu ) );
+
initAppMenu();
AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar );
@@ -219,11 +224,16 @@ SalMenu* AquaSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* pVCLMenu )
void AquaSalInstance::DestroyMenu( SalMenu* pSalMenu )
{
+ if( forwardToMainThread( boost::bind( &AquaSalInstance::DestroyMenu, this, pSalMenu ) ) )
+ return;
delete pSalMenu;
}
SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData )
{
+ if( ! isNSAppThread() )
+ return callOnMainThread< SalMenuItem* >( boost::bind( &AquaSalInstance::CreateMenuItem, this, pItemData ) );
+
if( !pItemData )
return NULL;
@@ -234,6 +244,8 @@ SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData )
void AquaSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
{
+ if( forwardToMainThread( boost::bind( &AquaSalInstance::DestroyMenuItem, this, pSalMenuItem ) ) )
+ return;
delete pSalMenuItem;
}
@@ -341,6 +353,10 @@ sal_Int32 removeUnusedItemsRunner(NSMenu * pMenu)
bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, sal_uLong nFlags)
{
+ AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if( ! pInst->isNSAppThread() )
+ return pInst->callOnMainThread< bool >( boost::bind( &AquaSalMenu::ShowNativePopupMenu, this, pWin, boost::cref( rRect ), nFlags ) );
+
// do not use native popup menu when AQUA_NATIVE_MENUS is set to sal_False
if( ! VisibleMenuBar() ) {
return false;
@@ -423,6 +439,9 @@ void AquaSalMenu::unsetMainMenu()
void AquaSalMenu::setMainMenu()
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::setMainMenu, this ) ) )
+ return;
+
DBG_ASSERT( mbMenuBar, "setMainMenu on non menubar" );
if( mbMenuBar )
{
@@ -446,6 +465,9 @@ void AquaSalMenu::setMainMenu()
void AquaSalMenu::setDefaultMenu()
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( &AquaSalMenu::setDefaultMenu ) )
+ return;
+
NSMenu* pMenu = [NSApp mainMenu];
unsetMainMenu();
@@ -462,6 +484,9 @@ void AquaSalMenu::setDefaultMenu()
void AquaSalMenu::enableMainMenu( bool bEnable )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::enableMainMenu, bEnable ) ) )
+ return;
+
NSMenu* pMainMenu = [NSApp mainMenu];
if( pMainMenu )
{
@@ -477,6 +502,9 @@ void AquaSalMenu::enableMainMenu( bool bEnable )
void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::addFallbackMenuItem, pNewItem ) ) )
+ return;
+
initAppMenu();
std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
@@ -499,6 +527,9 @@ void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem )
void AquaSalMenu::removeFallbackMenuItem( NSMenuItem* pOldItem )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::removeFallbackMenuItem, pOldItem ) ) )
+ return;
+
std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
// find item
@@ -542,6 +573,9 @@ void AquaSalMenu::SetFrame( const SalFrame *pFrame )
void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::InsertItem, this, pSalMenuItem, nPos ) ) )
+ return;
+
AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
pAquaSalMenuItem->mpParentMenu = this;
@@ -568,6 +602,9 @@ void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
void AquaSalMenu::RemoveItem( unsigned nPos )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::RemoveItem, this, nPos ) ) )
+ return;
+
AquaSalMenuItem* pRemoveItem = NULL;
if( nPos == MENU_APPEND || nPos == (maItems.size()-1) )
{
@@ -591,8 +628,11 @@ void AquaSalMenu::RemoveItem( unsigned nPos )
[mpMenu removeItemAtIndex: getItemIndexByPos(nPos)];
}
-void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned /*nPos*/ )
+void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::SetSubMenu, this, pSalMenuItem, pSubMenu, nPos ) ) )
+ return;
+
AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
AquaSalMenu *subAquaSalMenu = static_cast<AquaSalMenu*>(pSubMenu);
@@ -633,6 +673,9 @@ void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsi
void AquaSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::CheckItem, this, nPos, bCheck ) ) )
+ return;
+
if( nPos < maItems.size() )
{
NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
@@ -642,6 +685,9 @@ void AquaSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
void AquaSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::EnableItem, this, nPos, bEnable ) ) )
+ return;
+
if( nPos < maItems.size() )
{
NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
@@ -649,8 +695,11 @@ void AquaSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
}
}
-void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSMI, const Image& rImage )
+void AquaSalMenu::SetItemImage( unsigned nPos, SalMenuItem* pSMI, const Image& rImage )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::SetItemImage, this, nPos, pSMI, boost::cref( rImage ) ) ) )
+ return;
+
AquaSalMenuItem* pSalMenuItem = static_cast<AquaSalMenuItem*>( pSMI );
if( ! pSalMenuItem || ! pSalMenuItem->mpMenuItem )
return;
@@ -662,8 +711,11 @@ void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSMI, const Imag
[pImage release];
}
-void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem* i_pSalMenuItem, const XubString& i_rText )
+void AquaSalMenu::SetItemText( unsigned i_nPos, SalMenuItem* i_pSalMenuItem, const XubString& i_rText )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::SetItemText, this, i_nPos, i_pSalMenuItem, boost::cref( i_rText ) ) ) )
+ return;
+
if (!i_pSalMenuItem)
return;
@@ -699,8 +751,11 @@ void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem* i_pSalMenuItem,
}
}
-void AquaSalMenu::SetAccelerator( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& /*rKeyName*/ )
+void AquaSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& rKeyName )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::SetAccelerator, this, nPos, pSalMenuItem, boost::cref( rKeyCode ), boost::cref( rKeyName ) ) ) )
+ return;
+
sal_uInt16 nModifier;
sal_Unicode nCommandKey = 0;
@@ -824,6 +879,10 @@ void AquaSalMenu::releaseButtonEntry( MenuBarButtonEntry& i_rEntry )
bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem )
{
+ AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if( ! pInst->isNSAppThread() )
+ return pInst->callOnMainThread< bool >( boost::bind( &AquaSalMenu::AddMenuBarButton, this, boost::cref( i_rNewItem ) ) );
+
if( ! mbMenuBar || ! VisibleMenuBar() )
return false;
@@ -854,6 +913,9 @@ bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem )
void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId )
{
+ if( GetSalData()->mpFirstInstance->forwardToMainThread( boost::bind( &AquaSalMenu::RemoveMenuBarButton, this, i_nId ) ) )
+ return;
+
MenuBarButtonEntry* pEntry = findButtonItem( i_nId );
if( pEntry )
{
@@ -868,6 +930,10 @@ void AquaSalMenu::RemoveMenuBarButton( sal_uInt16 i_nId )
Rectangle AquaSalMenu::GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId, SalFrame* i_pReferenceFrame )
{
+ AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
+ if( ! pInst->isNSAppThread() )
+ return pInst->callOnMainThread< Rectangle >( boost::bind( &AquaSalMenu::GetMenuBarButtonRectPixel, this, i_nItemId, i_pReferenceFrame ) );
+
if( GetSalData()->mnSystemVersion < OSX_VER_LEOPARD )
return Rectangle( Point( -1, -1 ), Size( 1, 1 ) );
diff --git a/main/vcl/inc/aqua/salinst.h b/main/vcl/inc/aqua/salinst.h
index 37a4569848..927e737dd4 100644
--- a/main/vcl/inc/aqua/salinst.h
+++ b/main/vcl/inc/aqua/salinst.h
@@ -32,7 +32,13 @@
#include "salinst.hxx"
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/ref.hpp>
+
+#include <exception>
#include <list>
+#include <pthread.h>
class AquaSalFrame;
class ApplicationEvent;
@@ -46,14 +52,39 @@ class SalYieldMutex : public vos::OMutex
{
sal_uLong mnCount;
vos::OThread::TThreadIdentifier mnThreadId;
+ vos::OThread::TThreadIdentifier mnMainThreadId;
+
+ // state for running code on the main thread on behalf of the owner
+ pthread_mutex_t maMainThreadMutex;
+ pthread_cond_t maMainThreadCond;
+ pthread_cond_t maResultCond;
+ bool mbWakeUpMain;
+ bool mbMainWaiting;
+ bool mbResultReady;
+ const boost::function0<void>* mpMainThreadFunc;
+ std::exception_ptr maMainThreadException;
+ bool mbRunningForOwner; // main thread only
+
+ bool isMainThread() const;
+ void acquireOnMainThread();
+ void runForOwner( const boost::function0<void>* pFunc );
public:
SalYieldMutex();
+ ~SalYieldMutex();
virtual void acquire();
virtual void release();
virtual sal_Bool tryToAcquire();
sal_uLong GetAcquireCount() const { return mnCount; }
vos::OThread::TThreadIdentifier GetThreadId() const { return mnThreadId; }
+
+ void setMainThread( vos::OThread::TThreadIdentifier nId ) { mnMainThreadId = nId; }
+ // runs rFunc on the main thread while the calling thread keeps owning the mutex
+ void runOnMainThread( const boost::function0<void>& rFunc );
+ // true while the main thread runs code for the owner; the main thread then counts as owner
+ bool isRunningForOwner() const;
+ // runs a function handed over by runOnMainThread(), if any; main thread only
+ void runPendingOnMainThread();
};
#define YIELD_GUARD vos::OGuard aGuard( GetSalData()->mpFirstInstance->GetYieldMutex() )
@@ -157,6 +188,11 @@ public:
void wakeupYield();
+ template< typename R > static void assignResult( R* pResult, const boost::function0<R>& rFunc )
+ {
+ *pResult = rFunc();
+ }
+
public:
friend class AquaSalFrame;
@@ -165,6 +201,16 @@ public:
bool isNSAppThread() const;
+ // AppKit raises or deadlocks when used off the main thread (e.g. by remote UNO calls):
+ // off the main thread these run rFunc there and return true, on it they return false
+ bool forwardToMainThread( const boost::function0<void>& rFunc );
+ template< typename R > R callOnMainThread( const boost::function0<R>& rFunc )
+ {
+ R aResult = R();
+ mpSalYieldMutex->runOnMainThread( boost::bind( &AquaSalInstance::assignResult<R>, &aResult, boost::cref( rFunc ) ) );
+ return aResult;
+ }
+
void startedPrintJob() { mnActivePrintJobs++; }
void endedPrintJob() { mnActivePrintJobs--; }