Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / vcl / osx / salinst.cxx
blobb7a45c9a4f114f7ab962019df4545cf9de992829
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>
22 #include <condition_variable>
23 #include <mutex>
24 #include <utility>
26 #include <config_features.h>
28 #include <stdio.h>
30 #include <comphelper/solarmutex.hxx>
32 #include <comphelper/lok.hxx>
34 #include <osl/process.h>
36 #include <rtl/ustrbuf.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/window.hxx>
40 #include <vcl/idle.hxx>
41 #include <vcl/svmain.hxx>
42 #include <vcl/opengl/OpenGLContext.hxx>
44 #include <osx/saldata.hxx>
45 #include <osx/salinst.h>
46 #include <osx/salframe.h>
47 #include <osx/salobj.h>
48 #include <osx/salsys.h>
49 #include <quartz/salvd.h>
50 #include <quartz/salbmp.h>
51 #include <quartz/utils.h>
52 #include <osx/salprn.h>
53 #include <osx/saltimer.h>
54 #include <osx/vclnsapp.h>
55 #include <osx/runinmain.hxx>
57 #include <print.h>
58 #include <salimestatus.hxx>
60 #include <comphelper/processfactory.hxx>
62 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
63 #include <com/sun/star/uno/XComponentContext.hpp>
65 #include <premac.h>
66 #include <Foundation/Foundation.h>
67 #include <ApplicationServices/ApplicationServices.h>
68 #import "apple_remote/RemoteMainController.h"
69 #include <apple_remote/RemoteControl.h>
70 #include <postmac.h>
72 extern "C" {
73 #include <crt_externs.h>
76 using namespace std;
77 using namespace ::com::sun::star;
79 static int* gpnInit = nullptr;
80 static NSMenu* pDockMenu = nil;
81 static bool bNoSVMain = true;
82 static bool bLeftMain = false;
84 class AquaDelayedSettingsChanged : public Idle
86 bool mbInvalidate;
88 public:
89 AquaDelayedSettingsChanged( bool bInvalidate ) :
90 mbInvalidate( bInvalidate )
94 virtual void Invoke() override
96 AquaSalInstance *pInst = GetSalData()->mpInstance;
97 SalFrame *pAnyFrame = pInst->anyFrame();
98 if( pAnyFrame )
99 pAnyFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
101 if( mbInvalidate )
103 for( auto pSalFrame : pInst->getFrames() )
105 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
106 if( pFrame->mbShown )
107 pFrame->SendPaintEvent();
110 delete this;
114 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
116 osl::Guard< comphelper::SolarMutex > aGuard( *mpSalYieldMutex );
117 AquaDelayedSettingsChanged* pIdle = new AquaDelayedSettingsChanged( bInvalidate );
118 pIdle->SetDebugName( "AquaSalInstance AquaDelayedSettingsChanged" );
119 pIdle->Start();
122 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
123 std::list<const ApplicationEvent*> AquaSalInstance::aAppEventList;
125 NSMenu* AquaSalInstance::GetDynamicDockMenu()
127 if( ! pDockMenu && ! bLeftMain )
128 pDockMenu = [[NSMenu alloc] initWithTitle: @""];
129 return pDockMenu;
132 bool AquaSalInstance::isOnCommandLine( const OUString& rArg )
134 sal_uInt32 nArgs = osl_getCommandArgCount();
135 for( sal_uInt32 i = 0; i < nArgs; i++ )
137 OUString aArg;
138 osl_getCommandArg( i, &aArg.pData );
139 if( aArg.equals( rArg ) )
140 return true;
142 return false;
145 // initialize the cocoa VCL_NSApplication object
146 // returns an NSAutoreleasePool that must be released when the event loop begins
147 static void initNSApp()
149 // create our cocoa NSApplication
150 [VCL_NSApplication sharedApplication];
152 SalData::ensureThreadAutoreleasePool();
154 // put cocoa into multithreaded mode
155 [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
157 // activate our delegate methods
158 [NSApp setDelegate: NSApp];
161 void postInitVCLinitNSApp()
163 [[NSNotificationCenter defaultCenter] addObserver: NSApp
164 selector: @selector(systemColorsChanged:)
165 name: NSSystemColorsDidChangeNotification
166 object: nil ];
167 [[NSNotificationCenter defaultCenter] addObserver: NSApp
168 selector: @selector(screenParametersChanged:)
169 name: NSApplicationDidChangeScreenParametersNotification
170 object: nil ];
171 // add observers for some settings changes that affect vcl's settings
172 // scrollbar variant
173 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
174 selector: @selector(scrollbarVariantChanged:)
175 name: @"AppleAquaScrollBarVariantChanged"
176 object: nil ];
177 // scrollbar page behavior ("jump to here" or not)
178 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
179 selector: @selector(scrollbarSettingsChanged:)
180 name: @"AppleNoRedisplayAppearancePreferenceChanged"
181 object: nil ];
182 #if !HAVE_FEATURE_MACOSX_SANDBOX
183 // Initialize Apple Remote
184 GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init];
186 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
187 selector: @selector(applicationWillBecomeActive:)
188 name: @"AppleRemoteWillBecomeActive"
189 object: nil ];
191 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
192 selector: @selector(applicationWillResignActive:)
193 name: @"AppleRemoteWillResignActive"
194 object: nil ];
195 #endif
198 bool ImplSVMainHook( int * pnInit )
200 if (comphelper::LibreOfficeKit::isActive())
201 return false;
203 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
204 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
205 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
206 [ pool drain ];
208 gpnInit = pnInit;
210 bNoSVMain = false;
211 initNSApp();
213 OUString aExeURL, aExe;
214 osl_getExecutableFile( &aExeURL.pData );
215 osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData );
216 OString aByteExe( OUStringToOString( aExe, osl_getThreadTextEncoding() ) );
218 #ifdef DEBUG
219 aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" );
220 const char* pArgv[] = { aByteExe.getStr(), NULL };
221 NSApplicationMain( 3, pArgv );
222 #else
223 const char* pArgv[] = { aByteExe.getStr(), nullptr };
224 NSApplicationMain( 1, pArgv );
225 #endif
227 return TRUE; // indicate that ImplSVMainHook is implemented
230 void SalAbort( const OUString& rErrorText, bool bDumpCore )
232 if( rErrorText.isEmpty() )
233 fprintf( stderr, "Application Error " );
234 else
235 fprintf( stderr, "%s ",
236 OUStringToOString( rErrorText, osl_getThreadTextEncoding() ).getStr() );
237 if( bDumpCore )
238 abort();
239 else
240 _exit(1);
243 void InitSalData()
245 SalData *pSalData = new SalData;
246 SetSalData( pSalData );
249 const OUString& SalGetDesktopEnvironment()
251 static OUString aDesktopEnvironment( "MacOSX" );
252 return aDesktopEnvironment;
255 void DeInitSalData()
257 SalData *pSalData = GetSalData();
258 if( pSalData->mpStatusItem )
260 [pSalData->mpStatusItem release];
261 pSalData->mpStatusItem = nil;
263 delete pSalData;
264 SetSalData( nullptr );
267 void InitSalMain()
271 SalYieldMutex::SalYieldMutex()
272 : m_aCodeBlock( nullptr )
276 SalYieldMutex::~SalYieldMutex()
280 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
282 AquaSalInstance *pInst = GetSalData()->mpInstance;
283 if ( pInst && pInst->IsMainThread() )
285 if ( pInst->mbNoYieldLock )
286 return;
287 do {
288 RuninmainBlock block = nullptr;
290 std::unique_lock<std::mutex> g(m_runInMainMutex);
291 if (m_aMutex.tryToAcquire()) {
292 assert(m_aCodeBlock == nullptr);
293 m_wakeUpMain = false;
294 break;
296 // wait for doRelease() or RUNINMAIN_* to set the condition
297 m_aInMainCondition.wait(g, [this]() { return m_wakeUpMain; });
298 m_wakeUpMain = false;
299 std::swap(block, m_aCodeBlock);
301 if ( block )
303 assert( !pInst->mbNoYieldLock );
304 pInst->mbNoYieldLock = true;
305 block();
306 pInst->mbNoYieldLock = false;
307 Block_release( block );
308 std::unique_lock<std::mutex> g(m_runInMainMutex);
309 assert(!m_resultReady);
310 m_resultReady = true;
311 m_aResultCondition.notify_all();
314 while ( true );
316 else
317 m_aMutex.acquire();
318 ++m_nCount;
319 --nLockCount;
321 comphelper::GenericSolarMutex::doAcquire( nLockCount );
324 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
326 AquaSalInstance *pInst = GetSalData()->mpInstance;
327 if ( pInst->mbNoYieldLock && pInst->IsMainThread() )
328 return 1;
329 sal_uInt32 nCount;
331 std::unique_lock<std::mutex> g(m_runInMainMutex);
332 // read m_nCount before doRelease
333 bool const isReleased(bUnlockAll || m_nCount == 1);
334 nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll );
335 if (isReleased && !pInst->IsMainThread()) {
336 m_wakeUpMain = true;
337 m_aInMainCondition.notify_all();
340 return nCount;
343 bool SalYieldMutex::IsCurrentThread() const
345 if ( !GetSalData()->mpInstance->mbNoYieldLock )
346 return comphelper::GenericSolarMutex::IsCurrentThread();
347 else
348 return GetSalData()->mpInstance->IsMainThread();
351 // some convenience functions regarding the yield mutex, aka solar mutex
353 bool ImplSalYieldMutexTryToAcquire()
355 AquaSalInstance* pInst = GetSalData()->mpInstance;
356 if ( pInst )
357 return pInst->mpSalYieldMutex->tryToAcquire();
358 else
359 return FALSE;
362 void ImplSalYieldMutexRelease()
364 AquaSalInstance* pInst = GetSalData()->mpInstance;
365 if ( pInst )
366 pInst->mpSalYieldMutex->release();
369 SalInstance* CreateSalInstance()
371 // this is the case for not using SVMain
372 // not so good
373 if( bNoSVMain )
374 initNSApp();
376 SalData* pSalData = GetSalData();
377 SAL_WARN_IF( pSalData->mpInstance != nullptr, "vcl", "more than one instance created" );
378 AquaSalInstance* pInst = new AquaSalInstance;
380 // init instance (only one instance in this version !!!)
381 pSalData->mpInstance = pInst;
382 // this one is for outside AquaSalInstance::Yield
383 SalData::ensureThreadAutoreleasePool();
384 // no focus rects on NWF
385 ImplGetSVData()->maNWFData.mbNoFocusRects = true;
386 ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true;
387 ImplGetSVData()->maNWFData.mbCenteredTabs = true;
388 ImplGetSVData()->maNWFData.mbProgressNeedsErase = true;
389 ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10;
391 return pInst;
394 void DestroySalInstance( SalInstance* pInst )
396 delete pInst;
399 AquaSalInstance::AquaSalInstance()
400 : mnActivePrintJobs( 0 )
401 , mbIsLiveResize( false )
402 , mbNoYieldLock( false )
403 , mbTimerProcessed( false )
405 mpSalYieldMutex = new SalYieldMutex;
406 mpSalYieldMutex->acquire();
407 maMainThread = osl::Thread::getCurrentIdentifier();
410 AquaSalInstance::~AquaSalInstance()
412 mpSalYieldMutex->release();
413 delete mpSalYieldMutex;
416 void AquaSalInstance::TriggerUserEventProcessing()
418 dispatch_async(dispatch_get_main_queue(),^{
419 ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO );
423 void AquaSalInstance::ProcessEvent( SalUserEvent aEvent )
425 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
426 maWaitingYieldCond.set();
429 comphelper::SolarMutex* AquaSalInstance::GetYieldMutex()
431 return mpSalYieldMutex;
434 sal_uInt32 AquaSalInstance::ReleaseYieldMutexAll()
436 return mpSalYieldMutex->release( true/*bUnlockAll*/ );
439 void AquaSalInstance::AcquireYieldMutex( sal_uInt32 nCount )
441 mpSalYieldMutex->acquire( nCount );
444 bool AquaSalInstance::IsMainThread() const
446 return osl::Thread::getCurrentIdentifier() == maMainThread;
449 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
451 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
452 int nSubtype = [pEvent subtype];
453 switch( nSubtype )
455 case AppStartTimerEvent:
456 if ( pTimer )
457 pTimer->handleStartTimerEvent( pEvent );
458 break;
459 case AppEndLoopEvent:
460 [NSApp stop: NSApp];
461 break;
462 case AppExecuteSVMain:
464 int nResult = ImplSVMain();
465 if( gpnInit )
466 *gpnInit = nResult;
467 [NSApp stop: NSApp];
468 bLeftMain = true;
469 if( pDockMenu )
471 [pDockMenu release];
472 pDockMenu = nil;
474 break;
476 case DispatchTimerEvent:
478 AquaSalInstance *pInst = GetSalData()->mpInstance;
479 if ( pTimer && pInst )
480 pInst->mbTimerProcessed = pTimer->handleDispatchTimerEvent( pEvent );
481 break;
483 #if !HAVE_FEATURE_MACOSX_SANDBOX
484 case AppleRemoteControlEvent: // Defined in <apple_remote/RemoteMainController.h>
486 MediaCommand nCommand;
487 AquaSalInstance *pInst = GetSalData()->mpInstance;
488 bool bIsFullScreenMode = false;
490 for( auto pSalFrame : pInst->getFrames() )
492 const AquaSalFrame* pFrame = static_cast<const AquaSalFrame*>( pSalFrame );
493 if ( pFrame->mbFullScreen )
495 bIsFullScreenMode = true;
496 break;
500 switch ([pEvent data1])
502 case kRemoteButtonPlay:
503 nCommand = bIsFullScreenMode ? MediaCommand::PlayPause : MediaCommand::Play;
504 break;
506 // kept for experimentation purpose (scheduled for future implementation)
507 // case kRemoteButtonMenu: nCommand = MediaCommand::Menu; break;
509 case kRemoteButtonPlus: nCommand = MediaCommand::VolumeUp; break;
511 case kRemoteButtonMinus: nCommand = MediaCommand::VolumeDown; break;
513 case kRemoteButtonRight: nCommand = MediaCommand::NextTrack; break;
515 case kRemoteButtonRight_Hold: nCommand = MediaCommand::NextTrackHold; break;
517 case kRemoteButtonLeft: nCommand = MediaCommand::PreviousTrack; break;
519 case kRemoteButtonLeft_Hold: nCommand = MediaCommand::Rewind; break;
521 case kRemoteButtonPlay_Hold: nCommand = MediaCommand::PlayHold; break;
523 case kRemoteButtonMenu_Hold: nCommand = MediaCommand::Stop; break;
525 // FIXME : not detected
526 case kRemoteButtonPlus_Hold:
527 case kRemoteButtonMinus_Hold:
528 break;
530 default:
531 break;
533 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pInst->anyFrame() );
534 vcl::Window* pWindow = pFrame ? pFrame->GetWindow() : nullptr;
535 if( pWindow )
537 const Point aPoint;
538 CommandMediaData aMediaData(nCommand);
539 CommandEvent aCEvt( aPoint, CommandEventId::Media, FALSE, &aMediaData );
540 NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt );
542 if ( !ImplCallPreNotify( aNCmdEvt ) )
543 pWindow->Command( aCEvt );
547 break;
548 #endif
550 case YieldWakeupEvent:
551 // do nothing, fall out of Yield
552 break;
554 default:
555 OSL_FAIL( "unhandled NSApplicationDefined event" );
556 break;
560 bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents )
562 OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents), boolean )
563 assert( false && "Don't call this from the main thread!" );
564 return false;
568 static bool isWakeupEvent( NSEvent *pEvent )
570 SAL_WNODEPRECATED_DECLARATIONS_PUSH
571 return NSApplicationDefined == [pEvent type]
572 && AquaSalInstance::YieldWakeupEvent == static_cast<int>([pEvent subtype]);
573 SAL_WNODEPRECATED_DECLARATIONS_POP
576 bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
578 // ensure that the per thread autorelease pool is top level and
579 // will therefore not be destroyed by cocoa implicitly
580 SalData::ensureThreadAutoreleasePool();
582 // NSAutoreleasePool documentation suggests we should have
583 // an own pool for each yield level
584 ReleasePoolHolder aReleasePool;
586 // first, process current user events
587 bool bHadEvent = DispatchUserEvents( bHandleAllCurrentEvents );
588 if ( !bHandleAllCurrentEvents && bHadEvent )
589 return true;
591 // handle cocoa event queue
592 // cocoa events may be only handled in the thread the NSApp was created
593 if( IsMainThread() && mnActivePrintJobs == 0 )
595 // handle available events
596 NSEvent* pEvent = nil;
597 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
598 mbTimerProcessed = false;
602 SolarMutexReleaser aReleaser;
604 SAL_WNODEPRECATED_DECLARATIONS_PUSH
605 // 'NSAnyEventMask' is deprecated: first deprecated in macOS 10.12
606 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask
607 SAL_WNODEPRECATED_DECLARATIONS_POP
608 untilDate: nil
609 inMode: NSDefaultRunLoopMode
610 dequeue: YES];
611 if( pEvent )
613 [NSApp sendEvent: pEvent];
614 if ( isWakeupEvent( pEvent ) )
615 continue;
616 bHadEvent = true;
619 [NSApp updateWindows];
621 if ( !bHandleAllCurrentEvents || !pEvent || now < [pEvent timestamp] )
622 break;
624 while( true );
626 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
627 if ( !mbTimerProcessed && pTimer && pTimer->IsDirectTimeout() )
629 pTimer->handleTimerElapsed();
630 bHadEvent = true;
633 // if we had no event yet, wait for one if requested
634 if( bWait && ! bHadEvent )
636 SolarMutexReleaser aReleaser;
638 SAL_WNODEPRECATED_DECLARATIONS_PUSH
639 // 'NSAnyEventMask' is deprecated: first deprecated in macOS 10.12
640 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask
641 SAL_WNODEPRECATED_DECLARATIONS_POP
642 untilDate: [NSDate distantFuture]
643 inMode: NSDefaultRunLoopMode
644 dequeue: YES];
645 if( pEvent )
647 [NSApp sendEvent: pEvent];
648 if ( !isWakeupEvent( pEvent ) )
649 bHadEvent = true;
651 [NSApp updateWindows];
654 // collect update rectangles
655 for( auto pSalFrame : GetSalData()->mpInstance->getFrames() )
657 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
658 if( pFrame->mbShown && ! pFrame->maInvalidRect.IsEmpty() )
660 pFrame->Flush( pFrame->maInvalidRect );
661 pFrame->maInvalidRect.SetEmpty();
665 if ( bHadEvent )
666 maWaitingYieldCond.set();
668 else
670 bHadEvent = RunInMainYield( bHandleAllCurrentEvents );
671 if ( !bHadEvent && bWait )
673 // #i103162#
674 // wait until the main thread has dispatched an event
675 maWaitingYieldCond.reset();
676 SolarMutexReleaser aReleaser;
677 maWaitingYieldCond.wait();
681 // we get some apple events way too early
682 // before the application is ready to handle them,
683 // so their corresponding application events need to be delayed
684 // now is a good time to handle at least one of them
685 if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
687 // make sure that only one application event is active at a time
688 static bool bInAppEvent = false;
689 if( !bInAppEvent )
691 bInAppEvent = true;
692 // get the next delayed application event
693 const ApplicationEvent* pAppEvent = aAppEventList.front();
694 aAppEventList.pop_front();
695 // handle one application event (no recursion)
696 const ImplSVData* pSVData = ImplGetSVData();
697 pSVData->mpApp->AppEvent( *pAppEvent );
698 delete pAppEvent;
699 // allow the next delayed application event
700 bInAppEvent = false;
704 return bHadEvent;
707 bool AquaSalInstance::AnyInput( VclInputFlags nType )
709 if( nType & VclInputFlags::APPEVENT )
711 if( ! aAppEventList.empty() )
712 return true;
713 if( nType == VclInputFlags::APPEVENT )
714 return false;
717 OSX_INST_RUNINMAIN_UNION( AnyInput( nType ), boolean )
719 if( nType & VclInputFlags::TIMER )
721 AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
722 if (pTimer && pTimer->IsTimerElapsed())
723 return true;
726 unsigned/*NSUInteger*/ nEventMask = 0;
727 SAL_WNODEPRECATED_DECLARATIONS_PUSH
728 // 'NSFlagsChangedMask' is deprecated: first deprecated in macOS 10.12
729 // 'NSKeyDownMask' is deprecated: first deprecated in macOS 10.12
730 // 'NSKeyUpMask' is deprecated: first deprecated in macOS 10.12
731 // 'NSLeftMouseDownMask' is deprecated: first deprecated in macOS 10.12
732 // 'NSLeftMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
733 // 'NSLeftMouseUpMask' is deprecated: first deprecated in macOS 10.12
734 // 'NSMouseEnteredMask' is deprecated: first deprecated in macOS 10.12
735 // 'NSMouseExitedMask' is deprecated: first deprecated in macOS 10.12
736 // 'NSOtherMouseDownMask' is deprecated: first deprecated in macOS 10.12
737 // 'NSOtherMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
738 // 'NSOtherMouseUpMask' is deprecated: first deprecated in macOS 10.12
739 // 'NSRightMouseDownMask' is deprecated: first deprecated in macOS 10.12
740 // 'NSRightMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
741 // 'NSRightMouseUpMask' is deprecated: first deprecated in macOS 10.12
742 // 'NSScrollWheelMask' is deprecated: first deprecated in macOS 10.12
743 // 'NSTabletPoint' is deprecated: first deprecated in macOS 10.12
744 if( nType & VclInputFlags::MOUSE)
745 nEventMask |=
746 NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask |
747 NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask |
748 NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
749 NSScrollWheelMask |
750 // NSMouseMovedMask |
751 NSMouseEnteredMask | NSMouseExitedMask;
752 if( nType & VclInputFlags::KEYBOARD)
753 nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
754 if( nType & VclInputFlags::OTHER)
755 nEventMask |= NSTabletPoint | NSApplicationDefinedMask;
756 SAL_WNODEPRECATED_DECLARATIONS_POP
757 // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
758 if( !bool(nType) )
759 return false;
761 NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
762 inMode: NSDefaultRunLoopMode dequeue: NO];
763 return (pEvent != nullptr);
766 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, SalFrameStyleFlags /*nSalFrameStyle*/ )
768 return nullptr;
771 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
773 OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent, nSalFrameStyle ), SalFrame* )
774 return new AquaSalFrame( pParent, nSalFrameStyle );
777 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
779 OSX_INST_RUNINMAIN( DestroyFrame( pFrame ) )
780 delete pFrame;
783 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool /* bShow */ )
785 if ( !pParent )
786 return nullptr;
788 OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent, pWindowData, false ), SalObject* )
789 return new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData );
792 void AquaSalInstance::DestroyObject( SalObject* pObject )
794 OSX_INST_RUNINMAIN( DestroyObject( pObject ) )
795 delete pObject;
798 SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
800 return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) );
803 void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter )
805 delete pPrinter;
808 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
810 NSArray* pNames = [NSPrinter printerNames];
811 NSArray* pTypes = [NSPrinter printerTypes];
812 unsigned int nNameCount = pNames ? [pNames count] : 0;
813 unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
814 SAL_WARN_IF( nTypeCount != nNameCount, "vcl", "type count not equal to printer count" );
815 for( unsigned int i = 0; i < nNameCount; i++ )
817 NSString* pName = [pNames objectAtIndex: i];
818 NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
819 if( pName )
821 SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
822 pInfo->maPrinterName = GetOUString( pName );
823 if( pType )
824 pInfo->maDriver = GetOUString( pType );
825 pInfo->mnStatus = PrintQueueFlags::NONE;
826 pInfo->mnJobs = 0;
827 pInfo->mpSysData = nullptr;
829 pList->Add( pInfo );
834 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
838 void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
840 delete pInfo;
843 OUString AquaSalInstance::GetDefaultPrinter()
845 // #i113170# may not be the main thread if called from UNO API
846 SalData::ensureThreadAutoreleasePool();
848 if( maDefaultPrinter.isEmpty() )
850 NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
851 SAL_WARN_IF( !pPI, "vcl", "no print info" );
852 if( pPI )
854 NSPrinter* pPr = [pPI printer];
855 SAL_WARN_IF( !pPr, "vcl", "no printer in default info" );
856 if( pPr )
858 NSString* pDefName = [pPr name];
859 SAL_WARN_IF( !pDefName, "vcl", "printer has no name" );
860 maDefaultPrinter = GetOUString( pDefName );
864 return maDefaultPrinter;
867 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
868 ImplJobSetup* pSetupData )
870 // #i113170# may not be the main thread if called from UNO API
871 SalData::ensureThreadAutoreleasePool();
873 SalInfoPrinter* pNewInfoPrinter = nullptr;
874 if( pQueueInfo )
876 pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
877 if( pSetupData )
878 pNewInfoPrinter->SetPrinterData( pSetupData );
881 return pNewInfoPrinter;
884 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
886 // #i113170# may not be the main thread if called from UNO API
887 SalData::ensureThreadAutoreleasePool();
889 delete pPrinter;
892 OUString AquaSalInstance::GetConnectionIdentifier()
894 return OUString();
897 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
898 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
899 static OUString translateToExternalUrl(const OUString& internalUrl)
901 uno::Reference< uno::XComponentContext > context(
902 comphelper::getProcessComponentContext());
903 return uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
906 // #i104525# many versions of OSX have problems with some URLs:
907 // when an app requests OSX to add one of these URLs to the "Recent Items" list
908 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
909 static bool isDangerousUrl( const OUString& rUrl )
911 // use a heuristic that detects all known cases since there is no official comment
912 // on the exact impact and root cause of the OSX bug
913 const int nLen = rUrl.getLength();
914 const sal_Unicode* p = rUrl.getStr();
915 for( int i = 0; i < nLen-3; ++i, ++p ) {
916 if( p[0] != '%' )
917 continue;
918 // escaped percent?
919 if( (p[1] == '2') && (p[2] == '5') )
920 return true;
921 // escapes are considered to be UTF-8 encoded
922 // => check for invalid UTF-8 leading byte
923 if( (p[1] != 'f') && (p[1] != 'F') )
924 continue;
925 int cLowNibble = p[2];
926 if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
927 return false;
928 if( cLowNibble >= 'a' )
929 cLowNibble -= 'a' - 'A';
930 if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
931 return true;
934 return false;
937 void AquaSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& /*rDocumentService*/)
939 // Convert file URL for external use (see above)
940 OUString externalUrl = translateToExternalUrl(rFileUrl);
941 if( externalUrl.isEmpty() )
942 externalUrl = rFileUrl;
944 if( !externalUrl.isEmpty() && !isDangerousUrl( externalUrl ) )
946 NSString* pString = CreateNSString( externalUrl );
947 NSURL* pURL = [NSURL URLWithString: pString];
949 if( pURL )
951 NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
952 [pCtrl noteNewRecentDocumentURL: pURL];
954 if( pString )
955 [pString release];
959 SalTimer* AquaSalInstance::CreateSalTimer()
961 return new AquaSalTimer();
964 SalSystem* AquaSalInstance::CreateSalSystem()
966 return new AquaSalSystem();
969 SalBitmap* AquaSalInstance::CreateSalBitmap()
971 return new QuartzSalBitmap();
974 SalSession* AquaSalInstance::CreateSalSession()
976 return nullptr;
979 OUString AquaSalInstance::getOSVersion()
981 NSString * versionString = nullptr;
982 NSDictionary * sysVersionDict = [ NSDictionary dictionaryWithContentsOfFile: @"/System/Library/CoreServices/SystemVersion.plist" ];
983 if ( sysVersionDict )
984 versionString = [ sysVersionDict valueForKey: @"ProductVersion" ];
986 OUString aVersion = "Mac OS X ";
987 if ( versionString )
988 aVersion += OUString::fromUtf8( [ versionString UTF8String ] );
989 else
990 aVersion += "(unknown)";
992 return aVersion;
995 CGImageRef CreateCGImage( const Image& rImage )
997 BitmapEx aBmpEx( rImage.GetBitmapEx() );
998 Bitmap aBmp( aBmpEx.GetBitmap() );
1000 if( ! aBmp || ! aBmp.ImplGetSalBitmap() )
1001 return nullptr;
1003 // simple case, no transparency
1004 QuartzSalBitmap* pSalBmp = static_cast<QuartzSalBitmap*>(aBmp.ImplGetSalBitmap().get());
1006 if( ! pSalBmp )
1007 return nullptr;
1009 CGImageRef xImage = nullptr;
1010 if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
1011 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1012 else if( aBmpEx.IsAlpha() )
1014 AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
1015 Bitmap aMask( aAlphaMask.GetBitmap() );
1016 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
1017 if( pMaskBmp )
1018 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1019 else
1020 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1022 else if( aBmpEx.GetTransparentType() == TransparentType::Bitmap )
1024 Bitmap aMask( aBmpEx.GetMask() );
1025 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
1026 if( pMaskBmp )
1027 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1028 else
1029 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1031 else if( aBmpEx.GetTransparentType() == TransparentType::Color )
1033 Color aTransColor( aBmpEx.GetTransparentColor() );
1034 Color nTransColor( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
1035 xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
1038 return xImage;
1041 NSImage* CreateNSImage( const Image& rImage )
1043 CGImageRef xImage = CreateCGImage( rImage );
1045 if( ! xImage )
1046 return nil;
1048 Size aSize( rImage.GetSizePixel() );
1049 NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
1050 if( pImage )
1052 [pImage lockFocusFlipped:YES];
1053 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
1054 CGContextRef rCGContext = static_cast<CGContextRef>([pContext graphicsPort]);
1056 const CGRect aDstRect = { {0, 0}, { static_cast<CGFloat>(aSize.Width()), static_cast<CGFloat>(aSize.Height()) } };
1057 CGContextDrawImage( rCGContext, aDstRect, xImage );
1059 [pImage unlockFocus];
1062 CGImageRelease( xImage );
1064 return pImage;
1068 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */