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