bump product version to 6.4.0.3
[LibreOffice.git] / vcl / osx / salinst.cxx
blob45f9ba3546f3fe675fa2896faa5f4d62c29fbcbc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
24 #include <condition_variable>
25 #include <mutex>
26 #include <utility>
28 #include <config_features.h>
30 #include <stdio.h>
32 #include <comphelper/solarmutex.hxx>
34 #include <comphelper/lok.hxx>
36 #include <osl/process.h>
38 #include <rtl/ustrbuf.hxx>
39 #include <vclpluginapi.h>
40 #include <vcl/svapp.hxx>
41 #include <vcl/window.hxx>
42 #include <vcl/idle.hxx>
43 #include <vcl/svmain.hxx>
44 #include <vcl/opengl/OpenGLContext.hxx>
45 #include <vcl/commandevent.hxx>
46 #include <vcl/event.hxx>
48 #include <osx/saldata.hxx>
49 #include <osx/salinst.h>
50 #include <osx/salframe.h>
51 #include <osx/salobj.h>
52 #include <osx/salsys.h>
53 #include <quartz/salvd.h>
54 #include <quartz/salbmp.h>
55 #include <quartz/utils.h>
56 #include <osx/salprn.h>
57 #include <osx/saltimer.h>
58 #include <osx/vclnsapp.h>
59 #include <osx/runinmain.hxx>
61 #include <print.h>
63 #include <comphelper/processfactory.hxx>
65 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
66 #include <com/sun/star/uno/XComponentContext.hpp>
68 #include <premac.h>
69 #include <Foundation/Foundation.h>
70 #include <ApplicationServices/ApplicationServices.h>
71 #import "apple_remote/RemoteMainController.h"
72 #include <apple_remote/RemoteControl.h>
73 #include <postmac.h>
75 extern "C" {
76 #include <crt_externs.h>
79 using namespace std;
80 using namespace ::com::sun::star;
82 static int* gpnInit = nullptr;
83 static NSMenu* pDockMenu = nil;
84 static bool bLeftMain = false;
86 class AquaDelayedSettingsChanged : public Idle
88 bool mbInvalidate;
90 public:
91 AquaDelayedSettingsChanged( bool bInvalidate ) :
92 mbInvalidate( bInvalidate )
96 virtual void Invoke() override
98 AquaSalInstance *pInst = GetSalData()->mpInstance;
99 SalFrame *pAnyFrame = pInst->anyFrame();
100 if( pAnyFrame )
101 pAnyFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
103 if( mbInvalidate )
105 for( auto pSalFrame : pInst->getFrames() )
107 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
108 if( pFrame->mbShown )
109 pFrame->SendPaintEvent();
112 delete this;
116 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
118 osl::Guard< comphelper::SolarMutex > aGuard( *GetYieldMutex() );
119 AquaDelayedSettingsChanged* pIdle = new AquaDelayedSettingsChanged( bInvalidate );
120 pIdle->SetDebugName( "AquaSalInstance AquaDelayedSettingsChanged" );
121 pIdle->Start();
124 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
125 std::list<const ApplicationEvent*> AquaSalInstance::aAppEventList;
127 NSMenu* AquaSalInstance::GetDynamicDockMenu()
129 if( ! pDockMenu && ! bLeftMain )
130 pDockMenu = [[NSMenu alloc] initWithTitle: @""];
131 return pDockMenu;
134 bool AquaSalInstance::isOnCommandLine( const OUString& rArg )
136 sal_uInt32 nArgs = osl_getCommandArgCount();
137 for( sal_uInt32 i = 0; i < nArgs; i++ )
139 OUString aArg;
140 osl_getCommandArg( i, &aArg.pData );
141 if( aArg.equals( rArg ) )
142 return true;
144 return false;
147 void AquaSalInstance::AfterAppInit()
149 [[NSNotificationCenter defaultCenter] addObserver: NSApp
150 selector: @selector(systemColorsChanged:)
151 name: NSSystemColorsDidChangeNotification
152 object: nil ];
153 [[NSNotificationCenter defaultCenter] addObserver: NSApp
154 selector: @selector(screenParametersChanged:)
155 name: NSApplicationDidChangeScreenParametersNotification
156 object: nil ];
157 // add observers for some settings changes that affect vcl's settings
158 // scrollbar variant
159 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
160 selector: @selector(scrollbarVariantChanged:)
161 name: @"AppleAquaScrollBarVariantChanged"
162 object: nil ];
163 // scrollbar page behavior ("jump to here" or not)
164 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
165 selector: @selector(scrollbarSettingsChanged:)
166 name: @"AppleNoRedisplayAppearancePreferenceChanged"
167 object: nil ];
168 #if !HAVE_FEATURE_MACOSX_SANDBOX
169 // Initialize Apple Remote
170 GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init];
172 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
173 selector: @selector(applicationWillBecomeActive:)
174 name: @"AppleRemoteWillBecomeActive"
175 object: nil ];
177 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
178 selector: @selector(applicationWillResignActive:)
179 name: @"AppleRemoteWillResignActive"
180 object: nil ];
181 #endif
184 void SalAbort( const OUString& rErrorText, bool bDumpCore )
186 if( rErrorText.isEmpty() )
187 fprintf( stderr, "Application Error " );
188 else
189 fprintf( stderr, "%s ",
190 OUStringToOString( rErrorText, osl_getThreadTextEncoding() ).getStr() );
191 if( bDumpCore )
192 abort();
193 else
194 _exit(1);
197 SalYieldMutex::SalYieldMutex()
198 : m_aCodeBlock( nullptr )
202 SalYieldMutex::~SalYieldMutex()
206 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
208 AquaSalInstance *pInst = GetSalData()->mpInstance;
209 if ( pInst && pInst->IsMainThread() )
211 if ( pInst->mbNoYieldLock )
212 return;
213 do {
214 RuninmainBlock block = nullptr;
216 std::unique_lock<std::mutex> g(m_runInMainMutex);
217 if (m_aMutex.tryToAcquire()) {
218 assert(m_aCodeBlock == nullptr);
219 m_wakeUpMain = false;
220 break;
222 // wait for doRelease() or RUNINMAIN_* to set the condition
223 m_aInMainCondition.wait(g, [this]() { return m_wakeUpMain; });
224 m_wakeUpMain = false;
225 std::swap(block, m_aCodeBlock);
227 if ( block )
229 assert( !pInst->mbNoYieldLock );
230 pInst->mbNoYieldLock = true;
231 block();
232 pInst->mbNoYieldLock = false;
233 Block_release( block );
234 std::scoped_lock<std::mutex> g(m_runInMainMutex);
235 assert(!m_resultReady);
236 m_resultReady = true;
237 m_aResultCondition.notify_all();
240 while ( true );
242 else
243 m_aMutex.acquire();
244 ++m_nCount;
245 --nLockCount;
247 comphelper::SolarMutex::doAcquire( nLockCount );
250 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
252 AquaSalInstance *pInst = GetSalData()->mpInstance;
253 if ( pInst->mbNoYieldLock && pInst->IsMainThread() )
254 return 1;
255 sal_uInt32 nCount;
257 std::scoped_lock<std::mutex> g(m_runInMainMutex);
258 // read m_nCount before doRelease
259 bool const isReleased(bUnlockAll || m_nCount == 1);
260 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
261 if (isReleased && !pInst->IsMainThread()) {
262 m_wakeUpMain = true;
263 m_aInMainCondition.notify_all();
266 return nCount;
269 bool SalYieldMutex::IsCurrentThread() const
271 if ( !GetSalData()->mpInstance->mbNoYieldLock )
272 return comphelper::SolarMutex::IsCurrentThread();
273 else
274 return GetSalData()->mpInstance->IsMainThread();
277 // some convenience functions regarding the yield mutex, aka solar mutex
279 bool ImplSalYieldMutexTryToAcquire()
281 AquaSalInstance* pInst = GetSalData()->mpInstance;
282 if ( pInst )
283 return pInst->GetYieldMutex()->tryToAcquire();
284 else
285 return FALSE;
288 void ImplSalYieldMutexRelease()
290 AquaSalInstance* pInst = GetSalData()->mpInstance;
291 if ( pInst )
292 pInst->GetYieldMutex()->release();
295 extern "C" {
296 VCLPLUG_OSX_PUBLIC SalInstance* create_SalInstance()
298 SalData* pSalData = new SalData;
300 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
301 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
302 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
303 [ pool drain ];
305 // create our cocoa NSApplication
306 [VCL_NSApplication sharedApplication];
308 SalData::ensureThreadAutoreleasePool();
310 // put cocoa into multithreaded mode
311 [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
313 // activate our delegate methods
314 [NSApp setDelegate: NSApp];
316 SAL_WARN_IF( pSalData->mpInstance != nullptr, "vcl", "more than one instance created" );
317 AquaSalInstance* pInst = new AquaSalInstance;
319 // init instance (only one instance in this version !!!)
320 pSalData->mpInstance = pInst;
321 // this one is for outside AquaSalInstance::Yield
322 SalData::ensureThreadAutoreleasePool();
323 // no focus rects on NWF
324 ImplGetSVData()->maNWFData.mbNoFocusRects = true;
325 ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true;
326 ImplGetSVData()->maNWFData.mbCenteredTabs = true;
327 ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10;
329 return pInst;
333 AquaSalInstance::AquaSalInstance()
334 : SalInstance(std::make_unique<SalYieldMutex>())
335 , mnActivePrintJobs( 0 )
336 , mbIsLiveResize( false )
337 , mbNoYieldLock( false )
338 , mbTimerProcessed( false )
340 maMainThread = osl::Thread::getCurrentIdentifier();
342 ImplSVData* pSVData = ImplGetSVData();
343 pSVData->maAppData.mxToolkitName = OUString("osx");
346 AquaSalInstance::~AquaSalInstance()
348 [NSApp stop: NSApp];
349 bLeftMain = true;
350 if( pDockMenu )
352 [pDockMenu release];
353 pDockMenu = nil;
357 void AquaSalInstance::TriggerUserEventProcessing()
359 dispatch_async(dispatch_get_main_queue(),^{
360 ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO );
364 void AquaSalInstance::ProcessEvent( SalUserEvent aEvent )
366 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
367 maWaitingYieldCond.set();
370 bool AquaSalInstance::IsMainThread() const
372 return osl::Thread::getCurrentIdentifier() == maMainThread;
375 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
377 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
378 int nSubtype = [pEvent subtype];
379 switch( nSubtype )
381 case AppStartTimerEvent:
382 if ( pTimer )
383 pTimer->handleStartTimerEvent( pEvent );
384 break;
385 case AppExecuteSVMain:
387 int nRet = ImplSVMain();
388 if (gpnInit)
389 *gpnInit = nRet;
390 [NSApp stop: NSApp];
391 break;
393 case DispatchTimerEvent:
395 AquaSalInstance *pInst = GetSalData()->mpInstance;
396 if ( pTimer && pInst )
397 pInst->mbTimerProcessed = pTimer->handleDispatchTimerEvent( pEvent );
398 break;
400 #if !HAVE_FEATURE_MACOSX_SANDBOX
401 case AppleRemoteControlEvent: // Defined in <apple_remote/RemoteMainController.h>
403 MediaCommand nCommand;
404 AquaSalInstance *pInst = GetSalData()->mpInstance;
405 bool bIsFullScreenMode = false;
407 for( auto pSalFrame : pInst->getFrames() )
409 const AquaSalFrame* pFrame = static_cast<const AquaSalFrame*>( pSalFrame );
410 if ( pFrame->mbFullScreen )
412 bIsFullScreenMode = true;
413 break;
417 switch ([pEvent data1])
419 case kRemoteButtonPlay:
420 nCommand = bIsFullScreenMode ? MediaCommand::PlayPause : MediaCommand::Play;
421 break;
423 // kept for experimentation purpose (scheduled for future implementation)
424 // case kRemoteButtonMenu: nCommand = MediaCommand::Menu; break;
426 case kRemoteButtonPlus: nCommand = MediaCommand::VolumeUp; break;
428 case kRemoteButtonMinus: nCommand = MediaCommand::VolumeDown; break;
430 case kRemoteButtonRight: nCommand = MediaCommand::NextTrack; break;
432 case kRemoteButtonRight_Hold: nCommand = MediaCommand::NextTrackHold; break;
434 case kRemoteButtonLeft: nCommand = MediaCommand::PreviousTrack; break;
436 case kRemoteButtonLeft_Hold: nCommand = MediaCommand::Rewind; break;
438 case kRemoteButtonPlay_Hold: nCommand = MediaCommand::PlayHold; break;
440 case kRemoteButtonMenu_Hold: nCommand = MediaCommand::Stop; break;
442 // FIXME : not detected
443 case kRemoteButtonPlus_Hold:
444 case kRemoteButtonMinus_Hold:
445 break;
447 default:
448 break;
450 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pInst->anyFrame() );
451 vcl::Window* pWindow = pFrame ? pFrame->GetWindow() : nullptr;
452 if( pWindow )
454 const Point aPoint;
455 CommandMediaData aMediaData(nCommand);
456 CommandEvent aCEvt( aPoint, CommandEventId::Media, FALSE, &aMediaData );
457 NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt );
459 if ( !ImplCallPreNotify( aNCmdEvt ) )
460 pWindow->Command( aCEvt );
464 break;
465 #endif
467 case YieldWakeupEvent:
468 // do nothing, fall out of Yield
469 break;
471 default:
472 OSL_FAIL( "unhandled NSApplicationDefined event" );
473 break;
477 bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents )
479 OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents), boolean )
480 assert( false && "Don't call this from the main thread!" );
481 return false;
485 static bool isWakeupEvent( NSEvent *pEvent )
487 SAL_WNODEPRECATED_DECLARATIONS_PUSH
488 return NSApplicationDefined == [pEvent type]
489 && AquaSalInstance::YieldWakeupEvent == static_cast<int>([pEvent subtype]);
490 SAL_WNODEPRECATED_DECLARATIONS_POP
493 bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
495 // ensure that the per thread autorelease pool is top level and
496 // will therefore not be destroyed by cocoa implicitly
497 SalData::ensureThreadAutoreleasePool();
499 // NSAutoreleasePool documentation suggests we should have
500 // an own pool for each yield level
501 ReleasePoolHolder aReleasePool;
503 // first, process current user events
504 bool bHadEvent = DispatchUserEvents( bHandleAllCurrentEvents );
505 if ( !bHandleAllCurrentEvents && bHadEvent )
506 return true;
508 // handle cocoa event queue
509 // cocoa events may be only handled in the thread the NSApp was created
510 if( IsMainThread() && mnActivePrintJobs == 0 )
512 // handle available events
513 NSEvent* pEvent = nil;
514 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
515 mbTimerProcessed = false;
519 SolarMutexReleaser aReleaser;
521 pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
522 untilDate: nil
523 inMode: NSDefaultRunLoopMode
524 dequeue: YES];
525 if( pEvent )
527 [NSApp sendEvent: pEvent];
528 if ( isWakeupEvent( pEvent ) )
529 continue;
530 bHadEvent = true;
533 [NSApp updateWindows];
535 if ( !bHandleAllCurrentEvents || !pEvent || now < [pEvent timestamp] )
536 break;
538 while( true );
540 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
541 if ( !mbTimerProcessed && pTimer && pTimer->IsDirectTimeout() )
543 pTimer->handleTimerElapsed();
544 bHadEvent = true;
547 // if we had no event yet, wait for one if requested
548 if( bWait && ! bHadEvent )
550 SolarMutexReleaser aReleaser;
552 pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
553 untilDate: [NSDate distantFuture]
554 inMode: NSDefaultRunLoopMode
555 dequeue: YES];
556 if( pEvent )
558 [NSApp sendEvent: pEvent];
559 if ( !isWakeupEvent( pEvent ) )
560 bHadEvent = true;
562 [NSApp updateWindows];
565 // collect update rectangles
566 for( auto pSalFrame : GetSalData()->mpInstance->getFrames() )
568 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
569 if( pFrame->mbShown && ! pFrame->maInvalidRect.IsEmpty() )
571 pFrame->Flush( pFrame->maInvalidRect );
572 pFrame->maInvalidRect.SetEmpty();
576 if ( bHadEvent )
577 maWaitingYieldCond.set();
579 else
581 bHadEvent = RunInMainYield( bHandleAllCurrentEvents );
582 if ( !bHadEvent && bWait )
584 // #i103162#
585 // wait until the main thread has dispatched an event
586 maWaitingYieldCond.reset();
587 SolarMutexReleaser aReleaser;
588 maWaitingYieldCond.wait();
592 // we get some apple events way too early
593 // before the application is ready to handle them,
594 // so their corresponding application events need to be delayed
595 // now is a good time to handle at least one of them
596 if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
598 // make sure that only one application event is active at a time
599 static bool bInAppEvent = false;
600 if( !bInAppEvent )
602 bInAppEvent = true;
603 // get the next delayed application event
604 const ApplicationEvent* pAppEvent = aAppEventList.front();
605 aAppEventList.pop_front();
606 // handle one application event (no recursion)
607 const ImplSVData* pSVData = ImplGetSVData();
608 pSVData->mpApp->AppEvent( *pAppEvent );
609 delete pAppEvent;
610 // allow the next delayed application event
611 bInAppEvent = false;
615 return bHadEvent;
618 bool AquaSalInstance::AnyInput( VclInputFlags nType )
620 if( nType & VclInputFlags::APPEVENT )
622 if( ! aAppEventList.empty() )
623 return true;
624 if( nType == VclInputFlags::APPEVENT )
625 return false;
628 OSX_INST_RUNINMAIN_UNION( AnyInput( nType ), boolean )
630 if( nType & VclInputFlags::TIMER )
632 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
633 if (pTimer && pTimer->IsTimerElapsed())
634 return true;
637 unsigned/*NSUInteger*/ nEventMask = 0;
638 SAL_WNODEPRECATED_DECLARATIONS_PUSH
639 // 'NSFlagsChangedMask' is deprecated: first deprecated in macOS 10.12
640 // 'NSKeyDownMask' is deprecated: first deprecated in macOS 10.12
641 // 'NSKeyUpMask' is deprecated: first deprecated in macOS 10.12
642 // 'NSLeftMouseDownMask' is deprecated: first deprecated in macOS 10.12
643 // 'NSLeftMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
644 // 'NSLeftMouseUpMask' is deprecated: first deprecated in macOS 10.12
645 // 'NSMouseEnteredMask' is deprecated: first deprecated in macOS 10.12
646 // 'NSMouseExitedMask' is deprecated: first deprecated in macOS 10.12
647 // 'NSOtherMouseDownMask' is deprecated: first deprecated in macOS 10.12
648 // 'NSOtherMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
649 // 'NSOtherMouseUpMask' is deprecated: first deprecated in macOS 10.12
650 // 'NSRightMouseDownMask' is deprecated: first deprecated in macOS 10.12
651 // 'NSRightMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
652 // 'NSRightMouseUpMask' is deprecated: first deprecated in macOS 10.12
653 // 'NSScrollWheelMask' is deprecated: first deprecated in macOS 10.12
654 // 'NSTabletPoint' is deprecated: first deprecated in macOS 10.12
655 if( nType & VclInputFlags::MOUSE)
656 nEventMask |=
657 NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask |
658 NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask |
659 NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
660 NSScrollWheelMask |
661 // NSMouseMovedMask |
662 NSMouseEnteredMask | NSMouseExitedMask;
663 if( nType & VclInputFlags::KEYBOARD)
664 nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
665 if( nType & VclInputFlags::OTHER)
666 nEventMask |= NSTabletPoint | NSApplicationDefinedMask;
667 SAL_WNODEPRECATED_DECLARATIONS_POP
668 // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
669 if( !bool(nType) )
670 return false;
672 NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
673 inMode: NSDefaultRunLoopMode dequeue: NO];
674 return (pEvent != nullptr);
677 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, SalFrameStyleFlags /*nSalFrameStyle*/ )
679 return nullptr;
682 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
684 OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent, nSalFrameStyle ), SalFrame* )
685 return new AquaSalFrame( pParent, nSalFrameStyle );
688 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
690 OSX_INST_RUNINMAIN( DestroyFrame( pFrame ) )
691 delete pFrame;
694 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool /* bShow */ )
696 if ( !pParent )
697 return nullptr;
699 OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent, pWindowData, false ), SalObject* )
700 return new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData );
703 void AquaSalInstance::DestroyObject( SalObject* pObject )
705 OSX_INST_RUNINMAIN( DestroyObject( pObject ) )
706 delete pObject;
709 std::unique_ptr<SalPrinter> AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
711 return std::unique_ptr<SalPrinter>(new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ));
714 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
716 NSArray* pNames = [NSPrinter printerNames];
717 NSArray* pTypes = [NSPrinter printerTypes];
718 unsigned int nNameCount = pNames ? [pNames count] : 0;
719 unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
720 SAL_WARN_IF( nTypeCount != nNameCount, "vcl", "type count not equal to printer count" );
721 for( unsigned int i = 0; i < nNameCount; i++ )
723 NSString* pName = [pNames objectAtIndex: i];
724 NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
725 if( pName )
727 std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
728 pInfo->maPrinterName = GetOUString( pName );
729 if( pType )
730 pInfo->maDriver = GetOUString( pType );
731 pInfo->mnStatus = PrintQueueFlags::NONE;
732 pInfo->mnJobs = 0;
734 pList->Add( std::move(pInfo) );
739 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
743 OUString AquaSalInstance::GetDefaultPrinter()
745 // #i113170# may not be the main thread if called from UNO API
746 SalData::ensureThreadAutoreleasePool();
748 if( maDefaultPrinter.isEmpty() )
750 NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
751 SAL_WARN_IF( !pPI, "vcl", "no print info" );
752 if( pPI )
754 NSPrinter* pPr = [pPI printer];
755 SAL_WARN_IF( !pPr, "vcl", "no printer in default info" );
756 if( pPr )
758 NSString* pDefName = [pPr name];
759 SAL_WARN_IF( !pDefName, "vcl", "printer has no name" );
760 maDefaultPrinter = GetOUString( pDefName );
764 return maDefaultPrinter;
767 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
768 ImplJobSetup* pSetupData )
770 // #i113170# may not be the main thread if called from UNO API
771 SalData::ensureThreadAutoreleasePool();
773 SalInfoPrinter* pNewInfoPrinter = nullptr;
774 if( pQueueInfo )
776 pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
777 if( pSetupData )
778 pNewInfoPrinter->SetPrinterData( pSetupData );
781 return pNewInfoPrinter;
784 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
786 // #i113170# may not be the main thread if called from UNO API
787 SalData::ensureThreadAutoreleasePool();
789 delete pPrinter;
792 OUString AquaSalInstance::GetConnectionIdentifier()
794 return OUString();
797 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
798 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
799 static OUString translateToExternalUrl(const OUString& internalUrl)
801 uno::Reference< uno::XComponentContext > context(
802 comphelper::getProcessComponentContext());
803 return uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
806 // #i104525# many versions of OSX have problems with some URLs:
807 // when an app requests OSX to add one of these URLs to the "Recent Items" list
808 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
809 static bool isDangerousUrl( const OUString& rUrl )
811 // use a heuristic that detects all known cases since there is no official comment
812 // on the exact impact and root cause of the OSX bug
813 const int nLen = rUrl.getLength();
814 const sal_Unicode* p = rUrl.getStr();
815 for( int i = 0; i < nLen-3; ++i, ++p ) {
816 if( p[0] != '%' )
817 continue;
818 // escaped percent?
819 if( (p[1] == '2') && (p[2] == '5') )
820 return true;
821 // escapes are considered to be UTF-8 encoded
822 // => check for invalid UTF-8 leading byte
823 if( (p[1] != 'f') && (p[1] != 'F') )
824 continue;
825 int cLowNibble = p[2];
826 if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
827 return false;
828 if( cLowNibble >= 'a' )
829 cLowNibble -= 'a' - 'A';
830 if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
831 return true;
834 return false;
837 void AquaSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& /*rDocumentService*/)
839 // Convert file URL for external use (see above)
840 OUString externalUrl = translateToExternalUrl(rFileUrl);
841 if( externalUrl.isEmpty() )
842 externalUrl = rFileUrl;
844 if( !externalUrl.isEmpty() && !isDangerousUrl( externalUrl ) )
846 NSString* pString = CreateNSString( externalUrl );
847 NSURL* pURL = [NSURL URLWithString: pString];
849 if( pURL )
851 NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
852 [pCtrl noteNewRecentDocumentURL: pURL];
854 if( pString )
855 [pString release];
859 SalTimer* AquaSalInstance::CreateSalTimer()
861 return new AquaSalTimer();
864 SalSystem* AquaSalInstance::CreateSalSystem()
866 return new AquaSalSystem();
869 std::shared_ptr<SalBitmap> AquaSalInstance::CreateSalBitmap()
871 return std::make_shared<QuartzSalBitmap>();
874 OUString AquaSalInstance::getOSVersion()
876 NSString * versionString = nullptr;
877 NSDictionary * sysVersionDict = [ NSDictionary dictionaryWithContentsOfFile: @"/System/Library/CoreServices/SystemVersion.plist" ];
878 if ( sysVersionDict )
879 versionString = [ sysVersionDict valueForKey: @"ProductVersion" ];
881 OUString aVersion = "Mac OS X ";
882 if ( versionString )
883 aVersion += OUString::fromUtf8( [ versionString UTF8String ] );
884 else
885 aVersion += "(unknown)";
887 return aVersion;
890 CGImageRef CreateCGImage( const Image& rImage )
892 BitmapEx aBmpEx( rImage.GetBitmapEx() );
893 Bitmap aBmp( aBmpEx.GetBitmap() );
895 if( ! aBmp || ! aBmp.ImplGetSalBitmap() )
896 return nullptr;
898 // simple case, no transparency
899 QuartzSalBitmap* pSalBmp = static_cast<QuartzSalBitmap*>(aBmp.ImplGetSalBitmap().get());
901 if( ! pSalBmp )
902 return nullptr;
904 CGImageRef xImage = nullptr;
905 if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
906 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
907 else if( aBmpEx.IsAlpha() )
909 AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
910 Bitmap aMask( aAlphaMask.GetBitmap() );
911 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
912 if( pMaskBmp )
913 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
914 else
915 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
917 else if( aBmpEx.GetTransparentType() == TransparentType::Bitmap )
919 Bitmap aMask( aBmpEx.GetMask() );
920 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
921 if( pMaskBmp )
922 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
923 else
924 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
926 else if( aBmpEx.GetTransparentType() == TransparentType::Color )
928 Color aTransColor( aBmpEx.GetTransparentColor() );
929 Color nTransColor( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
930 xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
933 return xImage;
936 NSImage* CreateNSImage( const Image& rImage )
938 CGImageRef xImage = CreateCGImage( rImage );
940 if( ! xImage )
941 return nil;
943 Size aSize( rImage.GetSizePixel() );
944 NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
945 if( pImage )
947 [pImage lockFocusFlipped:YES];
948 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
949 CGContextRef rCGContext = [pContext CGContext];
951 const CGRect aDstRect = { {0, 0}, { static_cast<CGFloat>(aSize.Width()), static_cast<CGFloat>(aSize.Height()) } };
952 CGContextDrawImage( rCGContext, aDstRect, xImage );
954 [pImage unlockFocus];
957 CGImageRelease( xImage );
959 return pImage;
962 bool AquaSalInstance::SVMainHook(int* pnInit)
964 gpnInit = pnInit;
966 OUString aExeURL, aExe;
967 osl_getExecutableFile( &aExeURL.pData );
968 osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData );
969 OString aByteExe( OUStringToOString( aExe, osl_getThreadTextEncoding() ) );
971 #ifdef DEBUG
972 aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" );
973 const char* pArgv[] = { aByteExe.getStr(), NULL };
974 NSApplicationMain( 3, pArgv );
975 #else
976 const char* pArgv[] = { aByteExe.getStr(), nullptr };
977 NSApplicationMain( 1, pArgv );
978 #endif
980 return true;
983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */