bump product version to 6.3.0.0.beta1
[LibreOffice.git] / vcl / osx / salinst.cxx
blobb31f870d9e128265f0932cafcdcffc391ba64322
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>
62 #include <salimestatus.hxx>
64 #include <comphelper/processfactory.hxx>
66 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
67 #include <com/sun/star/uno/XComponentContext.hpp>
69 #include <premac.h>
70 #include <Foundation/Foundation.h>
71 #include <ApplicationServices/ApplicationServices.h>
72 #import "apple_remote/RemoteMainController.h"
73 #include <apple_remote/RemoteControl.h>
74 #include <postmac.h>
76 extern "C" {
77 #include <crt_externs.h>
80 using namespace std;
81 using namespace ::com::sun::star;
83 static int* gpnInit = nullptr;
84 static NSMenu* pDockMenu = nil;
85 static bool bLeftMain = false;
87 class AquaDelayedSettingsChanged : public Idle
89 bool mbInvalidate;
91 public:
92 AquaDelayedSettingsChanged( bool bInvalidate ) :
93 mbInvalidate( bInvalidate )
97 virtual void Invoke() override
99 AquaSalInstance *pInst = GetSalData()->mpInstance;
100 SalFrame *pAnyFrame = pInst->anyFrame();
101 if( pAnyFrame )
102 pAnyFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
104 if( mbInvalidate )
106 for( auto pSalFrame : pInst->getFrames() )
108 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
109 if( pFrame->mbShown )
110 pFrame->SendPaintEvent();
113 delete this;
117 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
119 osl::Guard< comphelper::SolarMutex > aGuard( *GetYieldMutex() );
120 AquaDelayedSettingsChanged* pIdle = new AquaDelayedSettingsChanged( bInvalidate );
121 pIdle->SetDebugName( "AquaSalInstance AquaDelayedSettingsChanged" );
122 pIdle->Start();
125 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
126 std::list<const ApplicationEvent*> AquaSalInstance::aAppEventList;
128 NSMenu* AquaSalInstance::GetDynamicDockMenu()
130 if( ! pDockMenu && ! bLeftMain )
131 pDockMenu = [[NSMenu alloc] initWithTitle: @""];
132 return pDockMenu;
135 bool AquaSalInstance::isOnCommandLine( const OUString& rArg )
137 sal_uInt32 nArgs = osl_getCommandArgCount();
138 for( sal_uInt32 i = 0; i < nArgs; i++ )
140 OUString aArg;
141 osl_getCommandArg( i, &aArg.pData );
142 if( aArg.equals( rArg ) )
143 return true;
145 return false;
148 void AquaSalInstance::AfterAppInit()
150 [[NSNotificationCenter defaultCenter] addObserver: NSApp
151 selector: @selector(systemColorsChanged:)
152 name: NSSystemColorsDidChangeNotification
153 object: nil ];
154 [[NSNotificationCenter defaultCenter] addObserver: NSApp
155 selector: @selector(screenParametersChanged:)
156 name: NSApplicationDidChangeScreenParametersNotification
157 object: nil ];
158 // add observers for some settings changes that affect vcl's settings
159 // scrollbar variant
160 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
161 selector: @selector(scrollbarVariantChanged:)
162 name: @"AppleAquaScrollBarVariantChanged"
163 object: nil ];
164 // scrollbar page behavior ("jump to here" or not)
165 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
166 selector: @selector(scrollbarSettingsChanged:)
167 name: @"AppleNoRedisplayAppearancePreferenceChanged"
168 object: nil ];
169 #if !HAVE_FEATURE_MACOSX_SANDBOX
170 // Initialize Apple Remote
171 GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init];
173 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
174 selector: @selector(applicationWillBecomeActive:)
175 name: @"AppleRemoteWillBecomeActive"
176 object: nil ];
178 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
179 selector: @selector(applicationWillResignActive:)
180 name: @"AppleRemoteWillResignActive"
181 object: nil ];
182 #endif
185 void SalAbort( const OUString& rErrorText, bool bDumpCore )
187 if( rErrorText.isEmpty() )
188 fprintf( stderr, "Application Error " );
189 else
190 fprintf( stderr, "%s ",
191 OUStringToOString( rErrorText, osl_getThreadTextEncoding() ).getStr() );
192 if( bDumpCore )
193 abort();
194 else
195 _exit(1);
198 SalYieldMutex::SalYieldMutex()
199 : m_aCodeBlock( nullptr )
203 SalYieldMutex::~SalYieldMutex()
207 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
209 AquaSalInstance *pInst = GetSalData()->mpInstance;
210 if ( pInst && pInst->IsMainThread() )
212 if ( pInst->mbNoYieldLock )
213 return;
214 do {
215 RuninmainBlock block = nullptr;
217 std::unique_lock<std::mutex> g(m_runInMainMutex);
218 if (m_aMutex.tryToAcquire()) {
219 assert(m_aCodeBlock == nullptr);
220 m_wakeUpMain = false;
221 break;
223 // wait for doRelease() or RUNINMAIN_* to set the condition
224 m_aInMainCondition.wait(g, [this]() { return m_wakeUpMain; });
225 m_wakeUpMain = false;
226 std::swap(block, m_aCodeBlock);
228 if ( block )
230 assert( !pInst->mbNoYieldLock );
231 pInst->mbNoYieldLock = true;
232 block();
233 pInst->mbNoYieldLock = false;
234 Block_release( block );
235 std::unique_lock<std::mutex> g(m_runInMainMutex);
236 assert(!m_resultReady);
237 m_resultReady = true;
238 m_aResultCondition.notify_all();
241 while ( true );
243 else
244 m_aMutex.acquire();
245 ++m_nCount;
246 --nLockCount;
248 comphelper::SolarMutex::doAcquire( nLockCount );
251 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
253 AquaSalInstance *pInst = GetSalData()->mpInstance;
254 if ( pInst->mbNoYieldLock && pInst->IsMainThread() )
255 return 1;
256 sal_uInt32 nCount;
258 std::unique_lock<std::mutex> g(m_runInMainMutex);
259 // read m_nCount before doRelease
260 bool const isReleased(bUnlockAll || m_nCount == 1);
261 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
262 if (isReleased && !pInst->IsMainThread()) {
263 m_wakeUpMain = true;
264 m_aInMainCondition.notify_all();
267 return nCount;
270 bool SalYieldMutex::IsCurrentThread() const
272 if ( !GetSalData()->mpInstance->mbNoYieldLock )
273 return comphelper::SolarMutex::IsCurrentThread();
274 else
275 return GetSalData()->mpInstance->IsMainThread();
278 // some convenience functions regarding the yield mutex, aka solar mutex
280 bool ImplSalYieldMutexTryToAcquire()
282 AquaSalInstance* pInst = GetSalData()->mpInstance;
283 if ( pInst )
284 return pInst->GetYieldMutex()->tryToAcquire();
285 else
286 return FALSE;
289 void ImplSalYieldMutexRelease()
291 AquaSalInstance* pInst = GetSalData()->mpInstance;
292 if ( pInst )
293 pInst->GetYieldMutex()->release();
296 extern "C" {
297 VCLPLUG_OSX_PUBLIC SalInstance* create_SalInstance()
299 SalData* pSalData = new SalData;
301 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
302 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
303 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
304 [ pool drain ];
306 // create our cocoa NSApplication
307 [VCL_NSApplication sharedApplication];
309 SalData::ensureThreadAutoreleasePool();
311 // put cocoa into multithreaded mode
312 [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
314 // activate our delegate methods
315 [NSApp setDelegate: NSApp];
317 SAL_WARN_IF( pSalData->mpInstance != nullptr, "vcl", "more than one instance created" );
318 AquaSalInstance* pInst = new AquaSalInstance;
320 // init instance (only one instance in this version !!!)
321 pSalData->mpInstance = pInst;
322 // this one is for outside AquaSalInstance::Yield
323 SalData::ensureThreadAutoreleasePool();
324 // no focus rects on NWF
325 ImplGetSVData()->maNWFData.mbNoFocusRects = true;
326 ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true;
327 ImplGetSVData()->maNWFData.mbCenteredTabs = true;
328 ImplGetSVData()->maNWFData.mbProgressNeedsErase = true;
329 ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10;
331 return pInst;
335 AquaSalInstance::AquaSalInstance()
336 : SalInstance(std::make_unique<SalYieldMutex>())
337 , mnActivePrintJobs( 0 )
338 , mbIsLiveResize( false )
339 , mbNoYieldLock( false )
340 , mbTimerProcessed( false )
342 maMainThread = osl::Thread::getCurrentIdentifier();
344 ImplSVData* pSVData = ImplGetSVData();
345 pSVData->maAppData.mxToolkitName = OUString("osx");
348 AquaSalInstance::~AquaSalInstance()
350 [NSApp stop: NSApp];
351 bLeftMain = true;
352 if( pDockMenu )
354 [pDockMenu release];
355 pDockMenu = nil;
359 void AquaSalInstance::TriggerUserEventProcessing()
361 dispatch_async(dispatch_get_main_queue(),^{
362 ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO );
366 void AquaSalInstance::ProcessEvent( SalUserEvent aEvent )
368 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
369 maWaitingYieldCond.set();
372 bool AquaSalInstance::IsMainThread() const
374 return osl::Thread::getCurrentIdentifier() == maMainThread;
377 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
379 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
380 int nSubtype = [pEvent subtype];
381 switch( nSubtype )
383 case AppStartTimerEvent:
384 if ( pTimer )
385 pTimer->handleStartTimerEvent( pEvent );
386 break;
387 case AppExecuteSVMain:
389 int nRet = ImplSVMain();
390 if (gpnInit)
391 *gpnInit = nRet;
392 [NSApp stop: NSApp];
393 break;
395 case DispatchTimerEvent:
397 AquaSalInstance *pInst = GetSalData()->mpInstance;
398 if ( pTimer && pInst )
399 pInst->mbTimerProcessed = pTimer->handleDispatchTimerEvent( pEvent );
400 break;
402 #if !HAVE_FEATURE_MACOSX_SANDBOX
403 case AppleRemoteControlEvent: // Defined in <apple_remote/RemoteMainController.h>
405 MediaCommand nCommand;
406 AquaSalInstance *pInst = GetSalData()->mpInstance;
407 bool bIsFullScreenMode = false;
409 for( auto pSalFrame : pInst->getFrames() )
411 const AquaSalFrame* pFrame = static_cast<const AquaSalFrame*>( pSalFrame );
412 if ( pFrame->mbFullScreen )
414 bIsFullScreenMode = true;
415 break;
419 switch ([pEvent data1])
421 case kRemoteButtonPlay:
422 nCommand = bIsFullScreenMode ? MediaCommand::PlayPause : MediaCommand::Play;
423 break;
425 // kept for experimentation purpose (scheduled for future implementation)
426 // case kRemoteButtonMenu: nCommand = MediaCommand::Menu; break;
428 case kRemoteButtonPlus: nCommand = MediaCommand::VolumeUp; break;
430 case kRemoteButtonMinus: nCommand = MediaCommand::VolumeDown; break;
432 case kRemoteButtonRight: nCommand = MediaCommand::NextTrack; break;
434 case kRemoteButtonRight_Hold: nCommand = MediaCommand::NextTrackHold; break;
436 case kRemoteButtonLeft: nCommand = MediaCommand::PreviousTrack; break;
438 case kRemoteButtonLeft_Hold: nCommand = MediaCommand::Rewind; break;
440 case kRemoteButtonPlay_Hold: nCommand = MediaCommand::PlayHold; break;
442 case kRemoteButtonMenu_Hold: nCommand = MediaCommand::Stop; break;
444 // FIXME : not detected
445 case kRemoteButtonPlus_Hold:
446 case kRemoteButtonMinus_Hold:
447 break;
449 default:
450 break;
452 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pInst->anyFrame() );
453 vcl::Window* pWindow = pFrame ? pFrame->GetWindow() : nullptr;
454 if( pWindow )
456 const Point aPoint;
457 CommandMediaData aMediaData(nCommand);
458 CommandEvent aCEvt( aPoint, CommandEventId::Media, FALSE, &aMediaData );
459 NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt );
461 if ( !ImplCallPreNotify( aNCmdEvt ) )
462 pWindow->Command( aCEvt );
466 break;
467 #endif
469 case YieldWakeupEvent:
470 // do nothing, fall out of Yield
471 break;
473 default:
474 OSL_FAIL( "unhandled NSApplicationDefined event" );
475 break;
479 bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents )
481 OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents), boolean )
482 assert( false && "Don't call this from the main thread!" );
483 return false;
487 static bool isWakeupEvent( NSEvent *pEvent )
489 SAL_WNODEPRECATED_DECLARATIONS_PUSH
490 return NSApplicationDefined == [pEvent type]
491 && AquaSalInstance::YieldWakeupEvent == static_cast<int>([pEvent subtype]);
492 SAL_WNODEPRECATED_DECLARATIONS_POP
495 bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
497 // ensure that the per thread autorelease pool is top level and
498 // will therefore not be destroyed by cocoa implicitly
499 SalData::ensureThreadAutoreleasePool();
501 // NSAutoreleasePool documentation suggests we should have
502 // an own pool for each yield level
503 ReleasePoolHolder aReleasePool;
505 // first, process current user events
506 bool bHadEvent = DispatchUserEvents( bHandleAllCurrentEvents );
507 if ( !bHandleAllCurrentEvents && bHadEvent )
508 return true;
510 // handle cocoa event queue
511 // cocoa events may be only handled in the thread the NSApp was created
512 if( IsMainThread() && mnActivePrintJobs == 0 )
514 // handle available events
515 NSEvent* pEvent = nil;
516 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
517 mbTimerProcessed = false;
521 SolarMutexReleaser aReleaser;
523 pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
524 untilDate: nil
525 inMode: NSDefaultRunLoopMode
526 dequeue: YES];
527 if( pEvent )
529 [NSApp sendEvent: pEvent];
530 if ( isWakeupEvent( pEvent ) )
531 continue;
532 bHadEvent = true;
535 [NSApp updateWindows];
537 if ( !bHandleAllCurrentEvents || !pEvent || now < [pEvent timestamp] )
538 break;
540 while( true );
542 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
543 if ( !mbTimerProcessed && pTimer && pTimer->IsDirectTimeout() )
545 pTimer->handleTimerElapsed();
546 bHadEvent = true;
549 // if we had no event yet, wait for one if requested
550 if( bWait && ! bHadEvent )
552 SolarMutexReleaser aReleaser;
554 pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
555 untilDate: [NSDate distantFuture]
556 inMode: NSDefaultRunLoopMode
557 dequeue: YES];
558 if( pEvent )
560 [NSApp sendEvent: pEvent];
561 if ( !isWakeupEvent( pEvent ) )
562 bHadEvent = true;
564 [NSApp updateWindows];
567 // collect update rectangles
568 for( auto pSalFrame : GetSalData()->mpInstance->getFrames() )
570 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
571 if( pFrame->mbShown && ! pFrame->maInvalidRect.IsEmpty() )
573 pFrame->Flush( pFrame->maInvalidRect );
574 pFrame->maInvalidRect.SetEmpty();
578 if ( bHadEvent )
579 maWaitingYieldCond.set();
581 else
583 bHadEvent = RunInMainYield( bHandleAllCurrentEvents );
584 if ( !bHadEvent && bWait )
586 // #i103162#
587 // wait until the main thread has dispatched an event
588 maWaitingYieldCond.reset();
589 SolarMutexReleaser aReleaser;
590 maWaitingYieldCond.wait();
594 // we get some apple events way too early
595 // before the application is ready to handle them,
596 // so their corresponding application events need to be delayed
597 // now is a good time to handle at least one of them
598 if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
600 // make sure that only one application event is active at a time
601 static bool bInAppEvent = false;
602 if( !bInAppEvent )
604 bInAppEvent = true;
605 // get the next delayed application event
606 const ApplicationEvent* pAppEvent = aAppEventList.front();
607 aAppEventList.pop_front();
608 // handle one application event (no recursion)
609 const ImplSVData* pSVData = ImplGetSVData();
610 pSVData->mpApp->AppEvent( *pAppEvent );
611 delete pAppEvent;
612 // allow the next delayed application event
613 bInAppEvent = false;
617 return bHadEvent;
620 bool AquaSalInstance::AnyInput( VclInputFlags nType )
622 if( nType & VclInputFlags::APPEVENT )
624 if( ! aAppEventList.empty() )
625 return true;
626 if( nType == VclInputFlags::APPEVENT )
627 return false;
630 OSX_INST_RUNINMAIN_UNION( AnyInput( nType ), boolean )
632 if( nType & VclInputFlags::TIMER )
634 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
635 if (pTimer && pTimer->IsTimerElapsed())
636 return true;
639 unsigned/*NSUInteger*/ nEventMask = 0;
640 SAL_WNODEPRECATED_DECLARATIONS_PUSH
641 // 'NSFlagsChangedMask' is deprecated: first deprecated in macOS 10.12
642 // 'NSKeyDownMask' is deprecated: first deprecated in macOS 10.12
643 // 'NSKeyUpMask' is deprecated: first deprecated in macOS 10.12
644 // 'NSLeftMouseDownMask' is deprecated: first deprecated in macOS 10.12
645 // 'NSLeftMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
646 // 'NSLeftMouseUpMask' is deprecated: first deprecated in macOS 10.12
647 // 'NSMouseEnteredMask' is deprecated: first deprecated in macOS 10.12
648 // 'NSMouseExitedMask' is deprecated: first deprecated in macOS 10.12
649 // 'NSOtherMouseDownMask' is deprecated: first deprecated in macOS 10.12
650 // 'NSOtherMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
651 // 'NSOtherMouseUpMask' is deprecated: first deprecated in macOS 10.12
652 // 'NSRightMouseDownMask' is deprecated: first deprecated in macOS 10.12
653 // 'NSRightMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
654 // 'NSRightMouseUpMask' is deprecated: first deprecated in macOS 10.12
655 // 'NSScrollWheelMask' is deprecated: first deprecated in macOS 10.12
656 // 'NSTabletPoint' is deprecated: first deprecated in macOS 10.12
657 if( nType & VclInputFlags::MOUSE)
658 nEventMask |=
659 NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask |
660 NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask |
661 NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
662 NSScrollWheelMask |
663 // NSMouseMovedMask |
664 NSMouseEnteredMask | NSMouseExitedMask;
665 if( nType & VclInputFlags::KEYBOARD)
666 nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
667 if( nType & VclInputFlags::OTHER)
668 nEventMask |= NSTabletPoint | NSApplicationDefinedMask;
669 SAL_WNODEPRECATED_DECLARATIONS_POP
670 // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
671 if( !bool(nType) )
672 return false;
674 NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
675 inMode: NSDefaultRunLoopMode dequeue: NO];
676 return (pEvent != nullptr);
679 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, SalFrameStyleFlags /*nSalFrameStyle*/ )
681 return nullptr;
684 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
686 OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent, nSalFrameStyle ), SalFrame* )
687 return new AquaSalFrame( pParent, nSalFrameStyle );
690 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
692 OSX_INST_RUNINMAIN( DestroyFrame( pFrame ) )
693 delete pFrame;
696 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool /* bShow */ )
698 if ( !pParent )
699 return nullptr;
701 OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent, pWindowData, false ), SalObject* )
702 return new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData );
705 void AquaSalInstance::DestroyObject( SalObject* pObject )
707 OSX_INST_RUNINMAIN( DestroyObject( pObject ) )
708 delete pObject;
711 std::unique_ptr<SalPrinter> AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
713 return std::unique_ptr<SalPrinter>(new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ));
716 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
718 NSArray* pNames = [NSPrinter printerNames];
719 NSArray* pTypes = [NSPrinter printerTypes];
720 unsigned int nNameCount = pNames ? [pNames count] : 0;
721 unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
722 SAL_WARN_IF( nTypeCount != nNameCount, "vcl", "type count not equal to printer count" );
723 for( unsigned int i = 0; i < nNameCount; i++ )
725 NSString* pName = [pNames objectAtIndex: i];
726 NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
727 if( pName )
729 std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
730 pInfo->maPrinterName = GetOUString( pName );
731 if( pType )
732 pInfo->maDriver = GetOUString( pType );
733 pInfo->mnStatus = PrintQueueFlags::NONE;
734 pInfo->mnJobs = 0;
736 pList->Add( std::move(pInfo) );
741 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
745 OUString AquaSalInstance::GetDefaultPrinter()
747 // #i113170# may not be the main thread if called from UNO API
748 SalData::ensureThreadAutoreleasePool();
750 if( maDefaultPrinter.isEmpty() )
752 NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
753 SAL_WARN_IF( !pPI, "vcl", "no print info" );
754 if( pPI )
756 NSPrinter* pPr = [pPI printer];
757 SAL_WARN_IF( !pPr, "vcl", "no printer in default info" );
758 if( pPr )
760 NSString* pDefName = [pPr name];
761 SAL_WARN_IF( !pDefName, "vcl", "printer has no name" );
762 maDefaultPrinter = GetOUString( pDefName );
766 return maDefaultPrinter;
769 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
770 ImplJobSetup* pSetupData )
772 // #i113170# may not be the main thread if called from UNO API
773 SalData::ensureThreadAutoreleasePool();
775 SalInfoPrinter* pNewInfoPrinter = nullptr;
776 if( pQueueInfo )
778 pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
779 if( pSetupData )
780 pNewInfoPrinter->SetPrinterData( pSetupData );
783 return pNewInfoPrinter;
786 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
788 // #i113170# may not be the main thread if called from UNO API
789 SalData::ensureThreadAutoreleasePool();
791 delete pPrinter;
794 OUString AquaSalInstance::GetConnectionIdentifier()
796 return OUString();
799 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
800 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
801 static OUString translateToExternalUrl(const OUString& internalUrl)
803 uno::Reference< uno::XComponentContext > context(
804 comphelper::getProcessComponentContext());
805 return uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
808 // #i104525# many versions of OSX have problems with some URLs:
809 // when an app requests OSX to add one of these URLs to the "Recent Items" list
810 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
811 static bool isDangerousUrl( const OUString& rUrl )
813 // use a heuristic that detects all known cases since there is no official comment
814 // on the exact impact and root cause of the OSX bug
815 const int nLen = rUrl.getLength();
816 const sal_Unicode* p = rUrl.getStr();
817 for( int i = 0; i < nLen-3; ++i, ++p ) {
818 if( p[0] != '%' )
819 continue;
820 // escaped percent?
821 if( (p[1] == '2') && (p[2] == '5') )
822 return true;
823 // escapes are considered to be UTF-8 encoded
824 // => check for invalid UTF-8 leading byte
825 if( (p[1] != 'f') && (p[1] != 'F') )
826 continue;
827 int cLowNibble = p[2];
828 if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
829 return false;
830 if( cLowNibble >= 'a' )
831 cLowNibble -= 'a' - 'A';
832 if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
833 return true;
836 return false;
839 void AquaSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& /*rDocumentService*/)
841 // Convert file URL for external use (see above)
842 OUString externalUrl = translateToExternalUrl(rFileUrl);
843 if( externalUrl.isEmpty() )
844 externalUrl = rFileUrl;
846 if( !externalUrl.isEmpty() && !isDangerousUrl( externalUrl ) )
848 NSString* pString = CreateNSString( externalUrl );
849 NSURL* pURL = [NSURL URLWithString: pString];
851 if( pURL )
853 NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
854 [pCtrl noteNewRecentDocumentURL: pURL];
856 if( pString )
857 [pString release];
861 SalTimer* AquaSalInstance::CreateSalTimer()
863 return new AquaSalTimer();
866 SalSystem* AquaSalInstance::CreateSalSystem()
868 return new AquaSalSystem();
871 std::shared_ptr<SalBitmap> AquaSalInstance::CreateSalBitmap()
873 return std::make_shared<QuartzSalBitmap>();
876 OUString AquaSalInstance::getOSVersion()
878 NSString * versionString = nullptr;
879 NSDictionary * sysVersionDict = [ NSDictionary dictionaryWithContentsOfFile: @"/System/Library/CoreServices/SystemVersion.plist" ];
880 if ( sysVersionDict )
881 versionString = [ sysVersionDict valueForKey: @"ProductVersion" ];
883 OUString aVersion = "Mac OS X ";
884 if ( versionString )
885 aVersion += OUString::fromUtf8( [ versionString UTF8String ] );
886 else
887 aVersion += "(unknown)";
889 return aVersion;
892 CGImageRef CreateCGImage( const Image& rImage )
894 BitmapEx aBmpEx( rImage.GetBitmapEx() );
895 Bitmap aBmp( aBmpEx.GetBitmap() );
897 if( ! aBmp || ! aBmp.ImplGetSalBitmap() )
898 return nullptr;
900 // simple case, no transparency
901 QuartzSalBitmap* pSalBmp = static_cast<QuartzSalBitmap*>(aBmp.ImplGetSalBitmap().get());
903 if( ! pSalBmp )
904 return nullptr;
906 CGImageRef xImage = nullptr;
907 if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
908 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
909 else if( aBmpEx.IsAlpha() )
911 AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
912 Bitmap aMask( aAlphaMask.GetBitmap() );
913 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
914 if( pMaskBmp )
915 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
916 else
917 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
919 else if( aBmpEx.GetTransparentType() == TransparentType::Bitmap )
921 Bitmap aMask( aBmpEx.GetMask() );
922 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
923 if( pMaskBmp )
924 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
925 else
926 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
928 else if( aBmpEx.GetTransparentType() == TransparentType::Color )
930 Color aTransColor( aBmpEx.GetTransparentColor() );
931 Color nTransColor( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
932 xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
935 return xImage;
938 NSImage* CreateNSImage( const Image& rImage )
940 CGImageRef xImage = CreateCGImage( rImage );
942 if( ! xImage )
943 return nil;
945 Size aSize( rImage.GetSizePixel() );
946 NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
947 if( pImage )
949 [pImage lockFocusFlipped:YES];
950 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
951 CGContextRef rCGContext = [pContext CGContext];
953 const CGRect aDstRect = { {0, 0}, { static_cast<CGFloat>(aSize.Width()), static_cast<CGFloat>(aSize.Height()) } };
954 CGContextDrawImage( rCGContext, aDstRect, xImage );
956 [pImage unlockFocus];
959 CGImageRelease( xImage );
961 return pImage;
964 bool AquaSalInstance::SVMainHook(int* pnInit)
966 gpnInit = pnInit;
968 OUString aExeURL, aExe;
969 osl_getExecutableFile( &aExeURL.pData );
970 osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData );
971 OString aByteExe( OUStringToOString( aExe, osl_getThreadTextEncoding() ) );
973 #ifdef DEBUG
974 aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" );
975 const char* pArgv[] = { aByteExe.getStr(), NULL };
976 NSApplicationMain( 3, pArgv );
977 #else
978 const char* pArgv[] = { aByteExe.getStr(), nullptr };
979 NSApplicationMain( 1, pArgv );
980 #endif
982 return true;
985 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */