1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
28 #include <config_features.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/QueueInfo.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/window.hxx>
43 #include <vcl/idle.hxx>
44 #include <vcl/svmain.hxx>
45 #include <vcl/opengl/OpenGLContext.hxx>
46 #include <vcl/commandevent.hxx>
47 #include <vcl/event.hxx>
49 #include <osx/saldata.hxx>
50 #include <osx/salinst.h>
51 #include <osx/salframe.h>
52 #include <osx/salobj.h>
53 #include <osx/salsys.h>
54 #include <quartz/salvd.h>
55 #include <quartz/salbmp.h>
56 #include <quartz/utils.h>
57 #include <osx/salprn.h>
58 #include <osx/saltimer.h>
59 #include <osx/vclnsapp.h>
60 #include <osx/runinmain.hxx>
64 #include <comphelper/processfactory.hxx>
66 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
67 #include <com/sun/star/uno/XComponentContext.hpp>
70 #include <Foundation/Foundation.h>
71 #include <ApplicationServices/ApplicationServices.h>
72 #import "apple_remote/RemoteMainController.h"
73 #include <apple_remote/RemoteControl.h>
77 #include <vcl/skia/SkiaHelper.hxx>
78 #include <skia/salbmp.hxx>
79 #include <skia/osx/gdiimpl.hxx>
80 #include <skia/osx/bitmap.hxx>
84 #include <crt_externs.h>
87 using namespace ::com::sun::star
;
89 static int* gpnInit
= nullptr;
90 static NSMenu
* pDockMenu
= nil
;
91 static bool bLeftMain
= false;
95 class AquaDelayedSettingsChanged
: public Idle
100 AquaDelayedSettingsChanged( bool bInvalidate
) :
101 Idle("AquaSalInstance AquaDelayedSettingsChanged"),
102 mbInvalidate( bInvalidate
)
106 virtual void Invoke() override
108 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
109 SalFrame
*pAnyFrame
= pInst
->anyFrame();
111 pAnyFrame
->CallCallback( SalEvent::SettingsChanged
, nullptr );
115 for( auto pSalFrame
: pInst
->getFrames() )
117 AquaSalFrame
* pFrame
= static_cast<AquaSalFrame
*>( pSalFrame
);
118 if( pFrame
->mbShown
)
119 pFrame
->SendPaintEvent();
128 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate
)
130 osl::Guard
< comphelper::SolarMutex
> aGuard( *GetYieldMutex() );
131 AquaDelayedSettingsChanged
* pIdle
= new AquaDelayedSettingsChanged( bInvalidate
);
135 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
136 std::list
<const ApplicationEvent
*> AquaSalInstance::aAppEventList
;
138 NSMenu
* AquaSalInstance::GetDynamicDockMenu()
140 if( ! pDockMenu
&& ! bLeftMain
)
141 pDockMenu
= [[NSMenu alloc
] initWithTitle
: @
""];
145 bool AquaSalInstance::isOnCommandLine( const OUString
& rArg
)
147 sal_uInt32 nArgs
= osl_getCommandArgCount();
148 for( sal_uInt32 i
= 0; i
< nArgs
; i
++ )
151 osl_getCommandArg( i
, &aArg
.pData
);
152 if( aArg
.equals( rArg
) )
158 void AquaSalInstance::AfterAppInit()
160 [[NSNotificationCenter defaultCenter
] addObserver
: NSApp
161 selector
: @
selector(systemColorsChanged
:)
162 name
: NSSystemColorsDidChangeNotification
164 [[NSNotificationCenter defaultCenter
] addObserver
: NSApp
165 selector
: @
selector(screenParametersChanged
:)
166 name
: NSApplicationDidChangeScreenParametersNotification
168 // add observers for some settings changes that affect vcl's settings
170 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
171 selector
: @
selector(scrollbarVariantChanged
:)
172 name
: @
"AppleAquaScrollBarVariantChanged"
174 // scrollbar page behavior ("jump to here" or not)
175 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
176 selector
: @
selector(scrollbarSettingsChanged
:)
177 name
: @
"AppleNoRedisplayAppearancePreferenceChanged"
179 #if !HAVE_FEATURE_MACOSX_SANDBOX
180 // Initialize Apple Remote
181 GetSalData()->mpAppleRemoteMainController
= [[AppleRemoteMainController alloc
] init
];
183 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
184 selector
: @
selector(applicationWillBecomeActive
:)
185 name
: @
"AppleRemoteWillBecomeActive"
188 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
189 selector
: @
selector(applicationWillResignActive
:)
190 name
: @
"AppleRemoteWillResignActive"
195 SalYieldMutex::SalYieldMutex()
196 : m_aCodeBlock( nullptr )
200 SalYieldMutex::~SalYieldMutex()
204 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount
)
206 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
207 if ( pInst
&& pInst
->IsMainThread() )
209 if ( pInst
->mbNoYieldLock
)
212 RuninmainBlock block
= nullptr;
214 std::unique_lock
<std::mutex
> g(m_runInMainMutex
);
215 if (m_aMutex
.tryToAcquire()) {
216 assert(m_aCodeBlock
== nullptr);
217 m_wakeUpMain
= false;
220 // wait for doRelease() or RUNINMAIN_* to set the condition
221 m_aInMainCondition
.wait(g
, [this]() { return m_wakeUpMain
; });
222 m_wakeUpMain
= false;
223 std::swap(block
, m_aCodeBlock
);
227 assert( !pInst
->mbNoYieldLock
);
228 pInst
->mbNoYieldLock
= true;
230 pInst
->mbNoYieldLock
= false;
231 Block_release( block
);
232 std::scoped_lock
<std::mutex
> g(m_runInMainMutex
);
233 assert(!m_resultReady
);
234 m_resultReady
= true;
235 m_aResultCondition
.notify_all();
245 comphelper::SolarMutex::doAcquire( nLockCount
);
248 sal_uInt32
SalYieldMutex::doRelease( const bool bUnlockAll
)
250 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
251 if ( pInst
->mbNoYieldLock
&& pInst
->IsMainThread() )
255 std::scoped_lock
<std::mutex
> g(m_runInMainMutex
);
256 // read m_nCount before doRelease
257 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
258 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
259 if (isReleased
&& !pInst
->IsMainThread()) {
261 m_aInMainCondition
.notify_all();
267 bool SalYieldMutex::IsCurrentThread() const
269 if ( !GetSalData()->mpInstance
->mbNoYieldLock
)
270 return comphelper::SolarMutex::IsCurrentThread();
272 return GetSalData()->mpInstance
->IsMainThread();
275 // some convenience functions regarding the yield mutex, aka solar mutex
277 bool ImplSalYieldMutexTryToAcquire()
279 AquaSalInstance
* pInst
= GetSalData()->mpInstance
;
281 return pInst
->GetYieldMutex()->tryToAcquire();
286 void ImplSalYieldMutexRelease()
288 AquaSalInstance
* pInst
= GetSalData()->mpInstance
;
290 pInst
->GetYieldMutex()->release();
294 VCLPLUG_OSX_PUBLIC SalInstance
* create_SalInstance()
296 SalData
* pSalData
= new SalData
;
298 NSAutoreleasePool
* pool
= [ [ NSAutoreleasePool alloc
] init
];
299 unlink([[NSString stringWithFormat
:@
"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER
] UTF8String
]);
300 unlink([[NSString stringWithFormat
:@
"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER
] UTF8String
]);
303 // create our cocoa NSApplication
304 [VCL_NSApplication sharedApplication
];
306 SalData::ensureThreadAutoreleasePool();
308 // put cocoa into multithreaded mode
309 [NSThread detachNewThreadSelector
:@
selector(enableCocoaThreads
:) toTarget
:[[CocoaThreadEnabler alloc
] init
] withObject
:nil
];
311 // Dark mode is disabled as long as it is not implemented completely. For development purposes, it may be controlled by
312 // environment variables: VCL_MACOS_FORCE_DARK_MODE enable dark mode independent of system settings,
313 // VCL_MACOS_USE_SYSTEM_APPEARANCE to use system settings (light mode or the dark mode as configured within system preferences).
315 // TODO: After implementation of dark mode, this code has to be removed.
317 if (@
available(macOS
10.14, iOS
13, *))
319 if (getenv("VCL_MACOS_FORCE_DARK_MODE"))
321 [NSApp setAppearance
: [NSAppearance appearanceNamed
: NSAppearanceNameDarkAqua
]];
324 if (!getenv("VCL_MACOS_USE_SYSTEM_APPEARANCE"))
325 [NSApp setAppearance
: [NSAppearance appearanceNamed
: NSAppearanceNameAqua
]];
328 // activate our delegate methods
329 [NSApp setDelegate
: NSApp
];
331 SAL_WARN_IF( pSalData
->mpInstance
!= nullptr, "vcl", "more than one instance created" );
332 AquaSalInstance
* pInst
= new AquaSalInstance
;
334 // init instance (only one instance in this version !!!)
335 pSalData
->mpInstance
= pInst
;
336 // this one is for outside AquaSalInstance::Yield
337 SalData::ensureThreadAutoreleasePool();
338 // no focus rects on NWF
339 ImplGetSVData()->maNWFData
.mbNoFocusRects
= true;
340 ImplGetSVData()->maNWFData
.mbNoActiveTabTextRaise
= true;
341 ImplGetSVData()->maNWFData
.mbCenteredTabs
= true;
342 ImplGetSVData()->maNWFData
.mnStatusBarLowerRightOffset
= 10;
348 AquaSalInstance::AquaSalInstance()
349 : SalInstance(std::make_unique
<SalYieldMutex
>())
350 , mnActivePrintJobs( 0 )
351 , mbIsLiveResize( false )
352 , mbNoYieldLock( false )
353 , mbTimerProcessed( false )
355 maMainThread
= osl::Thread::getCurrentIdentifier();
357 ImplSVData
* pSVData
= ImplGetSVData();
358 pSVData
->maAppData
.mxToolkitName
= OUString("osx");
359 m_bSupportsOpenGL
= true;
361 #if HAVE_FEATURE_SKIA
362 AquaSkiaSalGraphicsImpl::prepareSkia();
366 AquaSalInstance::~AquaSalInstance()
376 #if HAVE_FEATURE_SKIA
377 SkiaHelper::cleanup();
381 void AquaSalInstance::TriggerUserEventProcessing()
383 dispatch_async(dispatch_get_main_queue(),^{
384 ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent
, NO
);
388 void AquaSalInstance::ProcessEvent( SalUserEvent aEvent
)
390 aEvent
.m_pFrame
->CallCallback( aEvent
.m_nEvent
, aEvent
.m_pData
);
391 maWaitingYieldCond
.set();
394 bool AquaSalInstance::IsMainThread() const
396 return osl::Thread::getCurrentIdentifier() == maMainThread
;
399 void AquaSalInstance::handleAppDefinedEvent( NSEvent
* pEvent
)
401 AquaSalTimer
*pTimer
= static_cast<AquaSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
402 int nSubtype
= [pEvent subtype
];
405 case AppStartTimerEvent
:
407 pTimer
->handleStartTimerEvent( pEvent
);
409 case AppExecuteSVMain
:
411 int nRet
= ImplSVMain();
417 case DispatchTimerEvent
:
419 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
420 if ( pTimer
&& pInst
)
421 pInst
->mbTimerProcessed
= pTimer
->handleDispatchTimerEvent( pEvent
);
424 #if !HAVE_FEATURE_MACOSX_SANDBOX
425 case AppleRemoteControlEvent
: // Defined in <apple_remote/RemoteMainController.h>
427 MediaCommand nCommand
;
428 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
429 bool bIsFullScreenMode
= false;
431 for( auto pSalFrame
: pInst
->getFrames() )
433 const AquaSalFrame
* pFrame
= static_cast<const AquaSalFrame
*>( pSalFrame
);
434 if ( pFrame
->mbFullScreen
)
436 bIsFullScreenMode
= true;
441 switch ([pEvent data1
])
443 case kRemoteButtonPlay
:
444 nCommand
= bIsFullScreenMode
? MediaCommand::PlayPause
: MediaCommand::Play
;
447 // kept for experimentation purpose (scheduled for future implementation)
448 // case kRemoteButtonMenu: nCommand = MediaCommand::Menu; break;
450 case kRemoteButtonPlus
: nCommand
= MediaCommand::VolumeUp
; break;
452 case kRemoteButtonMinus
: nCommand
= MediaCommand::VolumeDown
; break;
454 case kRemoteButtonRight
: nCommand
= MediaCommand::NextTrack
; break;
456 case kRemoteButtonRight_Hold
: nCommand
= MediaCommand::NextTrackHold
; break;
458 case kRemoteButtonLeft
: nCommand
= MediaCommand::PreviousTrack
; break;
460 case kRemoteButtonLeft_Hold
: nCommand
= MediaCommand::Rewind
; break;
462 case kRemoteButtonPlay_Hold
: nCommand
= MediaCommand::PlayHold
; break;
464 case kRemoteButtonMenu_Hold
: nCommand
= MediaCommand::Stop
; break;
466 // FIXME : not detected
467 case kRemoteButtonPlus_Hold
:
468 case kRemoteButtonMinus_Hold
:
474 AquaSalFrame
* pFrame
= static_cast<AquaSalFrame
*>( pInst
->anyFrame() );
475 vcl::Window
* pWindow
= pFrame
? pFrame
->GetWindow() : nullptr;
479 CommandMediaData
aMediaData(nCommand
);
480 CommandEvent
aCEvt( aPoint
, CommandEventId::Media
, false, &aMediaData
);
481 NotifyEvent
aNCmdEvt( MouseNotifyEvent::COMMAND
, pWindow
, &aCEvt
);
483 if ( !ImplCallPreNotify( aNCmdEvt
) )
484 pWindow
->Command( aCEvt
);
491 case YieldWakeupEvent
:
492 // do nothing, fall out of Yield
496 OSL_FAIL( "unhandled NSEventTypeApplicationDefined event" );
501 bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents
)
503 OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents
), boolean
)
504 assert( false && "Don't call this from the main thread!" );
509 static bool isWakeupEvent( NSEvent
*pEvent
)
511 return NSEventTypeApplicationDefined
== [pEvent type
]
512 && AquaSalInstance::YieldWakeupEvent
== static_cast<int>([pEvent subtype
]);
515 bool AquaSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
517 // ensure that the per thread autorelease pool is top level and
518 // will therefore not be destroyed by cocoa implicitly
519 SalData::ensureThreadAutoreleasePool();
521 // NSAutoreleasePool documentation suggests we should have
522 // an own pool for each yield level
523 ReleasePoolHolder aReleasePool
;
525 // first, process current user events
526 bool bHadEvent
= DispatchUserEvents( bHandleAllCurrentEvents
);
527 if ( !bHandleAllCurrentEvents
&& bHadEvent
)
530 // handle cocoa event queue
531 // cocoa events may be only handled in the thread the NSApp was created
532 if( IsMainThread() && mnActivePrintJobs
== 0 )
534 // handle available events
535 NSEvent
* pEvent
= nil
;
536 NSTimeInterval now
= [[NSProcessInfo processInfo
] systemUptime
];
537 mbTimerProcessed
= false;
541 SolarMutexReleaser aReleaser
;
543 pEvent
= [NSApp nextEventMatchingMask
: NSEventMaskAny
545 inMode
: NSDefaultRunLoopMode
549 [NSApp sendEvent
: pEvent
];
550 if ( isWakeupEvent( pEvent
) )
555 [NSApp updateWindows
];
557 if ( !bHandleAllCurrentEvents
|| !pEvent
|| now
< [pEvent timestamp
] )
562 AquaSalTimer
*pTimer
= static_cast<AquaSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
563 if ( !mbTimerProcessed
&& pTimer
&& pTimer
->IsDirectTimeout() )
565 pTimer
->handleTimerElapsed();
569 // if we had no event yet, wait for one if requested
570 if( bWait
&& ! bHadEvent
)
572 SolarMutexReleaser aReleaser
;
574 pEvent
= [NSApp nextEventMatchingMask
: NSEventMaskAny
575 untilDate
: [NSDate distantFuture
]
576 inMode
: NSDefaultRunLoopMode
580 [NSApp sendEvent
: pEvent
];
581 if ( !isWakeupEvent( pEvent
) )
584 [NSApp updateWindows
];
587 // collect update rectangles
588 for( auto pSalFrame
: GetSalData()->mpInstance
->getFrames() )
590 AquaSalFrame
* pFrame
= static_cast<AquaSalFrame
*>( pSalFrame
);
591 if( pFrame
->mbShown
&& ! pFrame
->maInvalidRect
.IsEmpty() )
593 pFrame
->Flush( pFrame
->maInvalidRect
);
594 pFrame
->maInvalidRect
.SetEmpty();
599 maWaitingYieldCond
.set();
603 bHadEvent
= RunInMainYield( bHandleAllCurrentEvents
);
604 if ( !bHadEvent
&& bWait
)
607 // wait until the main thread has dispatched an event
608 maWaitingYieldCond
.reset();
609 SolarMutexReleaser aReleaser
;
610 maWaitingYieldCond
.wait();
614 // we get some apple events way too early
615 // before the application is ready to handle them,
616 // so their corresponding application events need to be delayed
617 // now is a good time to handle at least one of them
618 if( bWait
&& !aAppEventList
.empty() && ImplGetSVData()->maAppData
.mbInAppExecute
)
620 // make sure that only one application event is active at a time
621 static bool bInAppEvent
= false;
625 // get the next delayed application event
626 const ApplicationEvent
* pAppEvent
= aAppEventList
.front();
627 aAppEventList
.pop_front();
628 // handle one application event (no recursion)
629 const ImplSVData
* pSVData
= ImplGetSVData();
630 pSVData
->mpApp
->AppEvent( *pAppEvent
);
632 // allow the next delayed application event
640 bool AquaSalInstance::AnyInput( VclInputFlags nType
)
642 if( nType
& VclInputFlags::APPEVENT
)
644 if( ! aAppEventList
.empty() )
646 if( nType
== VclInputFlags::APPEVENT
)
650 OSX_INST_RUNINMAIN_UNION( AnyInput( nType
), boolean
)
652 if( nType
& VclInputFlags::TIMER
)
654 AquaSalTimer
*pTimer
= static_cast<AquaSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
655 if (pTimer
&& pTimer
->IsTimerElapsed())
659 unsigned/*NSUInteger*/ nEventMask
= 0;
660 if( nType
& VclInputFlags::MOUSE
)
662 NSEventMaskLeftMouseDown
| NSEventMaskRightMouseDown
| NSEventMaskOtherMouseDown
|
663 NSEventMaskLeftMouseUp
| NSEventMaskRightMouseUp
| NSEventMaskOtherMouseUp
|
664 NSEventMaskLeftMouseDragged
| NSEventMaskRightMouseDragged
| NSEventMaskOtherMouseDragged
|
665 NSEventMaskScrollWheel
|
666 // NSEventMaskMouseMoved |
667 NSEventMaskMouseEntered
| NSEventMaskMouseExited
;
668 if( nType
& VclInputFlags::KEYBOARD
)
669 nEventMask
|= NSEventMaskKeyDown
| NSEventMaskKeyUp
| NSEventMaskFlagsChanged
;
670 if( nType
& VclInputFlags::OTHER
)
671 nEventMask
|= NSEventMaskTabletPoint
| NSEventMaskApplicationDefined
;
672 // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
676 NSEvent
* pEvent
= [NSApp nextEventMatchingMask
: nEventMask untilDate
: nil
677 inMode
: NSDefaultRunLoopMode dequeue
: NO
];
678 return (pEvent
!= nullptr);
681 SalFrame
* AquaSalInstance::CreateChildFrame( SystemParentData
*, SalFrameStyleFlags
/*nSalFrameStyle*/ )
686 SalFrame
* AquaSalInstance::CreateFrame( SalFrame
* pParent
, SalFrameStyleFlags nSalFrameStyle
)
688 OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent
, nSalFrameStyle
), SalFrame
* )
689 return new AquaSalFrame( pParent
, nSalFrameStyle
);
692 void AquaSalInstance::DestroyFrame( SalFrame
* pFrame
)
694 OSX_INST_RUNINMAIN( DestroyFrame( pFrame
) )
698 SalObject
* AquaSalInstance::CreateObject( SalFrame
* pParent
, SystemWindowData
* pWindowData
, bool /* bShow */ )
703 OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent
, pWindowData
, false ), SalObject
* )
704 return new AquaSalObject( static_cast<AquaSalFrame
*>(pParent
), pWindowData
);
707 void AquaSalInstance::DestroyObject( SalObject
* pObject
)
709 OSX_INST_RUNINMAIN( DestroyObject( pObject
) )
713 std::unique_ptr
<SalPrinter
> AquaSalInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
715 return std::unique_ptr
<SalPrinter
>(new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter
*>(pInfoPrinter
) ));
718 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList
* pList
)
720 NSArray
* pNames
= [NSPrinter printerNames
];
721 NSArray
* pTypes
= [NSPrinter printerTypes
];
722 unsigned int nNameCount
= pNames
? [pNames count
] : 0;
723 unsigned int nTypeCount
= pTypes
? [pTypes count
] : 0;
724 SAL_WARN_IF( nTypeCount
!= nNameCount
, "vcl", "type count not equal to printer count" );
725 for( unsigned int i
= 0; i
< nNameCount
; i
++ )
727 NSString
* pName
= [pNames objectAtIndex
: i
];
728 NSString
* pType
= i
< nTypeCount
? [pTypes objectAtIndex
: i
] : nil
;
731 std::unique_ptr
<SalPrinterQueueInfo
> pInfo(new SalPrinterQueueInfo
);
732 pInfo
->maPrinterName
= GetOUString( pName
);
734 pInfo
->maDriver
= GetOUString( pType
);
735 pInfo
->mnStatus
= PrintQueueFlags::NONE
;
738 pList
->Add( std::move(pInfo
) );
743 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo
* )
747 OUString
AquaSalInstance::GetDefaultPrinter()
749 // #i113170# may not be the main thread if called from UNO API
750 SalData::ensureThreadAutoreleasePool();
752 if( maDefaultPrinter
.isEmpty() )
754 NSPrintInfo
* pPI
= [NSPrintInfo sharedPrintInfo
];
755 SAL_WARN_IF( !pPI
, "vcl", "no print info" );
758 NSPrinter
* pPr
= [pPI printer
];
759 SAL_WARN_IF( !pPr
, "vcl", "no printer in default info" );
762 NSString
* pDefName
= [pPr name
];
763 SAL_WARN_IF( !pDefName
, "vcl", "printer has no name" );
764 maDefaultPrinter
= GetOUString( pDefName
);
768 return maDefaultPrinter
;
771 SalInfoPrinter
* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
772 ImplJobSetup
* pSetupData
)
774 // #i113170# may not be the main thread if called from UNO API
775 SalData::ensureThreadAutoreleasePool();
777 SalInfoPrinter
* pNewInfoPrinter
= nullptr;
780 pNewInfoPrinter
= new AquaSalInfoPrinter( *pQueueInfo
);
782 pNewInfoPrinter
->SetPrinterData( pSetupData
);
785 return pNewInfoPrinter
;
788 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter
* pPrinter
)
790 // #i113170# may not be the main thread if called from UNO API
791 SalData::ensureThreadAutoreleasePool();
796 OUString
AquaSalInstance::GetConnectionIdentifier()
801 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
802 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
803 static OUString
translateToExternalUrl(const OUString
& internalUrl
)
805 uno::Reference
< uno::XComponentContext
> context(
806 comphelper::getProcessComponentContext());
807 return uri::ExternalUriReferenceTranslator::create(context
)->translateToExternal(internalUrl
);
810 // #i104525# many versions of OSX have problems with some URLs:
811 // when an app requests OSX to add one of these URLs to the "Recent Items" list
812 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
813 static bool isDangerousUrl( const OUString
& rUrl
)
815 // use a heuristic that detects all known cases since there is no official comment
816 // on the exact impact and root cause of the OSX bug
817 const int nLen
= rUrl
.getLength();
818 const sal_Unicode
* p
= rUrl
.getStr();
819 for( int i
= 0; i
< nLen
-3; ++i
, ++p
) {
823 if( (p
[1] == '2') && (p
[2] == '5') )
825 // escapes are considered to be UTF-8 encoded
826 // => check for invalid UTF-8 leading byte
827 if( (p
[1] != 'f') && (p
[1] != 'F') )
829 int cLowNibble
= p
[2];
830 if( (cLowNibble
>= '0' ) && (cLowNibble
<= '9'))
832 if( cLowNibble
>= 'a' )
833 cLowNibble
-= 'a' - 'A';
834 if( (cLowNibble
< 'A') || (cLowNibble
>= 'C'))
841 void AquaSalInstance::AddToRecentDocumentList(const OUString
& rFileUrl
, const OUString
& /*rMimeType*/, const OUString
& /*rDocumentService*/)
843 // Convert file URL for external use (see above)
844 OUString externalUrl
= translateToExternalUrl(rFileUrl
);
845 if( externalUrl
.isEmpty() )
846 externalUrl
= rFileUrl
;
848 if( !externalUrl
.isEmpty() && !isDangerousUrl( externalUrl
) )
850 NSString
* pString
= CreateNSString( externalUrl
);
851 NSURL
* pURL
= [NSURL URLWithString
: pString
];
855 NSDocumentController
* pCtrl
= [NSDocumentController sharedDocumentController
];
856 [pCtrl noteNewRecentDocumentURL
: pURL
];
863 SalTimer
* AquaSalInstance::CreateSalTimer()
865 return new AquaSalTimer();
868 SalSystem
* AquaSalInstance::CreateSalSystem()
870 return new AquaSalSystem();
873 std::shared_ptr
<SalBitmap
> AquaSalInstance::CreateSalBitmap()
875 #if HAVE_FEATURE_SKIA
876 if (SkiaHelper::isVCLSkiaEnabled())
877 return std::make_shared
<SkiaSalBitmap
>();
880 return std::make_shared
<QuartzSalBitmap
>();
883 OUString
AquaSalInstance::getOSVersion()
885 NSString
* versionString
= nullptr;
886 NSDictionary
* sysVersionDict
= [ NSDictionary dictionaryWithContentsOfFile
: @
"/System/Library/CoreServices/SystemVersion.plist" ];
887 if ( sysVersionDict
)
888 versionString
= [ sysVersionDict valueForKey
: @
"ProductVersion" ];
890 OUString aVersion
= "Mac OS X ";
892 aVersion
+= OUString::fromUtf8( [ versionString UTF8String
] );
894 aVersion
+= "(unknown)";
899 CGImageRef
CreateCGImage( const Image
& rImage
)
901 #if HAVE_FEATURE_SKIA
902 if (SkiaHelper::isVCLSkiaEnabled())
903 return SkiaHelper::createCGImage( rImage
);
906 BitmapEx
aBmpEx( rImage
.GetBitmapEx() );
907 Bitmap
aBmp( aBmpEx
.GetBitmap() );
909 if( aBmp
.IsEmpty() || ! aBmp
.ImplGetSalBitmap() )
912 // simple case, no transparency
913 QuartzSalBitmap
* pSalBmp
= static_cast<QuartzSalBitmap
*>(aBmp
.ImplGetSalBitmap().get());
918 CGImageRef xImage
= nullptr;
919 if( !aBmpEx
.IsAlpha() )
920 xImage
= pSalBmp
->CreateCroppedImage( 0, 0, pSalBmp
->mnWidth
, pSalBmp
->mnHeight
);
923 AlphaMask
aAlphaMask( aBmpEx
.GetAlpha() );
924 Bitmap
aMask( aAlphaMask
.GetBitmap() );
925 QuartzSalBitmap
* pMaskBmp
= static_cast<QuartzSalBitmap
*>(aMask
.ImplGetSalBitmap().get());
927 xImage
= pSalBmp
->CreateWithMask( *pMaskBmp
, 0, 0, pSalBmp
->mnWidth
, pSalBmp
->mnHeight
);
929 xImage
= pSalBmp
->CreateCroppedImage( 0, 0, pSalBmp
->mnWidth
, pSalBmp
->mnHeight
);
935 NSImage
* CreateNSImage( const Image
& rImage
)
937 CGImageRef xImage
= CreateCGImage( rImage
);
942 Size
aSize( rImage
.GetSizePixel() );
943 NSImage
* pImage
= [[NSImage alloc
] initWithSize
: NSMakeSize( aSize
.Width(), aSize
.Height() )];
946 [pImage lockFocusFlipped
:YES
];
947 NSGraphicsContext
* pContext
= [NSGraphicsContext currentContext
];
948 CGContextRef rCGContext
= [pContext CGContext
];
950 const CGRect aDstRect
= { {0, 0}, { static_cast<CGFloat
>(aSize
.Width()), static_cast<CGFloat
>(aSize
.Height()) } };
951 CGContextDrawImage( rCGContext
, aDstRect
, xImage
);
953 [pImage unlockFocus
];
956 CGImageRelease( xImage
);
961 bool AquaSalInstance::SVMainHook(int* pnInit
)
965 OUString aExeURL
, aExe
;
966 osl_getExecutableFile( &aExeURL
.pData
);
967 osl_getSystemPathFromFileURL( aExeURL
.pData
, &aExe
.pData
);
968 OString
aByteExe( OUStringToOString( aExe
, osl_getThreadTextEncoding() ) );
971 aByteExe
+= OString ( " NSAccessibilityDebugLogLevel 1" );
972 const char* pArgv
[] = { aByteExe
.getStr(), NULL
};
973 NSApplicationMain( 3, pArgv
);
975 const char* pArgv
[] = { aByteExe
.getStr(), nullptr };
976 NSApplicationMain( 1, pArgv
);
982 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */