Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / osx / salinst.cxx
blobb3b97e5ee01da944a3775f07d17aa30ccc25142c
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 <config_features.h>
22 #include <stdio.h>
24 #include <comphelper/solarmutex.hxx>
26 #include "comphelper/lok.hxx"
28 #include "osl/process.h"
30 #include "rtl/ustrbuf.hxx"
32 #include <vcl/svapp.hxx>
33 #include <vcl/window.hxx>
34 #include <vcl/idle.hxx>
35 #include <vcl/svmain.hxx>
36 #include <vcl/opengl/OpenGLContext.hxx>
38 #include "osx/saldata.hxx"
39 #include "osx/salinst.h"
40 #include "osx/salframe.h"
41 #include "osx/salobj.h"
42 #include "osx/salsys.h"
43 #include "quartz/salvd.h"
44 #include "quartz/salbmp.h"
45 #include "quartz/utils.h"
46 #include "osx/salprn.h"
47 #include "osx/saltimer.h"
48 #include "osx/vclnsapp.h"
50 #include "print.h"
51 #include "impbmp.hxx"
52 #include "salimestatus.hxx"
54 #include <comphelper/processfactory.hxx>
56 #include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp>
57 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
58 #include <com/sun/star/uno/XComponentContext.hpp>
60 #include "premac.h"
61 #include <Foundation/Foundation.h>
62 #include <ApplicationServices/ApplicationServices.h>
63 #import "apple_remote/RemoteMainController.h"
64 #include "apple_remote/RemoteControl.h"
65 #include "postmac.h"
67 extern "C" {
68 #include <crt_externs.h>
71 using namespace std;
72 using namespace ::com::sun::star;
74 static int* gpnInit = nullptr;
75 static NSMenu* pDockMenu = nil;
76 static bool bNoSVMain = true;
77 static bool bLeftMain = false;
79 class AquaDelayedSettingsChanged : public Idle
81 bool mbInvalidate;
82 public:
83 AquaDelayedSettingsChanged( bool bInvalidate ) :
84 mbInvalidate( bInvalidate )
88 virtual void Invoke() override
90 SalData* pSalData = GetSalData();
91 if( ! pSalData->maFrames.empty() )
92 pSalData->maFrames.front()->CallCallback( SalEvent::SettingsChanged, nullptr );
94 if( mbInvalidate )
96 for( std::list< AquaSalFrame* >::iterator it = pSalData->maFrames.begin();
97 it != pSalData->maFrames.end(); ++it )
99 if( (*it)->mbShown )
100 (*it)->SendPaintEvent();
103 Stop();
104 delete this;
108 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
110 osl::Guard< comphelper::SolarMutex > aGuard( *mpSalYieldMutex );
111 AquaDelayedSettingsChanged* pIdle = new AquaDelayedSettingsChanged( bInvalidate );
112 pIdle->SetPriority( TaskPriority::MEDIUM );
113 pIdle->Start();
116 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
117 std::list<const ApplicationEvent*> AquaSalInstance::aAppEventList;
119 NSMenu* AquaSalInstance::GetDynamicDockMenu()
121 if( ! pDockMenu && ! bLeftMain )
122 pDockMenu = [[NSMenu alloc] initWithTitle: @""];
123 return pDockMenu;
126 bool AquaSalInstance::isOnCommandLine( const OUString& rArg )
128 sal_uInt32 nArgs = osl_getCommandArgCount();
129 for( sal_uInt32 i = 0; i < nArgs; i++ )
131 OUString aArg;
132 osl_getCommandArg( i, &aArg.pData );
133 if( aArg.equals( rArg ) )
134 return true;
136 return false;
139 // initialize the cocoa VCL_NSApplication object
140 // returns an NSAutoreleasePool that must be released when the event loop begins
141 static void initNSApp()
143 // create our cocoa NSApplication
144 [VCL_NSApplication sharedApplication];
146 SalData::ensureThreadAutoreleasePool();
148 // put cocoa into multithreaded mode
149 [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
151 // activate our delegate methods
152 [NSApp setDelegate: NSApp];
154 [[NSNotificationCenter defaultCenter] addObserver: NSApp
155 selector: @selector(systemColorsChanged:)
156 name: NSSystemColorsDidChangeNotification
157 object: nil ];
158 [[NSNotificationCenter defaultCenter] addObserver: NSApp
159 selector: @selector(screenParametersChanged:)
160 name: NSApplicationDidChangeScreenParametersNotification
161 object: nil ];
162 // add observers for some settings changes that affect vcl's settings
163 // scrollbar variant
164 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
165 selector: @selector(scrollbarVariantChanged:)
166 name: @"AppleAquaScrollBarVariantChanged"
167 object: nil ];
168 // scrollbar page behavior ("jump to here" or not)
169 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
170 selector: @selector(scrollbarSettingsChanged:)
171 name: @"AppleNoRedisplayAppearancePreferenceChanged"
172 object: nil ];
173 #if !HAVE_FEATURE_MACOSX_SANDBOX
174 // Initialize Apple Remote
175 GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init];
177 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
178 selector: @selector(applicationWillBecomeActive:)
179 name: @"AppleRemoteWillBecomeActive"
180 object: nil ];
182 [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
183 selector: @selector(applicationWillResignActive:)
184 name: @"AppleRemoteWillResignActive"
185 object: nil ];
186 #endif
189 bool ImplSVMainHook( int * pnInit )
191 if (comphelper::LibreOfficeKit::isActive())
192 return false;
194 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
195 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
196 unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
197 [ pool drain ];
199 gpnInit = pnInit;
201 bNoSVMain = false;
202 initNSApp();
204 OUString aExeURL, aExe;
205 osl_getExecutableFile( &aExeURL.pData );
206 osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData );
207 OString aByteExe( OUStringToOString( aExe, osl_getThreadTextEncoding() ) );
209 #ifdef DEBUG
210 aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" );
211 const char* pArgv[] = { aByteExe.getStr(), NULL };
212 NSApplicationMain( 3, pArgv );
213 #else
214 const char* pArgv[] = { aByteExe.getStr(), nullptr };
215 NSApplicationMain( 1, pArgv );
216 #endif
218 return TRUE; // indicate that ImplSVMainHook is implemented
221 void SalAbort( const OUString& rErrorText, bool bDumpCore )
223 if( rErrorText.isEmpty() )
224 fprintf( stderr, "Application Error " );
225 else
226 fprintf( stderr, "%s ",
227 OUStringToOString( rErrorText, osl_getThreadTextEncoding() ).getStr() );
228 if( bDumpCore )
229 abort();
230 else
231 _exit(1);
234 void InitSalData()
236 SalData *pSalData = new SalData;
237 SetSalData( pSalData );
240 const OUString& SalGetDesktopEnvironment()
242 static OUString aDesktopEnvironment( "MacOSX" );
243 return aDesktopEnvironment;
246 void DeInitSalData()
248 SalData *pSalData = GetSalData();
249 if( pSalData->mpStatusItem )
251 [pSalData->mpStatusItem release];
252 pSalData->mpStatusItem = nil;
254 delete pSalData;
255 SetSalData( nullptr );
258 void InitSalMain()
262 SalYieldMutex::SalYieldMutex()
264 mnCount = 0;
265 mnThreadId = 0;
268 void SalYieldMutex::acquire()
270 m_mutex.acquire();
271 mnThreadId = osl::Thread::getCurrentIdentifier();
272 mnCount++;
275 void SalYieldMutex::release()
277 if ( mnThreadId == osl::Thread::getCurrentIdentifier() )
279 if ( mnCount == 1 )
281 // TODO: add OpenGLContext::prepareForYield with vcl OpenGL support
282 mnThreadId = 0;
284 mnCount--;
286 m_mutex.release();
289 bool SalYieldMutex::tryToAcquire()
291 if ( m_mutex.tryToAcquire() )
293 mnThreadId = osl::Thread::getCurrentIdentifier();
294 mnCount++;
295 return true;
297 else
298 return false;
301 // some convenience functions regarding the yield mutex, aka solar mutex
303 bool ImplSalYieldMutexTryToAcquire()
305 AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
306 if ( pInst )
307 return pInst->mpSalYieldMutex->tryToAcquire();
308 else
309 return FALSE;
312 void ImplSalYieldMutexRelease()
314 AquaSalInstance* pInst = GetSalData()->mpFirstInstance;
315 if ( pInst )
316 pInst->mpSalYieldMutex->release();
319 SalInstance* CreateSalInstance()
321 // this is the case for not using SVMain
322 // not so good
323 if( bNoSVMain )
324 initNSApp();
326 SalData* pSalData = GetSalData();
327 SAL_WARN_IF( pSalData->mpFirstInstance != nullptr, "vcl", "more than one instance created" );
328 AquaSalInstance* pInst = new AquaSalInstance;
330 // init instance (only one instance in this version !!!)
331 pSalData->mpFirstInstance = pInst;
332 // this one is for outside AquaSalInstance::Yield
333 SalData::ensureThreadAutoreleasePool();
334 // no focus rects on NWF
335 ImplGetSVData()->maNWFData.mbNoFocusRects = true;
336 ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true;
337 ImplGetSVData()->maNWFData.mbCenteredTabs = true;
338 ImplGetSVData()->maNWFData.mbProgressNeedsErase = true;
339 ImplGetSVData()->maNWFData.mbCheckBoxNeedsErase = true;
340 ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10;
342 return pInst;
345 void DestroySalInstance( SalInstance* pInst )
347 delete pInst;
350 AquaSalInstance::AquaSalInstance()
351 : maUserEventListMutex()
352 , maWaitingYieldCond()
354 mpSalYieldMutex = new SalYieldMutex;
355 mpSalYieldMutex->acquire();
356 ::comphelper::SolarMutex::setSolarMutex( mpSalYieldMutex );
357 maMainThread = osl::Thread::getCurrentIdentifier();
358 mbWaitingYield = false;
359 mnActivePrintJobs = 0;
362 AquaSalInstance::~AquaSalInstance()
364 ::comphelper::SolarMutex::setSolarMutex( nullptr );
365 mpSalYieldMutex->release();
366 delete mpSalYieldMutex;
369 void AquaSalInstance::wakeupYield()
371 // wakeup :Yield
372 if( mbWaitingYield )
374 SalData::ensureThreadAutoreleasePool();
375 SAL_WNODEPRECATED_DECLARATIONS_PUSH
376 // 'NSApplicationDefined' is deprecated: first deprecated in macOS 10.12
377 NSEvent* pEvent = [NSEvent otherEventWithType: NSApplicationDefined
378 location: NSZeroPoint
379 modifierFlags: 0
380 timestamp: 0
381 windowNumber: 0
382 context: nil
383 subtype: AquaSalInstance::YieldWakeupEvent
384 data1: 0
385 data2: 0 ];
386 SAL_WNODEPRECATED_DECLARATIONS_POP
387 if( pEvent )
388 [NSApp postEvent: pEvent atStart: NO];
392 void AquaSalInstance::PostUserEvent( AquaSalFrame* pFrame, SalEvent nType, void* pData )
395 osl::MutexGuard g( maUserEventListMutex );
396 maUserEvents.push_back( SalUserEvent( pFrame, pData, nType ) );
398 // notify main loop that an event has arrived
399 wakeupYield();
402 comphelper::SolarMutex* AquaSalInstance::GetYieldMutex()
404 return mpSalYieldMutex;
407 sal_uLong AquaSalInstance::ReleaseYieldMutex()
409 SalYieldMutex* pYieldMutex = mpSalYieldMutex;
410 if ( pYieldMutex->GetThreadId() ==
411 osl::Thread::getCurrentIdentifier() )
413 sal_uLong nCount = pYieldMutex->GetAcquireCount();
414 sal_uLong n = nCount;
415 while ( n )
417 pYieldMutex->release();
418 n--;
421 return nCount;
423 else
424 return 0;
427 void AquaSalInstance::AcquireYieldMutex( sal_uLong nCount )
429 SalYieldMutex* pYieldMutex = mpSalYieldMutex;
430 while ( nCount )
432 pYieldMutex->acquire();
433 nCount--;
437 bool AquaSalInstance::CheckYieldMutex()
439 bool bRet = true;
441 SalYieldMutex* pYieldMutex = mpSalYieldMutex;
442 if ( pYieldMutex->GetThreadId() != osl::Thread::getCurrentIdentifier())
444 bRet = false;
447 return bRet;
450 bool AquaSalInstance::isNSAppThread() const
452 return osl::Thread::getCurrentIdentifier() == maMainThread;
455 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
457 int nSubtype = [pEvent subtype];
458 switch( nSubtype )
460 case AppStartTimerEvent:
461 AquaSalTimer::handleStartTimerEvent( pEvent );
462 break;
463 case AppEndLoopEvent:
464 [NSApp stop: NSApp];
465 break;
466 case AppExecuteSVMain:
468 int nResult = ImplSVMain();
469 if( gpnInit )
470 *gpnInit = nResult;
471 [NSApp stop: NSApp];
472 bLeftMain = true;
473 if( pDockMenu )
475 [pDockMenu release];
476 pDockMenu = nil;
479 break;
480 #if !HAVE_FEATURE_MACOSX_SANDBOX
481 case AppleRemoteControlEvent: // Defined in <apple_remote/RemoteMainController.h>
483 MediaCommand nCommand;
484 SalData* pSalData = GetSalData();
485 bool bIsFullScreenMode = false;
487 std::list<AquaSalFrame*>::iterator it = pSalData->maFrames.begin();
488 while( it != pSalData->maFrames.end() )
490 if ( (*it) && (*it)->mbFullScreen )
491 bIsFullScreenMode = true;
492 ++it;
495 switch ([pEvent data1])
497 case kRemoteButtonPlay:
498 nCommand = bIsFullScreenMode ? MediaCommand::PlayPause : MediaCommand::Play;
499 break;
501 // kept for experimentation purpose (scheduled for future implementation)
502 // case kRemoteButtonMenu: nCommand = MediaCommand::Menu; break;
504 case kRemoteButtonPlus: nCommand = MediaCommand::VolumeUp; break;
506 case kRemoteButtonMinus: nCommand = MediaCommand::VolumeDown; break;
508 case kRemoteButtonRight: nCommand = MediaCommand::NextTrack; break;
510 case kRemoteButtonRight_Hold: nCommand = MediaCommand::NextTrackHold; break;
512 case kRemoteButtonLeft: nCommand = MediaCommand::PreviousTrack; break;
514 case kRemoteButtonLeft_Hold: nCommand = MediaCommand::Rewind; break;
516 case kRemoteButtonPlay_Hold: nCommand = MediaCommand::PlayHold; break;
518 case kRemoteButtonMenu_Hold: nCommand = MediaCommand::Stop; break;
520 // FIXME : not detected
521 case kRemoteButtonPlus_Hold:
522 case kRemoteButtonMinus_Hold:
523 break;
525 default:
526 break;
528 AquaSalFrame* pFrame = pSalData->maFrames.front();
529 vcl::Window* pWindow = pFrame ? pFrame->GetWindow() : nullptr;
531 if( pWindow )
533 const Point aPoint;
534 CommandMediaData aMediaData(nCommand);
535 CommandEvent aCEvt( aPoint, CommandEventId::Media, FALSE, &aMediaData );
536 NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt );
538 if ( !ImplCallPreNotify( aNCmdEvt ) )
539 pWindow->Command( aCEvt );
543 break;
544 #endif
546 case YieldWakeupEvent:
547 // do nothing, fall out of Yield
548 break;
550 default:
551 OSL_FAIL( "unhandled NSApplicationDefined event" );
552 break;
556 class ReleasePoolHolder
558 NSAutoreleasePool* mpPool;
559 public:
560 ReleasePoolHolder() : mpPool( [[NSAutoreleasePool alloc] init] ) {}
561 ~ReleasePoolHolder() { [mpPool release]; }
564 SalYieldResult AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents, sal_uLong const nReleased)
566 (void) nReleased;
567 assert(nReleased == 0); // not implemented
569 // ensure that the per thread autorelease pool is top level and
570 // will therefore not be destroyed by cocoa implicitly
571 SalData::ensureThreadAutoreleasePool();
573 // NSAutoreleasePool documentation suggests we should have
574 // an own pool for each yield level
575 ReleasePoolHolder aReleasePool;
577 // Release all locks so that we don't deadlock when we pull pending
578 // events from the event queue
579 bool bDispatchUser = true;
580 while( bDispatchUser )
582 sal_uLong nCount = ReleaseYieldMutex();
584 // get one user event
585 SalUserEvent aEvent( nullptr, nullptr, SalEvent::NONE );
587 osl::MutexGuard g( maUserEventListMutex );
588 if( ! maUserEvents.empty() )
590 aEvent = maUserEvents.front();
591 maUserEvents.pop_front();
593 else
594 bDispatchUser = false;
596 AcquireYieldMutex( nCount );
598 // dispatch it
599 if( aEvent.mpFrame && AquaSalFrame::isAlive( aEvent.mpFrame ) )
601 aEvent.mpFrame->CallCallback( aEvent.mnType, aEvent.mpData );
602 maWaitingYieldCond.set();
603 // return if only one event is asked for
604 if( ! bHandleAllCurrentEvents )
605 return SalYieldResult::EVENT;
609 // handle cocoa event queue
610 // cocoa events may be only handled in the thread the NSApp was created
611 bool bHadEvent = false;
612 if( isNSAppThread() && mnActivePrintJobs == 0 )
614 // we need to be woken up by a cocoa-event
615 // if a user event should be posted by the event handling below
616 bool bOldWaitingYield = mbWaitingYield;
617 mbWaitingYield = bWait;
619 // handle available events
620 NSEvent* pEvent = nil;
623 sal_uLong nCount = ReleaseYieldMutex();
625 SAL_WNODEPRECATED_DECLARATIONS_PUSH
626 // 'NSAnyEventMask' is deprecated: first deprecated in macOS 10.12
627 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: nil
628 SAL_WNODEPRECATED_DECLARATIONS_POP
629 inMode: NSDefaultRunLoopMode dequeue: YES];
630 if( pEvent )
632 [NSApp sendEvent: pEvent];
633 bHadEvent = true;
635 [NSApp updateWindows];
637 AcquireYieldMutex( nCount );
638 } while( bHandleAllCurrentEvents && pEvent );
640 // if we had no event yet, wait for one if requested
641 if( bWait && ! bHadEvent )
643 sal_uLong nCount = ReleaseYieldMutex();
645 NSDate* pDt = AquaSalTimer::pRunningTimer ? [AquaSalTimer::pRunningTimer fireDate] : [NSDate distantFuture];
646 SAL_WNODEPRECATED_DECLARATIONS_PUSH
647 // 'NSAnyEventMask' is deprecated: first deprecated in macOS 10.12
648 pEvent = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: pDt
649 SAL_WNODEPRECATED_DECLARATIONS_POP
650 inMode: NSDefaultRunLoopMode dequeue: YES];
651 if( pEvent )
652 [NSApp sendEvent: pEvent];
653 [NSApp updateWindows];
655 AcquireYieldMutex( nCount );
657 // #i86581#
658 // FIXME: sometimes the NSTimer will never fire. Firing it by hand then
659 // fixes the problem even seems to set the correct next firing date
660 // Why oh why?
661 if( ! pEvent && AquaSalTimer::pRunningTimer )
663 // this cause crashes on MacOSX 10.4
664 // [AquaSalTimer::pRunningTimer fire];
665 if (ImplGetSVData()->mpSalTimer != nullptr)
667 bool idle = true; // TODO
668 ImplGetSVData()->mpSalTimer->CallCallback( idle );
673 mbWaitingYield = bOldWaitingYield;
675 // collect update rectangles
676 const std::list< AquaSalFrame* > rFrames( GetSalData()->maFrames );
677 for( std::list< AquaSalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
679 if( (*it)->mbShown && ! (*it)->maInvalidRect.IsEmpty() )
681 (*it)->Flush( (*it)->maInvalidRect );
682 (*it)->maInvalidRect.SetEmpty();
685 maWaitingYieldCond.set();
687 else if( bWait )
689 // #i103162#
690 // wait until any thread (most likely the main thread)
691 // has dispatched an event, cop out at 200 ms
692 maWaitingYieldCond.reset();
693 TimeValue aVal = { 0, 200000000 };
694 sal_uLong nCount = ReleaseYieldMutex();
695 maWaitingYieldCond.wait( &aVal );
696 AcquireYieldMutex( nCount );
699 // we get some apple events way too early
700 // before the application is ready to handle them,
701 // so their corresponding application events need to be delayed
702 // now is a good time to handle at least one of them
703 if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
705 // make sure that only one application event is active at a time
706 static bool bInAppEvent = false;
707 if( !bInAppEvent )
709 bInAppEvent = true;
710 // get the next delayed application event
711 const ApplicationEvent* pAppEvent = aAppEventList.front();
712 aAppEventList.pop_front();
713 // handle one application event (no recursion)
714 const ImplSVData* pSVData = ImplGetSVData();
715 pSVData->mpApp->AppEvent( *pAppEvent );
716 delete pAppEvent;
717 // allow the next delayed application event
718 bInAppEvent = false;
722 return bHadEvent ? SalYieldResult::EVENT : SalYieldResult::TIMEOUT;
725 bool AquaSalInstance::AnyInput( VclInputFlags nType )
727 if( nType & VclInputFlags::APPEVENT )
729 if( ! aAppEventList.empty() )
730 return true;
731 if( nType == VclInputFlags::APPEVENT )
732 return false;
735 if( nType & VclInputFlags::TIMER )
737 if( AquaSalTimer::pRunningTimer )
739 NSDate* pDt = [AquaSalTimer::pRunningTimer fireDate];
740 if( pDt && [pDt timeIntervalSinceNow] < 0 )
742 return true;
747 if (![NSThread isMainThread])
748 return false;
750 unsigned/*NSUInteger*/ nEventMask = 0;
751 SAL_WNODEPRECATED_DECLARATIONS_PUSH
752 // 'NSFlagsChangedMask' is deprecated: first deprecated in macOS 10.12
753 // 'NSKeyDownMask' is deprecated: first deprecated in macOS 10.12
754 // 'NSKeyUpMask' is deprecated: first deprecated in macOS 10.12
755 // 'NSLeftMouseDownMask' is deprecated: first deprecated in macOS 10.12
756 // 'NSLeftMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
757 // 'NSLeftMouseUpMask' is deprecated: first deprecated in macOS 10.12
758 // 'NSMouseEnteredMask' is deprecated: first deprecated in macOS 10.12
759 // 'NSMouseExitedMask' is deprecated: first deprecated in macOS 10.12
760 // 'NSOtherMouseDownMask' is deprecated: first deprecated in macOS 10.12
761 // 'NSOtherMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
762 // 'NSOtherMouseUpMask' is deprecated: first deprecated in macOS 10.12
763 // 'NSRightMouseDownMask' is deprecated: first deprecated in macOS 10.12
764 // 'NSRightMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
765 // 'NSRightMouseUpMask' is deprecated: first deprecated in macOS 10.12
766 // 'NSScrollWheelMask' is deprecated: first deprecated in macOS 10.12
767 // 'NSTabletPoint' is deprecated: first deprecated in macOS 10.12
768 if( nType & VclInputFlags::MOUSE)
769 nEventMask |=
770 NSLeftMouseDownMask | NSRightMouseDownMask | NSOtherMouseDownMask |
771 NSLeftMouseUpMask | NSRightMouseUpMask | NSOtherMouseUpMask |
772 NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
773 NSScrollWheelMask |
774 // NSMouseMovedMask |
775 NSMouseEnteredMask | NSMouseExitedMask;
776 if( nType & VclInputFlags::KEYBOARD)
777 nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
778 if( nType & VclInputFlags::OTHER)
779 nEventMask |= NSTabletPoint;
780 SAL_WNODEPRECATED_DECLARATIONS_POP
781 // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
782 if( !bool(nType) )
783 return false;
785 NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
786 inMode: NSDefaultRunLoopMode dequeue: NO];
787 return (pEvent != nullptr);
790 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, SalFrameStyleFlags /*nSalFrameStyle*/ )
792 return nullptr;
795 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
797 SalData::ensureThreadAutoreleasePool();
799 SalFrame* pFrame = new AquaSalFrame( pParent, nSalFrameStyle );
800 return pFrame;
803 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
805 delete pFrame;
808 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool /* bShow */ )
810 AquaSalObject *pObject = nullptr;
812 if ( pParent )
813 pObject = new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData );
815 return pObject;
818 void AquaSalInstance::DestroyObject( SalObject* pObject )
820 delete ( pObject );
823 SalPrinter* AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
825 return new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) );
828 void AquaSalInstance::DestroyPrinter( SalPrinter* pPrinter )
830 delete pPrinter;
833 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
835 NSArray* pNames = [NSPrinter printerNames];
836 NSArray* pTypes = [NSPrinter printerTypes];
837 unsigned int nNameCount = pNames ? [pNames count] : 0;
838 unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
839 SAL_WARN_IF( nTypeCount != nNameCount, "vcl", "type count not equal to printer count" );
840 for( unsigned int i = 0; i < nNameCount; i++ )
842 NSString* pName = [pNames objectAtIndex: i];
843 NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
844 if( pName )
846 SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
847 pInfo->maPrinterName = GetOUString( pName );
848 if( pType )
849 pInfo->maDriver = GetOUString( pType );
850 pInfo->mnStatus = PrintQueueFlags::NONE;
851 pInfo->mnJobs = 0;
852 pInfo->mpSysData = nullptr;
854 pList->Add( pInfo );
859 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
863 void AquaSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
865 delete pInfo;
868 OUString AquaSalInstance::GetDefaultPrinter()
870 // #i113170# may not be the main thread if called from UNO API
871 SalData::ensureThreadAutoreleasePool();
873 if( maDefaultPrinter.isEmpty() )
875 NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
876 SAL_WARN_IF( !pPI, "vcl", "no print info" );
877 if( pPI )
879 NSPrinter* pPr = [pPI printer];
880 SAL_WARN_IF( !pPr, "vcl", "no printer in default info" );
881 if( pPr )
883 NSString* pDefName = [pPr name];
884 SAL_WARN_IF( !pDefName, "vcl", "printer has no name" );
885 maDefaultPrinter = GetOUString( pDefName );
889 return maDefaultPrinter;
892 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
893 ImplJobSetup* pSetupData )
895 // #i113170# may not be the main thread if called from UNO API
896 SalData::ensureThreadAutoreleasePool();
898 SalInfoPrinter* pNewInfoPrinter = nullptr;
899 if( pQueueInfo )
901 pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
902 if( pSetupData )
903 pNewInfoPrinter->SetPrinterData( pSetupData );
906 return pNewInfoPrinter;
909 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
911 // #i113170# may not be the main thread if called from UNO API
912 SalData::ensureThreadAutoreleasePool();
914 delete pPrinter;
917 OUString AquaSalInstance::GetConnectionIdentifier()
919 return OUString();
922 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
923 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
924 static OUString translateToExternalUrl(const OUString& internalUrl)
926 uno::Reference< uno::XComponentContext > context(
927 comphelper::getProcessComponentContext());
928 return uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
931 // #i104525# many versions of OSX have problems with some URLs:
932 // when an app requests OSX to add one of these URLs to the "Recent Items" list
933 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
934 static bool isDangerousUrl( const OUString& rUrl )
936 // use a heuristic that detects all known cases since there is no official comment
937 // on the exact impact and root cause of the OSX bug
938 const int nLen = rUrl.getLength();
939 const sal_Unicode* p = rUrl.getStr();
940 for( int i = 0; i < nLen-3; ++i, ++p ) {
941 if( p[0] != '%' )
942 continue;
943 // escaped percent?
944 if( (p[1] == '2') && (p[2] == '5') )
945 return true;
946 // escapes are considered to be UTF-8 encoded
947 // => check for invalid UTF-8 leading byte
948 if( (p[1] != 'f') && (p[1] != 'F') )
949 continue;
950 int cLowNibble = p[2];
951 if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
952 return false;
953 if( cLowNibble >= 'a' )
954 cLowNibble -= 'a' - 'A';
955 if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
956 return true;
959 return false;
962 void AquaSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& /*rDocumentService*/)
964 // Convert file URL for external use (see above)
965 OUString externalUrl = translateToExternalUrl(rFileUrl);
966 if( externalUrl.isEmpty() )
967 externalUrl = rFileUrl;
969 if( !externalUrl.isEmpty() && !isDangerousUrl( externalUrl ) )
971 NSString* pString = CreateNSString( externalUrl );
972 NSURL* pURL = [NSURL URLWithString: pString];
974 if( pURL )
976 NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
977 [pCtrl noteNewRecentDocumentURL: pURL];
979 if( pString )
980 [pString release];
984 SalTimer* AquaSalInstance::CreateSalTimer()
986 return new AquaSalTimer();
989 SalSystem* AquaSalInstance::CreateSalSystem()
991 return new AquaSalSystem();
994 SalBitmap* AquaSalInstance::CreateSalBitmap()
996 return new QuartzSalBitmap();
999 SalSession* AquaSalInstance::CreateSalSession()
1001 return nullptr;
1004 OUString AquaSalInstance::getOSVersion()
1006 NSString * versionString = nullptr;
1007 NSString * sysVersionDictionaryPath = @"/System/Library/CoreServices/SystemVersion.plist";
1008 NSDictionary * sysVersionDict = [ NSDictionary dictionaryWithContentsOfFile: sysVersionDictionaryPath ];
1009 if ( sysVersionDict )
1010 versionString = [ sysVersionDict valueForKey: @"ProductVersion" ];
1012 OUString aVersion = "Mac OS X ";
1013 if ( versionString )
1014 aVersion += OUString::fromUtf8( [ versionString UTF8String ] );
1015 else
1016 aVersion += "(unknown)";
1018 return aVersion;
1021 // YieldMutexReleaser
1022 YieldMutexReleaser::YieldMutexReleaser() : mnCount( 0 )
1024 SalData* pSalData = GetSalData();
1025 if( ! pSalData->mpFirstInstance->isNSAppThread() )
1027 SalData::ensureThreadAutoreleasePool();
1028 mnCount = pSalData->mpFirstInstance->ReleaseYieldMutex();
1032 YieldMutexReleaser::~YieldMutexReleaser()
1034 if( mnCount != 0 )
1035 GetSalData()->mpFirstInstance->AcquireYieldMutex( mnCount );
1038 CGImageRef CreateCGImage( const Image& rImage )
1040 BitmapEx aBmpEx( rImage.GetBitmapEx() );
1041 Bitmap aBmp( aBmpEx.GetBitmap() );
1043 if( ! aBmp || ! aBmp.ImplGetImpBitmap() )
1044 return nullptr;
1046 // simple case, no transparency
1047 QuartzSalBitmap* pSalBmp = static_cast<QuartzSalBitmap*>(aBmp.ImplGetImpBitmap()->ImplGetSalBitmap());
1049 if( ! pSalBmp )
1050 return nullptr;
1052 CGImageRef xImage = nullptr;
1053 if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
1054 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1055 else if( aBmpEx.IsAlpha() )
1057 AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
1058 Bitmap aMask( aAlphaMask.GetBitmap() );
1059 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap());
1060 if( pMaskBmp )
1061 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1062 else
1063 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1065 else if( aBmpEx.GetTransparentType() == TransparentType::Bitmap )
1067 Bitmap aMask( aBmpEx.GetMask() );
1068 QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetImpBitmap()->ImplGetSalBitmap());
1069 if( pMaskBmp )
1070 xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1071 else
1072 xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
1074 else if( aBmpEx.GetTransparentType() == TransparentType::Color )
1076 Color aTransColor( aBmpEx.GetTransparentColor() );
1077 SalColor nTransColor = MAKE_SALCOLOR( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
1078 xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
1081 return xImage;
1084 NSImage* CreateNSImage( const Image& rImage )
1086 CGImageRef xImage = CreateCGImage( rImage );
1088 if( ! xImage )
1089 return nil;
1091 Size aSize( rImage.GetSizePixel() );
1092 NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
1093 if( pImage )
1095 [pImage lockFocusFlipped:YES];
1096 NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
1097 CGContextRef rCGContext = static_cast<CGContextRef>([pContext graphicsPort]);
1099 const CGRect aDstRect = { {0, 0}, { static_cast<CGFloat>(aSize.Width()), static_cast<CGFloat>(aSize.Height()) } };
1100 CGContextDrawImage( rCGContext, aDstRect, xImage );
1102 [pImage unlockFocus];
1105 CGImageRelease( xImage );
1107 return pImage;
1111 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */