1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_vcl.hxx" 26 27 #include <stdio.h> 28 29 #include "tools/fsys.hxx" 30 #include "tools/getprocessworkingdir.hxx" 31 #include <tools/solarmutex.hxx> 32 33 #include "osl/process.h" 34 35 #include "rtl/ustrbuf.hxx" 36 37 #include "vcl/svapp.hxx" 38 #include "vcl/window.hxx" 39 #include "vcl/timer.hxx" 40 41 #include "aqua/saldata.hxx" 42 #include "aqua/salinst.h" 43 #include "aqua/salframe.h" 44 #include "aqua/salobj.h" 45 #include "aqua/salsys.h" 46 #include "aqua/salvd.h" 47 #include "aqua/salbmp.h" 48 #include "aqua/salprn.h" 49 #include "aqua/saltimer.h" 50 #include "aqua/vclnsapp.h" 51 52 #include "print.h" 53 #include "impbmp.hxx" 54 #include "salimestatus.hxx" 55 56 #include <comphelper/processfactory.hxx> 57 58 #include <com/sun/star/beans/XPropertySet.hpp> 59 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 60 #include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> 61 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> 62 #include <com/sun/star/uno/XComponentContext.hpp> 63 64 #include "premac.h" 65 #include <Foundation/Foundation.h> 66 #include <ApplicationServices/ApplicationServices.h> 67 #import "apple_remote/RemoteMainController.h" 68 #include "apple_remote/RemoteControl.h" 69 #include "postmac.h" 70 71 using namespace std; 72 using namespace ::com::sun::star; 73 74 extern sal_Bool ImplSVMain(); 75 76 static sal_Bool* gpbInit = 0; 77 static NSMenu* pDockMenu = nil; 78 static bool bNoSVMain = true; 79 static bool bLeftMain = false; 80 // ----------------------------------------------------------------------- 81 82 class AquaDelayedSettingsChanged : public Timer 83 { 84 bool mbInvalidate; 85 public: 86 AquaDelayedSettingsChanged( bool bInvalidate ) : 87 mbInvalidate( bInvalidate ) 88 { 89 } 90 91 virtual void Timeout() 92 { 93 SalData* pSalData = GetSalData(); 94 if( ! pSalData->maFrames.empty() ) 95 pSalData->maFrames.front()->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); 96 97 if( mbInvalidate ) 98 { 99 for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin(); 100 it != pSalData->maFrames.end(); ++it ) 101 { 102 if( (*it)->mbShown ) 103 (*it)->SendPaintEvent( NULL ); 104 } 105 } 106 Stop(); 107 delete this; 108 } 109 }; 110 111 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate ) 112 { 113 vos::OGuard aGuard( *mpSalYieldMutex ); 114 AquaDelayedSettingsChanged* pTimer = new AquaDelayedSettingsChanged( bInvalidate ); 115 pTimer->SetTimeout( 50 ); 116 pTimer->Start(); 117 } 118 119 120 // the AppEventList must be available before any SalData/SalInst/etc. objects are ready 121 typedef std::list<const ApplicationEvent*> AppEventList; 122 AppEventList AquaSalInstance::aAppEventList; 123 124 NSMenu* AquaSalInstance::GetDynamicDockMenu() 125 { 126 if( ! pDockMenu && ! bLeftMain ) 127 pDockMenu = [[NSMenu alloc] initWithTitle: @""]; 128 return pDockMenu; 129 } 130 131 bool AquaSalInstance::isOnCommandLine( const rtl::OUString& rArg ) 132 { 133 sal_uInt32 nArgs = osl_getCommandArgCount(); 134 for( sal_uInt32 i = 0; i < nArgs; i++ ) 135 { 136 rtl::OUString aArg; 137 osl_getCommandArg( i, &aArg.pData ); 138 if( aArg.equals( rArg ) ) 139 return true; 140 } 141 return false; 142 } 143 144 145 // initialize the cocoa VCL_NSApplication object 146 // returns an NSAutoreleasePool that must be released when the event loop begins 147 static void initNSApp() 148 { 149 // create our cocoa NSApplication 150 [VCL_NSApplication sharedApplication]; 151 152 SalData::ensureThreadAutoreleasePool(); 153 154 // put cocoa into multithreaded mode 155 [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil]; 156 157 // activate our delegate methods 158 [NSApp setDelegate: NSApp]; 159 160 [[NSNotificationCenter defaultCenter] addObserver: NSApp 161 selector: @selector(systemColorsChanged:) 162 name: NSSystemColorsDidChangeNotification 163 object: nil ]; 164 [[NSNotificationCenter defaultCenter] addObserver: NSApp 165 selector: @selector(screenParametersChanged:) 166 name: NSApplicationDidChangeScreenParametersNotification 167 object: nil ]; 168 // add observers for some settings changes that affect vcl's settings 169 // scrollbar variant 170 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 171 selector: @selector(scrollbarVariantChanged:) 172 name: @"AppleAquaScrollBarVariantChanged" 173 object: nil ]; 174 // scrollbar page behavior ("jump to here" or not) 175 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 176 selector: @selector(scrollbarSettingsChanged:) 177 name: @"AppleNoRedisplayAppearancePreferenceChanged" 178 object: nil ]; 179 180 // get System Version and store the value in GetSalData()->mnSystemVersion 181 SInt32 systemVersion = OSX_VER_LION; // initialize with the minimal requirement 182 const OSErr err = Gestalt( gestaltSystemVersion, &systemVersion); 183 if( err == noErr ) 184 { 185 GetSalData()->mnSystemVersion = systemVersion; 186 #if OSL_DEBUG_LEVEL > 1 187 fprintf( stderr, "OSX System Version 0x%04x\n", (unsigned int)systemVersion); 188 #endif 189 } 190 else 191 NSLog(@"Unable to obtain system version: %ld", (long)err); 192 193 GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init]; 194 195 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 196 selector: @selector(applicationWillBecomeActive:) 197 name: @"AppleRemoteWillBecomeActive" 198 object: nil ]; 199 200 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp 201 selector: @selector(applicationWillResignActive:) 202 name: @"AppleRemoteWillResignActive" 203 object: nil ]; 204 205 if( ImplGetSVData()->mbIsTestTool ) 206 [NSApp activateIgnoringOtherApps: YES]; 207 } 208 209 sal_Bool ImplSVMainHook( sal_Bool * pbInit ) 210 { 211 gpbInit = pbInit; 212 213 bNoSVMain = false; 214 initNSApp(); 215 216 rtl::OUString aExeURL, aExe; 217 osl_getExecutableFile( &aExeURL.pData ); 218 osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData ); 219 rtl::OString aByteExe( rtl::OUStringToOString( aExe, osl_getThreadTextEncoding() ) ); 220 221 #ifdef DEBUG 222 aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" ); 223 const char* pArgv[] = { aByteExe.getStr(), NULL }; 224 NSApplicationMain( 3, pArgv ); 225 #else 226 const char* pArgv[] = { aByteExe.getStr(), NULL }; 227 NSApplicationMain( 1, pArgv ); 228 #endif 229 230 return TRUE; // indicate that ImplSVMainHook is implemented 231 } 232 233 // ======================================================================= 234 235 void SalAbort( const XubString& rErrorText ) 236 { 237 if( !rErrorText.Len() ) 238 fprintf( stderr, "Application Error " ); 239 else 240 fprintf( stderr, "%s ", 241 ByteString( rErrorText, gsl_getSystemTextEncoding() ).GetBuffer() ); 242 abort(); 243 } 244 245 // ----------------------------------------------------------------------- 246 247 void InitSalData() 248 { 249 SalData *pSalData = new SalData; 250 SetSalData( pSalData ); 251 } 252 253 // ----------------------------------------------------------------------- 254 255 const ::rtl::OUString& SalGetDesktopEnvironment() 256 { 257 static OUString aDesktopEnvironment(RTL_CONSTASCII_USTRINGPARAM( "MacOSX" )); 258 return aDesktopEnvironment; 259 } 260 261 // ----------------------------------------------------------------------- 262 263 void DeInitSalData() 264 { 265 SalData *pSalData = GetSalData(); 266 if( pSalData->mpStatusItem ) 267 { 268 [pSalData->mpStatusItem release]; 269 pSalData->mpStatusItem = nil; 270 } 271 delete pSalData; 272 SetSalData( NULL ); 273 } 274 275 // ----------------------------------------------------------------------- 276 277 extern "C" { 278 #include <crt_externs.h> 279 } 280 281 // ----------------------------------------------------------------------- 282 283 void InitSalMain() 284 { 285 rtl::OUString urlWorkDir; 286 rtl_uString *sysWorkDir = NULL; 287 if (tools::getProcessWorkingDir(&urlWorkDir)) 288 { 289 oslFileError err2 = osl_getSystemPathFromFileURL(urlWorkDir.pData, &sysWorkDir); 290 if (err2 == osl_File_E_None) 291 { 292 ByteString aPath( getenv( "PATH" ) ); 293 ByteString aResPath( getenv( "STAR_RESOURCEPATH" ) ); 294 ByteString aLibPath( getenv( "DYLD_LIBRARY_PATH" ) ); 295 ByteString aCmdPath( OUStringToOString(OUString(sysWorkDir), RTL_TEXTENCODING_UTF8).getStr() ); 296 ByteString aTmpPath; 297 // Get absolute path of command's directory 298 if ( aCmdPath.Len() ) { 299 DirEntry aCmdDirEntry( aCmdPath ); 300 aCmdDirEntry.ToAbs(); 301 aCmdPath = ByteString( aCmdDirEntry.GetPath().GetFull(), RTL_TEXTENCODING_ASCII_US ); 302 } 303 // Assign to PATH environment variable 304 if ( aCmdPath.Len() ) 305 { 306 aTmpPath = ByteString( "PATH=" ); 307 aTmpPath += aCmdPath; 308 if ( aPath.Len() ) 309 aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); 310 aTmpPath += aPath; 311 putenv( (char*)aTmpPath.GetBuffer() ); 312 } 313 // Assign to STAR_RESOURCEPATH environment variable 314 if ( aCmdPath.Len() ) 315 { 316 aTmpPath = ByteString( "STAR_RESOURCEPATH=" ); 317 aTmpPath += aCmdPath; 318 if ( aResPath.Len() ) 319 aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); 320 aTmpPath += aResPath; 321 putenv( (char*)aTmpPath.GetBuffer() ); 322 } 323 // Assign to DYLD_LIBRARY_PATH environment variable 324 if ( aCmdPath.Len() ) 325 { 326 aTmpPath = ByteString( "DYLD_LIBRARY_PATH=" ); 327 aTmpPath += aCmdPath; 328 if ( aLibPath.Len() ) 329 aTmpPath += ByteString( DirEntry::GetSearchDelimiter(), RTL_TEXTENCODING_ASCII_US ); 330 aTmpPath += aLibPath; 331 putenv( (char*)aTmpPath.GetBuffer() ); 332 } 333 } 334 } 335 } 336 337 // ----------------------------------------------------------------------- 338 339 void DeInitSalMain() 340 { 341 } 342 343 // ======================================================================= 344 345 SalYieldMutex::SalYieldMutex() 346 { 347 mnCount = 0; 348 mnThreadId = 0; 349 } 350 351 void SalYieldMutex::acquire() 352 { 353 OMutex::acquire(); 354 mnThreadId = vos::OThread::getCurrentIdentifier(); 355 mnCount++; 356 } 357 358 void SalYieldMutex::release() 359 { 360 if ( mnThreadId == vos::OThread::getCurrentIdentifier() ) 361 { 362 if ( mnCount == 1 ) 363 mnThreadId = 0; 364 mnCount--; 365 } 366 OMutex::release(); 367 } 368 369 sal_Bool SalYieldMutex::tryToAcquire() 370 { 371 if ( OMutex::tryToAcquire() ) 372 { 373 mnThreadId = vos::OThread::getCurrentIdentifier(); 374 mnCount++; 375 return sal_True; 376 } 377 else 378 return sal_False; 379 } 380 381 // ----------------------------------------------------------------------- 382 383 // some convenience functions regarding the yield mutex, aka solar mutex 384 385 sal_Bool ImplSalYieldMutexTryToAcquire() 386 { 387 AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; 388 if ( pInst ) 389 return pInst->mpSalYieldMutex->tryToAcquire(); 390 else 391 return FALSE; 392 } 393 394 void ImplSalYieldMutexAcquire() 395 { 396 AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; 397 if ( pInst ) 398 pInst->mpSalYieldMutex->acquire(); 399 } 400 401 void ImplSalYieldMutexRelease() 402 { 403 AquaSalInstance* pInst = (AquaSalInstance*) GetSalData()->mpFirstInstance; 404 if ( pInst ) 405 pInst->mpSalYieldMutex->release(); 406 } 407 408 // ======================================================================= 409 410 SalInstance* CreateSalInstance() 411 { 412 // this is the case for not using SVMain 413 // not so good 414 if( bNoSVMain ) 415 initNSApp(); 416 417 SalData* pSalData = GetSalData(); 418 DBG_ASSERT( pSalData->mpFirstInstance == NULL, "more than one instance created" ); 419 AquaSalInstance* pInst = new AquaSalInstance; 420 421 // init instance (only one instance in this version !!!) 422 pSalData->mpFirstInstance = pInst; 423 // this one is for outside AquaSalInstance::Yield 424 SalData::ensureThreadAutoreleasePool(); 425 // no focus rects on NWF aqua 426 ImplGetSVData()->maNWFData.mbNoFocusRects = true; 427 ImplGetSVData()->maNWFData.mbNoBoldTabFocus = true; 428 ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true; 429 ImplGetSVData()->maNWFData.mbCenteredTabs = true; 430 ImplGetSVData()->maNWFData.mbProgressNeedsErase = true; 431 ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true; 432 ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10; 433 ImplGetSVData()->maGDIData.mbNoXORClipping = true; 434 ImplGetSVData()->maWinData.mbNoSaveBackground = true; 435 436 return pInst; 437 } 438 439 // ----------------------------------------------------------------------- 440 441 void DestroySalInstance( SalInstance* pInst ) 442 { 443 delete pInst; 444 } 445 446 // ----------------------------------------------------------------------- 447 448 AquaSalInstance::AquaSalInstance() 449 { 450 mpSalYieldMutex = new SalYieldMutex; 451 mpSalYieldMutex->acquire(); 452 ::tools::SolarMutex::SetSolarMutex( mpSalYieldMutex ); 453 maMainThread = vos::OThread::getCurrentIdentifier(); 454 mbWaitingYield = false; 455 maUserEventListMutex = osl_createMutex(); 456 mnActivePrintJobs = 0; 457 maWaitingYieldCond = osl_createCondition(); 458 } 459 460 // ----------------------------------------------------------------------- 461 462 AquaSalInstance::~AquaSalInstance() 463 { 464 ::tools::SolarMutex::SetSolarMutex( 0 ); 465 mpSalYieldMutex->release(); 466 delete mpSalYieldMutex; 467 osl_destroyMutex( maUserEventListMutex ); 468 osl_destroyCondition( maWaitingYieldCond ); 469 } 470 471 // ----------------------------------------------------------------------- 472 473 void AquaSalInstance::wakeupYield() 474 { 475 // wakeup :Yield 476 if( mbWaitingYield ) 477 { 478 SalData::ensureThreadAutoreleasePool(); 479 NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined 480 location: NSZeroPoint 481 modifierFlags: 0 482 timestamp: 0 483 windowNumber: 0 484 context: nil 485 subtype: AquaSalInstance::YieldWakeupEvent 486 data1: 0 487 data2: 0 ]; 488 if( pEvent ) 489 [NSApp postEvent: pEvent atStart: NO]; 490 } 491 } 492 493 // ----------------------------------------------------------------------- 494 495 void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, sal_uInt16 nType, void* pData ) 496 { 497 osl_acquireMutex( maUserEventListMutex ); 498 maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) ); 499 osl_releaseMutex( maUserEventListMutex ); 500 501 // notify main loop that an event has arrived 502 wakeupYield(); 503 } 504 505 // ----------------------------------------------------------------------- 506 507 vos::IMutex* AquaSalInstance::GetYieldMutex() 508 { 509 return mpSalYieldMutex; 510 } 511 512 // ----------------------------------------------------------------------- 513 514 sal_uLong AquaSalInstance::ReleaseYieldMutex() 515 { 516 SalYieldMutex* pYieldMutex = mpSalYieldMutex; 517 if ( pYieldMutex->GetThreadId() == 518 vos::OThread::getCurrentIdentifier() ) 519 { 520 sal_uLong nCount = pYieldMutex->GetAcquireCount(); 521 sal_uLong n = nCount; 522 while ( n ) 523 { 524 pYieldMutex->release(); 525 n--; 526 } 527 528 return nCount; 529 } 530 else 531 return 0; 532 } 533 534 // ----------------------------------------------------------------------- 535 536 void AquaSalInstance::AcquireYieldMutex( sal_uLong nCount ) 537 { 538 SalYieldMutex* pYieldMutex = mpSalYieldMutex; 539 while ( nCount ) 540 { 541 pYieldMutex->acquire(); 542 nCount--; 543 } 544 } 545 546 // ----------------------------------------------------------------------- 547 548 bool AquaSalInstance::CheckYieldMutex() 549 { 550 bool bRet = true; 551 552 SalYieldMutex* pYieldMutex = mpSalYieldMutex; 553 if ( pYieldMutex->GetThreadId() != 554 vos::OThread::getCurrentIdentifier() ) 555 { 556 bRet = false; 557 } 558 559 return bRet; 560 } 561 562 // ----------------------------------------------------------------------- 563 564 bool AquaSalInstance::isNSAppThread() const 565 { 566 return vos::OThread::getCurrentIdentifier() == maMainThread; 567 } 568 569 // ----------------------------------------------------------------------- 570 571 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent ) 572 { 573 switch( [pEvent subtype] ) 574 { 575 case AppStartTimerEvent: 576 AquaSalTimer::handleStartTimerEvent( pEvent ); 577 break; 578 case AppEndLoopEvent: 579 [NSApp stop: NSApp]; 580 break; 581 case AppExecuteSVMain: 582 { 583 sal_Bool bResult = ImplSVMain(); 584 if( gpbInit ) 585 *gpbInit = bResult; 586 [NSApp stop: NSApp]; 587 bLeftMain = true; 588 if( pDockMenu ) 589 { 590 [pDockMenu release]; 591 pDockMenu = nil; 592 } 593 } 594 break; 595 case AppleRemoteEvent: 596 { 597 sal_Int16 nCommand = 0; 598 SalData* pSalData = GetSalData(); 599 bool bIsFullScreenMode = false; 600 601 std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin(); 602 for(; it != pSalData->maFrames.end(); ++it ) 603 { 604 if( (*it)->mbFullScreen ) 605 bIsFullScreenMode = true; 606 } 607 608 switch ([pEvent data1]) 609 { 610 case kRemoteButtonPlay: 611 nCommand = ( bIsFullScreenMode == true ) ? MEDIA_COMMAND_PLAY_PAUSE : MEDIA_COMMAND_PLAY; 612 break; 613 614 // kept for experimentation purpose (scheduled for future implementation) 615 // case kRemoteButtonMenu: nCommand = MEDIA_COMMAND_MENU; break; 616 617 case kRemoteButtonPlus: nCommand = MEDIA_COMMAND_VOLUME_UP; break; 618 619 case kRemoteButtonMinus: nCommand = MEDIA_COMMAND_VOLUME_DOWN; break; 620 621 case kRemoteButtonRight: nCommand = MEDIA_COMMAND_NEXTTRACK; break; 622 623 case kRemoteButtonRight_Hold: nCommand = MEDIA_COMMAND_NEXTTRACK_HOLD; break; 624 625 case kRemoteButtonLeft: nCommand = MEDIA_COMMAND_PREVIOUSTRACK; break; 626 627 case kRemoteButtonLeft_Hold: nCommand = MEDIA_COMMAND_REWIND; break; 628 629 case kRemoteButtonPlay_Hold: nCommand = MEDIA_COMMAND_PLAY_HOLD; break; 630 631 case kRemoteButtonMenu_Hold: nCommand = MEDIA_COMMAND_STOP; break; 632 633 // FIXME : not detected 634 case kRemoteButtonPlus_Hold: 635 case kRemoteButtonMinus_Hold: 636 break; 637 638 default: 639 break; 640 } 641 AquaSalFrame* pFrame = pSalData->maFrames.front(); 642 Window* pWindow = pFrame ? pFrame->GetWindow() : NULL; 643 644 if( pWindow ) 645 { 646 const Point aPoint; 647 CommandEvent aCEvt( aPoint, COMMAND_MEDIA, FALSE, &nCommand ); 648 NotifyEvent aNCmdEvt( EVENT_COMMAND, pWindow, &aCEvt ); 649 650 if ( !ImplCallPreNotify( aNCmdEvt ) ) 651 pWindow->Command( aCEvt ); 652 } 653 654 } 655 break; 656 657 case YieldWakeupEvent: 658 // do nothing, fall out of Yield 659 break; 660 661 default: 662 DBG_ERROR( "unhandled NSApplicationDefined event" ); 663 break; 664 }; 665 } 666 667 // ----------------------------------------------------------------------- 668 669 class ReleasePoolHolder 670 { 671 NSAutoreleasePool* mpPool; 672 public: 673 ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {} 674 ~ReleasePoolHolder() { [mpPool release]; } 675 }; 676 677 void AquaSalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) 678 { 679 // ensure that the per thread autorelease pool is top level and 680 // will therefore not be destroyed by cocoa implicitly 681 SalData::ensureThreadAutoreleasePool(); 682 683 // NSAutoreleasePool documentation suggests we should have 684 // an own pool for each yield level 685 ReleasePoolHolder aReleasePool; 686 687 // Release all locks so that we don't deadlock when we pull pending 688 // events from the event queue 689 bool bDispatchUser = true; 690 while( bDispatchUser ) 691 { 692 sal_uLong nCount = ReleaseYieldMutex(); 693 694 // get one user event 695 osl_acquireMutex( maUserEventListMutex ); 696 SalUserEvent aEvent( NULL, NULL, 0 ); 697 if( ! maUserEvents.empty() ) 698 { 699 aEvent = maUserEvents.front(); 700 maUserEvents.pop_front(); 701 } 702 else 703 bDispatchUser = false; 704 osl_releaseMutex( maUserEventListMutex ); 705 706 AcquireYieldMutex( nCount ); 707 708 // dispatch it 709 if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) ) 710 { 711 aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData ); 712 osl_setCondition( maWaitingYieldCond ); 713 // return if only one event is asked for 714 if( ! bHandleAllCurrentEvents ) 715 return; 716 } 717 } 718 719 // handle cocoa event queue 720 // cocoa events mye be only handled in the thread the NSApp was created 721 if( isNSAppThread() && mnActivePrintJobs == 0 ) 722 { 723 // we need to be woken up by a cocoa-event 724 // if a user event should be posted by the event handling below 725 bool bOldWaitingYield = mbWaitingYield; 726 mbWaitingYield = bWait; 727 728 // handle available events 729 NSEvent* pEvent = nil; 730 bool bHadEvent = false; 731 do 732 { 733 sal_uLong nCount = ReleaseYieldMutex(); 734 735 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil 736 inMode: NSDefaultRunLoopMode dequeue: YES]; 737 if( pEvent ) 738 { 739 [NSApp sendEvent: pEvent]; 740 bHadEvent = true; 741 } 742 [NSApp updateWindows]; 743 744 AcquireYieldMutex( nCount ); 745 } while( bHandleAllCurrentEvents && pEvent ); 746 747 // if we had no event yet, wait for one if requested 748 if( bWait && ! bHadEvent ) 749 { 750 sal_uLong nCount = ReleaseYieldMutex(); 751 752 NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture]; 753 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt 754 inMode: NSDefaultRunLoopMode dequeue: YES]; 755 if( pEvent ) 756 [NSApp sendEvent: pEvent]; 757 [NSApp updateWindows]; 758 759 AcquireYieldMutex( nCount ); 760 761 // #i86581# 762 // FIXME: sometimes the NSTimer will never fire. Firing it by hand then 763 // fixes the problem even seems to set the correct next firing date 764 // Why oh why ? 765 if( ! pEvent && AquaSalTimer::pRunningTimer ) 766 { 767 // this cause crashes on MacOSX 10.4 768 // [AquaSalTimer::pRunningTimer fire]; 769 ImplGetSVData()->mpSalTimer->CallCallback(); 770 } 771 } 772 773 mbWaitingYield = bOldWaitingYield; 774 775 // collect update rectangles 776 const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames ); 777 for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) 778 { 779 if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() ) 780 { 781 (*it)->Flush( (*it)->maInvalidRect ); 782 (*it)->maInvalidRect.SetEmpty(); 783 } 784 } 785 osl_setCondition( maWaitingYieldCond ); 786 } 787 else if( bWait ) 788 { 789 // #i103162# 790 // wait until any thread (most likely the main thread) 791 // has dispatched an event, cop out at 200 ms 792 osl_resetCondition( maWaitingYieldCond ); 793 TimeValue aVal = { 0, 200000000 }; 794 sal_uLong nCount = ReleaseYieldMutex(); 795 osl_waitCondition( maWaitingYieldCond, &aVal ); 796 AcquireYieldMutex( nCount ); 797 } 798 799 // we get some apple events way too early 800 // before the application is ready to handle them, 801 // so their corresponding application events need to be delayed 802 // now is a good time to handle at least one of them 803 if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute ) 804 { 805 // make sure that only one application event is active at a time 806 static bool bInAppEvent = false; 807 if( !bInAppEvent ) 808 { 809 bInAppEvent = true; 810 // get the next delayed application event 811 const ApplicationEvent* pAppEvent = aAppEventList.front(); 812 aAppEventList.pop_front(); 813 // handle one application event (no recursion) 814 const ImplSVData* pSVData = ImplGetSVData(); 815 pSVData->mpApp->AppEvent( *pAppEvent ); 816 delete pAppEvent; 817 // allow the next delayed application event 818 bInAppEvent = false; 819 } 820 } 821 } 822 823 // ----------------------------------------------------------------------- 824 825 bool AquaSalInstance::AnyInput( sal_uInt16 nType ) 826 { 827 if( nType & INPUT_APPEVENT ) 828 { 829 if( ! aAppEventList.empty() ) 830 return true; 831 if( nType == INPUT_APPEVENT ) 832 return false; 833 } 834 835 if( nType & INPUT_TIMER ) 836 { 837 if( AquaSalTimer::pRunningTimer ) 838 { 839 NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate]; 840 if( pDt && [pDt timeIntervalSinceNow] < 0 ) 841 { 842 return true; 843 } 844 } 845 } 846 847 unsigned/*NSUInteger*/ nEventMask = 0; 848 if( nType & INPUT_MOUSE) 849 nEventMask |= 850 NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask | 851 NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask | 852 NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask | 853 NSScrollWheelMask | 854 // NSMouseMovedMask | 855 NSMouseEnteredMask | NSMouseExitedMask; 856 if( nType & INPUT_KEYBOARD) 857 nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; 858 if( nType & INPUT_OTHER) 859 nEventMask |= NSTabletPoint; 860 // TODO: INPUT_PAINT / more INPUT_OTHER 861 if( !nType) 862 return false; 863 864 NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil 865 inMode: NSDefaultRunLoopMode dequeue: NO]; 866 return (pEvent != NULL); 867 } 868 869 // ----------------------------------------------------------------------- 870 871 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, sal_uLong /*nSalFrameStyle*/ ) 872 { 873 return NULL; 874 } 875 876 // ----------------------------------------------------------------------- 877 878 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, sal_uLong nSalFrameStyle ) 879 { 880 SalData::ensureThreadAutoreleasePool(); 881 882 SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle ); 883 return pFrame; 884 } 885 886 // ----------------------------------------------------------------------- 887 888 void AquaSalInstance::DestroyFrame( SalFrame* pFrame ) 889 { 890 delete pFrame; 891 } 892 893 // ----------------------------------------------------------------------- 894 895 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* /* pWindowData */, sal_Bool /* bShow */ ) 896 { 897 // SystemWindowData is meaningless on Mac OS X 898 AquaSalObject *pObject = NULL; 899 900 if ( pParent ) 901 pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent) ); 902 903 return pObject; 904 } 905 906 // ----------------------------------------------------------------------- 907 908 void AquaSalInstance::DestroyObject( SalObject* pObject ) 909 { 910 delete ( pObject ); 911 } 912 913 // ----------------------------------------------------------------------- 914 915 SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) 916 { 917 return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ); 918 } 919 920 // ----------------------------------------------------------------------- 921 922 void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter ) 923 { 924 delete pPrinter; 925 } 926 927 // ----------------------------------------------------------------------- 928 929 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) 930 { 931 NSArray* pNames = [NSPrinter printerNames]; 932 NSArray* pTypes = [NSPrinter printerTypes]; 933 unsigned int nNameCount = pNames ? [pNames count] : 0; 934 unsigned int nTypeCount = pTypes ? [pTypes count] : 0; 935 DBG_ASSERT( nTypeCount == nNameCount, "type count not equal to printer count" ); 936 for( unsigned int i = 0; i < nNameCount; i++ ) 937 { 938 NSString* pName = [pNames objectAtIndex: i]; 939 NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil; 940 if( pName ) 941 { 942 SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; 943 pInfo->maPrinterName = GetOUString( pName ); 944 if( pType ) 945 pInfo->maDriver = GetOUString( pType ); 946 pInfo->mnStatus = 0; 947 pInfo->mnJobs = 0; 948 pInfo->mpSysData = NULL; 949 950 pList->Add( pInfo ); 951 } 952 } 953 } 954 955 // ----------------------------------------------------------------------- 956 957 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* ) 958 { 959 } 960 961 // ----------------------------------------------------------------------- 962 963 void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) 964 { 965 delete pInfo; 966 } 967 968 // ----------------------------------------------------------------------- 969 970 XubString AquaSalInstance::GetDefaultPrinter() 971 { 972 // #i113170# may not be the main thread if called from UNO API 973 SalData::ensureThreadAutoreleasePool(); 974 975 if( ! maDefaultPrinter.getLength() ) 976 { 977 NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo]; 978 DBG_ASSERT( pPI, "no print info" ); 979 if( pPI ) 980 { 981 NSPrinter* pPr = [pPI printer]; 982 DBG_ASSERT( pPr, "no printer in default info" ); 983 if( pPr ) 984 { 985 NSString* pDefName = [pPr name]; 986 DBG_ASSERT( pDefName, "printer has no name" ); 987 maDefaultPrinter = GetOUString( pDefName ); 988 } 989 } 990 } 991 return maDefaultPrinter; 992 } 993 994 // ----------------------------------------------------------------------- 995 996 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, 997 ImplJobSetup* pSetupData ) 998 { 999 // #i113170# may not be the main thread if called from UNO API 1000 SalData::ensureThreadAutoreleasePool(); 1001 1002 SalInfoPrinter* pNewInfoPrinter = NULL; 1003 if( pQueueInfo ) 1004 { 1005 pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo ); 1006 if( pSetupData ) 1007 pNewInfoPrinter->SetPrinterData( pSetupData ); 1008 } 1009 1010 return pNewInfoPrinter; 1011 } 1012 1013 // ----------------------------------------------------------------------- 1014 1015 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) 1016 { 1017 // #i113170# may not be the main thread if called from UNO API 1018 SalData::ensureThreadAutoreleasePool(); 1019 1020 delete pPrinter; 1021 } 1022 1023 // ----------------------------------------------------------------------- 1024 1025 SalSystem* AquaSalInstance::CreateSystem() 1026 { 1027 return new AquaSalSystem(); 1028 } 1029 1030 // ----------------------------------------------------------------------- 1031 1032 void AquaSalInstance::DestroySystem( SalSystem* pSystem ) 1033 { 1034 delete pSystem; 1035 } 1036 1037 // ----------------------------------------------------------------------- 1038 1039 void AquaSalInstance::SetEventCallback( void*, bool(*)(void*,void*,int) ) 1040 { 1041 } 1042 1043 // ----------------------------------------------------------------------- 1044 1045 void AquaSalInstance::SetErrorEventCallback( void*, bool(*)(void*,void*,int) ) 1046 { 1047 } 1048 1049 // ----------------------------------------------------------------------- 1050 1051 void* AquaSalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) 1052 { 1053 rReturnedBytes = 1; 1054 rReturnedType = AsciiCString; 1055 return (void*)""; 1056 } 1057 1058 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts 1059 // to UTF-8 before encoding non ascii characters, which is not what other apps expect. 1060 static rtl::OUString translateToExternalUrl(const rtl::OUString& internalUrl) 1061 { 1062 rtl::OUString extUrl; 1063 1064 uno::Reference< lang::XMultiServiceFactory > sm = comphelper::getProcessServiceFactory(); 1065 if (sm.is()) 1066 { 1067 uno::Reference< beans::XPropertySet > pset; 1068 sm->queryInterface( getCppuType( &pset )) >>= pset; 1069 if (pset.is()) 1070 { 1071 uno::Reference< uno::XComponentContext > context; 1072 static const rtl::OUString DEFAULT_CONTEXT( RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ); 1073 pset->getPropertyValue(DEFAULT_CONTEXT) >>= context; 1074 if (context.is()) 1075 extUrl = uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl); 1076 } 1077 } 1078 return extUrl; 1079 } 1080 1081 // #i104525# many versions of OSX have problems with some URLs: 1082 // when an app requests OSX to add one of these URLs to the "Recent Items" list 1083 // then this app gets killed (TextEdit, Preview, etc. and also OOo) 1084 static bool isDangerousUrl( const rtl::OUString& rUrl ) 1085 { 1086 // use a heuristic that detects all known cases since there is no official comment 1087 // on the exact impact and root cause of the OSX bug 1088 const int nLen = rUrl.getLength(); 1089 const sal_Unicode* p = rUrl.getStr(); 1090 for( int i = 0; i < nLen-3; ++i, ++p ) { 1091 if( p[0] != '%' ) 1092 continue; 1093 // escaped percent? 1094 if( (p[1] == '2') && (p[2] == '5') ) 1095 return true; 1096 // escapes are considered to be UTF-8 encoded 1097 // => check for invalid UTF-8 leading byte 1098 if( (p[1] != 'f') && (p[1] != 'F') ) 1099 continue; 1100 int cLowNibble = p[2]; 1101 if( (cLowNibble >= '0' ) && (cLowNibble <= '9')) 1102 return false; 1103 if( cLowNibble >= 'a' ) 1104 cLowNibble -= 'a' - 'A'; 1105 if( (cLowNibble < 'A') || (cLowNibble >= 'C')) 1106 return true; 1107 } 1108 1109 return false; 1110 } 1111 1112 void AquaSalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& /*rMimeType*/) 1113 { 1114 // Convert file URL for external use (see above) 1115 rtl::OUString externalUrl = translateToExternalUrl(rFileUrl); 1116 if( 0 == externalUrl.getLength() ) 1117 externalUrl = rFileUrl; 1118 1119 if( externalUrl.getLength() && !isDangerousUrl( externalUrl ) ) 1120 { 1121 NSString* pString = CreateNSString( externalUrl ); 1122 NSURL* pURL = [NSURL URLWithString: pString]; 1123 1124 if( pURL ) 1125 { 1126 NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController]; 1127 [pCtrl noteNewRecentDocumentURL: pURL]; 1128 } 1129 if( pString ) 1130 [pString release]; 1131 } 1132 } 1133 1134 1135 // ----------------------------------------------------------------------- 1136 1137 SalTimer* AquaSalInstance::CreateSalTimer() 1138 { 1139 return new AquaSalTimer(); 1140 } 1141 1142 // ----------------------------------------------------------------------- 1143 1144 SalSystem* AquaSalInstance::CreateSalSystem() 1145 { 1146 return new AquaSalSystem(); 1147 } 1148 1149 // ----------------------------------------------------------------------- 1150 1151 SalBitmap* AquaSalInstance::CreateSalBitmap() 1152 { 1153 return new AquaSalBitmap(); 1154 } 1155 1156 // ----------------------------------------------------------------------- 1157 1158 SalSession* AquaSalInstance::CreateSalSession() 1159 { 1160 return NULL; 1161 } 1162 1163 // ----------------------------------------------------------------------- 1164 1165 class MacImeStatus : public SalI18NImeStatus 1166 { 1167 public: 1168 MacImeStatus() {} 1169 virtual ~MacImeStatus() {} 1170 1171 // asks whether there is a status window available 1172 // to toggle into menubar 1173 virtual bool canToggle() { return false; } 1174 virtual void toggle() {} 1175 }; 1176 1177 // ----------------------------------------------------------------------- 1178 1179 SalI18NImeStatus* AquaSalInstance::CreateI18NImeStatus() 1180 { 1181 return new MacImeStatus(); 1182 } 1183 1184 // YieldMutexReleaser 1185 YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 ) 1186 { 1187 SalData* pSalData = GetSalData(); 1188 if( ! pSalData->mpFirstInstance->isNSAppThread() ) 1189 { 1190 SalData::ensureThreadAutoreleasePool(); 1191 mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex(); 1192 } 1193 } 1194 1195 YieldMutexReleaser::~YieldMutexReleaser() 1196 { 1197 if( mnCount != 0 ) 1198 GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount ); 1199 } 1200 1201 ////////////////////////////////////////////////////////////// 1202 rtl::OUString GetOUString( CFStringRef rStr ) 1203 { 1204 if( rStr == 0 ) 1205 return rtl::OUString(); 1206 CFIndex nLength = CFStringGetLength( rStr ); 1207 if( nLength == 0 ) 1208 return rtl::OUString(); 1209 const UniChar* pConstStr = CFStringGetCharactersPtr( rStr ); 1210 if( pConstStr ) 1211 return rtl::OUString( pConstStr, nLength ); 1212 UniChar* pStr = reinterpret_cast<UniChar*>( rtl_allocateMemory( sizeof(UniChar)*nLength ) ); 1213 CFRange aRange = { 0, nLength }; 1214 CFStringGetCharacters( rStr, aRange, pStr ); 1215 rtl::OUString aRet( pStr, nLength ); 1216 rtl_freeMemory( pStr ); 1217 return aRet; 1218 } 1219 1220 rtl::OUString GetOUString( NSString* pStr ) 1221 { 1222 if( ! pStr ) 1223 return rtl::OUString(); 1224 int nLen = [pStr length]; 1225 if( nLen == 0 ) 1226 return rtl::OUString(); 1227 1228 rtl::OUStringBuffer aBuf( nLen+1 ); 1229 aBuf.setLength( nLen ); 1230 [pStr getCharacters: const_cast<sal_Unicode*>(aBuf.getStr())]; 1231 return aBuf.makeStringAndClear(); 1232 } 1233 1234 CFStringRef CreateCFString( const rtl::OUString& rStr ) 1235 { 1236 return CFStringCreateWithCharacters(kCFAllocatorDefault, rStr.getStr(), rStr.getLength() ); 1237 } 1238 1239 NSString* CreateNSString( const rtl::OUString& rStr ) 1240 { 1241 return [[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()]; 1242 } 1243 1244 CGImageRef CreateCGImage( const Image& rImage ) 1245 { 1246 BitmapEx aBmpEx( rImage.GetBitmapEx() ); 1247 Bitmap aBmp( aBmpEx.GetBitmap() ); 1248 1249 if( ! aBmp || ! aBmp.ImplGetImpBitmap() ) 1250 return NULL; 1251 1252 // simple case, no transparency 1253 AquaSalBitmap* pSalBmp = static_cast<AquaSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap()); 1254 1255 if( ! pSalBmp ) 1256 return NULL; 1257 1258 CGImageRef xImage = NULL; 1259 if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) ) 1260 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1261 else if( aBmpEx.IsAlpha() ) 1262 { 1263 AlphaMask aAlphaMask( aBmpEx.GetAlpha() ); 1264 Bitmap aMask( aAlphaMask.GetBitmap() ); 1265 AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); 1266 if( pMaskBmp ) 1267 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1268 else 1269 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1270 } 1271 else if( aBmpEx.GetTransparentType() == TRANSPARENT_BITMAP ) 1272 { 1273 Bitmap aMask( aBmpEx.GetMask() ); 1274 AquaSalBitmap* pMaskBmp = static_cast<AquaSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); 1275 if( pMaskBmp ) 1276 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1277 else 1278 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight ); 1279 } 1280 else if( aBmpEx.GetTransparentType() == TRANSPARENT_COLOR ) 1281 { 1282 Color aTransColor( aBmpEx.GetTransparentColor() ); 1283 SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() ); 1284 xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor ); 1285 } 1286 1287 return xImage; 1288 } 1289 1290 NSImage* CreateNSImage( const Image& rImage ) 1291 { 1292 CGImageRef xImage = CreateCGImage( rImage ); 1293 1294 if( ! xImage ) 1295 return nil; 1296 1297 Size aSize( rImage.GetSizePixel() ); 1298 NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )]; 1299 if( pImage ) 1300 { 1301 [pImage setFlipped: YES]; 1302 [pImage lockFocus]; 1303 1304 NSGraphicsContext* pContext = [NSGraphicsContext currentContext]; 1305 CGContextRef rCGContext = reinterpret_cast<CGContextRef>([pContext graphicsPort]); 1306 1307 const CGRect aDstRect = CGRectMake( 0, 0, aSize.Width(), aSize.Height()); 1308 CGContextDrawImage( rCGContext, aDstRect, xImage ); 1309 1310 [pImage unlockFocus]; 1311 } 1312 1313 CGImageRelease( xImage ); 1314 1315 return pImage; 1316 } 1317