| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #include <stdio.h> |
| |
| #include "tools/fsys.hxx" |
| #include "tools/getprocessworkingdir.hxx" |
| #include <tools/solarmutex.hxx> |
| |
| #include "osl/process.h" |
| |
| #include "rtl/ustrbuf.hxx" |
| |
| #include "vcl/svapp.hxx" |
| #include "vcl/window.hxx" |
| #include "vcl/timer.hxx" |
| |
| #include "aqua/saldata.hxx" |
| #include "aqua/salinst.h" |
| #include "aqua/salframe.h" |
| #include "aqua/salobj.h" |
| #include "aqua/salsys.h" |
| #include "aqua/salvd.h" |
| #include "aqua/salbmp.h" |
| #include "aqua/salprn.h" |
| #include "aqua/saltimer.h" |
| #include "aqua/vclnsapp.h" |
| |
| #include "print.h" |
| #include "impbmp.hxx" |
| #include "salimestatus.hxx" |
| |
| #include <comphelper/processfactory.hxx> |
| |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
| #include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> |
| #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> |
| #include <com/sun/star/uno/XComponentContext.hpp> |
| |
| #include "premac.h" |
| #include <Foundation/Foundation.h> |
| #include <ApplicationServices/ApplicationServices.h> |
| #import "apple_remote/RemoteMainController.h" |
| #include "apple_remote/RemoteControl.h" |
| #include "postmac.h" |
| |
| using namespace std; |
| using namespace ::com::sun::star; |
| |
| extern sal_Bool ImplSVMain(); |
| |
| static sal_Bool* gpbInit = 0; |
| static NSMenu* pDockMenu = nil; |
| static bool bNoSVMain = true; |
| static bool bLeftMain = false; |
| // ----------------------------------------------------------------------- |
| |
| class AquaDelayedSettingsChanged : public Timer |
| { |
| bool mbInvalidate; |
| public: |
| AquaDelayedSettingsChanged( bool bInvalidate ) : |
| mbInvalidate( bInvalidate ) |
| { |
| } |
| |
| virtual void Timeout() |
| { |
| SalData* pSalData = GetSalData(); |
| if( ! pSalData->maFrames.empty() ) |
| pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); |
| |
| if( mbInvalidate ) |
| { |
| for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); |
| it != pSalData->maFrames.end(); ++it ) |
| { |
| if( (*it)->mbShown ) |
| (*it)->SendPaintEvent( NULL ); |
| } |
| } |
| Stop(); |
| delete this; |
| } |
| }; |
| |
| void AquaSalInstance::delayedSettingsChanged( bool bInvalidate ) |
| { |
| vos::OGuard aGuard( *mpSalYieldMutex ); |
| AquaDelayedSettingsChanged* pTimer = new AquaDelayedSettingsChanged( bInvalidate ); |
| pTimer->SetTimeout( 50 ); |
| pTimer->Start(); |
| } |
| |
| |
| // the AppEventList must be available before any SalData/SalInst/etc. objects are ready |
| typedef std::list<const ApplicationEvent*> AppEventList; |
| AppEventList AquaSalInstance::aAppEventList; |
| |
| NSMenu* AquaSalInstance::GetDynamicDockMenu() |
| { |
| if( ! pDockMenu && ! bLeftMain ) |
| pDockMenu = [[NSMenu alloc] initWithTitle: @""]; |
| return pDockMenu; |
| } |
| |
| bool AquaSalInstance::isOnCommandLine( const rtl::OUString& rArg ) |
| { |
| sal_uInt32 nArgs = osl_getCommandArgCount(); |
| for( sal_uInt32 i = 0; i < nArgs; i++ ) |
| { |
| rtl::OUString aArg; |
| osl_getCommandArg( i, &aArg.pData ); |
| if( aArg.equals( rArg ) ) |
| return true; |
| } |
| return false; |
| } |
| |
| |
| // initialize the cocoa VCL_NSApplication object |
| // returns an NSAutoreleasePool that must be released when the event loop begins |
| static void initNSApp() |
| { |
| // create our cocoa NSApplication |
| [VCL_NSApplication sharedApplication]; |
| |
| SalData::ensureThreadAutoreleasePool(); |
| |
| // put cocoa into multithreaded mode |
| [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil]; |
| |
| // activate our delegate methods |
| [NSApp setDelegate: NSApp]; |
| |
| [[NSNotificationCenter defaultCenter] addObserver: NSApp |
| selector: @selector(systemColorsChanged:) |
| name: NSSystemColorsDidChangeNotification |
| object: nil ]; |
| [[NSNotificationCenter defaultCenter] addObserver: NSApp |
| selector: @selector(screenParametersChanged:) |
| name: NSApplicationDidChangeScreenParametersNotification |
| object: nil ]; |
| // add observers for some settings changes that affect vcl's settings |
| // scrollbar variant |
| [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp |
| selector: @selector(scrollbarVariantChanged:) |
| name: @"AppleAquaScrollBarVariantChanged" |
| object: nil ]; |
| // scrollbar page behavior ("jump to here" or not) |
| [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp |
| selector: @selector(scrollbarSettingsChanged:) |
| name: @"AppleNoRedisplayAppearancePreferenceChanged" |
| object: nil ]; |
| |
| // get System Version and store the value in GetSalData()->mnSystemVersion |
| SInt32 systemVersion = OSX_VER_LION; // initialize with the minimal requirement |
| const OSErr err = Gestalt( gestaltSystemVersion, &systemVersion); |
| if( err == noErr ) |
| { |
| GetSalData()->mnSystemVersion = systemVersion; |
| #if OSL_DEBUG_LEVEL > 1 |
| fprintf( stderr, "OSX System Version 0x%04x\n", (unsigned int)systemVersion); |
| #endif |
| } |
| else |
| NSLog(@"Unable to obtain system version: %ld", (long)err); |
| |
| GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init]; |
| |
| [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp |
| selector: @selector(applicationWillBecomeActive:) |
| name: @"AppleRemoteWillBecomeActive" |
| object: nil ]; |
| |
| [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp |
| selector: @selector(applicationWillResignActive:) |
| name: @"AppleRemoteWillResignActive" |
| object: nil ]; |
| |
| if( ImplGetSVData()->mbIsTestTool ) |
| [NSApp activateIgnoringOtherApps: YES]; |
| } |
| |
| sal_Bool ImplSVMainHook( sal_Bool * pbInit ) |
| { |
| gpbInit = pbInit; |
| |
| bNoSVMain = false; |
| initNSApp(); |
| |
| rtl::OUString aExeURL, aExe; |
| osl_getExecutableFile( &aExeURL.pData ); |
| osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData ); |
| rtl::OString aByteExe( rtl::OUStringToOString( aExe, osl_getThreadTextEncoding() ) ); |
| |
| #ifdef DEBUG |
| aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" ); |
| const char* pArgv[] = { aByteExe.getStr(), NULL }; |
| NSApplicationMain( 3, pArgv ); |
| #else |
| const char* pArgv[] = { aByteExe.getStr(), NULL }; |
| NSApplicationMain( 1, pArgv ); |
| #endif |
| |
| return TRUE; // indicate that ImplSVMainHook is implemented |
| } |
| |
| // ======================================================================= |
| |
| void SalAbort( const XubString& rErrorText ) |
| { |
| if( !rErrorText.Len() ) |
| fprintf( stderr, "Application Error " ); |
| else |
| fprintf( stderr, "%s ", |
| ByteString( rErrorText, gsl_getSystemTextEncoding() ).GetBuffer() ); |
| abort(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void InitSalData() |
| { |
| SalData *pSalData = new SalData; |
| SetSalData( pSalData ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| const ::rtl::OUString& SalGetDesktopEnvironment() |
| { |
| static OUString aDesktopEnvironment(RTL_CONSTASCII_USTRINGPARAM( "MacOSX" )); |
| return aDesktopEnvironment; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void DeInitSalData() |
| { |
| SalData *pSalData = GetSalData(); |
| if( pSalData->mpStatusItem ) |
| { |
| [pSalData->mpStatusItem release]; |
| pSalData->mpStatusItem = nil; |
| } |
| delete pSalData; |
| SetSalData( NULL ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| extern "C" { |
| #include <crt_externs.h> |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void InitSalMain() |
| { |
| rtl::OUString urlWorkDir; |
| rtl_uString *sysWorkDir = NULL; |
| if (tools::getProcessWorkingDir(&urlWorkDir)) |
| { |
| oslFileError err2 = osl_getSystemPathFromFileURL(urlWorkDir.pData, &sysWorkDir); |
| if (err2 == osl_File_E_None) |
| { |
| ByteString aPath( getenv( "PATH" ) ); |
| ByteString aResPath( getenv( "STAR_RESOURCEPATH" ) ); |
| ByteString aLibPath( getenv( "DYLD_LIBRARY_PATH" ) ); |
| ByteString aCmdPath( OUStringToOString(OUString(sysWorkDir), RTL_TEXTENCODING_UTF8).getStr() ); |
| ByteString aTmpPath; |
| // Get absolute path of command's directory |
| if ( aCmdPath.Len() ) { |
| DirEntry aCmdDirEntry( aCmdPath ); |
| aCmdDirEntry.ToAbs(); |
| aCmdPath = ByteString( aCmdDirEntry.GetPath().GetFull(), RTL_TEXTENCODING_ASCII_US ); |
| } |
| // Assign to PATH environment variable |
| if ( aCmdPath.Len() ) |
| { |
| aTmpPath = aCmdPath; |
| if ( aPath.Len() ) |
| aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); |
| aTmpPath += aPath; |
| setenv( "PATH", aTmpPath.GetBuffer(), TRUE ); |
| } |
| // Assign to STAR_RESOURCEPATH environment variable |
| if ( aCmdPath.Len() ) |
| { |
| aTmpPath = aCmdPath; |
| if ( aResPath.Len() ) |
| aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); |
| aTmpPath += aResPath; |
| setenv( "STAR_RESOURCEPATH", aTmpPath.GetBuffer(), TRUE ); |
| } |
| // Assign to DYLD_LIBRARY_PATH environment variable |
| if ( aCmdPath.Len() ) |
| { |
| aTmpPath = aCmdPath; |
| if ( aLibPath.Len() ) |
| aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); |
| aTmpPath += aLibPath; |
| setenv( "DYLD_LIBRARY_PATH", aTmpPath.GetBuffer(), TRUE ); |
| } |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void DeInitSalMain() |
| { |
| } |
| |
| // ======================================================================= |
| |
| SalYieldMutex::SalYieldMutex() |
| { |
| mnCount = 0; |
| mnThreadId = 0; |
| } |
| |
| void SalYieldMutex::acquire() |
| { |
| OMutex::acquire(); |
| mnThreadId = vos::OThread::getCurrentIdentifier(); |
| mnCount++; |
| } |
| |
| void SalYieldMutex::release() |
| { |
| if ( mnThreadId == vos::OThread::getCurrentIdentifier() ) |
| { |
| if ( mnCount == 1 ) |
| mnThreadId = 0; |
| mnCount--; |
| } |
| OMutex::release(); |
| } |
| |
| sal_Bool SalYieldMutex::tryToAcquire() |
| { |
| if ( OMutex::tryToAcquire() ) |
| { |
| mnThreadId = vos::OThread::getCurrentIdentifier(); |
| mnCount++; |
| return sal_True; |
| } |
| else |
| return sal_False; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| // some convenience functions regarding the yield mutex, aka solar mutex |
| |
| sal_Bool ImplSalYieldMutexTryToAcquire() |
| { |
| AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; |
| if ( pInst ) |
| return pInst->mpSalYieldMutex->tryToAcquire(); |
| else |
| return FALSE; |
| } |
| |
| void ImplSalYieldMutexAcquire() |
| { |
| AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; |
| if ( pInst ) |
| pInst->mpSalYieldMutex->acquire(); |
| } |
| |
| void ImplSalYieldMutexRelease() |
| { |
| AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; |
| if ( pInst ) |
| pInst->mpSalYieldMutex->release(); |
| } |
| |
| // ======================================================================= |
| |
| SalInstance* CreateSalInstance() |
| { |
| // this is the case for not using SVMain |
| // not so good |
| if( bNoSVMain ) |
| initNSApp(); |
| |
| SalData* pSalData = GetSalData(); |
| DBG_ASSERT( pSalData->mpFirstInstance == NULL, "more than one instance created" ); |
| AquaSalInstance* pInst = new AquaSalInstance; |
| |
| // init instance (only one instance in this version !!!) |
| pSalData->mpFirstInstance = pInst; |
| // this one is for outside AquaSalInstance::Yield |
| SalData::ensureThreadAutoreleasePool(); |
| // no focus rects on NWF aqua |
| ImplGetSVData()->maNWFData.mbNoFocusRects = true; |
| ImplGetSVData()->maNWFData.mbNoBoldTabFocus = true; |
| ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true; |
| ImplGetSVData()->maNWFData.mbCenteredTabs = true; |
| ImplGetSVData()->maNWFData.mbProgressNeedsErase = true; |
| ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true; |
| ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10; |
| ImplGetSVData()->maGDIData.mbNoXORClipping = true; |
| ImplGetSVData()->maWinData.mbNoSaveBackground = true; |
| |
| return pInst; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void DestroySalInstance( SalInstance* pInst ) |
| { |
| delete pInst; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| AquaSalInstance::AquaSalInstance() |
| { |
| mpSalYieldMutex = new SalYieldMutex; |
| mpSalYieldMutex->acquire(); |
| ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex ); |
| maMainThread = vos::OThread::getCurrentIdentifier(); |
| mbWaitingYield = false; |
| maUserEventListMutex = osl_createMutex(); |
| mnActivePrintJobs = 0; |
| maWaitingYieldCond = osl_createCondition(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| AquaSalInstance::~AquaSalInstance() |
| { |
| ::tools::SolarMutex::SetSolarMutex( 0 ); |
| mpSalYieldMutex->release(); |
| delete mpSalYieldMutex; |
| osl_destroyMutex( maUserEventListMutex ); |
| osl_destroyCondition( maWaitingYieldCond ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| 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]; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, sal_uInt16 nType, void* pData ) |
| { |
| osl_acquireMutex( maUserEventListMutex ); |
| maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) ); |
| osl_releaseMutex( maUserEventListMutex ); |
| |
| // notify main loop that an event has arrived |
| wakeupYield(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| vos::IMutex* AquaSalInstance::GetYieldMutex() |
| { |
| return mpSalYieldMutex; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_uLong AquaSalInstance::ReleaseYieldMutex() |
| { |
| SalYieldMutex* pYieldMutex = mpSalYieldMutex; |
| if ( pYieldMutex->GetThreadId() == |
| vos::OThread::getCurrentIdentifier() ) |
| { |
| sal_uLong nCount = pYieldMutex->GetAcquireCount(); |
| sal_uLong n = nCount; |
| while ( n ) |
| { |
| pYieldMutex->release(); |
| n--; |
| } |
| |
| return nCount; |
| } |
| else |
| return 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::AcquireYieldMutex( sal_uLong nCount ) |
| { |
| SalYieldMutex* pYieldMutex = mpSalYieldMutex; |
| while ( nCount ) |
| { |
| pYieldMutex->acquire(); |
| nCount--; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| bool AquaSalInstance::CheckYieldMutex() |
| { |
| bool bRet = true; |
| |
| SalYieldMutex* pYieldMutex = mpSalYieldMutex; |
| if ( pYieldMutex->GetThreadId() != |
| vos::OThread::getCurrentIdentifier() ) |
| { |
| bRet = false; |
| } |
| |
| return bRet; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| bool AquaSalInstance::isNSAppThread() const |
| { |
| return vos::OThread::getCurrentIdentifier() == maMainThread; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) |
| { |
| switch( [pEvent subtype] ) |
| { |
| case AppStartTimerEvent: |
| AquaSalTimer::handleStartTimerEvent( pEvent ); |
| break; |
| case AppEndLoopEvent: |
| [NSApp stop: NSApp]; |
| break; |
| case AppExecuteSVMain: |
| { |
| sal_Bool bResult = ImplSVMain(); |
| if( gpbInit ) |
| *gpbInit = bResult; |
| [NSApp stop: NSApp]; |
| bLeftMain = true; |
| if( pDockMenu ) |
| { |
| [pDockMenu release]; |
| pDockMenu = nil; |
| } |
| } |
| break; |
| case AppleRemoteEvent: |
| { |
| sal_Int16 nCommand = 0; |
| SalData* pSalData = GetSalData(); |
| bool bIsFullScreenMode = false; |
| |
| std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin(); |
| for(; it != pSalData->maFrames.end(); ++it ) |
| { |
| if( (*it)->mbFullScreen ) |
| bIsFullScreenMode = true; |
| } |
| |
| switch ([pEvent data1]) |
| { |
| case kRemoteButtonPlay: |
| nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY; |
| break; |
| |
| // kept for experimentation purpose (scheduled for future implementation) |
| // case kRemoteButtonMenu: nCommand = MEDIA_COMMAND_MENU; break; |
| |
| case kRemoteButtonPlus: nCommand = MEDIA_COMMAND_VOLUME_UP; break; |
| |
| case kRemoteButtonMinus: nCommand = MEDIA_COMMAND_VOLUME_DOWN; break; |
| |
| case kRemoteButtonRight: nCommand = MEDIA_COMMAND_NEXTTRACK; break; |
| |
| case kRemoteButtonRight_Hold: nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break; |
| |
| case kRemoteButtonLeft: nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break; |
| |
| case kRemoteButtonLeft_Hold: nCommand = MEDIA_COMMAND_REWIND; break; |
| |
| case kRemoteButtonPlay_Hold: nCommand = MEDIA_COMMAND_PLAY_HOLD; break; |
| |
| case kRemoteButtonMenu_Hold: nCommand = MEDIA_COMMAND_STOP; break; |
| |
| // FIXME : not detected |
| case kRemoteButtonPlus_Hold: |
| case kRemoteButtonMinus_Hold: |
| break; |
| |
| default: |
| break; |
| } |
| AquaSalFrame* pFrame = pSalData->maFrames.front(); |
| Window* pWindow = pFrame ? pFrame->GetWindow() : NULL; |
| |
| if( pWindow ) |
| { |
| const Point aPoint; |
| CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand ); |
| NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt ); |
| |
| if ( !ImplCallPreNotify( aNCmdEvt ) ) |
| pWindow->Command( aCEvt ); |
| } |
| |
| } |
| break; |
| |
| case YieldWakeupEvent: |
| // do nothing, fall out of Yield |
| break; |
| |
| default: |
| DBG_ERROR( "unhandled NSApplicationDefined event" ); |
| break; |
| }; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| class ReleasePoolHolder |
| { |
| NSAutoreleasePool* mpPool; |
| public: |
| ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {} |
| ~ReleasePoolHolder() { [mpPool release]; } |
| }; |
| |
| void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) |
| { |
| // ensure that the per thread autorelease pool is top level and |
| // will therefore not be destroyed by cocoa implicitly |
| SalData::ensureThreadAutoreleasePool(); |
| |
| // NSAutoreleasePool documentation suggests we should have |
| // an own pool for each yield level |
| ReleasePoolHolder aReleasePool; |
| |
| // Release all locks so that we don't deadlock when we pull pending |
| // events from the event queue |
| bool bDispatchUser = true; |
| while( bDispatchUser ) |
| { |
| sal_uLong nCount = ReleaseYieldMutex(); |
| |
| // get one user event |
| osl_acquireMutex( maUserEventListMutex ); |
| SalUserEvent aEvent( NULL, NULL, 0 ); |
| if( ! maUserEvents.empty() ) |
| { |
| aEvent = maUserEvents.front(); |
| maUserEvents.pop_front(); |
| } |
| else |
| bDispatchUser = false; |
| osl_releaseMutex( maUserEventListMutex ); |
| |
| AcquireYieldMutex( nCount ); |
| |
| // dispatch it |
| if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) ) |
| { |
| aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData ); |
| osl_setCondition( maWaitingYieldCond ); |
| // return if only one event is asked for |
| if( ! bHandleAllCurrentEvents ) |
| return; |
| } |
| } |
| |
| // handle cocoa event queue |
| // cocoa events mye be only handled in the thread the NSApp was created |
| if( isNSAppThread() && mnActivePrintJobs == 0 ) |
| { |
| // we need to be woken up by a cocoa-event |
| // if a user event should be posted by the event handling below |
| bool bOldWaitingYield = mbWaitingYield; |
| mbWaitingYield = bWait; |
| |
| // handle available events |
| NSEvent* pEvent = nil; |
| bool bHadEvent = false; |
| do |
| { |
| sal_uLong nCount = ReleaseYieldMutex(); |
| |
| pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil |
| inMode: NSDefaultRunLoopMode dequeue: YES]; |
| if( pEvent ) |
| { |
| [NSApp sendEvent: pEvent]; |
| bHadEvent = true; |
| } |
| [NSApp updateWindows]; |
| |
| AcquireYieldMutex( nCount ); |
| } while( bHandleAllCurrentEvents && pEvent ); |
| |
| // if we had no event yet, wait for one if requested |
| if( bWait && ! bHadEvent ) |
| { |
| sal_uLong nCount = ReleaseYieldMutex(); |
| |
| NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture]; |
| pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt |
| inMode: NSDefaultRunLoopMode dequeue: YES]; |
| if( pEvent ) |
| [NSApp sendEvent: pEvent]; |
| [NSApp updateWindows]; |
| |
| AcquireYieldMutex( nCount ); |
| |
| // #i86581# |
| // FIXME: sometimes the NSTimer will never fire. Firing it by hand then |
| // fixes the problem even seems to set the correct next firing date |
| // Why oh why ? |
| if( ! pEvent && AquaSalTimer::pRunningTimer ) |
| { |
| // this cause crashes on MacOSX 10.4 |
| // [AquaSalTimer::pRunningTimer fire]; |
| ImplGetSVData()->mpSalTimer->CallCallback(); |
| } |
| } |
| |
| mbWaitingYield = bOldWaitingYield; |
| |
| // collect update rectangles |
| const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames ); |
| for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) |
| { |
| if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() ) |
| { |
| (*it)->Flush( (*it)->maInvalidRect ); |
| (*it)->maInvalidRect.SetEmpty(); |
| } |
| } |
| osl_setCondition( maWaitingYieldCond ); |
| } |
| else if( bWait ) |
| { |
| // #i103162# |
| // wait until any thread (most likely the main thread) |
| // has dispatched an event, cop out at 200 ms |
| osl_resetCondition( maWaitingYieldCond ); |
| TimeValue aVal = { 0, 200000000 }; |
| sal_uLong nCount = ReleaseYieldMutex(); |
| osl_waitCondition( maWaitingYieldCond, &aVal ); |
| AcquireYieldMutex( nCount ); |
| } |
| |
| // we get some apple events way too early |
| // before the application is ready to handle them, |
| // so their corresponding application events need to be delayed |
| // now is a good time to handle at least one of them |
| if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute ) |
| { |
| // make sure that only one application event is active at a time |
| static bool bInAppEvent = false; |
| if( !bInAppEvent ) |
| { |
| bInAppEvent = true; |
| // get the next delayed application event |
| const ApplicationEvent* pAppEvent = aAppEventList.front(); |
| aAppEventList.pop_front(); |
| // handle one application event (no recursion) |
| const ImplSVData* pSVData = ImplGetSVData(); |
| pSVData->mpApp->AppEvent( *pAppEvent ); |
| delete pAppEvent; |
| // allow the next delayed application event |
| bInAppEvent = false; |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| bool AquaSalInstance::AnyInput( sal_uInt16 nType ) |
| { |
| if( nType & INPUT_APPEVENT ) |
| { |
| if( ! aAppEventList.empty() ) |
| return true; |
| if( nType == INPUT_APPEVENT ) |
| return false; |
| } |
| |
| if( nType & INPUT_TIMER ) |
| { |
| if( AquaSalTimer::pRunningTimer ) |
| { |
| NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate]; |
| if( pDt && [pDt timeIntervalSinceNow] < 0 ) |
| { |
| return true; |
| } |
| } |
| } |
| |
| unsigned/*NSUInteger*/ nEventMask = 0; |
| if( nType & INPUT_MOUSE) |
| nEventMask |= |
| NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | |
| NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask | |
| NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask | |
| NSScrollWheelMask | |
| // NSMouseMovedMask | |
| NSMouseEnteredMask | NSMouseExitedMask; |
| if( nType & INPUT_KEYBOARD) |
| nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; |
| if( nType & INPUT_OTHER) |
| nEventMask |= NSTabletPoint; |
| // TODO: INPUT_PAINT / more INPUT_OTHER |
| if( !nType) |
| return false; |
| |
| NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil |
| inMode: NSDefaultRunLoopMode dequeue: NO]; |
| return (pEvent != NULL); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, sal_uLong /*nSalFrameStyle*/ ) |
| { |
| return NULL; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameStyle ) |
| { |
| SalData::ensureThreadAutoreleasePool(); |
| |
| SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle ); |
| return pFrame; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::DestroyFrame( SalFrame* pFrame ) |
| { |
| delete pFrame; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, sal_Bool /* bShow */ ) |
| { |
| // SystemWindowData is meaningless on Mac OS X |
| AquaSalObject *pObject = NULL; |
| |
| if ( pParent ) |
| pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) ); |
| |
| return pObject; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::DestroyObject( SalObject* pObject ) |
| { |
| delete ( pObject ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) |
| { |
| return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter ) |
| { |
| delete pPrinter; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) |
| { |
| NSArray* pNames = [NSPrinter printerNames]; |
| NSArray* pTypes = [NSPrinter printerTypes]; |
| unsigned int nNameCount = pNames ? [pNames count] : 0; |
| unsigned int nTypeCount = pTypes ? [pTypes count] : 0; |
| DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" ); |
| for( unsigned int i = 0; i < nNameCount; i++ ) |
| { |
| NSString* pName = [pNames objectAtIndex: i]; |
| NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil; |
| if( pName ) |
| { |
| SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; |
| pInfo->maPrinterName = GetOUString( pName ); |
| if( pType ) |
| pInfo->maDriver = GetOUString( pType ); |
| pInfo->mnStatus = 0; |
| pInfo->mnJobs = 0; |
| pInfo->mpSysData = NULL; |
| |
| pList->Add( pInfo ); |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) |
| { |
| delete pInfo; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| XubString AquaSalInstance::GetDefaultPrinter() |
| { |
| // #i113170# may not be the main thread if called from UNO API |
| SalData::ensureThreadAutoreleasePool(); |
| |
| if( ! maDefaultPrinter.getLength() ) |
| { |
| NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo]; |
| DBG_ASSERT( pPI, "no print info" ); |
| if( pPI ) |
| { |
| NSPrinter* pPr = [pPI printer]; |
| DBG_ASSERT( pPr, "no printer in default info" ); |
| if( pPr ) |
| { |
| NSString* pDefName = [pPr name]; |
| DBG_ASSERT( pDefName, "printer has no name" ); |
| maDefaultPrinter = GetOUString( pDefName ); |
| } |
| } |
| } |
| return maDefaultPrinter; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, |
| ImplJobSetup* pSetupData ) |
| { |
| // #i113170# may not be the main thread if called from UNO API |
| SalData::ensureThreadAutoreleasePool(); |
| |
| SalInfoPrinter* pNewInfoPrinter = NULL; |
| if( pQueueInfo ) |
| { |
| pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo ); |
| if( pSetupData ) |
| pNewInfoPrinter->SetPrinterData( pSetupData ); |
| } |
| |
| return pNewInfoPrinter; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) |
| { |
| // #i113170# may not be the main thread if called from UNO API |
| SalData::ensureThreadAutoreleasePool(); |
| |
| delete pPrinter; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalSystem* AquaSalInstance::CreateSystem() |
| { |
| return new AquaSalSystem(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::DestroySystem( SalSystem* pSystem ) |
| { |
| delete pSystem; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::SetEventCallback( void*, bool(*)(void*,void*,int) ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void AquaSalInstance::SetErrorEventCallback( void*, bool(*)(void*,void*,int) ) |
| { |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) |
| { |
| rReturnedBytes = 1; |
| rReturnedType = AsciiCString; |
| return (void*)""; |
| } |
| |
| // We need to re-encode file urls because osl_getFileURLFromSystemPath converts |
| // to UTF-8 before encoding non ascii characters, which is not what other apps expect. |
| static rtl::OUString translateToExternalUrl(const rtl::OUString& internalUrl) |
| { |
| rtl::OUString extUrl; |
| |
| uno::Reference< lang::XMultiServiceFactory > sm = comphelper::getProcessServiceFactory(); |
| if (sm.is()) |
| { |
| uno::Reference< beans::XPropertySet > pset; |
| sm->queryInterface( getCppuType( &pset )) >>= pset; |
| if (pset.is()) |
| { |
| uno::Reference< uno::XComponentContext > context; |
| static const rtl::OUString DEFAULT_CONTEXT( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ); |
| pset->getPropertyValue(DEFAULT_CONTEXT) >>= context; |
| if (context.is()) |
| extUrl = uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl); |
| } |
| } |
| return extUrl; |
| } |
| |
| // #i104525# many versions of OSX have problems with some URLs: |
| // when an app requests OSX to add one of these URLs to the "Recent Items" list |
| // then this app gets killed (TextEdit, Preview, etc. and also OOo) |
| static bool isDangerousUrl( const rtl::OUString& rUrl ) |
| { |
| // use a heuristic that detects all known cases since there is no official comment |
| // on the exact impact and root cause of the OSX bug |
| const int nLen = rUrl.getLength(); |
| const sal_Unicode* p = rUrl.getStr(); |
| for( int i = 0; i < nLen-3; ++i, ++p ) { |
| if( p[0] != '%' ) |
| continue; |
| // escaped percent? |
| if( (p[1] == '2') && (p[2] == '5') ) |
| return true; |
| // escapes are considered to be UTF-8 encoded |
| // => check for invalid UTF-8 leading byte |
| if( (p[1] != 'f') && (p[1] != 'F') ) |
| continue; |
| int cLowNibble = p[2]; |
| if( (cLowNibble >= '0' ) && (cLowNibble <= '9')) |
| return false; |
| if( cLowNibble >= 'a' ) |
| cLowNibble -= 'a' - 'A'; |
| if( (cLowNibble < 'A') || (cLowNibble >= 'C')) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void AquaSalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& /*rMimeType*/) |
| { |
| // Convert file URL for external use (see above) |
| rtl::OUString externalUrl = translateToExternalUrl(rFileUrl); |
| if( 0 == externalUrl.getLength() ) |
| externalUrl = rFileUrl; |
| |
| if( externalUrl.getLength() && !isDangerousUrl( externalUrl ) ) |
| { |
| NSString* pString = CreateNSString( externalUrl ); |
| NSURL* pURL = [NSURL URLWithString: pString]; |
| |
| if( pURL ) |
| { |
| NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController]; |
| [pCtrl noteNewRecentDocumentURL: pURL]; |
| } |
| if( pString ) |
| [pString release]; |
| } |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| |
| SalTimer* AquaSalInstance::CreateSalTimer() |
| { |
| return new AquaSalTimer(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalSystem* AquaSalInstance::CreateSalSystem() |
| { |
| return new AquaSalSystem(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalBitmap* AquaSalInstance::CreateSalBitmap() |
| { |
| return new AquaSalBitmap(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SalSession* AquaSalInstance::CreateSalSession() |
| { |
| return NULL; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| class MacImeStatus : public SalI18NImeStatus |
| { |
| public: |
| MacImeStatus() {} |
| virtual ~MacImeStatus() {} |
| |
| // asks whether there is a status window available |
| // to toggle into menubar |
| virtual bool canToggle() { return false; } |
| virtual void toggle() {} |
| }; |
| |
| // ----------------------------------------------------------------------- |
| |
| SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus() |
| { |
| return new MacImeStatus(); |
| } |
| |
| // YieldMutexReleaser |
| YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 ) |
| { |
| SalData* pSalData = GetSalData(); |
| if( ! pSalData->mpFirstInstance->isNSAppThread() ) |
| { |
| SalData::ensureThreadAutoreleasePool(); |
| mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex(); |
| } |
| } |
| |
| YieldMutexReleaser::~YieldMutexReleaser() |
| { |
| if( mnCount != 0 ) |
| GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount ); |
| } |
| |
| ////////////////////////////////////////////////////////////// |
| rtl::OUString GetOUString( CFStringRef rStr ) |
| { |
| if( rStr == 0 ) |
| return rtl::OUString(); |
| CFIndex nLength = CFStringGetLength( rStr ); |
| if( nLength == 0 ) |
| return rtl::OUString(); |
| const UniChar* pConstStr = CFStringGetCharactersPtr( rStr ); |
| if( pConstStr ) |
| return rtl::OUString( pConstStr, nLength ); |
| UniChar* pStr = reinterpret_cast<UniChar*>( rtl_allocateMemory( sizeof(UniChar)*nLength ) ); |
| CFRange aRange = { 0, nLength }; |
| CFStringGetCharacters( rStr, aRange, pStr ); |
| rtl::OUString aRet( pStr, nLength ); |
| rtl_freeMemory( pStr ); |
| return aRet; |
| } |
| |
| rtl::OUString GetOUString( NSString* pStr ) |
| { |
| if( ! pStr ) |
| return rtl::OUString(); |
| int nLen = [pStr length]; |
| if( nLen == 0 ) |
| return rtl::OUString(); |
| |
| rtl::OUStringBuffer aBuf( nLen+1 ); |
| aBuf.setLength( nLen ); |
| [pStr getCharacters: const_cast<sal_Unicode*>(aBuf.getStr())]; |
| return aBuf.makeStringAndClear(); |
| } |
| |
| CFStringRef CreateCFString( const rtl::OUString& rStr ) |
| { |
| return CFStringCreateWithCharacters(kCFAllocatorDefault, rStr.getStr(), rStr.getLength() ); |
| } |
| |
| NSString* CreateNSString( const rtl::OUString& rStr ) |
| { |
| return [[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()]; |
| } |
| |
| CGImageRef CreateCGImage( const Image& rImage ) |
| { |
| BitmapEx aBmpEx( rImage.GetBitmapEx() ); |
| Bitmap aBmp( aBmpEx.GetBitmap() ); |
| |
| if( ! aBmp || ! aBmp.ImplGetImpBitmap() ) |
| return NULL; |
| |
| // simple case, no transparency |
| AquaSalBitmap* pSalBmp = static_cast<AquaSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap()); |
| |
| if( ! pSalBmp ) |
| return NULL; |
| |
| CGImageRef xImage = NULL; |
| if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) ) |
| xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); |
| else if( aBmpEx.IsAlpha() ) |
| { |
| AlphaMask aAlphaMask( aBmpEx.GetAlpha() ); |
| Bitmap aMask( aAlphaMask.GetBitmap() ); |
| AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); |
| if( pMaskBmp ) |
| xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); |
| else |
| xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); |
| } |
| else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP ) |
| { |
| Bitmap aMask( aBmpEx.GetMask() ); |
| AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); |
| if( pMaskBmp ) |
| xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); |
| else |
| xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); |
| } |
| else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR ) |
| { |
| Color aTransColor( aBmpEx.GetTransparentColor() ); |
| SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() ); |
| xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor ); |
| } |
| |
| return xImage; |
| } |
| |
| NSImage* CreateNSImage( const Image& rImage ) |
| { |
| CGImageRef xImage = CreateCGImage( rImage ); |
| |
| if( ! xImage ) |
| return nil; |
| |
| Size aSize( rImage.GetSizePixel() ); |
| NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )]; |
| if( pImage ) |
| { |
| [pImage setFlipped: YES]; |
| [pImage lockFocus]; |
| |
| NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; |
| CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]); |
| |
| const CGRect aDstRect = CGRectMake( 0, 0, aSize.Width(), aSize.Height()); |
| CGContextDrawImage( rCGContext, aDstRect, xImage ); |
| |
| [pImage unlockFocus]; |
| } |
| |
| CGImageRelease( xImage ); |
| |
| return pImage; |
| } |